iwd.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/9] handshake: Add HANDSHAKE_EVENT_P2P_IP_REQUEST
@ 2021-08-23 14:14 Andrew Zaborowski
  2021-08-23 14:14 ` [PATCH 2/9] ap: Implement P2P GO-side 4-way handshake IP Allocation Andrew Zaborowski
                   ` (7 more replies)
  0 siblings, 8 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2021-08-23 14:14 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 2526 bytes --]

Add a handshake event for use by the AP side for mechanisms that
allocate client IPs during the handshake: P2P address allocation and
FILS address assignment.  This is emitted only when EAPOL or the
auth_proto is actually about to send the network configuration data to
the client so that ap.c can skip allocating a DHCP leases altogether if
the client doesn't send the required KDE or IE.
---
 src/eapol.c     | 17 +++++++++++++++++
 src/handshake.h |  1 +
 src/station.c   |  1 +
 3 files changed, 19 insertions(+)

diff --git a/src/eapol.c b/src/eapol.c
index f78bef2f..5a3ef7a0 100644
--- a/src/eapol.c
+++ b/src/eapol.c
@@ -1350,6 +1350,23 @@ static void eapol_send_ptk_3_of_4(struct eapol_sm *sm)
 		key_data_len += gtk_kde[1] + 2;
 	}
 
+	if (sm->handshake->support_ip_allocation &&
+			!sm->handshake->client_ip_addr) {
+		handshake_event(sm->handshake, HANDSHAKE_EVENT_P2P_IP_REQUEST);
+
+		/*
+		 * If .support_ip_allocation was set, the
+		 * HANDSHAKE_EVENT_P2P_IP_REQUEST handler is expected to set
+		 * .client_ip_addr if not already set.  Check if the handler
+		 * was successful in allocating an address, if it wasn't we'll
+		 * just skip the IP Address Allocation KDE.  In either case if
+		 * we need to resend message 3/4 the event callback won't be
+		 * triggered again because the condition above will be false.
+		 */
+		if (!sm->handshake->client_ip_addr)
+			sm->handshake->support_ip_allocation = false;
+	}
+
 	if (sm->handshake->support_ip_allocation) {
 		/* Wi-Fi P2P Technical Specification v1.7 Table 59 */
 		key_data_buf[key_data_len++] = IE_TYPE_VENDOR_SPECIFIC;
diff --git a/src/handshake.h b/src/handshake.h
index a4c54b5a..31dce117 100644
--- a/src/handshake.h
+++ b/src/handshake.h
@@ -57,6 +57,7 @@ enum handshake_event {
 	HANDSHAKE_EVENT_REKEY_FAILED,
 	HANDSHAKE_EVENT_EAP_NOTIFY,
 	HANDSHAKE_EVENT_TRANSITION_DISABLE,
+	HANDSHAKE_EVENT_P2P_IP_REQUEST,
 };
 
 typedef void (*handshake_event_func_t)(struct handshake_state *hs,
diff --git a/src/station.c b/src/station.c
index d3482e2c..d61e775a 100644
--- a/src/station.c
+++ b/src/station.c
@@ -753,6 +753,7 @@ static void station_handshake_event(struct handshake_state *hs,
 	case HANDSHAKE_EVENT_COMPLETE:
 	case HANDSHAKE_EVENT_SETTING_KEYS_FAILED:
 	case HANDSHAKE_EVENT_EAP_NOTIFY:
+	case HANDSHAKE_EVENT_P2P_IP_REQUEST:
 		/*
 		 * currently we don't care about any other events. The
 		 * netdev_connect_cb will notify us when the connection is
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 2/9] ap: Implement P2P GO-side 4-way handshake IP Allocation
  2021-08-23 14:14 [PATCH 1/9] handshake: Add HANDSHAKE_EVENT_P2P_IP_REQUEST Andrew Zaborowski
@ 2021-08-23 14:14 ` Andrew Zaborowski
  2021-08-23 14:14 ` [PATCH 3/9] autotests: Test GO-side IP Allocation in testP2P Andrew Zaborowski
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2021-08-23 14:14 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 4407 bytes --]

Use the struct handshake_state::support_ip_allocation field already
supported in eapol.c authenticator side to enable the P2P IP Allocation
mechanism in ap.c.  Add the P2P_GROUP_CAP_IP_ALLOCATION bit in P2P group
capabilities to signal the feature is now supported.

There's no harm in enabling this feature in every AP (not just P2P Group
Owner) but the clients won't know whether we support it other than
through that P2P-specific group capability bit.
---
 src/ap.c  | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/p2p.c |  1 +
 2 files changed, 59 insertions(+)

diff --git a/src/ap.c b/src/ap.c
index a16200df..c4473c66 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -118,6 +118,7 @@ struct sta_state {
 	struct l_settings *wsc_settings;
 	uint8_t wsc_uuid_e[16];
 	bool wsc_v2;
+	struct l_dhcp_lease *ip_alloc_lease;
 };
 
 struct ap_wsc_pbc_probe_record {
@@ -177,6 +178,10 @@ static void ap_sta_free(void *data)
 	if (sta->gtk_query_cmd_id)
 		l_genl_family_cancel(ap->nl80211, sta->gtk_query_cmd_id);
 
+	if (sta->ip_alloc_lease && ap->netconfig_dhcp)
+		l_dhcp_server_lease_remove(ap->netconfig_dhcp,
+						sta->ip_alloc_lease);
+
 	ap_stop_handshake(sta);
 
 	l_free(sta);
@@ -839,6 +844,12 @@ static uint32_t ap_send_mgmt_frame(struct ap_state *ap,
 					callback, user_data, NULL, NULL);
 }
 
+#define IP4_FROM_STR(str)						\
+	(__extension__ ({						\
+		struct in_addr ia;					\
+		inet_pton(AF_INET, str, &ia) == 1 ? ia.s_addr : 0;	\
+	}))
+
 static void ap_start_handshake(struct sta_state *sta, bool use_eapol_start,
 				const uint8_t *gtk_rsc)
 {
@@ -863,6 +874,9 @@ static void ap_start_handshake(struct sta_state *sta, bool use_eapol_start,
 		handshake_state_set_gtk(sta->hs, sta->ap->gtk,
 					sta->ap->gtk_index, gtk_rsc);
 
+	if (ap->netconfig_dhcp)
+		sta->hs->support_ip_allocation = true;
+
 	sta->sm = eapol_sm_new(sta->hs);
 	if (!sta->sm) {
 		ap_stop_handshake(sta);
@@ -886,12 +900,28 @@ static void ap_handshake_event(struct handshake_state *hs,
 		enum handshake_event event, void *user_data, ...)
 {
 	struct sta_state *sta = user_data;
+	struct ap_state *ap = sta->ap;
 	va_list args;
 
 	va_start(args, user_data);
 
 	switch (event) {
 	case HANDSHAKE_EVENT_COMPLETE:
+		if (sta->ip_alloc_lease) {
+			/*
+			 * Move the lease from offered to active state if the
+			 * client has actually used it.  In any case drop our
+			 * reference to the lease, the server owns the lease
+			 * and if we want to keep our reference we'd need to
+			 * react to relevant server events.
+			 */
+			if (hs->support_ip_allocation)
+				l_dhcp_server_request(ap->netconfig_dhcp,
+							sta->ip_alloc_lease);
+
+			sta->ip_alloc_lease = NULL;
+		}
+
 		ap_new_rsna(sta);
 		break;
 	case HANDSHAKE_EVENT_FAILED:
@@ -900,6 +930,34 @@ static void ap_handshake_event(struct handshake_state *hs,
 	case HANDSHAKE_EVENT_SETTING_KEYS_FAILED:
 		sta->sm = NULL;
 		ap_remove_sta(sta);
+		break;
+	case HANDSHAKE_EVENT_P2P_IP_REQUEST:
+	{
+		L_AUTO_FREE_VAR(char *, lease_addr_str) = NULL;
+		L_AUTO_FREE_VAR(char *, lease_netmask_str) = NULL;
+		char own_addr_str[INET_ADDRSTRLEN];
+
+		if (!sta->ip_alloc_lease)
+			sta->ip_alloc_lease = l_dhcp_server_discover(
+							ap->netconfig_dhcp,
+							0, NULL, sta->addr);
+
+		if (!sta->ip_alloc_lease) {
+			l_error("l_dhcp_server_discover() failed, see "
+				"IWD_DHCP_DEBUG output");
+			break;
+		}
+
+		lease_addr_str = l_dhcp_lease_get_address(sta->ip_alloc_lease);
+		lease_netmask_str =
+			l_dhcp_lease_get_netmask(sta->ip_alloc_lease);
+		l_rtnl_address_get_address(ap->netconfig_addr4, own_addr_str);
+
+		sta->hs->client_ip_addr = IP4_FROM_STR(lease_addr_str);
+		sta->hs->subnet_mask = IP4_FROM_STR(lease_netmask_str);
+		sta->hs->go_ip_addr = IP4_FROM_STR(own_addr_str);
+		break;
+	}
 	default:
 		break;
 	}
diff --git a/src/p2p.c b/src/p2p.c
index 6790caef..8170bbe0 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -1276,6 +1276,7 @@ static void p2p_group_start(struct p2p_device *dev)
 
 	dev->capability.group_caps |= P2P_GROUP_CAP_GO;
 	dev->capability.group_caps |= P2P_GROUP_CAP_GROUP_FORMATION;
+	dev->capability.group_caps |= P2P_GROUP_CAP_IP_ALLOCATION;
 
 	dev->group = ap_start(dev->conn_netdev, config, &p2p_go_ops, NULL, dev);
 	l_settings_free(config);
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 3/9] autotests: Test GO-side IP Allocation in testP2P
  2021-08-23 14:14 [PATCH 1/9] handshake: Add HANDSHAKE_EVENT_P2P_IP_REQUEST Andrew Zaborowski
  2021-08-23 14:14 ` [PATCH 2/9] ap: Implement P2P GO-side 4-way handshake IP Allocation Andrew Zaborowski
@ 2021-08-23 14:14 ` Andrew Zaborowski
  2021-08-23 14:14 ` [PATCH 4/9] ap: Expire client's leases on disconnect Andrew Zaborowski
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2021-08-23 14:14 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 2439 bytes --]

Check if our wpa_supplicant P2P Client has received the allocated
Clieant IP/netmask/GO IP values we sent in the 4-Way Handshake.
---
 autotests/testP2P/connection_test.py | 9 ++++++++-
 autotests/util/wpas.py               | 1 +
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/autotests/testP2P/connection_test.py b/autotests/testP2P/connection_test.py
index 22871547..52da0edc 100644
--- a/autotests/testP2P/connection_test.py
+++ b/autotests/testP2P/connection_test.py
@@ -91,6 +91,7 @@ class Test(unittest.TestCase):
 
         wd.wait_for_object_condition(wpas, 'obj.p2p_group is not None', max_wait=3)
         peer_ifname = wpas.p2p_group['ifname']
+        self.assertEqual(wpas.p2p_group['role'], 'GO' if not go else 'client')
 
         if not go:
             ctx.start_process(['ifconfig', peer_ifname, '192.168.1.20', 'netmask', '255.255.255.0'], wait=True)
@@ -102,6 +103,9 @@ class Test(unittest.TestCase):
             client = wpas.p2p_clients[request['peer_iface']]
             self.assertEqual(client['p2p_dev_addr'], wpas_peer['p2p_dev_addr'])
         else:
+            self.assertEqual(wpas.p2p_group['ip_addr'], '192.168.1.2')
+            self.assertEqual(wpas.p2p_group['ip_mask'], '255.255.255.240')
+            self.assertEqual(wpas.p2p_group['go_ip_addr'], '192.168.1.1')
             dhcp = ctx.start_process(['dhclient', '-v', '-d', '--no-pid', '-cf', '/dev/null', '-lf', '/tmp/dhcp.leases',
                 '-sf', '/tmp/dhclient-script', peer_ifname])
             self.dhcp = dhcp
@@ -136,7 +140,10 @@ class Test(unittest.TestCase):
 
     def tearDown(self):
         if self.p2p is not None:
-            self.p2p.enabled = False
+            try:
+                self.p2p.enabled = False
+            except:
+                pass
         if self.wpas is not None:
             self.wpas.clean_up()
             self.wpas = None
diff --git a/autotests/util/wpas.py b/autotests/util/wpas.py
index 1758eede..8fb725f8 100644
--- a/autotests/util/wpas.py
+++ b/autotests/util/wpas.py
@@ -159,6 +159,7 @@ class Wpas:
         elif event['event'] == 'P2P-GROUP-STARTED':
             event.pop('event')
             event['ifname'] = event.pop('arg1')
+            event['role'] = event.pop('arg2')
             self.p2p_group = event
         elif event['event'] == 'P2P-GROUP-REMOVED':
             self.p2p_group = None
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 4/9] ap: Expire client's leases on disconnect
  2021-08-23 14:14 [PATCH 1/9] handshake: Add HANDSHAKE_EVENT_P2P_IP_REQUEST Andrew Zaborowski
  2021-08-23 14:14 ` [PATCH 2/9] ap: Implement P2P GO-side 4-way handshake IP Allocation Andrew Zaborowski
  2021-08-23 14:14 ` [PATCH 3/9] autotests: Test GO-side IP Allocation in testP2P Andrew Zaborowski
@ 2021-08-23 14:14 ` Andrew Zaborowski
  2021-08-23 14:14 ` [PATCH 5/9] ie: Add FILS IP Address Assignment parsers and builders Andrew Zaborowski
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2021-08-23 14:14 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 1407 bytes --]

If netconfig is enabled tell the DHCP server to expire any leases owned
by the client that is disconnecting by using l_dhcp_server_expire_by_mac
to return the IPs to the IP pool.  They're added to the expired list
so they'd only be used if there are no other addresses left in the pool
and can be reactivated if the client comes back before the address is
used by somebody else.

This should ensure that we're always able to offer an address to a new
client as long as there are fewer concurrent clients than addresses in
the configured subnet or IP range.
---
 src/ap.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/ap.c b/src/ap.c
index c4473c66..e91f8b00 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -268,6 +268,17 @@ static void ap_del_station(struct sta_state *sta, uint16_t reason,
 
 	ap_stop_handshake(sta);
 
+	/*
+	 * Expire any DHCP leases owned by this client when it disconnects to
+	 * make it harder for somebody to DoS the IP pool.  If the client
+	 * comes back and the lease is still in the expired leases list they
+	 * will get their IP back.
+	 */
+	if (ap->netconfig_dhcp) {
+		sta->ip_alloc_lease = NULL;
+		l_dhcp_server_expire_by_mac(ap->netconfig_dhcp, sta->addr);
+	}
+
 	/*
 	 * If the event handler tears the AP down, we've made sure above that
 	 * a subsequent ap_sta_free(sta) has no need to access sta->ap.
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 5/9] ie: Add FILS IP Address Assignment parsers and builders
  2021-08-23 14:14 [PATCH 1/9] handshake: Add HANDSHAKE_EVENT_P2P_IP_REQUEST Andrew Zaborowski
                   ` (2 preceding siblings ...)
  2021-08-23 14:14 ` [PATCH 4/9] ap: Expire client's leases on disconnect Andrew Zaborowski
@ 2021-08-23 14:14 ` Andrew Zaborowski
  2021-08-25 13:47   ` Denis Kenzior
  2021-08-23 14:14 ` [PATCH 6/9] ap: Support FILS IP Address Assignment IE Andrew Zaborowski
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Andrew Zaborowski @ 2021-08-23 14:14 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 12336 bytes --]

---
 src/ie.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/ie.h |  38 ++++++
 2 files changed, 385 insertions(+)

diff --git a/src/ie.c b/src/ie.c
index a73d5bbc..c03b16b4 100644
--- a/src/ie.c
+++ b/src/ie.c
@@ -25,6 +25,7 @@
 #endif
 
 #include <errno.h>
+#include <arpa/inet.h>
 
 #include <ell/ell.h>
 
@@ -2103,3 +2104,349 @@ bool ie_rsnxe_capable(const uint8_t *rsnxe, unsigned int bit)
 
 	return test_bit(rsnxe + 2, bit);
 }
+
+/* 802.11ai-2016 Tables 9-589r, 9-262d, 9-262e */
+enum ie_fils_ip_addr_req_ctrl_bits {
+	IE_FILS_IP_ADDR_REQ_CTRL_IPV4_MASK = 3 << 0,
+	IE_FILS_IP_ADDR_REQ_CTRL_IPV4_NONE = 0 << 0,
+	IE_FILS_IP_ADDR_REQ_CTRL_IPV4_NEW = 2 << 0,
+	IE_FILS_IP_ADDR_REQ_CTRL_IPV4_SPECIFIC = 3 << 0,
+	IE_FILS_IP_ADDR_REQ_CTRL_IPV6_MASK = 3 << 2,
+	IE_FILS_IP_ADDR_REQ_CTRL_IPV6_NONE = 0 << 2,
+	IE_FILS_IP_ADDR_REQ_CTRL_IPV6_NEW = 2 << 2,
+	IE_FILS_IP_ADDR_REQ_CTRL_IPV6_SPECIFIC = 3 << 2,
+	IE_FILS_IP_ADDR_REQ_CTRL_DNS = 1 << 4,
+};
+
+/* 802.11ai-2016 Table 9-262f */
+enum ie_fils_ip_addr_resp_ctrl_bits {
+	IE_FILS_IP_ADDR_RESP_CTRL_IP_PENDING = 1 << 0,
+	IE_FILS_IP_ADDR_RESP_CTRL_IPV4_ASSIGNED = 1 << 1,
+	IE_FILS_IP_ADDR_RESP_CTRL_IPV4_GW_INCLUDED = 1 << 2,
+	IE_FILS_IP_ADDR_RESP_CTRL_IPV6_ASSIGNED = 1 << 3,
+	IE_FILS_IP_ADDR_RESP_CTRL_IPV6_GW_INCLUDED = 1 << 4,
+	IE_FILS_IP_ADDR_RESP_CTRL_IPV4_LIFETIME_INCLUDED = 1 << 5,
+	IE_FILS_IP_ADDR_RESP_CTRL_IPV6_LIFETIME_INCLUDED = 1 << 6,
+};
+
+/* 802.11ai-2016 Table 9-262h */
+enum ie_fils_ip_addr_resp_dns_ctrl_bits {
+	IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV4_DNS_INCLUDED = 1 << 0,
+	IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV6_DNS_INCLUDED = 1 << 1,
+	IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV4_DNS_MAC_INCLUDED = 1 << 2,
+	IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV6_DNS_MAC_INCLUDED = 1 << 3,
+};
+
+int ie_parse_fils_ip_addr_request(struct ie_tlv_iter *iter,
+				struct ie_fils_ip_addr_request_info *out)
+{
+	unsigned int len = ie_tlv_iter_get_length(iter);
+	const uint8_t *data = ie_tlv_iter_get_data(iter);
+	struct ie_fils_ip_addr_request_info info = {};
+	bool ipv4_specific_addr = false;
+	bool ipv6_specific_addr = false;
+
+	if (len < 1)
+		return -EMSGSIZE;
+
+	if (L_IN_SET(data[0] & IE_FILS_IP_ADDR_REQ_CTRL_IPV4_MASK,
+			IE_FILS_IP_ADDR_REQ_CTRL_IPV4_NEW,
+			IE_FILS_IP_ADDR_REQ_CTRL_IPV4_SPECIFIC)) {
+		info.ipv4 = true;
+		ipv4_specific_addr =
+			(data[0] & IE_FILS_IP_ADDR_REQ_CTRL_IPV4_MASK) ==
+			IE_FILS_IP_ADDR_REQ_CTRL_IPV4_SPECIFIC;
+	} else if ((data[0] & IE_FILS_IP_ADDR_REQ_CTRL_IPV4_MASK) !=
+			IE_FILS_IP_ADDR_REQ_CTRL_IPV4_NONE)
+		return -EINVAL;
+
+	if (L_IN_SET(data[0] & IE_FILS_IP_ADDR_REQ_CTRL_IPV6_MASK,
+			IE_FILS_IP_ADDR_REQ_CTRL_IPV6_NEW,
+			IE_FILS_IP_ADDR_REQ_CTRL_IPV6_SPECIFIC)) {
+		info.ipv6 = true;
+		ipv6_specific_addr =
+			(data[0] & IE_FILS_IP_ADDR_REQ_CTRL_IPV6_MASK) ==
+			IE_FILS_IP_ADDR_REQ_CTRL_IPV6_SPECIFIC;
+	} else if ((data[0] & IE_FILS_IP_ADDR_REQ_CTRL_IPV6_MASK) !=
+			IE_FILS_IP_ADDR_REQ_CTRL_IPV6_NONE)
+		return -EINVAL;
+
+	info.dns = !!(*data++ & IE_FILS_IP_ADDR_REQ_CTRL_DNS);
+
+	if (len < 1 + (ipv4_specific_addr ? 4u : 0u) +
+			(ipv6_specific_addr ? 16u : 0u))
+		return -EMSGSIZE;
+
+	if (ipv4_specific_addr) {
+		info.ipv4_requested_addr = l_get_u32(data);
+		data += 4;
+
+		if (!info.ipv4_requested_addr)
+			return -EINVAL;
+	}
+
+	if (ipv6_specific_addr) {
+		memcpy(info.ipv6_requested_addr, data, 16);
+		data += 16;
+
+		if (l_memeqzero(info.ipv6_requested_addr, 16))
+			return -EINVAL;
+	}
+
+	memcpy(out, &info, sizeof(info));
+	return 0;
+}
+
+void ie_build_fils_ip_addr_request(
+				const struct ie_fils_ip_addr_request_info *info,
+				uint8_t *to)
+{
+	uint8_t *len;
+	uint8_t *ctrl;
+
+	*to++ = IE_TYPE_EXTENSION;
+	len = to++;
+	*to++ = IE_TYPE_FILS_IP_ADDRESS & 0xff;
+	ctrl = to++;
+
+	*ctrl = info->dns ? IE_FILS_IP_ADDR_REQ_CTRL_DNS : 0;
+
+	if (info->ipv4) {
+		if (info->ipv4_requested_addr) {
+			l_put_u32(info->ipv4_requested_addr, to);
+			to += 4;
+			*ctrl |= IE_FILS_IP_ADDR_REQ_CTRL_IPV4_SPECIFIC;
+		} else
+			*ctrl |= IE_FILS_IP_ADDR_REQ_CTRL_IPV4_NEW;
+	}
+
+	if (info->ipv6) {
+		if (!l_memeqzero(info->ipv6_requested_addr, 16)) {
+			memcpy(to, info->ipv6_requested_addr, 16);
+			to += 16;
+			*ctrl |= IE_FILS_IP_ADDR_REQ_CTRL_IPV6_SPECIFIC;
+		} else
+			*ctrl |= IE_FILS_IP_ADDR_REQ_CTRL_IPV6_NEW;
+	}
+
+	*len = to - (len + 1);
+}
+
+#define NEXT_FIELD(data, len, size) (__extension__ ({	\
+	const uint8_t *_ptr = data;			\
+							\
+	if (len < size)					\
+		return -EMSGSIZE;			\
+							\
+	data += size;					\
+	len -= size;					\
+	_ptr; }))
+
+int ie_parse_fils_ip_addr_response(struct ie_tlv_iter *iter,
+				struct ie_fils_ip_addr_response_info *out)
+{
+	unsigned int len = ie_tlv_iter_get_length(iter);
+	const uint8_t *data = ie_tlv_iter_get_data(iter);
+	struct ie_fils_ip_addr_response_info info = {};
+	const uint8_t *resp_ctrl;
+	const uint8_t *dns_ctrl;
+	const uint8_t *ptr;
+
+	resp_ctrl = NEXT_FIELD(data, len, 2);
+	dns_ctrl = resp_ctrl + 1;
+
+	info.response_pending =
+		!!(*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IP_PENDING);
+
+	if (info.response_pending) {
+		info.response_timeout =
+			bit_field(*resp_ctrl, 1, 6); /* seconds */
+		return 0;
+	}
+
+	if (*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IPV4_ASSIGNED) {
+		uint32_t netmask;
+
+		ptr = NEXT_FIELD(data, len, 8);
+		info.ipv4_addr = l_get_u32(ptr);
+		netmask = l_get_be32(ptr + 4);
+		info.ipv4_prefix_len = __builtin_popcount(netmask);
+
+		if (!info.ipv4_addr || info.ipv4_prefix_len > 30 || netmask !=
+				util_netmask_from_prefix(info.ipv4_prefix_len))
+			return -EINVAL;
+	}
+
+	if (*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IPV4_GW_INCLUDED) {
+		ptr = NEXT_FIELD(data, len, 10);
+		info.ipv4_gateway = l_get_u32(ptr);
+		memcpy(info.ipv4_gateway_mac, ptr + 4, 6);
+
+		/* Check gateway is on the same subnet */
+		if (info.ipv4_addr && (ntohl(info.ipv4_addr ^ info.ipv4_gateway) &
+				util_netmask_from_prefix(info.ipv4_prefix_len)))
+			return -EINVAL;
+	}
+
+	if (*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IPV6_ASSIGNED) {
+		ptr = NEXT_FIELD(data, len, 17);
+		memcpy(info.ipv6_addr, ptr, 16);
+		info.ipv6_prefix_len = ptr[16];
+
+		if (l_memeqzero(info.ipv6_addr, 16) ||
+				info.ipv6_prefix_len > 126)
+			return -EINVAL;
+	}
+
+	if (*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IPV6_GW_INCLUDED) {
+		ptr = NEXT_FIELD(data, len, 22);
+		memcpy(info.ipv6_gateway, ptr, 16);
+		memcpy(info.ipv6_gateway_mac, ptr + 16, 6);
+
+		/* Check gateway is on the same subnet */
+		if (!l_memeqzero(info.ipv6_addr, 12)) {
+			int n = info.ipv6_prefix_len / 8;
+			uint8_t mask = (1 << (info.ipv6_prefix_len & 7)) - 1;
+
+			if (n && memcmp(info.ipv6_addr, info.ipv6_gateway, n))
+				return -EINVAL;
+
+			if (mask && ((info.ipv6_addr[n] ^
+						info.ipv6_gateway[n]) & mask))
+				return -EINVAL;
+		}
+	}
+
+	if (*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IPV4_LIFETIME_INCLUDED)
+		info.ipv4_lifetime = *NEXT_FIELD(data, len, 1); /* seconds */
+
+	if (*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IPV6_LIFETIME_INCLUDED)
+		info.ipv6_lifetime = *NEXT_FIELD(data, len, 1); /* seconds */
+
+	if (*dns_ctrl & IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV4_DNS_INCLUDED) {
+		info.ipv4_dns = l_get_u32(NEXT_FIELD(data, len, 4));
+
+		if (!info.ipv4_dns)
+			return -EINVAL;
+	}
+
+	if (*dns_ctrl & IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV6_DNS_INCLUDED) {
+		memcpy(info.ipv6_dns, NEXT_FIELD(data, len, 16), 16);
+
+		if (l_memeqzero(info.ipv6_dns, 16))
+			return -EINVAL;
+	}
+
+	if (*dns_ctrl & IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV4_DNS_MAC_INCLUDED)
+		memcpy(info.ipv4_dns_mac, NEXT_FIELD(data, len, 6), 6);
+
+	if (*dns_ctrl & IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV6_DNS_MAC_INCLUDED)
+		memcpy(info.ipv6_dns_mac, NEXT_FIELD(data, len, 6), 6);
+
+	memcpy(out, &info, sizeof(info));
+	return 0;
+}
+
+void ie_build_fils_ip_addr_response(
+			const struct ie_fils_ip_addr_response_info *info,
+			uint8_t *to)
+{
+	uint8_t *len;
+	uint8_t *resp_ctrl;
+	uint8_t *dns_ctrl;
+
+	*to++ = IE_TYPE_EXTENSION;
+	len = to++;
+	*to++ = IE_TYPE_FILS_IP_ADDRESS & 0xff;
+	resp_ctrl = to++;
+	dns_ctrl = to++;
+
+	*resp_ctrl = 0;
+	*dns_ctrl = 0;
+
+	if (info->response_pending) {
+		*resp_ctrl |= IE_FILS_IP_ADDR_RESP_CTRL_IP_PENDING;
+		*resp_ctrl |= info->response_timeout << 1;
+		goto done;
+	}
+
+	if (info->ipv4_addr) {
+		uint32_t netmask =
+			util_netmask_from_prefix(info->ipv4_prefix_len);
+
+		*resp_ctrl |= IE_FILS_IP_ADDR_RESP_CTRL_IPV4_ASSIGNED;
+
+		l_put_u32(info->ipv4_addr, to);
+		l_put_u32(htonl(netmask), to + 4);
+		to += 8;
+	}
+
+	if (info->ipv4_gateway) {
+		*resp_ctrl |= IE_FILS_IP_ADDR_RESP_CTRL_IPV4_GW_INCLUDED;
+
+		l_put_u32(info->ipv4_gateway, to);
+		memcpy(to + 4, info->ipv4_gateway_mac, 6);
+		to += 10;
+	}
+
+	if (!l_memeqzero(info->ipv6_addr, 16)) {
+		*resp_ctrl |= IE_FILS_IP_ADDR_RESP_CTRL_IPV6_ASSIGNED;
+
+		memcpy(to, info->ipv6_addr, 16);
+		to[16] = info->ipv6_prefix_len;
+		to += 17;
+	}
+
+	if (!l_memeqzero(info->ipv6_gateway, 16)) {
+		*resp_ctrl |= IE_FILS_IP_ADDR_RESP_CTRL_IPV6_GW_INCLUDED;
+
+		memcpy(to, info->ipv6_gateway, 16);
+		memcpy(to + 16, info->ipv6_gateway_mac, 6);
+		to += 22;
+	}
+
+	if (info->ipv4_lifetime) {
+		*resp_ctrl |= IE_FILS_IP_ADDR_RESP_CTRL_IPV4_LIFETIME_INCLUDED;
+
+		*to++ = info->ipv4_lifetime;
+	}
+
+	if (info->ipv6_lifetime) {
+		*resp_ctrl |= IE_FILS_IP_ADDR_RESP_CTRL_IPV6_LIFETIME_INCLUDED;
+
+		*to++ = info->ipv6_lifetime;
+	}
+
+	if (info->ipv4_dns) {
+		*dns_ctrl |= IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV4_DNS_INCLUDED;
+
+		l_put_u32(info->ipv4_dns, to);
+		to += 4;
+	}
+
+	if (!l_memeqzero(info->ipv6_dns, 16)) {
+		*dns_ctrl |= IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV6_DNS_INCLUDED;
+
+		memcpy(to, info->ipv6_dns, 16);
+		to += 16;
+	}
+
+	if (!l_memeqzero(info->ipv4_dns_mac, 6)) {
+		*dns_ctrl |=
+			IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV4_DNS_MAC_INCLUDED;
+
+		memcpy(to, info->ipv4_dns_mac, 6);
+		to += 6;
+	}
+
+	if (!l_memeqzero(info->ipv6_dns_mac, 6)) {
+		*dns_ctrl |=
+			IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV6_DNS_MAC_INCLUDED;
+
+		memcpy(to, info->ipv6_dns_mac, 6);
+		to += 6;
+	}
+
+done:
+	*len = to - (len + 1);
+}
diff --git a/src/ie.h b/src/ie.h
index 25b56302..05afce39 100644
--- a/src/ie.h
+++ b/src/ie.h
@@ -459,6 +459,33 @@ struct ie_neighbor_report_info {
 	bool bss_transition_pref_present : 1;
 };
 
+struct ie_fils_ip_addr_request_info {
+	bool ipv4 : 1;
+	uint32_t ipv4_requested_addr;		/* Zero if none */
+	bool ipv6 : 1;
+	uint8_t ipv6_requested_addr[16];	/* Zero if none */
+	bool dns : 1;
+};
+
+struct ie_fils_ip_addr_response_info {
+	bool response_pending : 1;
+	uint8_t response_timeout;	/* Seconds */
+	uint32_t ipv4_addr;		/* Zero if not provided */
+	uint8_t ipv4_prefix_len;
+	uint32_t ipv4_gateway;		/* Zero if not provided */
+	uint8_t ipv4_gateway_mac[6];
+	uint32_t ipv4_dns;		/* Zero if not provided */
+	uint8_t ipv4_dns_mac[6];	/* Zero if not provided */
+	uint8_t ipv4_lifetime;		/* Zero if not provided */
+	uint8_t ipv6_addr[16];		/* Zero if not provided */
+	uint8_t ipv6_prefix_len;
+	uint8_t ipv6_gateway[16];	/* Zero if not provided */
+	uint8_t ipv6_gateway_mac[6];
+	uint8_t ipv6_dns[16];		/* Zero if not provided */
+	uint8_t ipv6_dns_mac[6];	/* Zero if not provided */
+	uint8_t ipv6_lifetime;		/* Zero if not provided */
+};
+
 extern const unsigned char ieee_oui[3];
 extern const unsigned char microsoft_oui[3];
 extern const unsigned char wifi_alliance_oui[3];
@@ -586,3 +613,14 @@ enum ie_rsnx_capability {
 };
 
 bool ie_rsnxe_capable(const uint8_t *rsnxe, unsigned int bit);
+
+int ie_parse_fils_ip_addr_request(struct ie_tlv_iter *iter,
+				struct ie_fils_ip_addr_request_info *out);
+void ie_build_fils_ip_addr_request(
+				const struct ie_fils_ip_addr_request_info *info,
+				uint8_t *to);
+int ie_parse_fils_ip_addr_response(struct ie_tlv_iter *iter,
+				struct ie_fils_ip_addr_response_info *out);
+void ie_build_fils_ip_addr_response(
+			const struct ie_fils_ip_addr_response_info *info,
+			uint8_t *to);
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 6/9] ap: Support FILS IP Address Assignment IE
  2021-08-23 14:14 [PATCH 1/9] handshake: Add HANDSHAKE_EVENT_P2P_IP_REQUEST Andrew Zaborowski
                   ` (3 preceding siblings ...)
  2021-08-23 14:14 ` [PATCH 5/9] ie: Add FILS IP Address Assignment parsers and builders Andrew Zaborowski
@ 2021-08-23 14:14 ` Andrew Zaborowski
  2021-08-23 14:14 ` [PATCH 7/9] netconfig: Move loading settings to new method, refactor Andrew Zaborowski
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2021-08-23 14:14 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 8032 bytes --]

Handle the 802.11ai FILS IP Address Assignment IEs in Association
Request frames when netconfig is enabled.  Only IPv4 is supported.
Like the P2P IP Allocation mechanism, since the payload format and logic
is independent from the rest of the FILS standard this is enabled
unconditionally for clients who want to use it even though we don't
actually do FILS in AP mode.
---
 src/ap.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 98 insertions(+), 16 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index e91f8b00..edffe906 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -119,6 +119,7 @@ struct sta_state {
 	uint8_t wsc_uuid_e[16];
 	bool wsc_v2;
 	struct l_dhcp_lease *ip_alloc_lease;
+	bool ip_alloc_sent;
 };
 
 struct ap_wsc_pbc_probe_record {
@@ -907,6 +908,25 @@ error:
 	ap_del_station(sta, MMPDU_REASON_CODE_UNSPECIFIED, true);
 }
 
+static bool ap_sta_get_dhcp4_lease(struct sta_state *sta)
+{
+	if (sta->ip_alloc_lease)
+		return true;
+
+	if (!sta->ap->netconfig_dhcp)
+		return false;
+
+	sta->ip_alloc_lease = l_dhcp_server_discover(sta->ap->netconfig_dhcp,
+							0, NULL, sta->addr);
+	if (!sta->ip_alloc_lease) {
+		l_error("l_dhcp_server_discover() failed, see IWD_DHCP_DEBUG "
+			"output");
+		return false;
+	}
+
+	return true;
+}
+
 static void ap_handshake_event(struct handshake_state *hs,
 		enum handshake_event event, void *user_data, ...)
 {
@@ -919,6 +939,9 @@ static void ap_handshake_event(struct handshake_state *hs,
 	switch (event) {
 	case HANDSHAKE_EVENT_COMPLETE:
 		if (sta->ip_alloc_lease) {
+			if (hs->support_ip_allocation)
+				sta->ip_alloc_sent = true;
+
 			/*
 			 * Move the lease from offered to active state if the
 			 * client has actually used it.  In any case drop our
@@ -926,7 +949,7 @@ static void ap_handshake_event(struct handshake_state *hs,
 			 * and if we want to keep our reference we'd need to
 			 * react to relevant server events.
 			 */
-			if (hs->support_ip_allocation)
+			if (sta->ip_alloc_sent)
 				l_dhcp_server_request(ap->netconfig_dhcp,
 							sta->ip_alloc_lease);
 
@@ -948,16 +971,8 @@ static void ap_handshake_event(struct handshake_state *hs,
 		L_AUTO_FREE_VAR(char *, lease_netmask_str) = NULL;
 		char own_addr_str[INET_ADDRSTRLEN];
 
-		if (!sta->ip_alloc_lease)
-			sta->ip_alloc_lease = l_dhcp_server_discover(
-							ap->netconfig_dhcp,
-							0, NULL, sta->addr);
-
-		if (!sta->ip_alloc_lease) {
-			l_error("l_dhcp_server_discover() failed, see "
-				"IWD_DHCP_DEBUG output");
+		if (!ap_sta_get_dhcp4_lease(sta))
 			break;
-		}
 
 		lease_addr_str = l_dhcp_lease_get_address(sta->ip_alloc_lease);
 		lease_netmask_str =
@@ -1377,14 +1392,16 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
 				const uint8_t *dest,
 				enum mmpdu_reason_code status_code,
 				bool reassoc, const struct mmpdu_header *req,
-				size_t req_len, frame_xchg_cb_t callback)
+				size_t req_len,
+				const struct ie_fils_ip_addr_request_info *
+				ip_req_info, frame_xchg_cb_t callback)
 {
 	const uint8_t *addr = netdev_get_address(ap->netdev);
 	enum mpdu_management_subtype stype = reassoc ?
 		MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_RESPONSE :
 		MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_RESPONSE;
 	L_AUTO_FREE_VAR(uint8_t *, mpdu_buf) =
-		l_malloc(128 + ap_get_extra_ies_len(ap, stype, req, req_len));
+		l_malloc(256 + ap_get_extra_ies_len(ap, stype, req, req_len));
 	struct mmpdu_header *mpdu = (void *) mpdu_buf;
 	struct mmpdu_association_response *resp;
 	size_t ies_len = 0;
@@ -1430,6 +1447,57 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
 	ies_len += ap_write_extra_ies(ap, stype, req, req_len,
 					resp->ies + ies_len);
 
+	if (ip_req_info) {
+		struct ie_fils_ip_addr_response_info ip_resp_info = {};
+
+		if (ip_req_info->ipv4 && sta && ap_sta_get_dhcp4_lease(sta)) {
+			L_AUTO_FREE_VAR(char *, lease_addr_str) =
+				l_dhcp_lease_get_address(sta->ip_alloc_lease);
+			L_AUTO_FREE_VAR(char *, lease_netmask_str) =
+				l_dhcp_lease_get_netmask(sta->ip_alloc_lease);
+			uint32_t lease_lifetime =
+				l_dhcp_lease_get_lifetime(sta->ip_alloc_lease);
+			L_AUTO_FREE_VAR(char *, lease_gateway_str) =
+				l_dhcp_lease_get_gateway(sta->ip_alloc_lease);
+			char **lease_dns_str_list =
+				l_dhcp_lease_get_dns(sta->ip_alloc_lease);
+
+			ip_resp_info.ipv4_addr = IP4_FROM_STR(lease_addr_str);
+			ip_resp_info.ipv4_prefix_len =
+				__builtin_popcount(IP4_FROM_STR(
+							lease_netmask_str));
+
+			if (lease_lifetime != 0xffffffff)
+				ip_resp_info.ipv4_lifetime = lease_lifetime;
+
+			if (lease_gateway_str)
+				ip_resp_info.ipv4_gateway =
+					IP4_FROM_STR(lease_gateway_str);
+
+			if (lease_dns_str_list && lease_dns_str_list[0])
+				ip_resp_info.ipv4_dns =
+					IP4_FROM_STR(lease_dns_str_list[0]);
+
+			l_strv_free(lease_dns_str_list);
+			sta->ip_alloc_sent = true;
+		} else if (ip_req_info->ipv4 || ip_req_info->ipv6) {
+			/*
+			 * 802.11ai-2016 Section 11.47.3.3: "If the AP is unable
+			 * to assign an IP address in the (Re)Association
+			 * Response frame, then the AP sets the IP address
+			 * assignment pending flag in the IP Address Response
+			 * Control field of the FILS IP Address Assignment
+			 * element to 1 and sets the IP address request timeout
+			 * to 0 in (Re)Association Response frame."
+			 */
+			ip_resp_info.response_pending = 1;
+			ip_resp_info.response_timeout = 0;
+		}
+
+		ie_build_fils_ip_addr_response(&ip_resp_info, resp->ies + ies_len);
+		ies_len += 2 + resp->ies[ies_len + 1];
+	}
+
 	return ap_send_mgmt_frame(ap, mpdu, resp->ies + ies_len - mpdu_buf,
 					callback, sta);
 }
@@ -1518,6 +1586,8 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 	struct ie_tlv_iter iter;
 	uint8_t *wsc_data = NULL;
 	ssize_t wsc_data_len;
+	bool fils_ip_req = false;
+	struct ie_fils_ip_addr_request_info fils_ip_req_info;
 
 	if (sta->assoc_resp_cmd_id)
 		return;
@@ -1564,6 +1634,17 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 
 			rsn = (const uint8_t *) ie_tlv_iter_get_data(&iter) - 2;
 			break;
+
+		case IE_TYPE_FILS_IP_ADDRESS:
+			if (fils_ip_req || ie_parse_fils_ip_addr_request(&iter,
+						&fils_ip_req_info) < 0) {
+				l_debug("Can't parse FILS IP Address Assignment"
+					" IE, ignoring it");
+				break;
+			}
+
+			fils_ip_req = true;
+			break;
 		}
 
 	if (!rates || !ssid || (!wsc_data && !rsn) ||
@@ -1696,7 +1777,8 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 
 	sta->assoc_resp_cmd_id = ap_assoc_resp(ap, sta, sta->addr, 0, reassoc,
 						req, (void *) ies + ies_len -
-						(void *) req,
+						(void *) req, fils_ip_req ?
+						&fils_ip_req_info : NULL,
 						ap_success_assoc_resp_cb);
 	if (!sta->assoc_resp_cmd_id)
 		l_error("Sending success (Re)Association Response failed");
@@ -1729,7 +1811,7 @@ bad_frame:
 
 	if (!ap_assoc_resp(ap, sta, sta->addr, err, reassoc,
 				req, (void *) ies + ies_len - (void *) req,
-				ap_fail_assoc_resp_cb))
+				NULL, ap_fail_assoc_resp_cb))
 		l_error("Sending error (Re)Association Response failed");
 }
 
@@ -1754,7 +1836,7 @@ static void ap_assoc_req_cb(const struct mmpdu_header *hdr, const void *body,
 		if (!ap_assoc_resp(ap, NULL, from,
 				MMPDU_REASON_CODE_STA_REQ_ASSOC_WITHOUT_AUTH,
 				false, hdr, body + body_len - (void *) hdr,
-				ap_fail_assoc_resp_cb))
+				NULL, ap_fail_assoc_resp_cb))
 			l_error("Sending error Association Response failed");
 
 		return;
@@ -1802,7 +1884,7 @@ static void ap_reassoc_req_cb(const struct mmpdu_header *hdr, const void *body,
 bad_frame:
 	if (!ap_assoc_resp(ap, NULL, from, err, true,
 				hdr, body + body_len - (void *) hdr,
-				ap_fail_assoc_resp_cb))
+				NULL, ap_fail_assoc_resp_cb))
 		l_error("Sending error Reassociation Response failed");
 }
 
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 7/9] netconfig: Move loading settings to new method, refactor
  2021-08-23 14:14 [PATCH 1/9] handshake: Add HANDSHAKE_EVENT_P2P_IP_REQUEST Andrew Zaborowski
                   ` (4 preceding siblings ...)
  2021-08-23 14:14 ` [PATCH 6/9] ap: Support FILS IP Address Assignment IE Andrew Zaborowski
@ 2021-08-23 14:14 ` Andrew Zaborowski
  2021-08-25 13:50   ` Denis Kenzior
  2021-08-23 14:14 ` [PATCH 8/9] netconfig: FILS IP assigment API Andrew Zaborowski
  2021-08-23 14:14 ` [PATCH 9/9] station, netdev: Enable FILS IP Address Assignment Andrew Zaborowski
  7 siblings, 1 reply; 15+ messages in thread
From: Andrew Zaborowski @ 2021-08-23 14:14 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 11493 bytes --]

Split loading settings out of network_configure into a new method,
network_load_settings.  Make sure both consistently handle errors by
printing messages and informing the caller.
---
 src/netconfig.c | 157 +++++++++++++++++++++++++++++++-----------------
 src/netconfig.h |   5 +-
 src/p2p.c       |  12 +++-
 src/station.c   |  20 +++---
 4 files changed, 127 insertions(+), 67 deletions(-)

diff --git a/src/netconfig.c b/src/netconfig.c
index 005316cd..d6113c0c 100644
--- a/src/netconfig.c
+++ b/src/netconfig.c
@@ -273,10 +273,10 @@ static struct l_rtnl_address *netconfig_get_static4_address(
 						struct netconfig *netconfig)
 {
 	struct l_rtnl_address *ifaddr = NULL;
-	char *ip;
-	char *netmask;
+	L_AUTO_FREE_VAR(char *, ip) = NULL;
+	L_AUTO_FREE_VAR(char *, netmask) = NULL;
 	struct in_addr in_addr;
-	char *broadcast;
+	L_AUTO_FREE_VAR(char *, broadcast) = NULL;
 	uint32_t prefix_len;
 
 	ip = l_settings_get_string(netconfig->active_settings, "IPv4",
@@ -286,33 +286,34 @@ static struct l_rtnl_address *netconfig_get_static4_address(
 
 	netmask = l_settings_get_string(netconfig->active_settings,
 						"IPv4", "Netmask");
+	if (netmask) {
+		if (inet_pton(AF_INET, netmask, &in_addr) != 1) {
+			l_error("netconfig: Can't parse IPv4 Netmask");
+			return NULL;
+		}
 
-	if (netmask && inet_pton(AF_INET, netmask, &in_addr) > 0)
 		prefix_len = __builtin_popcountl(in_addr.s_addr);
-	else
-		prefix_len = 24;
 
-	l_free(netmask);
+		if (ntohl(in_addr.s_addr) !=
+				util_netmask_from_prefix(prefix_len)) {
+			l_error("netconfig: Invalid IPv4 Netmask");
+			return NULL;
+		}
+	} else
+		prefix_len = 24;
 
 	ifaddr = l_rtnl_address_new(ip, prefix_len);
-	l_free(ip);
-
 	if (!ifaddr) {
-		l_error("Unable to parse IPv4.Address, ignoring...");
+		l_error("netconfig: Unable to parse IPv4.Address");
 		return NULL;
 	}
 
 	broadcast = l_settings_get_string(netconfig->active_settings,
 						"IPv4", "Broadcast");
-	if (broadcast) {
-		bool r = l_rtnl_address_set_broadcast(ifaddr, broadcast);
-		l_free(broadcast);
-
-		if (!r) {
-			l_error("Unable to parse IPv4.Broadcast, ignoring...");
-			l_rtnl_address_free(ifaddr);
-			return NULL;
-		}
+	if (broadcast && !l_rtnl_address_set_broadcast(ifaddr, broadcast)) {
+		l_error("netconfig: Unable to parse IPv4.Broadcast");
+		l_rtnl_address_free(ifaddr);
+		return NULL;
 	}
 
 	l_rtnl_address_set_noprefixroute(ifaddr, true);
@@ -898,14 +899,17 @@ static void netconfig_ipv4_acd_event(enum l_acd_event event, void *user_data)
 	}
 }
 
-static void netconfig_ipv4_select_and_install(struct netconfig *netconfig)
+static bool netconfig_ipv4_select_and_install(struct netconfig *netconfig)
 {
-	char ip[INET6_ADDRSTRLEN];
+	if (netconfig->rtm_protocol == RTPROT_STATIC) {
+		char ip[INET6_ADDRSTRLEN];
+
+		if (L_WARN_ON(!netconfig->v4_address ||
+					!l_rtnl_address_get_address(
+							netconfig->v4_address,
+							ip)))
+			return false;
 
-	netconfig->v4_address = netconfig_get_static4_address(netconfig);
-	if (netconfig->v4_address &&
-			l_rtnl_address_get_address(netconfig->v4_address, ip)) {
-		netconfig->rtm_protocol = RTPROT_STATIC;
 		netconfig->acd = l_acd_new(netconfig->ifindex);
 		l_acd_set_event_handler(netconfig->acd,
 					netconfig_ipv4_acd_event, netconfig,
@@ -926,47 +930,42 @@ static void netconfig_ipv4_select_and_install(struct netconfig *netconfig)
 					netconfig, NULL)));
 		}
 
-		return;
+		return true;
 	}
 
-	netconfig->rtm_protocol = RTPROT_DHCP;
-
 	if (l_dhcp_client_start(netconfig->dhcp_client))
-		return;
+		return true;
 
 	l_error("netconfig: Failed to start DHCPv4 client for interface %u",
 							netconfig->ifindex);
+	return false;
 }
 
-static void netconfig_ipv6_select_and_install(struct netconfig *netconfig)
+static bool netconfig_ipv6_select_and_install(struct netconfig *netconfig)
 {
 	struct netdev *netdev = netdev_find(netconfig->ifindex);
-	struct l_rtnl_address *address;
-	bool enabled;
-
-	if (!l_settings_get_bool(netconfig->active_settings, "IPv6",
-					"Enabled", &enabled))
-		enabled = ipv6_enabled;
 
-	if (!enabled) {
+	if (netconfig->rtm_v6_protocol == RTPROT_UNSPEC) {
 		l_debug("IPV6 configuration disabled");
-		return;
+		return true;
 	}
 
 	sysfs_write_ipv6_setting(netdev_get_name(netdev), "disable_ipv6", "0");
 
-	address = netconfig_get_static6_address(netconfig);
-	if (address) {
-		netconfig->rtm_v6_protocol = RTPROT_STATIC;
+	if (netconfig->rtm_v6_protocol == RTPROT_STATIC) {
+		struct l_rtnl_address *address =
+			netconfig_get_static6_address(netconfig);
+
 		L_WARN_ON(!(netconfig->addr6_add_cmd_id =
 			l_rtnl_ifaddr_add(rtnl, netconfig->ifindex, address,
 					netconfig_ipv6_ifaddr_add_cmd_cb,
 					netconfig, NULL)));
 		l_rtnl_address_free(address);
-		return;
+		return true;
 	}
 
-	netconfig->rtm_v6_protocol = RTPROT_DHCP;
+	/* DHCP */
+	return true;
 }
 
 static int validate_dns_list(int family, char **dns_list)
@@ -998,22 +997,22 @@ static int validate_dns_list(int family, char **dns_list)
 	return n_valid;
 }
 
-bool netconfig_configure(struct netconfig *netconfig,
+bool netconfig_load_settings(struct netconfig *netconfig,
 				const struct l_settings *active_settings,
-				const uint8_t *mac_address,
-				netconfig_notify_func_t notify, void *user_data)
+				const uint8_t *mac_address)
 {
 	char *mdns;
-	char hostname[HOST_NAME_MAX + 1];
 	bool send_hostname;
+	bool enabled;
 
+	l_strfreev(netconfig->dns4_overrides);
 	netconfig->dns4_overrides = l_settings_get_string_list(active_settings,
 							"IPv4", "DNS", ' ');
 
 	if (netconfig->dns4_overrides) {
 		int r = validate_dns_list(AF_INET, netconfig->dns4_overrides);
 
-		if (r <= 0) {
+		if (unlikely(r <= 0)) {
 			l_strfreev(netconfig->dns4_overrides);
 			netconfig->dns4_overrides = NULL;
 		}
@@ -1022,13 +1021,14 @@ bool netconfig_configure(struct netconfig *netconfig,
 			l_error("netconfig: Empty IPv4.DNS entry, skipping...");
 	}
 
+	l_strfreev(netconfig->dns6_overrides);
 	netconfig->dns6_overrides = l_settings_get_string_list(active_settings,
 							"IPv6", "DNS", ' ');
 
 	if (netconfig->dns6_overrides) {
 		int r = validate_dns_list(AF_INET6, netconfig->dns6_overrides);
 
-		if (r <= 0) {
+		if (unlikely(r <= 0)) {
 			l_strfreev(netconfig->dns6_overrides);
 			netconfig->dns6_overrides = NULL;
 		}
@@ -1038,8 +1038,6 @@ bool netconfig_configure(struct netconfig *netconfig,
 	}
 
 	netconfig->active_settings = active_settings;
-	netconfig->notify = notify;
-	netconfig->user_data = user_data;
 
 	l_dhcp_client_set_address(netconfig->dhcp_client, ARPHRD_ETHER,
 							mac_address, ETH_ALEN);
@@ -1051,6 +1049,8 @@ bool netconfig_configure(struct netconfig *netconfig,
 		send_hostname = false;
 
 	if (send_hostname) {
+		char hostname[HOST_NAME_MAX + 1];
+
 		if (gethostname(hostname, sizeof(hostname)) == 0) {
 			l_dhcp_client_set_hostname(
 				netconfig->dhcp_client, hostname);
@@ -1060,15 +1060,62 @@ bool netconfig_configure(struct netconfig *netconfig,
 		}
 	}
 
-	netconfig_ipv4_select_and_install(netconfig);
-
-	netconfig_ipv6_select_and_install(netconfig);
-
 	mdns = l_settings_get_string(active_settings,
 						"Network", "MulticastDNS");
 	resolve_set_mdns(netconfig->resolve, mdns);
 	l_free(mdns);
 
+	if (l_settings_get_value(netconfig->active_settings, "IPv4",
+					"Address")) {
+		l_rtnl_address_free(netconfig->v4_address);
+		netconfig->v4_address =
+			netconfig_get_static4_address(netconfig);
+
+		if (unlikely(!netconfig->v4_address)) {
+			l_error("netconfig: Can't parse IPv4 address");
+			return false;
+		}
+
+		netconfig->rtm_protocol = RTPROT_STATIC;
+	} else
+		netconfig->rtm_protocol = RTPROT_DHCP;
+
+	if (!l_settings_get_bool(netconfig->active_settings, "IPv6",
+					"Enabled", &enabled))
+		enabled = ipv6_enabled;
+
+	if (!enabled)
+		netconfig->rtm_v6_protocol = RTPROT_UNSPEC;
+	else if (l_settings_get_value(netconfig->active_settings, "IPv6",
+					"Address")) {
+		__auto_type address = netconfig_get_static6_address(netconfig);
+
+		l_rtnl_address_free(address);
+
+		if (unlikely(!address)) {
+			l_error("netconfig: Can't parse IPv6 address");
+			return false;
+		}
+
+		netconfig->rtm_v6_protocol = RTPROT_STATIC;
+	} else
+		netconfig->rtm_v6_protocol = RTPROT_DHCP;
+
+	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 (unlikely(!netconfig_ipv4_select_and_install(netconfig)))
+		return false;
+
+	if (unlikely(!netconfig_ipv6_select_and_install(netconfig)))
+		return false;
+
 	return true;
 }
 
diff --git a/src/netconfig.h b/src/netconfig.h
index 2c68cb1c..73e4df8b 100644
--- a/src/netconfig.h
+++ b/src/netconfig.h
@@ -29,9 +29,10 @@ enum netconfig_event {
 typedef void (*netconfig_notify_func_t)(enum netconfig_event event,
 							void *user_data);
 
-bool netconfig_configure(struct netconfig *netconfig,
+bool netconfig_load_settings(struct netconfig *netconfig,
 				const struct l_settings *active_settings,
-				const uint8_t *mac_address,
+				const uint8_t *mac_address);
+bool netconfig_configure(struct netconfig *netconfig,
 				netconfig_notify_func_t notify,
 				void *user_data);
 bool netconfig_reconfigure(struct netconfig *netconfig);
diff --git a/src/p2p.c b/src/p2p.c
index 8170bbe0..3328271b 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -1328,8 +1328,16 @@ static void p2p_start_client_netconfig(struct p2p_device *dev)
 	}
 
 	settings = dev->conn_netconfig_settings ?: p2p_dhcp_settings;
-	netconfig_configure(dev->conn_netconfig, settings, dev->conn_addr,
-				p2p_netconfig_event_handler, dev);
+
+	if (!netconfig_load_settings(dev->conn_netconfig, settings,
+					dev->conn_addr) ||
+			!netconfig_configure(dev->conn_netconfig,
+						p2p_netconfig_event_handler,
+						dev)) {
+		p2p_connect_failed(dev);
+		return;
+	}
+
 	dev->conn_dhcp_timeout = l_timeout_create(p2p_dhcp_timeout_val,
 						p2p_dhcp_timeout, dev,
 						p2p_dhcp_timeout_destroy);
diff --git a/src/station.c b/src/station.c
index d61e775a..f6237ade 100644
--- a/src/station.c
+++ b/src/station.c
@@ -2443,14 +2443,12 @@ static void station_connect_ok(struct station *station)
 
 	network_connected(station->connected_network);
 
-	if (station->netconfig)
-		netconfig_configure(station->netconfig,
-					network_get_settings(
-						station->connected_network),
-					netdev_get_address(station->netdev),
-					station_netconfig_event_handler,
-					station);
-	else
+	if (station->netconfig) {
+		if (L_WARN_ON(!netconfig_configure(station->netconfig,
+						station_netconfig_event_handler,
+						station)))
+			return;
+	} else
 		station_enter_state(station, STATION_STATE_CONNECTED);
 }
 
@@ -2592,6 +2590,12 @@ int __station_connect_network(struct station *station, struct network *network,
 	struct handshake_state *hs;
 	int r;
 
+	if (station->netconfig && !netconfig_load_settings(
+					station->netconfig,
+					network_get_settings(network),
+					netdev_get_address(station->netdev)))
+		return -EINVAL;
+
 	hs = station_handshake_setup(station, network, bss);
 	if (!hs)
 		return -ENOTSUP;
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 8/9] netconfig: FILS IP assigment API
  2021-08-23 14:14 [PATCH 1/9] handshake: Add HANDSHAKE_EVENT_P2P_IP_REQUEST Andrew Zaborowski
                   ` (5 preceding siblings ...)
  2021-08-23 14:14 ` [PATCH 7/9] netconfig: Move loading settings to new method, refactor Andrew Zaborowski
@ 2021-08-23 14:14 ` Andrew Zaborowski
  2021-08-23 14:14 ` [PATCH 9/9] station, netdev: Enable FILS IP Address Assignment Andrew Zaborowski
  7 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2021-08-23 14:14 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 10180 bytes --]

Add two methods that will allow station to implement FILS IP Address
Assigment, one method to decide whether to send the request during
association, and fill in the values to be used in the request IE, and
another to handle the response IE values received from the server and
apply them.  The netconfig->rtm_protocol value used when the address is
assigned this way remains RTPROT_DHCP because from the user's point of
view this is automatic IP assigment by the server, a replacement for
DHCP.
---
 src/netconfig.c | 168 ++++++++++++++++++++++++++++++++++++++++++++----
 src/netconfig.h |   6 ++
 2 files changed, 160 insertions(+), 14 deletions(-)

diff --git a/src/netconfig.c b/src/netconfig.c
index d6113c0c..803a8707 100644
--- a/src/netconfig.c
+++ b/src/netconfig.c
@@ -47,6 +47,8 @@
 #include "src/common.h"
 #include "src/network.h"
 #include "src/resolve.h"
+#include "src/util.h"
+#include "src/ie.h"
 #include "src/netconfig.h"
 
 struct netconfig {
@@ -58,6 +60,7 @@ struct netconfig {
 	struct l_rtnl_address *v4_address;
 	char **dns4_overrides;
 	char **dns6_overrides;
+	struct ie_fils_ip_addr_response_info *fils_override;
 
 	const struct l_settings *active_settings;
 
@@ -163,6 +166,36 @@ static struct netconfig *netconfig_find(uint32_t ifindex)
 			dest[index++] = *p;			\
 	} while (0)						\
 
+static inline char *netconfig_ipv4_to_string(uint32_t addr)
+{
+	struct in_addr in_addr = { .s_addr = addr };
+	char *addr_str = l_malloc(INET_ADDRSTRLEN);
+
+	if (L_WARN_ON(unlikely(!inet_ntop(AF_INET, &in_addr, addr_str,
+						INET_ADDRSTRLEN)))) {
+		l_free(addr_str);
+		return NULL;
+	}
+
+	return addr_str;
+}
+
+static inline char *netconfig_ipv6_to_string(const uint8_t *addr)
+{
+	struct in6_addr in6_addr;
+	char *addr_str = l_malloc(INET6_ADDRSTRLEN);
+
+	memcpy(in6_addr.__in6_u.__u6_addr8, addr, 16);
+
+	if (L_WARN_ON(unlikely(!inet_ntop(AF_INET6, &in6_addr, addr_str,
+						INET6_ADDRSTRLEN)))) {
+		l_free(addr_str);
+		return NULL;
+	}
+
+	return addr_str;
+}
+
 static int netconfig_set_dns(struct netconfig *netconfig)
 {
 	char **dns6_list = NULL;
@@ -172,19 +205,30 @@ static int netconfig_set_dns(struct netconfig *netconfig)
 
 	if (!netconfig->dns4_overrides &&
 			netconfig->rtm_protocol == RTPROT_DHCP) {
-		const struct l_dhcp_lease *lease =
-			l_dhcp_client_get_lease(netconfig->dhcp_client);
-
-		if (lease)
+		const struct l_dhcp_lease *lease;
+
+		if (netconfig->fils_override &&
+				netconfig->fils_override->ipv4_dns) {
+			dns4_list = l_new(char *, 2);
+			dns4_list[0] = netconfig_ipv4_to_string(
+					netconfig->fils_override->ipv4_dns);
+		} else if ((lease = l_dhcp_client_get_lease(
+						netconfig->dhcp_client)))
 			dns4_list = l_dhcp_lease_get_dns(lease);
 	}
 
 	if (!netconfig->dns6_overrides &&
 			netconfig->rtm_v6_protocol == RTPROT_DHCP) {
-		const struct l_dhcp6_lease *lease =
-			l_dhcp6_client_get_lease(netconfig->dhcp6_client);
-
-		if (lease)
+		const struct l_dhcp6_lease *lease;
+
+		if (netconfig->fils_override &&
+				!l_memeqzero(netconfig->fils_override->ipv6_dns,
+						16)) {
+			dns6_list = l_new(char *, 2);
+			dns6_list[0] = netconfig_ipv6_to_string(
+					netconfig->fils_override->ipv6_dns);
+		} else if ((lease = l_dhcp6_client_get_lease(
+						netconfig->dhcp6_client)))
 			dns6_list = l_dhcp6_lease_get_dns(lease);
 	}
 
@@ -337,6 +381,11 @@ static char *netconfig_ipv4_get_gateway(struct netconfig *netconfig)
 		return gateway;
 
 	case RTPROT_DHCP:
+		if (netconfig->fils_override &&
+				netconfig->fils_override->ipv4_gateway)
+			return netconfig_ipv4_to_string(
+					netconfig->fils_override->ipv4_gateway);
+
 		lease = l_dhcp_client_get_lease(netconfig->dhcp_client);
 		if (!lease)
 			return NULL;
@@ -393,7 +442,13 @@ static struct l_rtnl_route *netconfig_get_static6_gateway(
 
 	gateway = l_settings_get_string(netconfig->active_settings,
 						"IPv6", "Gateway");
-	if (!gateway)
+	if (!gateway && netconfig->rtm_v6_protocol == RTPROT_DHCP &&
+			netconfig->fils_override &&
+			!l_memeqzero(netconfig->fils_override->ipv6_gateway,
+					16))
+		gateway = netconfig_ipv6_to_string(
+					netconfig->fils_override->ipv6_gateway);
+	else if (!gateway)
 		return NULL;
 
 	ret = l_rtnl_route_new_gateway(gateway);
@@ -523,7 +578,9 @@ static void netconfig_ifaddr_ipv6_added(struct netconfig *netconfig,
 	l_debug("ifindex %u: ifaddr %s/%u", netconfig->ifindex,
 			ip, ifa->ifa_prefixlen);
 
-	if (netconfig->rtm_v6_protocol != RTPROT_DHCP)
+	if (netconfig->rtm_v6_protocol != RTPROT_DHCP ||
+			(netconfig->fils_override &&
+			 !l_memeqzero(netconfig->fils_override->ipv6_addr, 16)))
 		return;
 
 	inet_pton(AF_INET6, ip, &in6);
@@ -901,7 +958,35 @@ static void netconfig_ipv4_acd_event(enum l_acd_event event, void *user_data)
 
 static bool netconfig_ipv4_select_and_install(struct netconfig *netconfig)
 {
-	if (netconfig->rtm_protocol == RTPROT_STATIC) {
+	bool set_address = (netconfig->rtm_protocol == RTPROT_STATIC);
+
+	if (netconfig->rtm_protocol == RTPROT_DHCP &&
+			netconfig->fils_override &&
+			netconfig->fils_override->ipv4_addr) {
+		L_AUTO_FREE_VAR(char *, addr_str) = netconfig_ipv4_to_string(
+					netconfig->fils_override->ipv4_addr);
+		uint8_t prefix_len = netconfig->fils_override->ipv4_prefix_len;
+
+		if (unlikely(!addr_str))
+			return false;
+
+		if (L_WARN_ON(unlikely(!(netconfig->v4_address =
+						l_rtnl_address_new(addr_str,
+								prefix_len)))))
+			return false;
+
+		l_rtnl_address_set_noprefixroute(netconfig->v4_address, true);
+		set_address = true;
+
+		/*
+		 * TODO: If netconfig->fils_override->ipv4_lifetime is set,
+		 * start a timeout to renew the address using FILS IP Address
+		 * Assignment or perhaps just start the DHCP client at that
+		 * time.
+		 */
+	}
+
+	if (set_address) {
 		char ip[INET6_ADDRSTRLEN];
 
 		if (L_WARN_ON(!netconfig->v4_address ||
@@ -944,6 +1029,7 @@ static bool netconfig_ipv4_select_and_install(struct netconfig *netconfig)
 static bool netconfig_ipv6_select_and_install(struct netconfig *netconfig)
 {
 	struct netdev *netdev = netdev_find(netconfig->ifindex);
+	struct l_rtnl_address *address = NULL;
 
 	if (netconfig->rtm_v6_protocol == RTPROT_UNSPEC) {
 		l_debug("IPV6 configuration disabled");
@@ -952,10 +1038,33 @@ static bool netconfig_ipv6_select_and_install(struct netconfig *netconfig)
 
 	sysfs_write_ipv6_setting(netdev_get_name(netdev), "disable_ipv6", "0");
 
-	if (netconfig->rtm_v6_protocol == RTPROT_STATIC) {
-		struct l_rtnl_address *address =
-			netconfig_get_static6_address(netconfig);
+	if (netconfig->rtm_v6_protocol == RTPROT_STATIC)
+		address = netconfig_get_static6_address(netconfig);
+	else if (netconfig->rtm_v6_protocol == RTPROT_DHCP &&
+			netconfig->fils_override &&
+			!l_memeqzero(netconfig->fils_override->ipv6_addr, 16)) {
+		uint8_t prefix_len = netconfig->fils_override->ipv6_prefix_len;
+		L_AUTO_FREE_VAR(char *, addr_str) = netconfig_ipv6_to_string(
+					netconfig->fils_override->ipv6_addr);
+
+		if (unlikely(!addr_str))
+			return false;
+
+		if (L_WARN_ON(unlikely(!(address = l_rtnl_address_new(addr_str,
+								prefix_len)))))
+			return false;
+
+		l_rtnl_address_set_noprefixroute(address, true);
+
+		/*
+		 * TODO: If netconfig->fils_override->ipv6_lifetime is set,
+		 * start a timeout to renew the address using FILS IP Address
+		 * Assignment or perhaps just start the DHCP client at that
+		 * time.
+		 */
+	}
 
+	if (address) {
 		L_WARN_ON(!(netconfig->addr6_add_cmd_id =
 			l_rtnl_ifaddr_add(rtnl, netconfig->ifindex, address,
 					netconfig_ipv6_ifaddr_add_cmd_cb,
@@ -1167,6 +1276,8 @@ bool netconfig_reset(struct netconfig *netconfig)
 						"disable_ipv6", "1");
 	}
 
+	l_free(l_steal_ptr(netconfig->fils_override));
+
 	return true;
 }
 
@@ -1184,6 +1295,35 @@ char *netconfig_get_dhcp_server_ipv4(struct netconfig *netconfig)
 	return l_dhcp_lease_get_server_id(lease);
 }
 
+bool netconfig_get_fils_ip_req(struct netconfig *netconfig,
+				struct ie_fils_ip_addr_request_info *info)
+{
+	/*
+	 * Fill in the fields used for building the FILS IP Address Assigment
+	 * IE during connection if we're configured to do automatic network
+	 * configuration (usually DHCP).  If we're configured with static
+	 * values return false to mean the IE should not be sent.
+	 */
+	if (netconfig->rtm_protocol != RTPROT_DHCP &&
+			netconfig->rtm_v6_protocol != RTPROT_DHCP)
+		return false;
+
+	memset(info, 0, sizeof(*info));
+	info->ipv4 = (netconfig->rtm_protocol == RTPROT_DHCP);
+	info->ipv6 = (netconfig->rtm_v6_protocol == RTPROT_DHCP);
+	info->dns = (info->ipv4 && !netconfig->dns4_overrides) ||
+		(info->ipv6 && !netconfig->dns6_overrides);
+
+	return true;
+}
+
+void netconfig_handle_fils_ip_resp(struct netconfig *netconfig,
+			const struct ie_fils_ip_addr_response_info *info)
+{
+	l_free(netconfig->fils_override);
+	netconfig->fils_override = l_memdup(info, sizeof(*info));
+}
+
 struct netconfig *netconfig_new(uint32_t ifindex)
 {
 	struct netdev *netdev = netdev_find(ifindex);
diff --git a/src/netconfig.h b/src/netconfig.h
index 73e4df8b..fa46c7c8 100644
--- a/src/netconfig.h
+++ b/src/netconfig.h
@@ -21,6 +21,8 @@
  */
 
 struct netconfig;
+struct ie_fils_ip_addr_request_info;
+struct ie_fils_ip_addr_response_info;
 
 enum netconfig_event {
 	NETCONFIG_EVENT_CONNECTED,
@@ -38,6 +40,10 @@ bool netconfig_configure(struct netconfig *netconfig,
 bool netconfig_reconfigure(struct netconfig *netconfig);
 bool netconfig_reset(struct netconfig *netconfig);
 char *netconfig_get_dhcp_server_ipv4(struct netconfig *netconfig);
+bool netconfig_get_fils_ip_req(struct netconfig *netconfig,
+				struct ie_fils_ip_addr_request_info *info);
+void netconfig_handle_fils_ip_resp(struct netconfig *netconfig,
+			const struct ie_fils_ip_addr_response_info *info);
 
 struct netconfig *netconfig_new(uint32_t ifindex);
 void netconfig_destroy(struct netconfig *netconfig);
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 9/9] station, netdev: Enable FILS IP Address Assignment
  2021-08-23 14:14 [PATCH 1/9] handshake: Add HANDSHAKE_EVENT_P2P_IP_REQUEST Andrew Zaborowski
                   ` (6 preceding siblings ...)
  2021-08-23 14:14 ` [PATCH 8/9] netconfig: FILS IP assigment API Andrew Zaborowski
@ 2021-08-23 14:14 ` Andrew Zaborowski
  7 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2021-08-23 14:14 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 4591 bytes --]

Send and receive the FILS IP Address Assignment IEs during association.
As implemented this would work independently of FILS although the only
AP software handling this mechanism without FILS is likely IWD itself.

No support is added for handling the IP assignment information sent from
the server after the initial Association Request/Response frames, i.e.
the information is only used if it is received directly in the
Association Response without the "response pending" bit, otherwise the
DHCP client will be started.
---
 src/handshake.c |  2 ++
 src/handshake.h |  2 ++
 src/netdev.c    | 15 +++++++++++++++
 src/station.c   | 40 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 59 insertions(+)

diff --git a/src/handshake.c b/src/handshake.c
index f36df572..146434c2 100644
--- a/src/handshake.c
+++ b/src/handshake.c
@@ -106,6 +106,8 @@ void handshake_state_free(struct handshake_state *s)
 	l_free(s->supplicant_rsnxe);
 	l_free(s->mde);
 	l_free(s->fte);
+	l_free(s->fils_ip_req_ie);
+	l_free(s->fils_ip_resp_ie);
 
 	if (s->erp_cache)
 		erp_cache_put(s->erp_cache);
diff --git a/src/handshake.h b/src/handshake.h
index 31dce117..f3f6680b 100644
--- a/src/handshake.h
+++ b/src/handshake.h
@@ -140,6 +140,8 @@ struct handshake_state {
 	uint32_t client_ip_addr;
 	uint32_t subnet_mask;
 	uint32_t go_ip_addr;
+	uint8_t *fils_ip_req_ie;
+	uint8_t *fils_ip_resp_ie;
 	void *user_data;
 
 	void (*free)(struct handshake_state *s);
diff --git a/src/netdev.c b/src/netdev.c
index d886efad..d7945d04 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -292,6 +292,9 @@ static unsigned int netdev_populate_common_ies(struct netdev *netdev,
 
 	c_iov = iov_ie_append(iov, n_iov, c_iov, hs->vendor_ies);
 
+	if (hs->fils_ip_req_ie)
+		c_iov = iov_ie_append(iov, n_iov, c_iov, hs->fils_ip_req_ie);
+
 	return c_iov;
 }
 
@@ -2186,6 +2189,18 @@ process_resp_ies:
 				qos_set = data;
 				qos_len = ie_tlv_iter_get_length(&iter);
 				break;
+			case IE_TYPE_FILS_IP_ADDRESS:
+				if (netdev->handshake->fils_ip_resp_ie) {
+					l_debug("Duplicate response FILS IP "
+						"Address Assignment IE");
+					l_free(netdev->handshake->
+						fils_ip_resp_ie);
+				}
+
+				netdev->handshake->fils_ip_resp_ie = l_memdup(
+					data - 3,
+					ie_tlv_iter_get_length(&iter) + 3);
+				break;
 			}
 		}
 
diff --git a/src/station.c b/src/station.c
index f6237ade..9f00bfb3 100644
--- a/src/station.c
+++ b/src/station.c
@@ -924,6 +924,7 @@ static struct handshake_state *station_handshake_setup(struct station *station,
 	struct handshake_state *hs;
 	const struct iovec *vendor_ies;
 	size_t iov_elems = 0;
+	struct ie_fils_ip_addr_request_info fils_ip_req;
 
 	hs = netdev_handshake_state_new(station->netdev);
 
@@ -940,6 +941,16 @@ static struct handshake_state *station_handshake_setup(struct station *station,
 	vendor_ies = network_info_get_extra_ies(info, bss, &iov_elems);
 	handshake_state_set_vendor_ies(hs, vendor_ies, iov_elems);
 
+	/*
+	 * It can't hurt to try the FILS IP Address Assigment independent of
+	 * which auth-proto is actually used.
+	 */
+	if (station->netconfig && netconfig_get_fils_ip_req(station->netconfig,
+								&fils_ip_req)) {
+		hs->fils_ip_req_ie = l_malloc(32);
+		ie_build_fils_ip_addr_request(&fils_ip_req, hs->fils_ip_req_ie);
+	}
+
 	return hs;
 
 not_supported:
@@ -2444,6 +2455,35 @@ static void station_connect_ok(struct station *station)
 	network_connected(station->connected_network);
 
 	if (station->netconfig) {
+		if (hs->fils_ip_req_ie && hs->fils_ip_resp_ie) {
+			struct ie_fils_ip_addr_response_info info;
+			struct ie_tlv_iter iter;
+			int r;
+
+			ie_tlv_iter_init(&iter, hs->fils_ip_resp_ie,
+						hs->fils_ip_resp_ie[1] + 2);
+			ie_tlv_iter_next(&iter);
+			r = ie_parse_fils_ip_addr_response(&iter, &info);
+
+			if (r != 0)
+				l_debug("Error parsing the FILS IP Address "
+					"Assignment response: %s (%i)",
+					strerror(-r), -r);
+			else if (info.response_pending &&
+					info.response_timeout)
+				l_debug("FILS IP Address Assignment response "
+					"is pending (unsupported)");
+			else if (info.response_pending)
+				l_debug("FILS IP Address Assignment failed");
+			else {
+				l_debug("FILS IP Address Assignment response "
+					"OK");
+				netconfig_handle_fils_ip_resp(
+							station->netconfig,
+							&info);
+			}
+		}
+
 		if (L_WARN_ON(!netconfig_configure(station->netconfig,
 						station_netconfig_event_handler,
 						station)))
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 5/9] ie: Add FILS IP Address Assignment parsers and builders
  2021-08-23 14:14 ` [PATCH 5/9] ie: Add FILS IP Address Assignment parsers and builders Andrew Zaborowski
@ 2021-08-25 13:47   ` Denis Kenzior
  2021-08-25 21:34     ` Andrew Zaborowski
  0 siblings, 1 reply; 15+ messages in thread
From: Denis Kenzior @ 2021-08-25 13:47 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 1072 bytes --]

Hi Andrew,

On 8/23/21 9:14 AM, Andrew Zaborowski wrote:
> ---
>   src/ie.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   src/ie.h |  38 ++++++
>   2 files changed, 385 insertions(+)
> 

<snip>

> +	if (*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IPV4_GW_INCLUDED) {
> +		ptr = NEXT_FIELD(data, len, 10);
> +		info.ipv4_gateway = l_get_u32(ptr);
> +		memcpy(info.ipv4_gateway_mac, ptr + 4, 6);
> +
> +		/* Check gateway is on the same subnet */
> +		if (info.ipv4_addr && (ntohl(info.ipv4_addr ^ info.ipv4_gateway) &
> +				util_netmask_from_prefix(info.ipv4_prefix_len)))

This line is over 80 chars.  Also, it seems you're repeating this pattern in 
many places by now.  Please add a utility function so one doesn't wonder what 
this is doing on every reading ;)

> +			return -EINVAL;
> +	}
> +

<snip>

> +
> +			if (mask && ((info.ipv6_addr[n] ^
> +						info.ipv6_gateway[n]) & mask))

Same thing here it seems

> +				return -EINVAL;
> +		}
> +	}
> +

I went ahead and applied patches 1-6.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 7/9] netconfig: Move loading settings to new method, refactor
  2021-08-23 14:14 ` [PATCH 7/9] netconfig: Move loading settings to new method, refactor Andrew Zaborowski
@ 2021-08-25 13:50   ` Denis Kenzior
  2021-08-25 22:17     ` Andrew Zaborowski
  0 siblings, 1 reply; 15+ messages in thread
From: Denis Kenzior @ 2021-08-25 13:50 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 3171 bytes --]

On 8/23/21 9:14 AM, Andrew Zaborowski wrote:
> Split loading settings out of network_configure into a new method,
> network_load_settings.  Make sure both consistently handle errors by
> printing messages and informing the caller.
> ---
>   src/netconfig.c | 157 +++++++++++++++++++++++++++++++-----------------
>   src/netconfig.h |   5 +-
>   src/p2p.c       |  12 +++-
>   src/station.c   |  20 +++---
>   4 files changed, 127 insertions(+), 67 deletions(-)
> 

   CC       src/netconfig.o
src/netconfig.c: In function ‘netconfig_get_static4_address’:
src/netconfig.c:298:5: error: implicit declaration of function 
‘util_netmask_from_prefix’ [-Werror=implicit-function-declaration]
   298 |     util_netmask_from_prefix(prefix_len)) {
       |     ^~~~~~~~~~~~~~~~~~~~~~~~
src/netconfig.c:297:29: error: comparison of integer expressions of different 
signedness: ‘__uint32_t’ {aka ‘unsigned int’} and ‘int’ [-Werror=sign-compare]
   297 |   if (ntohl(in_addr.s_addr) !=
       |                             ^~
cc1: all warnings being treated as errors

<snip>

> @@ -998,22 +997,22 @@ static int validate_dns_list(int family, char **dns_list)
>   	return n_valid;
>   }
>   
> -bool netconfig_configure(struct netconfig *netconfig,
> +bool netconfig_load_settings(struct netconfig *netconfig,
>   				const struct l_settings *active_settings,
> -				const uint8_t *mac_address,
> -				netconfig_notify_func_t notify, void *user_data)
> +				const uint8_t *mac_address)
>   {
>   	char *mdns;
> -	char hostname[HOST_NAME_MAX + 1];
>   	bool send_hostname;
> +	bool enabled;
>   
> +	l_strfreev(netconfig->dns4_overrides);

? shouldn't this be taken care of by netconfig_reset?  We generally don't like 
side-effects in our APIs.

>   	netconfig->dns4_overrides = l_settings_get_string_list(active_settings,
>   							"IPv4", "DNS", ' ');
>   
>   	if (netconfig->dns4_overrides) {
>   		int r = validate_dns_list(AF_INET, netconfig->dns4_overrides);
>   
> -		if (r <= 0) {
> +		if (unlikely(r <= 0)) {
>   			l_strfreev(netconfig->dns4_overrides);
>   			netconfig->dns4_overrides = NULL;
>   		}
> @@ -1022,13 +1021,14 @@ bool netconfig_configure(struct netconfig *netconfig,
>   			l_error("netconfig: Empty IPv4.DNS entry, skipping...");
>   	}
>   
> +	l_strfreev(netconfig->dns6_overrides);

Same here?

>   	netconfig->dns6_overrides = l_settings_get_string_list(active_settings,
>   							"IPv6", "DNS", ' ');
>   
>   	if (netconfig->dns6_overrides) {
>   		int r = validate_dns_list(AF_INET6, netconfig->dns6_overrides);
>   
> -		if (r <= 0) {
> +		if (unlikely(r <= 0)) {
>   			l_strfreev(netconfig->dns6_overrides);
>   			netconfig->dns6_overrides = NULL;
>   		}
> @@ -1038,8 +1038,6 @@ bool netconfig_configure(struct netconfig *netconfig,
>   	}
>   
>   	netconfig->active_settings = active_settings;

Do we even need active settings now?

> -	netconfig->notify = notify;
> -	netconfig->user_data = user_data;
>   
>   	l_dhcp_client_set_address(netconfig->dhcp_client, ARPHRD_ETHER,
>   							mac_address, ETH_ALEN);

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 5/9] ie: Add FILS IP Address Assignment parsers and builders
  2021-08-25 13:47   ` Denis Kenzior
@ 2021-08-25 21:34     ` Andrew Zaborowski
  0 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2021-08-25 21:34 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 1028 bytes --]

Hi Denis.

On Wed, 25 Aug 2021 at 15:49, Denis Kenzior <denkenz@gmail.com> wrote:
> On 8/23/21 9:14 AM, Andrew Zaborowski wrote:
> > ---
> >   src/ie.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >   src/ie.h |  38 ++++++
> >   2 files changed, 385 insertions(+)
> >
>
> <snip>
>
> > +     if (*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IPV4_GW_INCLUDED) {
> > +             ptr = NEXT_FIELD(data, len, 10);
> > +             info.ipv4_gateway = l_get_u32(ptr);
> > +             memcpy(info.ipv4_gateway_mac, ptr + 4, 6);
> > +
> > +             /* Check gateway is on the same subnet */
> > +             if (info.ipv4_addr && (ntohl(info.ipv4_addr ^ info.ipv4_gateway) &
> > +                             util_netmask_from_prefix(info.ipv4_prefix_len)))
>
> This line is over 80 chars.  Also, it seems you're repeating this pattern in
> many places by now.  Please add a utility function so one doesn't wonder what
> this is doing on every reading ;)

Ok, makes sense.

Best regards

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 7/9] netconfig: Move loading settings to new method, refactor
  2021-08-25 13:50   ` Denis Kenzior
@ 2021-08-25 22:17     ` Andrew Zaborowski
  2021-08-25 22:37       ` Denis Kenzior
  0 siblings, 1 reply; 15+ messages in thread
From: Andrew Zaborowski @ 2021-08-25 22:17 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 4209 bytes --]

On Wed, 25 Aug 2021 at 15:53, Denis Kenzior <denkenz@gmail.com> wrote:
> On 8/23/21 9:14 AM, Andrew Zaborowski wrote:
> > Split loading settings out of network_configure into a new method,
> > network_load_settings.  Make sure both consistently handle errors by
> > printing messages and informing the caller.
> > ---
> >   src/netconfig.c | 157 +++++++++++++++++++++++++++++++-----------------
> >   src/netconfig.h |   5 +-
> >   src/p2p.c       |  12 +++-
> >   src/station.c   |  20 +++---
> >   4 files changed, 127 insertions(+), 67 deletions(-)
> >
>
>    CC       src/netconfig.o
> src/netconfig.c: In function ‘netconfig_get_static4_address’:
> src/netconfig.c:298:5: error: implicit declaration of function
> ‘util_netmask_from_prefix’ [-Werror=implicit-function-declaration]
>    298 |     util_netmask_from_prefix(prefix_len)) {
>        |     ^~~~~~~~~~~~~~~~~~~~~~~~
> src/netconfig.c:297:29: error: comparison of integer expressions of different
> signedness: ‘__uint32_t’ {aka ‘unsigned int’} and ‘int’ [-Werror=sign-compare]
>    297 |   if (ntohl(in_addr.s_addr) !=
>        |                             ^~
> cc1: all warnings being treated as errors

Oops, apparently I added the #include in the wrong commit.

>
> <snip>
>
> > @@ -998,22 +997,22 @@ static int validate_dns_list(int family, char **dns_list)
> >       return n_valid;
> >   }
> >
> > -bool netconfig_configure(struct netconfig *netconfig,
> > +bool netconfig_load_settings(struct netconfig *netconfig,
> >                               const struct l_settings *active_settings,
> > -                             const uint8_t *mac_address,
> > -                             netconfig_notify_func_t notify, void *user_data)
> > +                             const uint8_t *mac_address)
> >   {
> >       char *mdns;
> > -     char hostname[HOST_NAME_MAX + 1];
> >       bool send_hostname;
> > +     bool enabled;
> >
> > +     l_strfreev(netconfig->dns4_overrides);
>
> ? shouldn't this be taken care of by netconfig_reset?

I thought we might have a future use case for calling
netconfig_load_settings() multiple times without netconfig_reset
between, but no strong motivation for that.

>  We generally don't like
> side-effects in our APIs.

I wouldn't call avoiding a leak a side effect ;)

>
> >       netconfig->dns4_overrides = l_settings_get_string_list(active_settings,
> >                                                       "IPv4", "DNS", ' ');
> >
> >       if (netconfig->dns4_overrides) {
> >               int r = validate_dns_list(AF_INET, netconfig->dns4_overrides);
> >
> > -             if (r <= 0) {
> > +             if (unlikely(r <= 0)) {
> >                       l_strfreev(netconfig->dns4_overrides);
> >                       netconfig->dns4_overrides = NULL;
> >               }
> > @@ -1022,13 +1021,14 @@ bool netconfig_configure(struct netconfig *netconfig,
> >                       l_error("netconfig: Empty IPv4.DNS entry, skipping...");
> >       }
> >
> > +     l_strfreev(netconfig->dns6_overrides);
>
> Same here?
>
> >       netconfig->dns6_overrides = l_settings_get_string_list(active_settings,
> >                                                       "IPv6", "DNS", ' ');
> >
> >       if (netconfig->dns6_overrides) {
> >               int r = validate_dns_list(AF_INET6, netconfig->dns6_overrides);
> >
> > -             if (r <= 0) {
> > +             if (unlikely(r <= 0)) {
> >                       l_strfreev(netconfig->dns6_overrides);
> >                       netconfig->dns6_overrides = NULL;
> >               }
> > @@ -1038,8 +1038,6 @@ bool netconfig_configure(struct netconfig *netconfig,
> >       }
> >
> >       netconfig->active_settings = active_settings;
>
> Do we even need active settings now?

So in this method I only removed the usage of the l_settings from
netconfig_configure() but not from other places (*_get_gateway(),
netconfig_set_domains()) which may be called asynchronously.  It's
something we could do separately if desired but my use case was only
for setting the value of netconfig->rtm_protocol early.

Best regards

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 7/9] netconfig: Move loading settings to new method, refactor
  2021-08-25 22:17     ` Andrew Zaborowski
@ 2021-08-25 22:37       ` Denis Kenzior
  2021-08-25 22:56         ` Andrew Zaborowski
  0 siblings, 1 reply; 15+ messages in thread
From: Denis Kenzior @ 2021-08-25 22:37 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 1055 bytes --]

Hi Andrew,

>>> +     l_strfreev(netconfig->dns4_overrides);
>>
>> ? shouldn't this be taken care of by netconfig_reset?
> 
> I thought we might have a future use case for calling
> netconfig_load_settings() multiple times without netconfig_reset
> between, but no strong motivation for that.
> 

Can you elaborate on why we would want that?  But even more reason not to 
side-effect.

>>   We generally don't like
>> side-effects in our APIs.
> 
> I wouldn't call avoiding a leak a side effect ;)
> 

My point is, you shouldn't be setting anything inside netconfig until you know 
the operation will succeed completely.  Doing otherwise makes error handling a 
nightmare.

netconfig_configure would never fail since it would ignore invalid input.  In 
fact, I'm not sure why it had a bool return in the first place.

In this patch you're changing the contract such that netconfig_load_settings 
checks up-front if the networking settings are correct or not.  Side-effecting 
is not desirable in this case.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 7/9] netconfig: Move loading settings to new method, refactor
  2021-08-25 22:37       ` Denis Kenzior
@ 2021-08-25 22:56         ` Andrew Zaborowski
  0 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2021-08-25 22:56 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 1477 bytes --]

On Thu, 26 Aug 2021 at 00:43, Denis Kenzior <denkenz@gmail.com> wrote:
> > I thought we might have a future use case for calling
> > netconfig_load_settings() multiple times without netconfig_reset
> > between, but no strong motivation for that.
> >
>
> Can you elaborate on why we would want that?

When implementing a protocol similar to the FILS or P2P early IP
allocation, we might want to first load only the user l_settings to
find out whether the user even wants automatic netconfig.  Then
calling load_settings() would be one way, not necessarily the best
way, to pass the newly received network configuration to netconfig.c.

But even then calling netconfig_reset() in between is an option.

> But even more reason not to
> side-effect.

True.

>
> >>   We generally don't like
> >> side-effects in our APIs.
> >
> > I wouldn't call avoiding a leak a side effect ;)
> >
>
> My point is, you shouldn't be setting anything inside netconfig until you know
> the operation will succeed completely.  Doing otherwise makes error handling a
> nightmare.
>
> netconfig_configure would never fail since it would ignore invalid input.  In
> fact, I'm not sure why it had a bool return in the first place.
>
> In this patch you're changing the contract such that netconfig_load_settings
> checks up-front if the networking settings are correct or not.  Side-effecting
> is not desirable in this case.

Agreed, I'll fix this.

Best regards

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2021-08-25 22:56 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-23 14:14 [PATCH 1/9] handshake: Add HANDSHAKE_EVENT_P2P_IP_REQUEST Andrew Zaborowski
2021-08-23 14:14 ` [PATCH 2/9] ap: Implement P2P GO-side 4-way handshake IP Allocation Andrew Zaborowski
2021-08-23 14:14 ` [PATCH 3/9] autotests: Test GO-side IP Allocation in testP2P Andrew Zaborowski
2021-08-23 14:14 ` [PATCH 4/9] ap: Expire client's leases on disconnect Andrew Zaborowski
2021-08-23 14:14 ` [PATCH 5/9] ie: Add FILS IP Address Assignment parsers and builders Andrew Zaborowski
2021-08-25 13:47   ` Denis Kenzior
2021-08-25 21:34     ` Andrew Zaborowski
2021-08-23 14:14 ` [PATCH 6/9] ap: Support FILS IP Address Assignment IE Andrew Zaborowski
2021-08-23 14:14 ` [PATCH 7/9] netconfig: Move loading settings to new method, refactor Andrew Zaborowski
2021-08-25 13:50   ` Denis Kenzior
2021-08-25 22:17     ` Andrew Zaborowski
2021-08-25 22:37       ` Denis Kenzior
2021-08-25 22:56         ` Andrew Zaborowski
2021-08-23 14:14 ` [PATCH 8/9] netconfig: FILS IP assigment API Andrew Zaborowski
2021-08-23 14:14 ` [PATCH 9/9] station, netdev: Enable FILS IP Address Assignment Andrew Zaborowski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).