diff mbox series

[08/10] netconfig: Re-add FILS handling

Message ID 20220829173601.1963953-8-andrew.zaborowski@intel.com (mailing list archive)
State Accepted, archived
Headers show
Series [01/10] doc: Update Netconfig Agent API doc | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
prestwoj/iwd-ci-gitlint success GitLint

Commit Message

Andrew Zaborowski Aug. 29, 2022, 5:35 p.m. UTC
Load the settings from FILS IE data into our l_netconfig instance when
appropriate.
---
 src/netconfig-commit.c |  51 ++++++++++++++++++
 src/netconfig.c        | 115 ++++++++++++++++++++++++++++++++++++++++-
 src/netconfig.h        |   1 +
 3 files changed, 166 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/src/netconfig-commit.c b/src/netconfig-commit.c
index 95b2a9d9..d675d396 100644
--- a/src/netconfig-commit.c
+++ b/src/netconfig-commit.c
@@ -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 =
diff --git a/src/netconfig.c b/src/netconfig.c
index 91830416..feb7f87a 100644
--- a/src/netconfig.c
+++ b/src/netconfig.c
@@ -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 */
 	}
diff --git a/src/netconfig.h b/src/netconfig.h
index 50085aa7..0ecd2583 100644
--- a/src/netconfig.h
+++ b/src/netconfig.h
@@ -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);