@@ -103,6 +103,10 @@ void netconfig_commit(struct netconfig *netconfig, uint8_t family,
if (family == AF_INET &&
!netconfig->static_config[INDEX_FOR_AF(family)])
netconfig_dhcp_gateway_to_arp(netconfig);
+
+ if (!netconfig->connected[INDEX_FOR_AF(family)] &&
+ netconfig_use_fils_addr(netconfig, family))
+ netconfig_commit_fils_macs(netconfig, family);
}
}
@@ -187,6 +191,53 @@ void netconfig_dhcp_gateway_to_arp(struct netconfig *netconfig)
l_debug("l_rtnl_neighbor_set_hwaddr failed");
}
+void netconfig_commit_fils_macs(struct netconfig *netconfig, uint8_t family)
+{
+ const struct ie_fils_ip_addr_response_info *fils =
+ netconfig->fils_override;
+ const void *addr;
+ const void *hwaddr;
+ size_t addr_len = (family == AF_INET ? 4 : 16);
+ uint32_t ifindex = netdev_get_ifindex(netconfig->netdev);
+
+ if (!fils)
+ return;
+
+ /*
+ * Attempt to use the gateway/DNS MAC addressed received from the AP
+ * by writing the mapping directly into the netdev's ARP table so as
+ * to save one data frame roundtrip before first IP connections are
+ * established. This is very low-priority but print error messages
+ * just because they may indicate bigger problems.
+ */
+
+ addr = (family == AF_INET ? (void *) &fils->ipv4_gateway :
+ (void *) &fils->ipv6_gateway);
+ hwaddr = (family == AF_INET ?
+ &fils->ipv4_gateway_mac : &fils->ipv6_gateway_mac);
+
+ if (!l_memeqzero(addr, addr_len) && !l_memeqzero(hwaddr, ETH_ALEN) &&
+ unlikely(!l_rtnl_neighbor_set_hwaddr(rtnl, ifindex,
+ family, addr, hwaddr, ETH_ALEN,
+ netconfig_set_neighbor_entry_cb,
+ NULL, NULL)))
+ l_debug("l_rtnl_neighbor_set_hwaddr(%s, gateway) failed",
+ family == AF_INET ? "AF_INET" : "AF_INET6");
+
+ addr = (family == AF_INET ? (void *) &fils->ipv4_dns :
+ (void *) &fils->ipv6_dns);
+ hwaddr = (family == AF_INET ?
+ &fils->ipv4_dns_mac : &fils->ipv6_dns_mac);
+
+ if (!l_memeqzero(addr, addr_len) && !l_memeqzero(hwaddr, ETH_ALEN) &&
+ unlikely(!l_rtnl_neighbor_set_hwaddr(rtnl, ifindex,
+ family, addr, hwaddr, ETH_ALEN,
+ netconfig_set_neighbor_entry_cb,
+ NULL, NULL)))
+ l_debug("l_rtnl_neighbor_set_hwaddr(%s, DNS) failed",
+ family == AF_INET ? "AF_INET" : "AF_INET6");
+}
+
static void netconfig_dns_list_update(struct netconfig *netconfig)
{
_auto_(l_strv_free) char **dns_list =
@@ -90,6 +90,26 @@ static void netconfig_free(void *data)
l_free(netconfig);
}
+static bool netconfig_addr_to_str(uint8_t af, const void *v4_addr,
+ const void *v6_addr, char *out_str,
+ bool *out_is_zero)
+{
+ const void *addr = (af == AF_INET ? v4_addr : v6_addr);
+ uint8_t bytes = (af == AF_INET ? 4 : 16);
+
+ if (l_memeqzero(addr, bytes)) {
+ *out_is_zero = true;
+ return true;
+ }
+
+ *out_is_zero = false;
+
+ if (L_WARN_ON(!inet_ntop(af, addr, out_str, INET6_ADDRSTRLEN)))
+ return false;
+
+ return true;
+}
+
bool netconfig_use_fils_addr(struct netconfig *netconfig, int af)
{
if (!netconfig->enabled[INDEX_FOR_AF(af)])
@@ -408,12 +428,96 @@ mdns:
return false;
}
+static bool netconfig_load_fils_settings(struct netconfig *netconfig,
+ uint8_t af)
+{
+ struct ie_fils_ip_addr_response_info *fils = netconfig->fils_override;
+ char addr_str[INET6_ADDRSTRLEN];
+ char gw_addr_str[INET6_ADDRSTRLEN];
+ char dns_addr_str[INET6_ADDRSTRLEN];
+ _auto_(l_rtnl_address_free) struct l_rtnl_address *rtnl_addr = NULL;
+ bool is_zero = false;
+ uint8_t prefix_len;
+
+ if (!netconfig_addr_to_str(af, &fils->ipv4_addr, &fils->ipv6_addr,
+ addr_str, &is_zero) || is_zero)
+ return is_zero;
+
+ prefix_len = (af == AF_INET ? fils->ipv4_prefix_len :
+ fils->ipv6_prefix_len);
+
+ if (L_WARN_ON(!(rtnl_addr = l_rtnl_address_new(addr_str, prefix_len))))
+ return false;
+
+ if (L_WARN_ON(!l_netconfig_set_static_addr(netconfig->nc, af,
+ rtnl_addr)))
+ return false;
+
+ if (af == AF_INET &&
+ L_WARN_ON(!l_netconfig_set_acd_enabled(netconfig->nc,
+ false)))
+ return false;
+
+ /*
+ * Done with local address, move on to gateway and DNS.
+ *
+ * Since load_settings is called early, generally before the actual
+ * connection setup starts, and load_fils_settings is called after
+ * 802.11 Authentication & Association, we need to check if either
+ * the gateway or DNS settings were overridden in load_settings so
+ * as not to overwrite the user-provided values. Values received
+ * with FILS are expected to have the same weight as those from
+ * DHCP/SLAAC.
+ *
+ * TODO: If netconfig->fils_override->ipv{4,6}_lifetime is set,
+ * start a timeout to renew the address using FILS IP Address
+ * Assignment or perhaps just start the DHCP client after that
+ * time.
+ *
+ * TODO: validate gateway and/or DNS on local subnet, link-local,
+ * etc.?
+ */
+
+ if (!netconfig_addr_to_str(af, &fils->ipv4_gateway, &fils->ipv6_gateway,
+ gw_addr_str, &is_zero))
+ return false;
+
+ if (!netconfig->gateway_overridden[INDEX_FOR_AF(af)] && !is_zero &&
+ L_WARN_ON(!l_netconfig_set_gateway_override(
+ netconfig->nc,
+ af,
+ gw_addr_str)))
+ return false;
+
+ if (!netconfig_addr_to_str(af, &fils->ipv4_dns, &fils->ipv6_dns,
+ dns_addr_str, &is_zero))
+ return is_zero;
+
+ if (!netconfig->dns_overridden[INDEX_FOR_AF(af)] && !is_zero) {
+ char *dns_list[2] = { dns_addr_str, NULL };
+
+ if (L_WARN_ON(!l_netconfig_set_dns_override(netconfig->nc,
+ af, dns_list)))
+ return false;
+ }
+
+ return true;
+}
+
bool netconfig_configure(struct netconfig *netconfig,
netconfig_notify_func_t notify, void *user_data)
{
netconfig->notify = notify;
netconfig->user_data = user_data;
+ if (netconfig_use_fils_addr(netconfig, AF_INET) &&
+ !netconfig_load_fils_settings(netconfig, AF_INET))
+ return false;
+
+ if (netconfig_use_fils_addr(netconfig, AF_INET6) &&
+ !netconfig_load_fils_settings(netconfig, AF_INET6))
+ return false;
+
if (unlikely(!l_netconfig_start(netconfig->nc)))
return false;
@@ -429,9 +533,18 @@ bool netconfig_reconfigure(struct netconfig *netconfig, bool set_arp_gw)
* lost or delayed. Try to force the gateway into the ARP cache
* to alleviate this
*/
- if (set_arp_gw)
+ if (set_arp_gw) {
netconfig_dhcp_gateway_to_arp(netconfig);
+ if (netconfig->connected[INDEX_FOR_AF(AF_INET)] &&
+ netconfig_use_fils_addr(netconfig, AF_INET))
+ netconfig_commit_fils_macs(netconfig, AF_INET);
+
+ if (netconfig->connected[INDEX_FOR_AF(AF_INET6)] &&
+ netconfig_use_fils_addr(netconfig, AF_INET6))
+ netconfig_commit_fils_macs(netconfig, AF_INET6);
+ }
+
if (!netconfig->static_config[INDEX_FOR_AF(AF_INET)]) {
/* TODO l_dhcp_client sending a DHCP inform request */
}
@@ -83,3 +83,4 @@ void netconfig_commit(struct netconfig *netconfig, uint8_t family,
enum l_netconfig_event event);
void netconfig_dhcp_gateway_to_arp(struct netconfig *netconfig);
+void netconfig_commit_fils_macs(struct netconfig *netconfig, uint8_t family);