All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED
@ 2022-09-21 22:31 James Prestwood
  2022-09-21 22:31 ` [PATCH v4 02/15] nl80211util: include frame type with build_cmd_frame James Prestwood
                   ` (14 more replies)
  0 siblings, 15 replies; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

FT is now driven (mostly) by station which removes the connect
callback. Instead once FT is completed, keys set, etc. netdev
will send an event to notify station.
---
 src/netdev.h  | 1 +
 src/station.c | 2 ++
 2 files changed, 3 insertions(+)

diff --git a/src/netdev.h b/src/netdev.h
index dcf3ad1b..c5933ad0 100644
--- a/src/netdev.h
+++ b/src/netdev.h
@@ -50,6 +50,7 @@ enum netdev_event {
 	NETDEV_EVENT_RSSI_THRESHOLD_HIGH,
 	NETDEV_EVENT_RSSI_LEVEL_NOTIFY,
 	NETDEV_EVENT_PACKET_LOSS_NOTIFY,
+	NETDEV_EVENT_FT_ROAMED,
 };
 
 enum netdev_watch_event {
diff --git a/src/station.c b/src/station.c
index 79d2c755..868bb725 100644
--- a/src/station.c
+++ b/src/station.c
@@ -3129,6 +3129,8 @@ static void station_netdev_event(struct netdev *netdev, enum netdev_event event,
 	case NETDEV_EVENT_PACKET_LOSS_NOTIFY:
 		station_packets_lost(station, l_get_u32(event_data));
 		break;
+	case NETDEV_EVENT_FT_ROAMED:
+		break;
 	}
 }
 
-- 
2.34.3


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

* [PATCH v4 02/15] nl80211util: include frame type with build_cmd_frame
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
@ 2022-09-21 22:31 ` James Prestwood
  2022-09-21 22:31 ` [PATCH v4 03/15] wiphy: add new work priority for FT James Prestwood
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

The CMD_FRAME builder assumed action frames but can just as easily
be used with any frame type.
---
 src/netdev.c      |  1 +
 src/nl80211util.c | 18 +++++++++---------
 src/nl80211util.h |  1 +
 src/rrm.c         |  2 +-
 4 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/src/netdev.c b/src/netdev.c
index 7a14f1dd..7f4101be 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -4338,6 +4338,7 @@ static uint32_t netdev_send_action_framev(struct netdev *netdev,
 {
 	uint32_t id;
 	struct l_genl_msg *msg = nl80211_build_cmd_frame(netdev->index,
+								0x00d0,
 								netdev->addr,
 								to, freq,
 								iov, iov_len);
diff --git a/src/nl80211util.c b/src/nl80211util.c
index 8fe86ee6..5ba0097f 100644
--- a/src/nl80211util.c
+++ b/src/nl80211util.c
@@ -431,6 +431,7 @@ const void *nl80211_parse_get_key_seq(struct l_genl_msg *msg)
 }
 
 struct l_genl_msg *nl80211_build_cmd_frame(uint32_t ifindex,
+						uint16_t frame_type,
 						const uint8_t *addr,
 						const uint8_t *to,
 						uint32_t freq,
@@ -439,18 +440,17 @@ struct l_genl_msg *nl80211_build_cmd_frame(uint32_t ifindex,
 {
 	struct l_genl_msg *msg;
 	struct iovec iovs[iov_len + 1];
-	const uint16_t frame_type = 0x00d0;
-	uint8_t action_frame[24];
+	uint8_t hdr[24];
 
-	memset(action_frame, 0, 24);
+	memset(hdr, 0, 24);
 
-	l_put_le16(frame_type, action_frame + 0);
-	memcpy(action_frame + 4, to, 6);
-	memcpy(action_frame + 10, addr, 6);
-	memcpy(action_frame + 16, to, 6);
+	l_put_le16(frame_type, hdr + 0);
+	memcpy(hdr + 4, to, 6);
+	memcpy(hdr + 10, addr, 6);
+	memcpy(hdr + 16, to, 6);
 
-	iovs[0].iov_base = action_frame;
-	iovs[0].iov_len = sizeof(action_frame);
+	iovs[0].iov_base = hdr;
+	iovs[0].iov_len = sizeof(hdr);
 	memcpy(iovs + 1, iov, sizeof(*iov) * iov_len);
 
 	msg = l_genl_msg_new_sized(NL80211_CMD_FRAME, 128 + 512);
diff --git a/src/nl80211util.h b/src/nl80211util.h
index 1d32b700..44555a25 100644
--- a/src/nl80211util.h
+++ b/src/nl80211util.h
@@ -49,6 +49,7 @@ struct l_genl_msg *nl80211_build_get_key(uint32_t ifindex, uint8_t key_index);
 const void *nl80211_parse_get_key_seq(struct l_genl_msg *msg);
 
 struct l_genl_msg *nl80211_build_cmd_frame(uint32_t ifindex,
+						uint16_t frame_type,
 						const uint8_t *addr,
 						const uint8_t *to,
 						uint32_t freq,
diff --git a/src/rrm.c b/src/rrm.c
index fceff5c7..ed44cee6 100644
--- a/src/rrm.c
+++ b/src/rrm.c
@@ -194,7 +194,7 @@ static bool rrm_send_response(struct rrm_state *rrm,
 	iov.iov_base = (void *)frame;
 	iov.iov_len = len;
 
-	msg = nl80211_build_cmd_frame(rrm->ifindex, own_addr, bss->addr,
+	msg = nl80211_build_cmd_frame(rrm->ifindex, 0x00d0, own_addr, bss->addr,
 					bss->frequency, &iov, 1);
 
 	if (!l_genl_family_send(nl80211, msg, rrm_send_response_cb,
-- 
2.34.3


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

* [PATCH v4 03/15] wiphy: add new work priority for FT
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
  2022-09-21 22:31 ` [PATCH v4 02/15] nl80211util: include frame type with build_cmd_frame James Prestwood
@ 2022-09-21 22:31 ` James Prestwood
  2022-09-21 22:31 ` [PATCH v4 04/15] offchannel: add priority to start call James Prestwood
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

FT is special in that it really should not be interrupted. Since
FRAME/OFFCHANNEL have the highest priority we run the risk of
DPP or some other offchannel operation interfering with FT.
---
 src/wiphy.h | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/wiphy.h b/src/wiphy.h
index 2159fc00..9cd18451 100644
--- a/src/wiphy.h
+++ b/src/wiphy.h
@@ -46,11 +46,12 @@ struct wiphy_radio_work_item {
 };
 
 enum {
-	WIPHY_WORK_PRIORITY_FRAME = 0,
-	WIPHY_WORK_PRIORITY_OFFCHANNEL = 0,
-	WIPHY_WORK_PRIORITY_CONNECT = 1,
-	WIPHY_WORK_PRIORITY_SCAN = 2,
-	WIPHY_WORK_PRIORITY_PERIODIC_SCAN = 3,
+	WIPHY_WORK_PRIORITY_FT = 0,
+	WIPHY_WORK_PRIORITY_FRAME = 1,
+	WIPHY_WORK_PRIORITY_OFFCHANNEL = 1,
+	WIPHY_WORK_PRIORITY_CONNECT = 2,
+	WIPHY_WORK_PRIORITY_SCAN = 3,
+	WIPHY_WORK_PRIORITY_PERIODIC_SCAN = 4,
 };
 
 enum wiphy_state_watch_event {
-- 
2.34.3


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

* [PATCH v4 04/15] offchannel: add priority to start call
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
  2022-09-21 22:31 ` [PATCH v4 02/15] nl80211util: include frame type with build_cmd_frame James Prestwood
  2022-09-21 22:31 ` [PATCH v4 03/15] wiphy: add new work priority for FT James Prestwood
@ 2022-09-21 22:31 ` James Prestwood
  2022-09-21 22:31 ` [PATCH v4 05/15] ft: netdev: prep for FT isolation into ft.c James Prestwood
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

This will let the caller specify their own priority.
---
 src/dpp.c        | 1 +
 src/offchannel.c | 9 ++++-----
 src/offchannel.h | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/dpp.c b/src/dpp.c
index 2c8832ff..6abf539a 100644
--- a/src/dpp.c
+++ b/src/dpp.c
@@ -1622,6 +1622,7 @@ static void dpp_start_offchannel(struct dpp_sm *dpp, uint32_t freq)
 	 * called).
 	 */
 	uint32_t id = offchannel_start(netdev_get_wdev_id(dpp->netdev),
+				WIPHY_WORK_PRIORITY_OFFCHANNEL,
 				freq, dpp->dwell, dpp_roc_started,
 				dpp, dpp_presence_timeout);
 
diff --git a/src/offchannel.c b/src/offchannel.c
index 80d4ad56..b9cdc117 100644
--- a/src/offchannel.c
+++ b/src/offchannel.c
@@ -150,9 +150,9 @@ static const struct wiphy_radio_work_item_ops offchannel_work_ops = {
 	.destroy = offchannel_work_destroy,
 };
 
-uint32_t offchannel_start(uint64_t wdev_id, uint32_t freq, uint32_t duration,
-			offchannel_started_cb_t started, void *user_data,
-			offchannel_destroy_cb_t destroy)
+uint32_t offchannel_start(uint64_t wdev_id, int priority, uint32_t freq,
+			uint32_t duration, offchannel_started_cb_t started,
+			void *user_data, offchannel_destroy_cb_t destroy)
 {
 	struct offchannel_info *info = l_new(struct offchannel_info, 1);
 
@@ -169,8 +169,7 @@ uint32_t offchannel_start(uint64_t wdev_id, uint32_t freq, uint32_t duration,
 	info->error = -ECANCELED;
 
 	return wiphy_radio_work_insert(wiphy_find_by_wdev(wdev_id), &info->work,
-					WIPHY_WORK_PRIORITY_OFFCHANNEL,
-					&offchannel_work_ops);
+					priority, &offchannel_work_ops);
 }
 
 void offchannel_cancel(uint64_t wdev_id, uint32_t id)
diff --git a/src/offchannel.h b/src/offchannel.h
index 1ffa94f1..7912f1e8 100644
--- a/src/offchannel.h
+++ b/src/offchannel.h
@@ -23,7 +23,7 @@
 typedef void (*offchannel_started_cb_t)(void *user_data);
 typedef void (*offchannel_destroy_cb_t)(int error, void *user_data);
 
-uint32_t offchannel_start(uint64_t wdev_id, uint32_t freq, uint32_t duration,
-			offchannel_started_cb_t started, void *user_data,
-			offchannel_destroy_cb_t destroy);
+uint32_t offchannel_start(uint64_t wdev_id, int priority, uint32_t freq,
+			uint32_t duration, offchannel_started_cb_t started,
+			void *user_data, offchannel_destroy_cb_t destroy);
 void offchannel_cancel(uint64_t wdev_id, uint32_t id);
-- 
2.34.3


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

* [PATCH v4 05/15] ft: netdev: prep for FT isolation into ft.c
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
                   ` (2 preceding siblings ...)
  2022-09-21 22:31 ` [PATCH v4 04/15] offchannel: add priority to start call James Prestwood
@ 2022-09-21 22:31 ` James Prestwood
  2022-09-22  2:40   ` Denis Kenzior
  2022-09-21 22:31 ` [PATCH v4 06/15] netdev: add FT TX frame hook James Prestwood
                   ` (10 subsequent siblings)
  14 siblings, 1 reply; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

Currently netdev handles caching FT auth information and uses FT
parsers/auth-proto to manage the protocol. This sets up to remove
this state machine from netdev and isolate it into ft.c.

This does not break the existing auth-proto (hence the slight
modifications, which will be removed soon).

Eventually the auth-proto will be removed from FT entirely, replaced
just by an FT state machine, similar to how EAPoL works (netdev hooks
to TX/RX frames).
---
 src/ft.c     | 312 ++++++++++++++++++++++++++++++++++++++++++++++++---
 src/ft.h     |  22 +++-
 src/netdev.c |  28 +++--
 3 files changed, 329 insertions(+), 33 deletions(-)

diff --git a/src/ft.c b/src/ft.c
index 2285a86f..5f37b29a 100644
--- a/src/ft.c
+++ b/src/ft.c
@@ -33,6 +33,30 @@
 #include "src/mpdu.h"
 #include "src/auth-proto.h"
 #include "src/band.h"
+#include "src/scan.h"
+#include "src/util.h"
+#include "src/netdev.h"
+#include "src/module.h"
+
+static ft_tx_frame_func_t tx_frame = NULL;
+static ft_tx_associate_func_t tx_assoc = NULL;
+static struct l_queue *info_list = NULL;
+
+struct ft_info {
+	uint32_t ifindex;
+	uint8_t spa[6];
+	uint8_t aa[6];
+	uint8_t snonce[32];
+	uint8_t mde[3];
+	uint8_t *fte;
+	uint8_t *authenticator_ie;
+	uint8_t prev_bssid[6];
+	uint32_t frequency;
+
+	struct ie_ft_info ft_info;
+
+	bool parsed : 1;
+};
 
 struct ft_sm {
 	struct auth_proto ap;
@@ -45,6 +69,9 @@ struct ft_sm {
 	void *user_data;
 
 	bool over_ds : 1;
+
+	uint8_t prev_bssid[6];
+	struct l_queue *ft_auths;
 };
 
 /*
@@ -202,11 +229,13 @@ static bool ft_parse_associate_resp_frame(const uint8_t *frame, size_t frame_len
 	return true;
 }
 
-static int ft_tx_reassociate(struct ft_sm *ft)
+static int ft_tx_reassociate(uint32_t ifindex, uint32_t freq,
+				const uint8_t *prev_bssid)
 {
+	struct netdev *netdev = netdev_find(ifindex);
+	struct handshake_state *hs = netdev_get_handshake(netdev);
 	struct iovec iov[3];
 	int iov_elems = 0;
-	struct handshake_state *hs = ft->hs;
 	uint32_t kck_len = handshake_state_get_kck_len(hs);
 	bool is_rsn = hs->supplicant_ie != NULL;
 	uint8_t *rsne = NULL;
@@ -233,9 +262,8 @@ static int ft_tx_reassociate(struct ft_sm *ft)
 		rsn_info.num_pmkids = 1;
 		rsn_info.pmkids = hs->pmk_r1_name;
 
-		/* Always set OCVC false for FT-over-DS */
-		if (ft->over_ds)
-			rsn_info.ocvc = false;
+		/* Always set OCVC false for FT for now */
+		rsn_info.ocvc = false;
 
 		rsne = alloca(256);
 		ie_build_rsne(&rsn_info, rsne);
@@ -306,7 +334,7 @@ static int ft_tx_reassociate(struct ft_sm *ft)
 		iov_elems += 1;
 	}
 
-	return ft->tx_assoc(iov, iov_elems, ft->user_data);
+	return tx_assoc(ifindex, freq, prev_bssid, iov, iov_elems);
 
 error:
 	return -EINVAL;
@@ -348,7 +376,7 @@ static bool ft_verify_rsne(const uint8_t *rsne, const uint8_t *pmk_r0_name,
 	return true;
 }
 
-static int ft_parse_ies(struct handshake_state *hs,
+static int parse_ies(struct handshake_state *hs,
 			const uint8_t *authenticator_ie,
 			const uint8_t *ies, size_t ies_len,
 			const uint8_t **mde_out,
@@ -480,7 +508,7 @@ bool ft_over_ds_parse_action_ies(struct ft_ds_info *info,
 	const uint8_t *fte = NULL;
 	bool is_rsn = hs->supplicant_ie != NULL;
 
-	if (ft_parse_ies(hs, info->authenticator_ie, ies, ies_len,
+	if (parse_ies(hs, info->authenticator_ie, ies, ies_len,
 				&mde, &fte) < 0)
 		return false;
 
@@ -512,7 +540,7 @@ static int ft_process_ies(struct handshake_state *hs, const uint8_t *ies,
 	if (!ies)
 		goto ft_error;
 
-	if (ft_parse_ies(hs, hs->authenticator_ie, ies, ies_len,
+	if (parse_ies(hs, hs->authenticator_ie, ies, ies_len,
 				&mde, &fte) < 0)
 		goto ft_error;
 
@@ -654,11 +682,10 @@ auth_error:
 	return (int)status_code;
 }
 
-static int ft_rx_associate(struct auth_proto *ap, const uint8_t *frame,
-				size_t frame_len)
+int __ft_rx_associate(uint32_t ifindex, const uint8_t *frame, size_t frame_len)
 {
-	struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
-	struct handshake_state *hs = ft->hs;
+	struct netdev *netdev = netdev_find(ifindex);
+	struct handshake_state *hs = netdev_get_handshake(netdev);
 	uint32_t kck_len = handshake_state_get_kck_len(hs);
 	const uint8_t *rsne = NULL;
 	const uint8_t *mde = NULL;
@@ -671,6 +698,9 @@ static int ft_rx_associate(struct auth_proto *ap, const uint8_t *frame,
 					&mde, &fte))
 		return -EBADMSG;
 
+	if (out_status != 0)
+		return (int)out_status;
+
 	/*
 	 * During a transition in an RSN, check for an RSNE containing the
 	 * PMK-R1-Name and the remaining fields same as in the advertised
@@ -785,17 +815,25 @@ static int ft_rx_associate(struct auth_proto *ap, const uint8_t *frame,
 						ft_info.igtk_ipn);
 		}
 
-		handshake_state_install_ptk(ft->hs);
+		handshake_state_install_ptk(hs);
 	}
 
 	return 0;
 }
 
+static int ft_rx_associate(struct auth_proto *ap, const uint8_t *frame,
+				size_t frame_len)
+{
+	struct ft_sm *sm = l_container_of(ap, struct ft_sm, ap);
+
+	return __ft_rx_associate(sm->hs->ifindex, frame, frame_len);
+}
+
 static int ft_rx_oci(struct auth_proto *ap)
 {
 	struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
 
-	return ft_tx_reassociate(ft);
+	return ft_tx_reassociate(ft->hs->ifindex, 0, ft->prev_bssid);
 }
 
 static void ft_sm_free(struct auth_proto *ap)
@@ -809,7 +847,7 @@ static bool ft_over_ds_start(struct auth_proto *ap)
 {
 	struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
 
-	return ft_tx_reassociate(ft) == 0;
+	return ft_tx_reassociate(ft->hs->ifindex, 0, ft->prev_bssid) == 0;
 }
 
 bool ft_build_authenticate_ies(struct handshake_state *hs, bool ocvc,
@@ -924,6 +962,8 @@ struct auth_proto *ft_over_air_sm_new(struct handshake_state *hs,
 	ft->ap.free = ft_sm_free;
 	ft->ap.rx_oci = ft_rx_oci;
 
+	memcpy(ft->prev_bssid, hs->aa, 6);
+
 	return &ft->ap;
 }
 
@@ -942,5 +982,245 @@ struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
 	ft->ap.start = ft_over_ds_start;
 	ft->ap.free = ft_sm_free;
 
+	memcpy(ft->prev_bssid, hs->aa, 6);
+
 	return &ft->ap;
 }
+
+void __ft_set_tx_frame_func(ft_tx_frame_func_t func)
+{
+	tx_frame = func;
+}
+
+void __ft_set_tx_associate_func(ft_tx_associate_func_t func)
+{
+	tx_assoc = func;
+}
+
+static bool ft_parse_ies(struct ft_info *info, struct handshake_state *hs,
+			const uint8_t *ies, size_t ies_len)
+{
+	const uint8_t *mde = NULL;
+	const uint8_t *fte = NULL;
+	bool is_rsn = hs->supplicant_ie != NULL;
+
+	if (parse_ies(hs, info->authenticator_ie, ies, ies_len,
+				&mde, &fte) < 0)
+		return false;
+
+	if (!mde_equal(info->mde, mde))
+		goto ft_error;
+
+	if (is_rsn) {
+		if (!ft_parse_fte(hs, info->snonce, fte, &info->ft_info))
+			goto ft_error;
+
+		info->fte = l_memdup(fte, fte[1] + 2);
+	} else if (fte)
+		goto ft_error;
+
+	return true;
+
+ft_error:
+	return false;
+}
+
+static struct ft_info *ft_info_find(uint32_t ifindex, const uint8_t *aa)
+{
+	const struct l_queue_entry *e;
+
+	for (e = l_queue_get_entries(info_list); e; e = e->next) {
+		struct ft_info *info = e->data;
+
+		if (info->ifindex != ifindex)
+			continue;
+
+		if (aa && memcmp(info->aa, aa, 6))
+			continue;
+
+		return info;
+	}
+
+	return NULL;
+}
+
+void __ft_rx_action(uint32_t ifindex, const uint8_t *frame, size_t frame_len)
+{
+	struct netdev *netdev = netdev_find(ifindex);
+	struct handshake_state *hs = netdev_get_handshake(netdev);
+	struct ft_info *info;
+	int ret;
+	const uint8_t *aa = NULL;
+	const uint8_t *spa = NULL;
+	const uint8_t *ies = NULL;
+	size_t ies_len = 0;
+
+	ret = ft_over_ds_parse_action_response(frame, frame_len, &spa, &aa,
+						&ies, &ies_len);
+	if (ret != 0)
+		return;
+
+	info = ft_info_find(ifindex, aa);
+	if (!info)
+		return;
+
+	if (!ft_parse_ies(info, hs, ies, ies_len))
+		goto ft_error;
+
+	info->parsed = true;
+
+	return;
+
+ft_error:
+	l_debug("FT-over-DS authenticate to "MAC" failed", MAC_STR(info->aa));
+}
+
+static struct ft_info *ft_info_new(struct handshake_state *hs,
+					const struct scan_bss *target_bss)
+{
+	struct ft_info *info = l_new(struct ft_info, 1);
+
+	info->ifindex = hs->ifindex;
+	memcpy(info->spa, hs->spa, 6);
+	memcpy(info->aa, target_bss->addr, 6);
+	memcpy(info->mde, target_bss->mde, sizeof(info->mde));
+	memcpy(info->prev_bssid, hs->aa, 6);
+
+	info->frequency = target_bss->frequency;
+
+	if (target_bss->rsne)
+		info->authenticator_ie = l_memdup(target_bss->rsne,
+						target_bss->rsne[1] + 2);
+
+	l_getrandom(info->snonce, 32);
+
+	return info;
+}
+
+static void ft_info_destroy(void *data)
+{
+	struct ft_info *info = data;
+
+	if (info->fte)
+		l_free(info->fte);
+
+	if (info->authenticator_ie)
+		l_free(info->authenticator_ie);
+
+	l_free(info);
+}
+
+static void ft_prepare_handshake(struct ft_info *info,
+					struct handshake_state *hs)
+{
+	if (!hs->supplicant_ie)
+		return;
+
+	memcpy(hs->snonce, info->snonce, sizeof(hs->snonce));
+
+	handshake_state_set_fte(hs, info->fte);
+
+	handshake_state_set_anonce(hs, info->ft_info.anonce);
+
+	handshake_state_set_kh_ids(hs, info->ft_info.r0khid,
+						info->ft_info.r0khid_len,
+						info->ft_info.r1khid);
+
+	handshake_state_derive_ptk(hs);
+}
+
+int ft_action(uint32_t ifindex, uint32_t freq, const struct scan_bss *target)
+{
+	struct netdev *netdev = netdev_find(ifindex);
+	struct handshake_state *hs = netdev_get_handshake(netdev);
+	struct ft_info *info;
+	uint8_t ft_req[14];
+	struct iovec iov[5];
+	uint8_t ies[512];
+	size_t len;
+	int ret = -EINVAL;
+
+	info = ft_info_new(hs, target);
+
+	ft_req[0] = 6; /* FT category */
+	ft_req[1] = 1; /* FT Request action */
+	memcpy(ft_req + 2, info->spa, 6);
+	memcpy(ft_req + 8, info->aa, 6);
+
+	if (!ft_build_authenticate_ies(hs, hs->supplicant_ocvc, info->snonce,
+					ies, &len))
+		goto failed;
+
+	iov[0].iov_base = ft_req;
+	iov[0].iov_len = sizeof(ft_req);
+
+	iov[1].iov_base = ies;
+	iov[1].iov_len = len;
+
+	ret = tx_frame(hs->ifindex, 0x00d0, freq, hs->aa, iov, 2);
+	if (ret < 0)
+		goto failed;
+
+	l_queue_push_tail(info_list, info);
+
+	return 0;
+
+failed:
+	l_free(info);
+	return ret;
+}
+
+int ft_associate(uint32_t ifindex, const uint8_t *addr)
+{
+	struct netdev *netdev = netdev_find(ifindex);
+	struct handshake_state *hs = netdev_get_handshake(netdev);
+	struct ft_info *info;
+	int ret;
+
+	/*
+	 * TODO: Since FT-over-DS is done early, before the time of roaming, it
+	 *       may end up that a completely new BSS is the best candidate and
+	 *       we haven't yet authenticated. We could actually authenticate
+	 *       at this point, but for now just assume the caller will choose
+	 *       a different BSS.
+	 */
+	info = ft_info_find(ifindex, addr);
+	if (!info)
+		return -ENOENT;
+
+	ft_prepare_handshake(info, hs);
+
+	ret = ft_tx_reassociate(ifindex, info->frequency, info->prev_bssid);
+
+	/* After this no previous auths will be valid */
+	l_queue_clear(info_list, ft_info_destroy);
+
+	return ret;
+}
+
+void ft_reset(uint32_t ifindex)
+{
+	struct ft_info *info;
+
+	while ((info = ft_info_find(ifindex, NULL))) {
+		l_queue_remove(info_list, info);
+		ft_info_destroy(info);
+	}
+}
+
+static int ft_init(void)
+{
+	info_list = l_queue_new();
+
+	return 0;
+}
+
+static void ft_exit(void)
+{
+	if (!l_queue_isempty(info_list))
+		l_warn("stale FT info objects found!");
+
+	l_queue_destroy(info_list, ft_info_destroy);
+}
+
+IWD_MODULE(ft, ft_init, ft_exit);
diff --git a/src/ft.h b/src/ft.h
index f90fc1b2..2228c90b 100644
--- a/src/ft.h
+++ b/src/ft.h
@@ -21,11 +21,19 @@
  */
 
 struct handshake_state;
+struct scan_bss;
+
+typedef int (*ft_tx_frame_func_t)(uint32_t ifindex, uint16_t frame_type,
+					uint32_t frequency,
+					const uint8_t *dest, struct iovec *iov,
+					size_t iov_len);
 
 typedef void (*ft_tx_authenticate_func_t)(struct iovec *iov, size_t iov_len,
 					void *user_data);
-typedef int (*ft_tx_associate_func_t)(struct iovec *ie_iov, size_t iov_len,
-					void *user_data);
+
+typedef int (*ft_tx_associate_func_t)(uint32_t ifindex, uint32_t freq,
+					const uint8_t *prev_bssid,
+					struct iovec *ie_iov, size_t iov_len);
 typedef int (*ft_get_oci)(void *user_data);
 
 typedef void (*ft_ds_free_func_t)(void *user_data);
@@ -71,3 +79,13 @@ struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
 
 bool ft_over_ds_prepare_handshake(struct ft_ds_info *info,
 					struct handshake_state *hs);
+
+void __ft_set_tx_frame_func(ft_tx_frame_func_t func);
+void __ft_set_tx_associate_func(ft_tx_associate_func_t func);
+int __ft_rx_associate(uint32_t ifindex, const uint8_t *frame,
+			size_t frame_len);
+void __ft_rx_action(uint32_t ifindex, const uint8_t *frame, size_t frame_len);
+
+void ft_reset(uint32_t ifindex);
+int ft_action(uint32_t ifindex, uint32_t freq, const struct scan_bss *target);
+int ft_associate(uint32_t ifindex, const uint8_t *addr);
diff --git a/src/netdev.c b/src/netdev.c
index 7f4101be..3510c0a6 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -4409,11 +4409,11 @@ restore_snonce:
 					MMPDU_STATUS_CODE_UNSPECIFIED);
 }
 
-static int netdev_ft_tx_associate(struct iovec *ft_iov, size_t n_ft_iov,
-					void *user_data)
+static int netdev_ft_tx_associate(uint32_t ifindex, uint32_t freq,
+					const uint8_t *prev_bssid,
+					struct iovec *ft_iov, size_t n_ft_iov)
 {
-	struct netdev *netdev = user_data;
-	struct auth_proto *ap = netdev->ap;
+	struct netdev *netdev = netdev_find(ifindex);
 	struct handshake_state *hs = netdev->handshake;
 	struct l_genl_msg *msg;
 	struct iovec iov[64];
@@ -4434,7 +4434,7 @@ static int netdev_ft_tx_associate(struct iovec *ft_iov, size_t n_ft_iov,
 	mpdu_sort_ies(subtype, iov, c_iov);
 
 	l_genl_msg_append_attr(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
-				ap->prev_bssid);
+				prev_bssid);
 	l_genl_msg_append_attrv(msg, NL80211_ATTR_IE, iov, c_iov);
 
 	netdev->connect_cmd_id = l_genl_family_send(nl80211, msg,
@@ -4646,17 +4646,15 @@ int netdev_fast_transition(struct netdev *netdev,
 			l_get_le16(target_bss->mde))
 		return -EINVAL;
 
-	prepare_ft(netdev, target_bss);
-
-	handshake_state_new_snonce(netdev->handshake);
-
 	netdev->connect_cb = cb;
 
 	netdev->ap = ft_over_air_sm_new(netdev->handshake,
 					netdev_ft_tx_authenticate,
 					netdev_ft_tx_associate,
 					netdev_get_oci, netdev);
-	memcpy(netdev->ap->prev_bssid, orig_bss->addr, ETH_ALEN);
+	prepare_ft(netdev, target_bss);
+
+	handshake_state_new_snonce(netdev->handshake);
 
 	wiphy_radio_work_insert(netdev->wiphy, &netdev->work,
 				WIPHY_WORK_PRIORITY_CONNECT, &ft_work_ops);
@@ -4688,16 +4686,14 @@ int netdev_fast_transition_over_ds(struct netdev *netdev,
 	if (!info || !info->parsed)
 		return -ENOENT;
 
-	prepare_ft(netdev, target_bss);
-
-	ft_over_ds_prepare_handshake(&info->super, netdev->handshake);
-
 	netdev->connect_cb = cb;
 
 	netdev->ap = ft_over_ds_sm_new(netdev->handshake,
 					netdev_ft_tx_associate,
 					netdev);
-	memcpy(netdev->ap->prev_bssid, orig_bss->addr, ETH_ALEN);
+	prepare_ft(netdev, target_bss);
+
+	ft_over_ds_prepare_handshake(&info->super, netdev->handshake);
 
 	wiphy_radio_work_insert(netdev->wiphy, &netdev->work,
 				WIPHY_WORK_PRIORITY_CONNECT, &ft_work_ops);
@@ -6643,6 +6639,8 @@ static int netdev_init(void)
 	__eapol_set_tx_packet_func(netdev_control_port_frame);
 	__eapol_set_install_pmk_func(netdev_set_pmk);
 
+	__ft_set_tx_associate_func(netdev_ft_tx_associate);
+
 	unicast_watch = l_genl_add_unicast_watch(genl, NL80211_GENL_NAME,
 						netdev_unicast_notify,
 						NULL, NULL);
-- 
2.34.3


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

* [PATCH v4 06/15] netdev: add FT TX frame hook
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
                   ` (3 preceding siblings ...)
  2022-09-21 22:31 ` [PATCH v4 05/15] ft: netdev: prep for FT isolation into ft.c James Prestwood
@ 2022-09-21 22:31 ` James Prestwood
  2022-09-21 22:31 ` [PATCH v4 07/15] ft: implement offchannel authentication James Prestwood
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

A netdev hook for FT to send out frames. This will be used both for
FT-over-DS action frames and FT-over-Air authentication.
---
 src/netdev.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/src/netdev.c b/src/netdev.c
index 3510c0a6..60d832d2 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -4367,6 +4367,38 @@ static uint32_t netdev_send_action_frame(struct netdev *netdev,
 						user_data);
 }
 
+static void netdev_ft_frame_cb(struct l_genl_msg *msg, void *user_data)
+{
+	if (l_genl_msg_get_error(msg) < 0)
+		l_debug("Failed to send FT-Frame");
+}
+
+static int netdev_tx_ft_frame_frame(uint32_t ifindex, uint16_t frame_type,
+					uint32_t frequency, const uint8_t *dest,
+					struct iovec *iov, size_t iov_len)
+{
+	struct netdev *netdev = netdev_find(ifindex);
+	struct l_genl_msg *msg = nl80211_build_cmd_frame(netdev->index,
+							frame_type,
+							netdev->addr, dest,
+							frequency,
+							iov, iov_len);
+
+	/*
+	 * Even though the kernel is doing offchannel for Authentication this
+	 * flag is still required otherwise the kernel gives -EBUSY.
+	 */
+	l_genl_msg_append_attr(msg, NL80211_ATTR_OFFCHANNEL_TX_OK, 0, NULL);
+
+	if (!l_genl_family_send(nl80211, msg, netdev_ft_frame_cb,
+				netdev, NULL)) {
+		l_genl_msg_unref(msg);
+		return -EIO;
+	}
+
+	return 0;
+}
+
 static void netdev_cmd_authenticate_ft_cb(struct l_genl_msg *msg,
 						void *user_data)
 {
@@ -6639,6 +6671,7 @@ static int netdev_init(void)
 	__eapol_set_tx_packet_func(netdev_control_port_frame);
 	__eapol_set_install_pmk_func(netdev_set_pmk);
 
+	__ft_set_tx_frame_func(netdev_tx_ft_frame_frame);
 	__ft_set_tx_associate_func(netdev_ft_tx_associate);
 
 	unicast_watch = l_genl_add_unicast_watch(genl, NL80211_GENL_NAME,
-- 
2.34.3


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

* [PATCH v4 07/15] ft: implement offchannel authentication
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
                   ` (4 preceding siblings ...)
  2022-09-21 22:31 ` [PATCH v4 06/15] netdev: add FT TX frame hook James Prestwood
@ 2022-09-21 22:31 ` James Prestwood
  2022-09-21 22:31 ` [PATCH v4 08/15] station: create list of roam candidates James Prestwood
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

A new API was added, ft_authenticate, which will send an
authentication frame offchannel via CMD_FRAME. This bypasses
the kernel's authentication state allowing multiple auth
attempts to take place without disconnecting.
---
 src/ft.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/ft.h |  3 ++
 2 files changed, 92 insertions(+)

diff --git a/src/ft.c b/src/ft.c
index 5f37b29a..db060839 100644
--- a/src/ft.c
+++ b/src/ft.c
@@ -37,6 +37,8 @@
 #include "src/util.h"
 #include "src/netdev.h"
 #include "src/module.h"
+#include "src/offchannel.h"
+#include "src/wiphy.h"
 
 static ft_tx_frame_func_t tx_frame = NULL;
 static ft_tx_associate_func_t tx_assoc = NULL;
@@ -52,6 +54,7 @@ struct ft_info {
 	uint8_t *authenticator_ie;
 	uint8_t prev_bssid[6];
 	uint32_t frequency;
+	uint32_t offchannel_id;
 
 	struct ie_ft_info ft_info;
 
@@ -1170,6 +1173,92 @@ failed:
 	return ret;
 }
 
+void __ft_rx_authenticate(uint32_t ifindex, const uint8_t *frame,
+				size_t frame_len)
+{
+	struct netdev *netdev = netdev_find(ifindex);
+	struct handshake_state *hs = netdev_get_handshake(netdev);
+	struct ft_info *info;
+	uint16_t status;
+	const uint8_t *ies;
+	size_t ies_len;
+
+	info = ft_info_find(ifindex, NULL);
+	if (!info)
+		return;
+
+	if (!ft_parse_authentication_resp_frame(frame, frame_len,
+					info->spa, info->aa, info->aa, 2,
+					&status, &ies, &ies_len))
+		return;
+
+	if (status != 0)
+		return;
+
+	if (!ft_parse_ies(info, hs, ies, ies_len))
+		return;
+
+	info->parsed = true;
+
+	return;
+}
+
+static void ft_send_authenticate(void *user_data)
+{
+	struct ft_info *info = user_data;
+	struct netdev *netdev = netdev_find(info->ifindex);
+	struct handshake_state *hs = netdev_get_handshake(netdev);
+	uint8_t ies[256];
+	size_t len;
+	struct iovec iov[2];
+	struct mmpdu_authentication auth;
+
+	/* Authentication body */
+	auth.algorithm = L_CPU_TO_LE16(MMPDU_AUTH_ALGO_FT);
+	auth.transaction_sequence = L_CPU_TO_LE16(1);
+	auth.status = L_CPU_TO_LE16(0);
+
+	iov[0].iov_base = &auth;
+	iov[0].iov_len = sizeof(struct mmpdu_authentication);
+
+	if (!ft_build_authenticate_ies(hs, hs->supplicant_ocvc, info->snonce,
+					ies, &len))
+		return;
+
+	iov[1].iov_base = ies;
+	iov[1].iov_len = len;
+
+	tx_frame(info->ifindex, 0x00b0, info->frequency, info->aa, iov, 2);
+}
+
+static void ft_authenticate_destroy(int error, void *user_data)
+{
+	if (error == 0)
+		return;
+
+	l_debug("Error in authentication offchannel (%d)", error);
+
+	l_queue_clear(info_list, ft_info_destroy);
+}
+
+int ft_authenticate(uint32_t ifindex, const struct scan_bss *target)
+{
+	struct netdev *netdev = netdev_find(ifindex);
+	struct handshake_state *hs = netdev_get_handshake(netdev);
+	struct ft_info *info = ft_info_new(hs, target);
+
+	info->offchannel_id = offchannel_start(netdev_get_wdev_id(netdev),
+						WIPHY_WORK_PRIORITY_FT,
+						target->frequency,
+						200, ft_send_authenticate, info,
+						ft_authenticate_destroy);
+	l_queue_clear(info_list, ft_info_destroy);
+
+	l_queue_push_tail(info_list, info);
+
+	return 0;
+}
+
 int ft_associate(uint32_t ifindex, const uint8_t *addr)
 {
 	struct netdev *netdev = netdev_find(ifindex);
diff --git a/src/ft.h b/src/ft.h
index 2228c90b..89b70850 100644
--- a/src/ft.h
+++ b/src/ft.h
@@ -85,7 +85,10 @@ void __ft_set_tx_associate_func(ft_tx_associate_func_t func);
 int __ft_rx_associate(uint32_t ifindex, const uint8_t *frame,
 			size_t frame_len);
 void __ft_rx_action(uint32_t ifindex, const uint8_t *frame, size_t frame_len);
+void __ft_rx_authenticate(uint32_t ifindex, const uint8_t *frame,
+				size_t frame_len);
 
 void ft_reset(uint32_t ifindex);
 int ft_action(uint32_t ifindex, uint32_t freq, const struct scan_bss *target);
 int ft_associate(uint32_t ifindex, const uint8_t *addr);
+int ft_authenticate(uint32_t ifindex, const struct scan_bss *target);
-- 
2.34.3


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

* [PATCH v4 08/15] station: create list of roam candidates
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
                   ` (5 preceding siblings ...)
  2022-09-21 22:31 ` [PATCH v4 07/15] ft: implement offchannel authentication James Prestwood
@ 2022-09-21 22:31 ` James Prestwood
  2022-09-22  3:09   ` Denis Kenzior
  2022-09-21 22:31 ` [PATCH v4 09/15] netdev: hook in RX for FT-Action/Authentication/Association James Prestwood
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

The current behavior is to only find the best roam candidate, which
generally is fine. But if for whatever reason IWD fails to roam it
would be nice having a few backup BSS's rather than having to
re-scan, or worse disassociate and reconnect entirely.

This patch doesn't change the roam behavior, just prepares for
using a roam candidate list. One difference though is any roam
candidates are added to station->bss_list, rather than just the
best BSS. This shouldn't effect any external behavior.

The candidate list is built based on scan_bss rank. First we establish
a base rank, the rank of the current BSS (or zero if AP roaming). Any
BSS in the results with a higher rank, excluding the current BSS, will
be added to the sorted station->roam_bss_list as well as stations
overall BSS list. If the resulting list is empty there were no better
BSS's, otherwise station can now try to roam starting with the best
candidate (head of the roam list).
---
 src/station.c | 84 +++++++++++++++++++++++++++++++++------------------
 1 file changed, 54 insertions(+), 30 deletions(-)

diff --git a/src/station.c b/src/station.c
index 868bb725..df54936e 100644
--- a/src/station.c
+++ b/src/station.c
@@ -107,6 +107,7 @@ struct station {
 
 	/* Set of frequencies to scan first when attempting a roam */
 	struct scan_freq_set *roam_freqs;
+	struct l_queue *roam_bss_list;
 
 	/* Frequencies split into subsets by priority */
 	struct scan_freq_set *scan_freqs_order[3];
@@ -1640,6 +1641,11 @@ static void station_roam_state_clear(struct station *station)
 		scan_freq_set_free(station->roam_freqs);
 		station->roam_freqs = NULL;
 	}
+
+	if (station->roam_bss_list) {
+		l_queue_destroy(station->roam_bss_list, NULL);
+		station->roam_bss_list = NULL;
+	}
 }
 
 static void station_reset_connection_state(struct station *station)
@@ -1981,6 +1987,11 @@ static void station_roamed(struct station *station)
 			l_warn("Could not request neighbor report");
 	}
 
+	if (station->roam_bss_list) {
+		l_queue_destroy(station->roam_bss_list, NULL);
+		station->roam_bss_list = NULL;
+	}
+
 	station_ft_ds_action_start(station);
 
 	station_enter_state(station, STATION_STATE_CONNECTED);
@@ -2005,6 +2016,11 @@ static void station_roam_failed(struct station *station)
 {
 	l_debug("%u", netdev_get_ifindex(station->netdev));
 
+	if (station->roam_bss_list) {
+		l_queue_destroy(station->roam_bss_list, NULL);
+		station->roam_bss_list = NULL;
+	}
+
 	/*
 	 * If we attempted a reassociation or a fast transition, and ended up
 	 * here then we are now disconnected.
@@ -2186,8 +2202,7 @@ static void station_preauthenticate_cb(struct netdev *netdev,
 	station_transition_reassociate(station, bss, new_hs);
 }
 
-static void station_transition_start(struct station *station,
-							struct scan_bss *bss)
+static void station_transition_start(struct station *station)
 {
 	struct handshake_state *hs = netdev_get_handshake(station->netdev);
 	struct network *connected = station->connected_network;
@@ -2195,6 +2210,7 @@ static void station_transition_start(struct station *station,
 	struct handshake_state *new_hs;
 	struct ie_rsn_info cur_rsne, target_rsne;
 	int ret;
+	struct scan_bss *bss = l_queue_peek_head(station->roam_bss_list);
 
 	l_debug("%u, target %s", netdev_get_ifindex(station->netdev),
 			util_address_to_string(bss->addr));
@@ -2334,12 +2350,10 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
 	struct handshake_state *hs = netdev_get_handshake(station->netdev);
 	struct scan_bss *current_bss = station->connected_bss;
 	struct scan_bss *bss;
-	struct scan_bss *best_bss = NULL;
-	double best_bss_rank = 0.0;
+	double cur_bss_rank = 0.0;
 	static const double RANK_FT_FACTOR = 1.3;
 	uint16_t mdid;
 	enum security orig_security, security;
-	bool seen = false;
 
 	if (err) {
 		station_roam_failed(station);
@@ -2358,6 +2372,21 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
 		ie_parse_mobility_domain_from_data(hs->mde, hs->mde[1] + 2,
 							&mdid, NULL, NULL);
 
+	station->roam_bss_list = l_queue_new();
+
+	/*
+	 * Find the current BSS rank, use the updated result if it exists. If
+	 * this is an AP roam keep the current rank as zero to force the roam
+	 * to occur.
+	 */
+	bss = l_queue_find(bss_list, bss_match_bssid, current_bss->addr);
+	if (bss && !station->ap_directed_roaming) {
+		cur_bss_rank = bss->rank;
+
+		if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
+			cur_bss_rank *= RANK_FT_FACTOR;
+	}
+
 	/*
 	 * BSSes in the bss_list come already ranked with their initial
 	 * association preference rank value.  We only need to add preference
@@ -2379,9 +2408,8 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
 				bss->frequency, bss->rank, bss->signal_strength,
 				kbps100 / 10, kbps100 % 10);
 
-		/* Skip the BSS we are connected to if doing an AP roam */
-		if (station->ap_directed_roaming && !memcmp(bss->addr,
-				station->connected_bss->addr, 6))
+		/* Skip the BSS we are connected to */
+		if (!memcmp(bss->addr, station->connected_bss->addr, 6))
 			goto next;
 
 		/* Skip result if it is not part of the ESS */
@@ -2395,8 +2423,6 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
 		if (security != orig_security)
 			goto next;
 
-		seen = true;
-
 		if (network_can_connect_bss(network, bss) < 0)
 			goto next;
 
@@ -2408,15 +2434,19 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
 		if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
 			rank *= RANK_FT_FACTOR;
 
-		if (rank > best_bss_rank) {
-			if (best_bss)
-				scan_bss_free(best_bss);
+		if (rank <= cur_bss_rank)
+			goto next;
 
-			best_bss = bss;
-			best_bss_rank = rank;
+		/*
+		 * We need to update/add any potential roam candidate so
+		 * station/network know it exists.
+		 */
+		station_update_roam_bss(station, bss);
 
-			continue;
-		}
+		l_queue_insert(station->roam_bss_list, bss,
+				scan_bss_rank_compare, NULL);
+
+		continue;
 
 next:
 		scan_bss_free(bss);
@@ -2424,25 +2454,17 @@ next:
 
 	l_queue_destroy(bss_list, NULL);
 
-	if (!seen)
-		goto fail_free_bss;
-
 	/* See if we have anywhere to roam to */
-	if (!best_bss || scan_bss_addr_eq(best_bss, station->connected_bss)) {
+	if (l_queue_isempty(station->roam_bss_list)) {
 		station_debug_event(station, "no-roam-candidates");
-		goto fail_free_bss;
+		goto fail;
 	}
 
-	station_update_roam_bss(station, best_bss);
-
-	station_transition_start(station, best_bss);
+	station_transition_start(station);
 
 	return true;
 
-fail_free_bss:
-	if (best_bss)
-		scan_bss_free(best_bss);
-
+fail:
 	station_roam_failed(station);
 
 	return true;
@@ -4398,10 +4420,12 @@ static bool station_force_roam_scan_notify(int err, struct l_queue *bss_list,
 
 	/* The various roam routines expect this to be set from scanning */
 	station->preparing_roam = true;
+	station->roam_bss_list = l_queue_new();
+	l_queue_push_tail(station->roam_bss_list, target);
 
 	station_update_roam_bss(station, target);
 
-	station_transition_start(station, target);
+	station_transition_start(station);
 
 	reply = l_dbus_message_new_method_return(data->pending);
 
-- 
2.34.3


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

* [PATCH v4 09/15] netdev: hook in RX for FT-Action/Authentication/Association
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
                   ` (6 preceding siblings ...)
  2022-09-21 22:31 ` [PATCH v4 08/15] station: create list of roam candidates James Prestwood
@ 2022-09-21 22:31 ` James Prestwood
  2022-09-21 22:31 ` [PATCH v4 10/15] ft: update action response parsing to include header James Prestwood
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

This forwards Action, Authentication and Association frames to
ft.c via their new hooks in netdev.

Note that this will break FT-over-Air temporarily since the
auth-proto still is in use.
---
 src/netdev.c | 147 ++++++++++++++++++++++-----------------------------
 1 file changed, 64 insertions(+), 83 deletions(-)

diff --git a/src/netdev.c b/src/netdev.c
index 60d832d2..e14fb7cc 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -3192,13 +3192,15 @@ static void netdev_associate_event(struct l_genl_msg *msg,
 	const uint8_t *frame = NULL;
 	uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
 	int ret;
+	const struct mmpdu_header *hdr;
+	const struct mmpdu_association_response *assoc;
 
 	l_debug("");
 
 	if (!netdev->connected || netdev->aborting)
 		return;
 
-	if (!netdev->ap) {
+	if (!netdev->ap && !netdev->in_ft) {
 		netdev->associated = true;
 		netdev->in_reassoc = false;
 		return;
@@ -3238,61 +3240,59 @@ static void netdev_associate_event(struct l_genl_msg *msg,
 	if (L_WARN_ON(!frame))
 		goto assoc_failed;
 
-	if (netdev->ap) {
-		const struct mmpdu_header *hdr;
-		const struct mmpdu_association_response *assoc;
-
-		hdr = mpdu_validate(frame, frame_len);
-		if (L_WARN_ON(!hdr))
-			goto assoc_failed;
-
-		assoc = mmpdu_body(hdr);
-		status_code = L_CPU_TO_LE16(assoc->status_code);
+	hdr = mpdu_validate(frame, frame_len);
+	if (L_WARN_ON(!hdr))
+		goto assoc_failed;
 
-		ret = auth_proto_rx_associate(netdev->ap, frame, frame_len);
-		if (ret == 0) {
-			bool fils = !!(netdev->handshake->akm_suite &
-					(IE_RSN_AKM_SUITE_FILS_SHA256 |
-					 IE_RSN_AKM_SUITE_FILS_SHA384 |
-					 IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384 |
-					 IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256));
+	assoc = mmpdu_body(hdr);
+	status_code = L_CPU_TO_LE16(assoc->status_code);
 
+	if (netdev->ap)
+		ret = auth_proto_rx_associate(netdev->ap, frame,
+							frame_len);
+	else
+		ret = __ft_rx_associate(netdev->index, frame,
+							frame_len);
+	if (ret == 0) {
+		bool fils = !!(netdev->handshake->akm_suite &
+				(IE_RSN_AKM_SUITE_FILS_SHA256 |
+				 IE_RSN_AKM_SUITE_FILS_SHA384 |
+				 IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384 |
+				 IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256));
+
+		if (netdev->ap) {
 			auth_proto_free(netdev->ap);
 			netdev->ap = NULL;
+		}
 
-			netdev->sm = eapol_sm_new(netdev->handshake);
-			eapol_register(netdev->sm);
-
-			/* Just in case this was a retry */
-			netdev->ignore_connect_event = false;
-
-			/*
-			 * If in FT and/or FILS we don't force an initial 4-way
-			 * handshake and instead just keep the EAPoL state
-			 * machine for the rekeys.
-			 */
-			if (netdev->in_ft || fils)
-				eapol_sm_set_require_handshake(netdev->sm,
-								false);
+		netdev->sm = eapol_sm_new(netdev->handshake);
+		eapol_register(netdev->sm);
 
-			netdev->in_ft = false;
-			netdev->in_reassoc = false;
-			netdev->associated = true;
-			return;
-		} else if (ret == -EAGAIN) {
-			/*
-			 * Here to support OWE retries. OWE will retry
-			 * internally, but a connect event will still be emitted
-			 */
-			netdev->ignore_connect_event = true;
-			return;
-		} else if (ret > 0)
-			status_code = (uint16_t)ret;
+		/* Just in case this was a retry */
+		netdev->ignore_connect_event = false;
 
-		goto assoc_failed;
-	}
+		/*
+		 * If in FT and/or FILS we don't force an initial 4-way
+		 * handshake and instead just keep the EAPoL state
+		 * machine for the rekeys.
+		 */
+		if (netdev->in_ft || fils)
+			eapol_sm_set_require_handshake(netdev->sm,
+							false);
 
-	return;
+		netdev->in_ft = false;
+		netdev->in_reassoc = false;
+		netdev->associated = true;
+		return;
+	} else if (ret == -EAGAIN) {
+		/*
+		 * Here to support OWE retries. OWE will retry
+		 * internally, but a connect event will still be emitted
+		 */
+		netdev->ignore_connect_event = true;
+		return;
+	} else if (ret > 0)
+		status_code = (uint16_t)ret;
 
 assoc_failed:
 	netdev->result = NETDEV_RESULT_ASSOCIATION_FAILED;
@@ -4578,49 +4578,25 @@ static void netdev_ft_response_frame_event(const struct mmpdu_header *hdr,
 					int rssi, void *user_data)
 {
 	struct netdev *netdev = user_data;
-	struct netdev_ft_over_ds_info *info;
-	int ret;
-	uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
-	const uint8_t *aa;
-	const uint8_t *spa;
-	const uint8_t *ies;
-	size_t ies_len;
-	struct ft_ds_finder finder;
 
 	if (!netdev->connected)
 		return;
 
-	ret = ft_over_ds_parse_action_response(body, body_len, &spa, &aa,
-						&ies, &ies_len);
-	if (ret < 0)
-		return;
+	__ft_rx_action(netdev->index, (const uint8_t *)hdr,
+			mmpdu_header_len(hdr) + body_len);
+}
 
-	finder.spa = spa;
-	finder.aa = aa;
+static void netdev_ft_auth_response_frame_event(const struct mmpdu_header *hdr,
+					const void *body, size_t body_len,
+					int rssi, void *user_data)
+{
+	struct netdev *netdev = user_data;
 
-	info = l_queue_find(netdev->ft_ds_list, match_ft_ds_info, &finder);
-	if (!info)
+	if (!netdev->connected)
 		return;
 
-	/* Lookup successful, now check the status code */
-	if (ret > 0) {
-		status_code = (uint16_t)ret;
-		goto ft_error;
-	}
-
-	if (!ft_over_ds_parse_action_ies(&info->super, netdev->handshake,
-						ies, ies_len))
-		goto ft_error;
-
-	info->parsed = true;
-
-	return;
-
-ft_error:
-	l_debug("FT-over-DS to "MAC" failed (%d)", MAC_STR(info->super.aa),
-			status_code);
-
-	netdev_ft_over_ds_auth_failed(info, status_code);
+	__ft_rx_authenticate(netdev->index, (const uint8_t *)hdr,
+			mmpdu_header_len(hdr) + body_len);
 }
 
 static void netdev_qos_map_frame_event(const struct mmpdu_header *hdr,
@@ -5846,6 +5822,7 @@ static void netdev_add_station_frame_watches(struct netdev *netdev)
 	static const uint8_t action_sa_query_resp_prefix[2] = { 0x08, 0x01 };
 	static const uint8_t action_sa_query_req_prefix[2] = { 0x08, 0x00 };
 	static const uint8_t action_ft_response_prefix[] =  { 0x06, 0x02 };
+	static const uint8_t auth_ft_response_prefix[] = { 0x02, 0x00 };
 	static const uint8_t action_qos_map_prefix[] = { 0x01, 0x04 };
 	uint64_t wdev = netdev->wdev_id;
 
@@ -5866,6 +5843,10 @@ static void netdev_add_station_frame_watches(struct netdev *netdev)
 			sizeof(action_ft_response_prefix),
 			netdev_ft_response_frame_event, netdev, NULL);
 
+	frame_watch_add(wdev, 0, 0x00b0, auth_ft_response_prefix,
+			sizeof(auth_ft_response_prefix),
+			netdev_ft_auth_response_frame_event, netdev, NULL);
+
 	if (wiphy_supports_qos_set_map(netdev->wiphy))
 		frame_watch_add(wdev, 0, 0x00d0, action_qos_map_prefix,
 				sizeof(action_qos_map_prefix),
-- 
2.34.3


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

* [PATCH v4 10/15] ft: update action response parsing to include header
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
                   ` (7 preceding siblings ...)
  2022-09-21 22:31 ` [PATCH v4 09/15] netdev: hook in RX for FT-Action/Authentication/Association James Prestwood
@ 2022-09-21 22:31 ` James Prestwood
  2022-09-21 22:31 ` [PATCH v4 11/15] station: handle NETDEV_EVENT_FT_ROAMED James Prestwood
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

Now the full action frame including the header is provided to ft
which breaks the existing parser since it assumes the buffer starts
at the body of the message.
---
 src/ft.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/ft.c b/src/ft.c
index db060839..613c6477 100644
--- a/src/ft.c
+++ b/src/ft.c
@@ -580,13 +580,18 @@ int ft_over_ds_parse_action_response(const uint8_t *frame, size_t frame_len,
 					const uint8_t **ies_out,
 					size_t *ies_len)
 {
+	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
+	size_t hdr_len = mmpdu_header_len(hdr);
 	uint16_t status;
 	const uint8_t *aa;
 	const uint8_t *spa;
 
-	if (frame_len < 16)
+	if (frame_len < hdr_len + 16)
 		return -EINVAL;
 
+	frame += hdr_len;
+	frame_len -= hdr_len;
+
 	/* Category FT */
 	if (frame[0] != 6)
 		return -EINVAL;
@@ -598,6 +603,9 @@ int ft_over_ds_parse_action_response(const uint8_t *frame, size_t frame_len,
 	spa = frame + 2;
 	aa = frame + 8;
 
+	if (memcmp(spa, hdr->address_1, 6))
+		return -EINVAL;
+
 	status = l_get_le16(frame + 14);
 	if (status != 0)
 		return (int)status;
-- 
2.34.3


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

* [PATCH v4 11/15] station: handle NETDEV_EVENT_FT_ROAMED
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
                   ` (8 preceding siblings ...)
  2022-09-21 22:31 ` [PATCH v4 10/15] ft: update action response parsing to include header James Prestwood
@ 2022-09-21 22:31 ` James Prestwood
  2022-09-21 22:31 ` [PATCH v4 12/15] station: try multiple roam candidates James Prestwood
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

---
 src/station.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/station.c b/src/station.c
index df54936e..03b0ddcb 100644
--- a/src/station.c
+++ b/src/station.c
@@ -3152,6 +3152,10 @@ static void station_netdev_event(struct netdev *netdev, enum netdev_event event,
 		station_packets_lost(station, l_get_u32(event_data));
 		break;
 	case NETDEV_EVENT_FT_ROAMED:
+		if (station->state != STATION_STATE_ROAMING)
+			return;
+
+		station_roamed(station);
 		break;
 	}
 }
-- 
2.34.3


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

* [PATCH v4 12/15] station: try multiple roam candidates
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
                   ` (9 preceding siblings ...)
  2022-09-21 22:31 ` [PATCH v4 11/15] station: handle NETDEV_EVENT_FT_ROAMED James Prestwood
@ 2022-09-21 22:31 ` James Prestwood
  2022-09-21 22:31 ` [PATCH v4 13/15] netdev: ft: complete FT refactor James Prestwood
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

This converts station to using ft_action/ft_authenticate and
ft_associate and dropping the use of the netdev-only/auth-proto
logic.

Doing this allows for more flexibility if FT fails by letting
IWD try another roam candidate instead of disconnecting.
---
 src/station.c | 213 +++++++++++++++++++++++++++++++-------------------
 1 file changed, 131 insertions(+), 82 deletions(-)

diff --git a/src/station.c b/src/station.c
index 03b0ddcb..27858557 100644
--- a/src/station.c
+++ b/src/station.c
@@ -59,6 +59,7 @@
 #include "src/frame-xchg.h"
 #include "src/sysfs.h"
 #include "src/band.h"
+#include "src/ft.h"
 
 static struct l_queue *station_list;
 static uint32_t netdev_watch;
@@ -115,6 +116,8 @@ struct station {
 
 	uint32_t wiphy_watch;
 
+	struct wiphy_radio_work_item ft_work;
+
 	bool preparing_roam : 1;
 	bool roam_scan_full : 1;
 	bool signal_low : 1;
@@ -1646,6 +1649,8 @@ static void station_roam_state_clear(struct station *station)
 		l_queue_destroy(station->roam_bss_list, NULL);
 		station->roam_bss_list = NULL;
 	}
+
+	ft_reset(netdev_get_ifindex(station->netdev));
 }
 
 static void station_reset_connection_state(struct station *station)
@@ -1957,7 +1962,8 @@ static void station_ft_ds_action_start(struct station *station)
 		 * Fire and forget. Netdev will maintain a cache of responses
 		 * and when the time comes these can be referenced for a roam
 		 */
-		netdev_fast_transition_over_ds_action(station->netdev, bss);
+		ft_action(netdev_get_ifindex(station->netdev),
+				station->connected_bss->frequency, bss);
 	}
 }
 
@@ -2092,42 +2098,26 @@ static void station_reassociate_cb(struct netdev *netdev,
 		station_roam_failed(station);
 }
 
-static void station_fast_transition_cb(struct netdev *netdev,
-					enum netdev_result result,
-					void *event_data,
-					void *user_data)
-{
-	struct station *station = user_data;
-
-	l_debug("%u, result: %d", netdev_get_ifindex(station->netdev), result);
-
-	if (station->state != STATION_STATE_ROAMING)
-		return;
-
-	if (result == NETDEV_RESULT_OK)
-		station_roamed(station);
-	else
-		station_roam_failed(station);
-}
-
 static void station_netdev_event(struct netdev *netdev, enum netdev_event event,
 					void *event_data, void *user_data);
 
-static void station_transition_reassociate(struct station *station,
+static int station_transition_reassociate(struct station *station,
 						struct scan_bss *bss,
 						struct handshake_state *new_hs)
 {
-	if (netdev_reassociate(station->netdev, bss, station->connected_bss,
+	int ret;
+
+	ret = netdev_reassociate(station->netdev, bss, station->connected_bss,
 				new_hs, station_netdev_event,
-				station_reassociate_cb, station) < 0) {
-		handshake_state_free(new_hs);
-		station_roam_failed(station);
-		return;
-	}
+				station_reassociate_cb, station);
+	if (ret < 0)
+		return ret;
 
 	station->connected_bss = bss;
 	station->preparing_roam = false;
 	station_enter_state(station, STATION_STATE_ROAMING);
+
+	return 0;
 }
 
 static bool bss_match_bssid(const void *a, const void *b)
@@ -2199,18 +2189,98 @@ static void station_preauthenticate_cb(struct netdev *netdev,
 		handshake_state_set_supplicant_ie(new_hs, rsne_buf);
 	}
 
-	station_transition_reassociate(station, bss, new_hs);
+	if (station_transition_reassociate(station, bss, new_hs) < 0) {
+		handshake_state_free(new_hs);
+		station_roam_failed(station);
+	}
 }
 
-static void station_transition_start(struct station *station)
+static void station_transition_start(struct station *station);
+
+static bool station_ft_work_ready(struct wiphy_radio_work_item *item)
+{
+	struct station *station = l_container_of(item, struct station, ft_work);
+	struct scan_bss *bss = l_queue_pop_head(station->roam_bss_list);
+	int ret;
+
+	ret = ft_associate(netdev_get_ifindex(station->netdev), bss->addr);
+	if (ret == -ENOENT) {
+		station_transition_start(station);
+		return true;
+	} else if (ret < 0)
+		goto assoc_failed;
+
+	station->connected_bss = bss;
+	station->preparing_roam = false;
+	station_enter_state(station, STATION_STATE_ROAMING);
+
+	return true;
+
+assoc_failed:
+	station_roam_failed(station);
+	return true;
+}
+
+static const struct wiphy_radio_work_item_ops ft_work_ops = {
+	.do_work = station_ft_work_ready,
+};
+
+static bool station_fast_transition(struct station *station,
+					struct scan_bss *bss)
+{
+	struct handshake_state *hs = netdev_get_handshake(station->netdev);
+	struct network *connected = station->connected_network;
+	const struct network_info *info = network_get_info(connected);
+	const struct iovec *vendor_ies;
+	size_t iov_elems = 0;
+	int ret;
+
+	/* Rebuild handshake RSN for target AP */
+	if (station_build_handshake_rsn(hs, station->wiphy,
+				station->connected_network, bss) < 0)
+		return false;
+
+	/* Reset the vendor_ies in case they're different */
+	vendor_ies = network_info_get_extra_ies(info, bss, &iov_elems);
+	handshake_state_set_vendor_ies(hs, vendor_ies, iov_elems);
+
+	if ((hs->mde[4] & 1)) {
+		ret = ft_associate(netdev_get_ifindex(station->netdev),
+					bss->addr);
+		/* No action responses from this BSS, try over air */
+		if (ret == -ENOENT)
+			goto try_over_air;
+		else if (ret < 0)
+			return false;
+
+		station->connected_bss = bss;
+		station->preparing_roam = false;
+		station_enter_state(station, STATION_STATE_ROAMING);
+
+		return true;
+	} else {
+try_over_air:
+		/*
+		 * Send FT-Authenticate and insert a work item which will be
+		 * gated until authentication completes
+		 */
+		ft_authenticate(netdev_get_ifindex(station->netdev), bss);
+
+		wiphy_radio_work_insert(station->wiphy, &station->ft_work,
+				WIPHY_WORK_PRIORITY_CONNECT, &ft_work_ops);
+
+		return true;
+	}
+}
+
+static bool station_try_next_transition(struct station *station,
+					struct scan_bss *bss)
 {
 	struct handshake_state *hs = netdev_get_handshake(station->netdev);
 	struct network *connected = station->connected_network;
 	enum security security = network_get_security(connected);
 	struct handshake_state *new_hs;
 	struct ie_rsn_info cur_rsne, target_rsne;
-	int ret;
-	struct scan_bss *bss = l_queue_peek_head(station->roam_bss_list);
 
 	l_debug("%u, target %s", netdev_get_ifindex(station->netdev),
 			util_address_to_string(bss->addr));
@@ -2219,54 +2289,8 @@ static void station_transition_start(struct station *station)
 	station->ap_directed_roaming = false;
 
 	/* Can we use Fast Transition? */
-	if (station_can_fast_transition(hs, bss)) {
-		const struct network_info *info = network_get_info(connected);
-		const struct iovec *vendor_ies;
-		size_t iov_elems = 0;
-
-		/* Rebuild handshake RSN for target AP */
-		if (station_build_handshake_rsn(hs, station->wiphy,
-				station->connected_network, bss) < 0) {
-			l_error("rebuilding handshake rsne failed");
-			station_roam_failed(station);
-			return;
-		}
-
-		/* Reset the vendor_ies in case they're different */
-		vendor_ies = network_info_get_extra_ies(info, bss, &iov_elems);
-		handshake_state_set_vendor_ies(hs, vendor_ies, iov_elems);
-
-		if ((hs->mde[4] & 1)) {
-			ret = netdev_fast_transition_over_ds(station->netdev,
-					bss, station->connected_bss,
-					station_fast_transition_cb);
-			/* No action responses from this BSS, try over air */
-			if (ret == -ENOENT)
-				goto try_over_air;
-			else if (ret < 0) {
-				/*
-				 * If we are here FT-over-air will not work
-				 * either (identical checks) so try again later.
-				 */
-				station_roam_retry(station);
-				return;
-			}
-		} else {
-try_over_air:
-			if (netdev_fast_transition(station->netdev, bss,
-					station->connected_bss,
-					station_fast_transition_cb) < 0) {
-				station_roam_failed(station);
-				return;
-			}
-		}
-
-		station->connected_bss = bss;
-		station->preparing_roam = false;
-		station_enter_state(station, STATION_STATE_ROAMING);
-
-		return;
-	}
+	if (station_can_fast_transition(hs, bss))
+		return station_fast_transition(station, bss);
 
 	/* Non-FT transition */
 
@@ -2296,17 +2320,42 @@ try_over_air:
 		if (netdev_preauthenticate(station->netdev, bss,
 						station_preauthenticate_cb,
 						station) >= 0)
-			return;
+			return true;
 	}
 
 	new_hs = station_handshake_setup(station, connected, bss);
 	if (!new_hs) {
 		l_error("station_handshake_setup failed in reassociation");
-		station_roam_failed(station);
-		return;
+		return false;
+	}
+
+	if (station_transition_reassociate(station, bss, new_hs) < 0) {
+		handshake_state_free(new_hs);
+		return false;
 	}
 
-	station_transition_reassociate(station, bss, new_hs);
+	return true;
+}
+
+static void station_transition_start(struct station *station)
+{
+	struct scan_bss *bss;
+	bool roaming = false;
+
+	/*
+	 * For each failed attempt pop the BSS leaving the head of the queue
+	 * with the current roam candidate.
+	 */
+	while ((bss = l_queue_peek_head(station->roam_bss_list))) {
+		roaming = station_try_next_transition(station, bss);
+		if (roaming)
+			break;
+
+		l_queue_pop_head(station->roam_bss_list);
+	}
+
+	if (!roaming)
+		station_roam_failed(station);
 }
 
 static void station_roam_scan_triggered(int err, void *user_data)
-- 
2.34.3


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

* [PATCH v4 13/15] netdev: ft: complete FT refactor
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
                   ` (10 preceding siblings ...)
  2022-09-21 22:31 ` [PATCH v4 12/15] station: try multiple roam candidates James Prestwood
@ 2022-09-21 22:31 ` James Prestwood
  2022-09-21 22:31 ` [PATCH v4 14/15] netdev: remove FT auth proto James Prestwood
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

This finalizes the refactor by moving all the handshake prep
into FT itself (most was already in there). The netdev-specific
flags and state were added into netdev_ft_tx_associate which
now avoids any need for a netdev API related to FT.

The NETDEV_EVENT_FT_ROAMED event is now emitted once FT completes
(netdev_connect_ok). This did require moving the 'in_ft' flag
setting until after the keys are set into the kernel otherwise
netdev_connect_ok has no context as to if this was FT or some
other connection attempt.

In addition the prev_snonce was removed from netdev. Restoring
the snonce has no value once association begins. If association
fails it will result in a disconnect regardless which requires
a new snonce to be generated
---
 src/ft.c     | 10 ++++++++
 src/netdev.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 68 insertions(+), 7 deletions(-)

diff --git a/src/ft.c b/src/ft.c
index 613c6477..65cd38cb 100644
--- a/src/ft.c
+++ b/src/ft.c
@@ -1124,9 +1124,19 @@ static void ft_info_destroy(void *data)
 static void ft_prepare_handshake(struct ft_info *info,
 					struct handshake_state *hs)
 {
+	handshake_state_set_authenticator_address(hs, info->aa);
+
+	memcpy(hs->mde + 2, info->mde, 3);
+
+	handshake_state_set_chandef(hs, NULL);
+
 	if (!hs->supplicant_ie)
 		return;
 
+	if (info->authenticator_ie)
+		handshake_state_set_authenticator_ie(hs,
+							info->authenticator_ie);
+
 	memcpy(hs->snonce, info->snonce, sizeof(hs->snonce));
 
 	handshake_state_set_fte(hs, info->fte);
diff --git a/src/netdev.c b/src/netdev.c
index e14fb7cc..536ae644 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -1413,6 +1413,15 @@ static void netdev_connect_ok(struct netdev *netdev)
 			scan_bss_free(netdev->fw_roam_bss);
 
 		netdev->fw_roam_bss = NULL;
+	} else if (netdev->in_ft) {
+		if (netdev->event_filter)
+			netdev->event_filter(netdev, NETDEV_EVENT_FT_ROAMED,
+						NULL, netdev->user_data);
+		netdev->in_ft = false;
+	} else if (netdev->connect_cb) {
+		netdev->connect_cb(netdev, NETDEV_RESULT_OK, NULL,
+					netdev->user_data);
+		netdev->connect_cb = NULL;
 	}
 
 	if (netdev->ft_ds_list) {
@@ -1420,12 +1429,6 @@ static void netdev_connect_ok(struct netdev *netdev)
 		netdev->ft_ds_list = NULL;
 	}
 
-	if (netdev->connect_cb) {
-		netdev->connect_cb(netdev, NETDEV_RESULT_OK, NULL,
-					netdev->user_data);
-		netdev->connect_cb = NULL;
-	}
-
 	netdev_rssi_polling_update(netdev);
 
 	if (netdev->work.id)
@@ -3280,7 +3283,6 @@ static void netdev_associate_event(struct l_genl_msg *msg,
 			eapol_sm_set_require_handshake(netdev->sm,
 							false);
 
-		netdev->in_ft = false;
 		netdev->in_reassoc = false;
 		netdev->associated = true;
 		return;
@@ -4446,6 +4448,7 @@ static int netdev_ft_tx_associate(uint32_t ifindex, uint32_t freq,
 					struct iovec *ft_iov, size_t n_ft_iov)
 {
 	struct netdev *netdev = netdev_find(ifindex);
+	struct netdev_handshake_state *nhs;
 	struct handshake_state *hs = netdev->handshake;
 	struct l_genl_msg *msg;
 	struct iovec iov[64];
@@ -4454,6 +4457,54 @@ static int netdev_ft_tx_associate(uint32_t ifindex, uint32_t freq,
 	enum mpdu_management_subtype subtype =
 				MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_REQUEST;
 
+	/*
+	 * At this point there is no going back with FT so reset all the flags
+	 * needed to associate with a new BSS.
+	 */
+	netdev->frequency = freq;
+	netdev->handshake->active_tk_index = 0;
+	netdev->associated = false;
+	netdev->operational = false;
+	netdev->in_ft = true;
+
+	/*
+	 * Cancel commands that could be running because of EAPoL activity
+	 * like re-keying, this way the callbacks for those commands don't
+	 * have to check if failures resulted from the transition.
+	 */
+	nhs = l_container_of(netdev->handshake,
+				struct netdev_handshake_state, super);
+
+	/* reset key states just as we do in initialization */
+	nhs->complete = false;
+	nhs->ptk_installed = false;
+	nhs->gtk_installed = true;
+	nhs->igtk_installed = true;
+
+	if (nhs->group_new_key_cmd_id) {
+		l_genl_family_cancel(nl80211, nhs->group_new_key_cmd_id);
+		nhs->group_new_key_cmd_id = 0;
+	}
+
+	if (nhs->group_management_new_key_cmd_id) {
+		l_genl_family_cancel(nl80211,
+			nhs->group_management_new_key_cmd_id);
+		nhs->group_management_new_key_cmd_id = 0;
+	}
+
+	if (netdev->rekey_offload_cmd_id) {
+		l_genl_family_cancel(nl80211, netdev->rekey_offload_cmd_id);
+		netdev->rekey_offload_cmd_id = 0;
+	}
+
+	netdev_rssi_polling_update(netdev);
+	netdev_cqm_rssi_update(netdev);
+
+	if (netdev->sm) {
+		eapol_sm_free(netdev->sm);
+		netdev->sm = NULL;
+	}
+
 	msg = netdev_build_cmd_associate_common(netdev);
 
 	c_iov = netdev_populate_common_ies(netdev, hs, msg, iov, n_iov, c_iov);
-- 
2.34.3


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

* [PATCH v4 14/15] netdev: remove FT auth proto
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
                   ` (11 preceding siblings ...)
  2022-09-21 22:31 ` [PATCH v4 13/15] netdev: ft: complete FT refactor James Prestwood
@ 2022-09-21 22:31 ` James Prestwood
  2022-09-21 22:31 ` [PATCH v4 15/15] ft: remove auth-proto/ft_sm James Prestwood
  2022-09-22  2:25 ` [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED Denis Kenzior
  14 siblings, 0 replies; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

This removes the FT auth-proto from netdev and all associated helpers
and APIs.
---
 src/netdev.c | 341 ---------------------------------------------------
 src/netdev.h |  11 +-
 2 files changed, 1 insertion(+), 351 deletions(-)

diff --git a/src/netdev.c b/src/netdev.c
index 536ae644..6c206fe9 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -96,13 +96,6 @@ struct netdev_handshake_state {
 	enum connection_type type;
 };
 
-struct netdev_ft_over_ds_info {
-	struct ft_ds_info super;
-	struct netdev *netdev;
-
-	bool parsed : 1;
-};
-
 struct netdev_ext_key_info {
 	uint16_t proto;
 	bool noencrypt;
@@ -145,7 +138,6 @@ struct netdev {
 	struct l_timeout *sa_query_delay;
 	struct l_timeout *group_handshake_timeout;
 	uint16_t sa_query_id;
-	uint8_t prev_snonce[32];
 	int8_t rssi_levels[16];
 	uint8_t rssi_levels_num;
 	uint8_t cur_rssi_level_idx;
@@ -176,8 +168,6 @@ struct netdev {
 	struct l_genl_msg *auth_cmd;
 	struct wiphy_radio_work_item work;
 
-	struct l_queue *ft_ds_list;
-
 	struct netdev_ext_key_info *ext_key_info;
 
 	bool connected : 1;
@@ -752,13 +742,6 @@ static void netdev_preauth_destroy(void *data)
 	l_free(state);
 }
 
-static void netdev_ft_ds_entry_free(void *data)
-{
-	struct netdev_ft_over_ds_info *info = data;
-
-	ft_ds_info_free(&info->super);
-}
-
 static void netdev_connect_free(struct netdev *netdev)
 {
 	if (netdev->work.id)
@@ -847,11 +830,6 @@ static void netdev_connect_free(struct netdev *netdev)
 		l_genl_family_cancel(nl80211, netdev->get_oci_cmd_id);
 		netdev->get_oci_cmd_id = 0;
 	}
-
-	if (netdev->ft_ds_list) {
-		l_queue_destroy(netdev->ft_ds_list, netdev_ft_ds_entry_free);
-		netdev->ft_ds_list = NULL;
-	}
 }
 
 static void netdev_connect_failed(struct netdev *netdev,
@@ -962,11 +940,6 @@ static void netdev_free(void *data)
 	if (netdev->fw_roam_bss)
 		scan_bss_free(netdev->fw_roam_bss);
 
-	if (netdev->ft_ds_list) {
-		l_queue_destroy(netdev->ft_ds_list, netdev_ft_ds_entry_free);
-		netdev->ft_ds_list = NULL;
-	}
-
 	scan_wdev_remove(netdev->wdev_id);
 
 	watchlist_destroy(&netdev->station_watches);
@@ -1424,11 +1397,6 @@ static void netdev_connect_ok(struct netdev *netdev)
 		netdev->connect_cb = NULL;
 	}
 
-	if (netdev->ft_ds_list) {
-		l_queue_destroy(netdev->ft_ds_list, netdev_ft_ds_entry_free);
-		netdev->ft_ds_list = NULL;
-	}
-
 	netdev_rssi_polling_update(netdev);
 
 	if (netdev->work.id)
@@ -4401,48 +4369,6 @@ static int netdev_tx_ft_frame_frame(uint32_t ifindex, uint16_t frame_type,
 	return 0;
 }
 
-static void netdev_cmd_authenticate_ft_cb(struct l_genl_msg *msg,
-						void *user_data)
-{
-	struct netdev *netdev = user_data;
-
-	netdev->connect_cmd_id = 0;
-
-	if (l_genl_msg_get_error(msg) < 0)
-		netdev_connect_failed(netdev,
-					NETDEV_RESULT_AUTHENTICATION_FAILED,
-					MMPDU_STATUS_CODE_UNSPECIFIED);
-}
-
-static void netdev_ft_tx_authenticate(struct iovec *iov,
-					size_t iov_len, void *user_data)
-{
-	struct netdev *netdev = user_data;
-	struct l_genl_msg *cmd_authenticate;
-
-	cmd_authenticate = netdev_build_cmd_authenticate(netdev,
-							NL80211_AUTHTYPE_FT);
-	l_genl_msg_append_attrv(cmd_authenticate, NL80211_ATTR_IE, iov,
-					iov_len);
-
-	netdev->connect_cmd_id = l_genl_family_send(nl80211,
-						cmd_authenticate,
-						netdev_cmd_authenticate_ft_cb,
-						netdev, NULL);
-	if (!netdev->connect_cmd_id) {
-		l_genl_msg_unref(cmd_authenticate);
-		goto restore_snonce;
-	}
-
-	return;
-
-restore_snonce:
-	memcpy(netdev->handshake->snonce, netdev->prev_snonce, 32);
-
-	netdev_connect_failed(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED,
-					MMPDU_STATUS_CODE_UNSPECIFIED);
-}
-
 static int netdev_ft_tx_associate(uint32_t ifindex, uint32_t freq,
 					const uint8_t *prev_bssid,
 					struct iovec *ft_iov, size_t n_ft_iov)
@@ -4532,98 +4458,6 @@ static int netdev_ft_tx_associate(uint32_t ifindex, uint32_t freq,
 	return 0;
 }
 
-static void prepare_ft(struct netdev *netdev, const struct scan_bss *target_bss)
-{
-	struct netdev_handshake_state *nhs;
-
-	/*
-	 * We reuse the handshake_state object and reset what's needed.
-	 * Could also create a new object and copy most of the state but
-	 * we would end up doing more work.
-	 */
-	memcpy(netdev->prev_snonce, netdev->handshake->snonce, 32);
-
-	netdev->frequency = target_bss->frequency;
-
-	handshake_state_set_authenticator_address(netdev->handshake,
-							target_bss->addr);
-
-	if (target_bss->rsne)
-		handshake_state_set_authenticator_ie(netdev->handshake,
-							target_bss->rsne);
-	memcpy(netdev->handshake->mde + 2, target_bss->mde, 3);
-
-	netdev->handshake->active_tk_index = 0;
-	netdev->associated = false;
-	netdev->operational = false;
-	netdev->in_ft = true;
-
-	handshake_state_set_chandef(netdev->handshake, NULL);
-
-	/*
-	 * Cancel commands that could be running because of EAPoL activity
-	 * like re-keying, this way the callbacks for those commands don't
-	 * have to check if failures resulted from the transition.
-	 */
-	nhs = l_container_of(netdev->handshake,
-				struct netdev_handshake_state, super);
-
-	/* reset key states just as we do in initialization */
-	nhs->complete = false;
-	nhs->ptk_installed = false;
-	nhs->gtk_installed = true;
-	nhs->igtk_installed = true;
-
-	if (nhs->group_new_key_cmd_id) {
-		l_genl_family_cancel(nl80211, nhs->group_new_key_cmd_id);
-		nhs->group_new_key_cmd_id = 0;
-	}
-
-	if (nhs->group_management_new_key_cmd_id) {
-		l_genl_family_cancel(nl80211,
-			nhs->group_management_new_key_cmd_id);
-		nhs->group_management_new_key_cmd_id = 0;
-	}
-
-	if (netdev->rekey_offload_cmd_id) {
-		l_genl_family_cancel(nl80211, netdev->rekey_offload_cmd_id);
-		netdev->rekey_offload_cmd_id = 0;
-	}
-
-	netdev_rssi_polling_update(netdev);
-	netdev_cqm_rssi_update(netdev);
-
-	if (netdev->sm) {
-		eapol_sm_free(netdev->sm);
-		netdev->sm = NULL;
-	}
-}
-
-static void netdev_ft_over_ds_auth_failed(struct netdev_ft_over_ds_info *info,
-						uint16_t status)
-{
-	l_queue_remove(info->netdev->ft_ds_list, info);
-	ft_ds_info_free(&info->super);
-}
-
-struct ft_ds_finder {
-	const uint8_t *spa;
-	const uint8_t *aa;
-};
-
-static bool match_ft_ds_info(const void *a, const void *b)
-{
-	const struct netdev_ft_over_ds_info *info = a;
-	const struct ft_ds_finder *finder = b;
-
-	if (memcmp(info->super.spa, finder->spa, 6))
-		return false;
-	if (memcmp(info->super.aa, finder->aa, 6))
-		return false;
-
-	return true;
-}
-
 static void netdev_ft_response_frame_event(const struct mmpdu_header *hdr,
 					const void *body, size_t body_len,
 					int rssi, void *user_data)
@@ -4672,181 +4506,6 @@ static void netdev_qos_map_frame_event(const struct mmpdu_header *hdr,
 	netdev_send_qos_map_set(netdev, body + 4, body_len - 4);
 }
 
-static bool netdev_ft_work_ready(struct wiphy_radio_work_item *item)
-{
-	struct netdev *netdev = l_container_of(item, struct netdev, work);
-
-	if (!auth_proto_start(netdev->ap)) {
-		/* Restore original nonce */
-		memcpy(netdev->handshake->snonce, netdev->prev_snonce, 32);
-
-		netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
-						MMPDU_STATUS_CODE_UNSPECIFIED);
-		return true;
-	}
-
-	return false;
-}
-
-static const struct wiphy_radio_work_item_ops ft_work_ops = {
-	.do_work = netdev_ft_work_ready,
-};
-
-int netdev_fast_transition(struct netdev *netdev,
-				const struct scan_bss *target_bss,
-				const struct scan_bss *orig_bss,
-				netdev_connect_cb_t cb)
-{
-	if (!netdev->operational)
-		return -ENOTCONN;
-
-	if (!netdev->handshake->mde || !target_bss->mde_present ||
-			l_get_le16(netdev->handshake->mde + 2) !=
-			l_get_le16(target_bss->mde))
-		return -EINVAL;
-
-	netdev->connect_cb = cb;
-
-	netdev->ap = ft_over_air_sm_new(netdev->handshake,
-					netdev_ft_tx_authenticate,
-					netdev_ft_tx_associate,
-					netdev_get_oci, netdev);
-	prepare_ft(netdev, target_bss);
-
-	handshake_state_new_snonce(netdev->handshake);
-
-	wiphy_radio_work_insert(netdev->wiphy, &netdev->work,
-				WIPHY_WORK_PRIORITY_CONNECT, &ft_work_ops);
-
-	return 0;
-}
-
-int netdev_fast_transition_over_ds(struct netdev *netdev,
-					const struct scan_bss *target_bss,
-					const struct scan_bss *orig_bss,
-					netdev_connect_cb_t cb)
-{
-	struct netdev_ft_over_ds_info *info;
-	struct ft_ds_finder finder;
-
-	if (!netdev->operational)
-		return -ENOTCONN;
-
-	if (!netdev->handshake->mde || !target_bss->mde_present ||
-			l_get_le16(netdev->handshake->mde + 2) !=
-			l_get_le16(target_bss->mde))
-		return -EINVAL;
-
-	finder.spa = netdev->addr;
-	finder.aa = target_bss->addr;
-
-	info = l_queue_find(netdev->ft_ds_list, match_ft_ds_info, &finder);
-
-	if (!info || !info->parsed)
-		return -ENOENT;
-
-	netdev->connect_cb = cb;
-
-	netdev->ap = ft_over_ds_sm_new(netdev->handshake,
-					netdev_ft_tx_associate,
-					netdev);
-	prepare_ft(netdev, target_bss);
-
-	ft_over_ds_prepare_handshake(&info->super, netdev->handshake);
-
-	wiphy_radio_work_insert(netdev->wiphy, &netdev->work,
-				WIPHY_WORK_PRIORITY_CONNECT, &ft_work_ops);
-
-	return 0;
-}
-
-static void netdev_ft_request_cb(struct l_genl_msg *msg, void *user_data)
-{
-	struct netdev_ft_over_ds_info *info = user_data;
-
-	if (l_genl_msg_get_error(msg) < 0) {
-		l_error("Could not send CMD_FRAME for FT-over-DS");
-		netdev_ft_over_ds_auth_failed(info,
-					MMPDU_STATUS_CODE_UNSPECIFIED);
-	}
-}
-
-static void netdev_ft_ds_info_free(struct ft_ds_info *ft)
-{
-	struct netdev_ft_over_ds_info *info = l_container_of(ft,
-					struct netdev_ft_over_ds_info, super);
-
-	l_free(info);
-}
-
-int netdev_fast_transition_over_ds_action(struct netdev *netdev,
-					const struct scan_bss *target_bss)
-{
-	struct netdev_ft_over_ds_info *info;
-	uint8_t ft_req[14];
-	struct handshake_state *hs = netdev->handshake;
-	struct iovec iovs[5];
-	uint8_t buf[512];
-	size_t len;
-
-	if (!netdev->operational)
-		return -ENOTCONN;
-
-	if (!netdev->handshake->mde || !target_bss->mde_present ||
-			l_get_le16(netdev->handshake->mde + 2) !=
-			l_get_le16(target_bss->mde))
-		return -EINVAL;
-
-	l_debug("");
-
-	info = l_new(struct netdev_ft_over_ds_info, 1);
-	info->netdev = netdev;
-
-	memcpy(info->super.spa, hs->spa, ETH_ALEN);
-	memcpy(info->super.aa, target_bss->addr, ETH_ALEN);
-	memcpy(info->super.mde, target_bss->mde, sizeof(info->super.mde));
-
-	if (target_bss->rsne)
-		info->super.authenticator_ie = l_memdup(target_bss->rsne,
-						target_bss->rsne[1] + 2);
-
-	l_getrandom(info->super.snonce, 32);
-	info->super.free = netdev_ft_ds_info_free;
-
-	ft_req[0] = 6; /* FT category */
-	ft_req[1] = 1; /* FT Request action */
-	memcpy(ft_req + 2, netdev->addr, 6);
-	memcpy(ft_req + 8, info->super.aa, 6);
-
-	iovs[0].iov_base = ft_req;
-	iovs[0].iov_len = sizeof(ft_req);
-
-	if (!ft_build_authenticate_ies(hs, false, info->super.snonce,
-						buf, &len))
-		goto failed;
-
-	iovs[1].iov_base = buf;
-	iovs[1].iov_len = len;
-
-	iovs[2].iov_base = NULL;
-
-	if (!netdev->ft_ds_list)
-		netdev->ft_ds_list = l_queue_new();
-
-	l_queue_push_head(netdev->ft_ds_list, info);
-
-	netdev_send_action_framev(netdev, netdev->handshake->aa, iovs, 2,
-					netdev->frequency,
-					netdev_ft_request_cb,
-					info);
-
-	return 0;
-
-failed:
-	l_free(info);
-	return -EIO;
-}
-
 static void netdev_preauth_cb(const uint8_t *pmk, void *user_data)
 {
 	struct netdev_preauth_state *preauth = user_data;
diff --git a/src/netdev.h b/src/netdev.h
index c5933ad0..73d38c32 100644
--- a/src/netdev.h
+++ b/src/netdev.h
@@ -166,16 +166,7 @@ int netdev_reassociate(struct netdev *netdev,
 			struct handshake_state *hs,
 			netdev_event_func_t event_filter,
 			netdev_connect_cb_t cb, void *user_data);
-int netdev_fast_transition(struct netdev *netdev,
-				const struct scan_bss *target_bss,
-				const struct scan_bss *orig_bss,
-				netdev_connect_cb_t cb);
-int netdev_fast_transition_over_ds_action(struct netdev *netdev,
-					const struct scan_bss *target_bss);
-int netdev_fast_transition_over_ds(struct netdev *netdev,
-					const struct scan_bss *target_bss,
-					const struct scan_bss *orig_bss,
-					netdev_connect_cb_t cb);
+
 int netdev_preauthenticate(struct netdev *netdev,
 				const struct scan_bss *target_bss,
 				netdev_preauthenticate_cb_t cb,
-- 
2.34.3


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

* [PATCH v4 15/15] ft: remove auth-proto/ft_sm
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
                   ` (12 preceding siblings ...)
  2022-09-21 22:31 ` [PATCH v4 14/15] netdev: remove FT auth proto James Prestwood
@ 2022-09-21 22:31 ` James Prestwood
  2022-09-22  2:25 ` [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED Denis Kenzior
  14 siblings, 0 replies; 20+ messages in thread
From: James Prestwood @ 2022-09-21 22:31 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

This is no longer used.
---
 src/ft.c | 256 +------------------------------------------------------
 src/ft.h |  49 -----------
 2 files changed, 3 insertions(+), 302 deletions(-)

diff --git a/src/ft.c b/src/ft.c
index 65cd38cb..0e51aefc 100644
--- a/src/ft.c
+++ b/src/ft.c
@@ -61,22 +61,6 @@ struct ft_info {
 	bool parsed : 1;
 };
 
-struct ft_sm {
-	struct auth_proto ap;
-	struct handshake_state *hs;
-
-	ft_tx_authenticate_func_t tx_auth;
-	ft_tx_associate_func_t tx_assoc;
-	ft_get_oci get_oci;
-
-	void *user_data;
-
-	bool over_ds : 1;
-
-	uint8_t prev_bssid[6];
-	struct l_queue *ft_auths;
-};
-
 /*
  * Calculate the MIC field of the FTE and write it directly to that FTE,
  * assuming it was all zeros before.  See 12.8.4 and 12.8.5.
@@ -502,79 +486,8 @@ static bool mde_equal(const uint8_t *mde1, const uint8_t *mde2)
 	return memcmp(mde1, mde1, mde1[1] + 2) == 0;
 }
 
-bool ft_over_ds_parse_action_ies(struct ft_ds_info *info,
-					struct handshake_state *hs,
-					const uint8_t *ies,
-					size_t ies_len)
-{
-	const uint8_t *mde = NULL;
-	const uint8_t *fte = NULL;
-	bool is_rsn = hs->supplicant_ie != NULL;
-
-	if (parse_ies(hs, info->authenticator_ie, ies, ies_len,
-				&mde, &fte) < 0)
-		return false;
-
-	if (!mde_equal(info->mde, mde))
-		goto ft_error;
-
-	if (is_rsn) {
-		if (!ft_parse_fte(hs, info->snonce, fte, &info->ft_info))
-			goto ft_error;
-
-		info->fte = l_memdup(fte, fte[1] + 2);
-	} else if (fte)
-		goto ft_error;
-
-	return true;
-
-ft_error:
-	return false;
-}
-
-static int ft_process_ies(struct handshake_state *hs, const uint8_t *ies,
-			size_t ies_len)
-{
-	const uint8_t *mde = NULL;
-	const uint8_t *fte = NULL;
-	bool is_rsn = hs->supplicant_ie != NULL;
-
-	/* Check 802.11r IEs */
-	if (!ies)
-		goto ft_error;
-
-	if (parse_ies(hs, hs->authenticator_ie, ies, ies_len,
-				&mde, &fte) < 0)
-		goto ft_error;
-
-	if (!mde_equal(hs->mde, mde))
-		goto ft_error;
-
-	if (is_rsn) {
-		struct ie_ft_info ft_info;
-
-		if (!ft_parse_fte(hs, hs->snonce, fte, &ft_info))
-			goto ft_error;
-
-		handshake_state_set_fte(hs, fte);
-
-		handshake_state_set_anonce(hs, ft_info.anonce);
-
-		handshake_state_set_kh_ids(hs, ft_info.r0khid,
-						ft_info.r0khid_len,
-						ft_info.r1khid);
-
-		handshake_state_derive_ptk(hs);
-	} else if (fte)
-		goto ft_error;
-
-	return 0;
-
-ft_error:
-	return -EBADMSG;
-}
-
-int ft_over_ds_parse_action_response(const uint8_t *frame, size_t frame_len,
+static int ft_over_ds_parse_action_response(const uint8_t *frame,
+					size_t frame_len,
 					const uint8_t **spa_out,
 					const uint8_t **aa_out,
 					const uint8_t **ies_out,
@@ -624,75 +537,6 @@ int ft_over_ds_parse_action_response(const uint8_t *frame, size_t frame_len,
 	return 0;
 }
 
-bool ft_over_ds_prepare_handshake(struct ft_ds_info *info,
-					struct handshake_state *hs)
-{
-	if (!hs->supplicant_ie)
-		return true;
-
-	memcpy(hs->snonce, info->snonce, sizeof(hs->snonce));
-
-	handshake_state_set_fte(hs, info->fte);
-
-	handshake_state_set_anonce(hs, info->ft_info.anonce);
-
-	handshake_state_set_kh_ids(hs, info->ft_info.r0khid,
-						info->ft_info.r0khid_len,
-						info->ft_info.r1khid);
-
-	handshake_state_derive_ptk(hs);
-
-	return true;
-}
-
-void ft_ds_info_free(struct ft_ds_info *info)
-{
-	__typeof__(info->free) destroy = info->free;
-
-	if (info->fte)
-		l_free(info->fte);
-
-	if (info->authenticator_ie)
-		l_free(info->authenticator_ie);
-
-	if (destroy)
-		destroy(info);
-}
-
-static int ft_rx_authenticate(struct auth_proto *ap, const uint8_t *frame,
-				size_t frame_len)
-{
-	struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
-	uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
-	const uint8_t *ies = NULL;
-	size_t ies_len;
-	int ret;
-
-	/*
-	 * Parse the Authentication Response and validate the contents
-	 * according to 12.5.2 / 12.5.4: RSN or non-RSN Over-the-air
-	 * FT Protocol.
-	 */
-	if (!ft_parse_authentication_resp_frame(frame, frame_len, ft->hs->spa,
-						ft->hs->aa, ft->hs->aa,
-						2, &status_code,
-						&ies, &ies_len))
-		goto auth_error;
-
-	/* AP Rejected the authenticate / associate */
-	if (status_code != 0)
-		goto auth_error;
-
-	ret = ft_process_ies(ft->hs, ies, ies_len);
-	if (ret < 0)
-		goto auth_error;
-
-	return ft->get_oci(ft->user_data);
-
-auth_error:
-	return (int)status_code;
-}
-
 int __ft_rx_associate(uint32_t ifindex, const uint8_t *frame, size_t frame_len)
 {
 	struct netdev *netdev = netdev_find(ifindex);
@@ -832,36 +676,7 @@ int __ft_rx_associate(uint32_t ifindex, const uint8_t *frame, size_t frame_len)
 	return 0;
 }
 
-static int ft_rx_associate(struct auth_proto *ap, const uint8_t *frame,
-				size_t frame_len)
-{
-	struct ft_sm *sm = l_container_of(ap, struct ft_sm, ap);
-
-	return __ft_rx_associate(sm->hs->ifindex, frame, frame_len);
-}
-
-static int ft_rx_oci(struct auth_proto *ap)
-{
-	struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
-
-	return ft_tx_reassociate(ft->hs->ifindex, 0, ft->prev_bssid);
-}
-
-static void ft_sm_free(struct auth_proto *ap)
-{
-	struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
-
-	l_free(ft);
-}
-
-static bool ft_over_ds_start(struct auth_proto *ap)
-{
-	struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
-
-	return ft_tx_reassociate(ft->hs->ifindex, 0, ft->prev_bssid) == 0;
-}
-
-bool ft_build_authenticate_ies(struct handshake_state *hs, bool ocvc,
+static bool ft_build_authenticate_ies(struct handshake_state *hs, bool ocvc,
 				const uint8_t *new_snonce, uint8_t *buf,
 				size_t *len)
 {
@@ -933,71 +748,6 @@ bool ft_build_authenticate_ies(struct handshake_state *hs, bool ocvc,
 	return true;
 }
 
-static bool ft_start(struct auth_proto *ap)
-{
-	struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
-	struct handshake_state *hs = ft->hs;
-	struct iovec iov;
-	uint8_t buf[512];
-	size_t len;
-
-	if (!ft_build_authenticate_ies(hs, hs->supplicant_ocvc, hs->snonce,
-					buf, &len))
-		return false;
-
-	iov.iov_base = buf;
-	iov.iov_len = len;
-
-	ft->tx_auth(&iov, 1, ft->user_data);
-
-	return true;
-}
-
-struct auth_proto *ft_over_air_sm_new(struct handshake_state *hs,
-				ft_tx_authenticate_func_t tx_auth,
-				ft_tx_associate_func_t tx_assoc,
-				ft_get_oci get_oci,
-				void *user_data)
-{
-	struct ft_sm *ft = l_new(struct ft_sm, 1);
-
-	ft->tx_auth = tx_auth;
-	ft->tx_assoc = tx_assoc;
-	ft->get_oci = get_oci;
-	ft->hs = hs;
-	ft->user_data = user_data;
-
-	ft->ap.rx_authenticate = ft_rx_authenticate;
-	ft->ap.rx_associate = ft_rx_associate;
-	ft->ap.start = ft_start;
-	ft->ap.free = ft_sm_free;
-	ft->ap.rx_oci = ft_rx_oci;
-
-	memcpy(ft->prev_bssid, hs->aa, 6);
-
-	return &ft->ap;
-}
-
-struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
-				ft_tx_associate_func_t tx_assoc,
-				void *user_data)
-{
-	struct ft_sm *ft = l_new(struct ft_sm, 1);
-
-	ft->tx_assoc = tx_assoc;
-	ft->hs = hs;
-	ft->user_data = user_data;
-	ft->over_ds = true;
-
-	ft->ap.rx_associate = ft_rx_associate;
-	ft->ap.start = ft_over_ds_start;
-	ft->ap.free = ft_sm_free;
-
-	memcpy(ft->prev_bssid, hs->aa, 6);
-
-	return &ft->ap;
-}
-
 void __ft_set_tx_frame_func(ft_tx_frame_func_t func)
 {
 	tx_frame = func;
diff --git a/src/ft.h b/src/ft.h
index 89b70850..cce0fb35 100644
--- a/src/ft.h
+++ b/src/ft.h
@@ -20,7 +20,6 @@
  *
  */
 
-struct handshake_state;
 struct scan_bss;
 
 typedef int (*ft_tx_frame_func_t)(uint32_t ifindex, uint16_t frame_type,
@@ -28,57 +27,9 @@ typedef int (*ft_tx_frame_func_t)(uint32_t ifindex, uint16_t frame_type,
 					const uint8_t *dest, struct iovec *iov,
 					size_t iov_len);
 
-typedef void (*ft_tx_authenticate_func_t)(struct iovec *iov, size_t iov_len,
-					void *user_data);
-
 typedef int (*ft_tx_associate_func_t)(uint32_t ifindex, uint32_t freq,
 					const uint8_t *prev_bssid,
 					struct iovec *ie_iov, size_t iov_len);
-typedef int (*ft_get_oci)(void *user_data);
-
-typedef void (*ft_ds_free_func_t)(void *user_data);
-
-struct ft_ds_info {
-	uint8_t spa[6];
-	uint8_t aa[6];
-	uint8_t snonce[32];
-	uint8_t mde[3];
-	uint8_t *fte;
-	uint8_t *authenticator_ie;
-
-	struct ie_ft_info ft_info;
-
-	void (*free)(struct ft_ds_info *s);
-};
-
-void ft_ds_info_free(struct ft_ds_info *info);
-
-bool ft_build_authenticate_ies(struct handshake_state *hs, bool ocvc,
-				const uint8_t *new_snonce, uint8_t *buf,
-				size_t *len);
-
-int ft_over_ds_parse_action_response(const uint8_t *frame, size_t frame_len,
-					const uint8_t **spa_out,
-					const uint8_t **aa_out,
-					const uint8_t **ies_out,
-					size_t *ies_len);
-bool ft_over_ds_parse_action_ies(struct ft_ds_info *info,
-					struct handshake_state *hs,
-					const uint8_t *ies,
-					size_t ies_len);
-
-struct auth_proto *ft_over_air_sm_new(struct handshake_state *hs,
-				ft_tx_authenticate_func_t tx_auth,
-				ft_tx_associate_func_t tx_assoc,
-				ft_get_oci get_oci,
-				void *user_data);
-
-struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
-				ft_tx_associate_func_t tx_assoc,
-				void *user_data);
-
-bool ft_over_ds_prepare_handshake(struct ft_ds_info *info,
-					struct handshake_state *hs);
 
 void __ft_set_tx_frame_func(ft_tx_frame_func_t func);
 void __ft_set_tx_associate_func(ft_tx_associate_func_t func);
-- 
2.34.3


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

* Re: [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED
  2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
                   ` (13 preceding siblings ...)
  2022-09-21 22:31 ` [PATCH v4 15/15] ft: remove auth-proto/ft_sm James Prestwood
@ 2022-09-22  2:25 ` Denis Kenzior
  14 siblings, 0 replies; 20+ messages in thread
From: Denis Kenzior @ 2022-09-22  2:25 UTC (permalink / raw)
  To: James Prestwood, iwd

Hi James,

On 9/21/22 17:31, James Prestwood wrote:
> FT is now driven (mostly) by station which removes the connect
> callback. Instead once FT is completed, keys set, etc. netdev
> will send an event to notify station.
> ---
>   src/netdev.h  | 1 +
>   src/station.c | 2 ++
>   2 files changed, 3 insertions(+)
> 

Patches 1-4 applied, thanks.

Regards,
-Denis


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

* Re: [PATCH v4 05/15] ft: netdev: prep for FT isolation into ft.c
  2022-09-21 22:31 ` [PATCH v4 05/15] ft: netdev: prep for FT isolation into ft.c James Prestwood
@ 2022-09-22  2:40   ` Denis Kenzior
  2022-09-22 15:42     ` James Prestwood
  0 siblings, 1 reply; 20+ messages in thread
From: Denis Kenzior @ 2022-09-22  2:40 UTC (permalink / raw)
  To: James Prestwood, iwd

Hi James,

On 9/21/22 17:31, James Prestwood wrote:
> Currently netdev handles caching FT auth information and uses FT
> parsers/auth-proto to manage the protocol. This sets up to remove
> this state machine from netdev and isolate it into ft.c.
> 
> This does not break the existing auth-proto (hence the slight
> modifications, which will be removed soon).
> 
> Eventually the auth-proto will be removed from FT entirely, replaced
> just by an FT state machine, similar to how EAPoL works (netdev hooks
> to TX/RX frames).
> ---
>   src/ft.c     | 312 ++++++++++++++++++++++++++++++++++++++++++++++++---
>   src/ft.h     |  22 +++-
>   src/netdev.c |  28 +++--
>   3 files changed, 329 insertions(+), 33 deletions(-)
> 

<snip>

> @@ -45,6 +69,9 @@ struct ft_sm {
>   	void *user_data;
>   
>   	bool over_ds : 1;
> +
> +	uint8_t prev_bssid[6];
> +	struct l_queue *ft_auths;

Is ft_auths still needed?

>   };
>   
>   /*

<snip>

> +int ft_associate(uint32_t ifindex, const uint8_t *addr)
> +{
> +	struct netdev *netdev = netdev_find(ifindex);
> +	struct handshake_state *hs = netdev_get_handshake(netdev);
> +	struct ft_info *info;
> +	int ret;
> +
> +	/*
> +	 * TODO: Since FT-over-DS is done early, before the time of roaming, it
> +	 *       may end up that a completely new BSS is the best candidate and
> +	 *       we haven't yet authenticated. We could actually authenticate
> +	 *       at this point, but for now just assume the caller will choose
> +	 *       a different BSS.
> +	 */
> +	info = ft_info_find(ifindex, addr);
> +	if (!info)
> +		return -ENOENT;
> +
> +	ft_prepare_handshake(info, hs);
> +
> +	ret = ft_tx_reassociate(ifindex, info->frequency, info->prev_bssid);
> +
> +	/* After this no previous auths will be valid */
> +	l_queue_clear(info_list, ft_info_destroy);

So why are we clearing the entire list?  Shouldn't we be clearing it only for 
the ifindex in question?

> +
> +	return ret;
> +}
> +
> +void ft_reset(uint32_t ifindex)

I think a more descriptive name is required.  Like 
ft_purge_authentications_for_ifindex() or something? :)

> +{
> +	struct ft_info *info;
> +
> +	while ((info = ft_info_find(ifindex, NULL))) {
> +		l_queue_remove(info_list, info);

This looks like a O(n^2) loop instead of doing the purge in linear time?

> +		ft_info_destroy(info);
> +	}
> +}
> +
> +static int ft_init(void)
> +{
> +	info_list = l_queue_new();
> +
> +	return 0;
> +}
> +
> +static void ft_exit(void)
> +{
> +	if (!l_queue_isempty(info_list))
> +		l_warn("stale FT info objects found!");
> +
> +	l_queue_destroy(info_list, ft_info_destroy);
> +}
> +
> +IWD_MODULE(ft, ft_init, ft_exit);

Does netdev need an IWD_MODULE_DEPENDS on ft now?

Regards,
-Denis

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

* Re: [PATCH v4 08/15] station: create list of roam candidates
  2022-09-21 22:31 ` [PATCH v4 08/15] station: create list of roam candidates James Prestwood
@ 2022-09-22  3:09   ` Denis Kenzior
  0 siblings, 0 replies; 20+ messages in thread
From: Denis Kenzior @ 2022-09-22  3:09 UTC (permalink / raw)
  To: James Prestwood, iwd

Hi James,

On 9/21/22 17:31, James Prestwood wrote:
> The current behavior is to only find the best roam candidate, which
> generally is fine. But if for whatever reason IWD fails to roam it
> would be nice having a few backup BSS's rather than having to
> re-scan, or worse disassociate and reconnect entirely.
> 
> This patch doesn't change the roam behavior, just prepares for
> using a roam candidate list. One difference though is any roam
> candidates are added to station->bss_list, rather than just the
> best BSS. This shouldn't effect any external behavior.
> 
> The candidate list is built based on scan_bss rank. First we establish
> a base rank, the rank of the current BSS (or zero if AP roaming). Any
> BSS in the results with a higher rank, excluding the current BSS, will
> be added to the sorted station->roam_bss_list as well as stations
> overall BSS list. If the resulting list is empty there were no better
> BSS's, otherwise station can now try to roam starting with the best
> candidate (head of the roam list).
> ---
>   src/station.c | 84 +++++++++++++++++++++++++++++++++------------------
>   1 file changed, 54 insertions(+), 30 deletions(-)
> 
> diff --git a/src/station.c b/src/station.c
> index 868bb725..df54936e 100644
> --- a/src/station.c
> +++ b/src/station.c
> @@ -107,6 +107,7 @@ struct station {
>   
>   	/* Set of frequencies to scan first when attempting a roam */
>   	struct scan_freq_set *roam_freqs;
> +	struct l_queue *roam_bss_list;
>   
>   	/* Frequencies split into subsets by priority */
>   	struct scan_freq_set *scan_freqs_order[3];
> @@ -1640,6 +1641,11 @@ static void station_roam_state_clear(struct station *station)
>   		scan_freq_set_free(station->roam_freqs);
>   		station->roam_freqs = NULL;
>   	}
> +
> +	if (station->roam_bss_list) {
> +		l_queue_destroy(station->roam_bss_list, NULL);
> +		station->roam_bss_list = NULL;

Maybe I'm nitpicking, but why would you destroy the roam_bss_list queue itself? 
  Wouldn't just clearing it be enough?  There's not much sense in destroying / 
re-creating the queue itself all the time.

> +	}
>   }
>   
>   static void station_reset_connection_state(struct station *station)
> @@ -1981,6 +1987,11 @@ static void station_roamed(struct station *station)
>   			l_warn("Could not request neighbor report");
>   	}
>   
> +	if (station->roam_bss_list) {
> +		l_queue_destroy(station->roam_bss_list, NULL);
> +		station->roam_bss_list = NULL;
> +	}
> +

as above?

>   	station_ft_ds_action_start(station);
>   
>   	station_enter_state(station, STATION_STATE_CONNECTED);
> @@ -2005,6 +2016,11 @@ static void station_roam_failed(struct station *station)
>   {
>   	l_debug("%u", netdev_get_ifindex(station->netdev));
>   
> +	if (station->roam_bss_list) {
> +		l_queue_destroy(station->roam_bss_list, NULL);
> +		station->roam_bss_list = NULL;
> +	}
> +

And here

>   	/*
>   	 * If we attempted a reassociation or a fast transition, and ended up
>   	 * here then we are now disconnected.
> @@ -2186,8 +2202,7 @@ static void station_preauthenticate_cb(struct netdev *netdev,
>   	station_transition_reassociate(station, bss, new_hs);
>   }
>   
> -static void station_transition_start(struct station *station,
> -							struct scan_bss *bss)
> +static void station_transition_start(struct station *station)
>   {
>   	struct handshake_state *hs = netdev_get_handshake(station->netdev);
>   	struct network *connected = station->connected_network;
> @@ -2195,6 +2210,7 @@ static void station_transition_start(struct station *station,
>   	struct handshake_state *new_hs;
>   	struct ie_rsn_info cur_rsne, target_rsne;
>   	int ret;
> +	struct scan_bss *bss = l_queue_peek_head(station->roam_bss_list);
>   
>   	l_debug("%u, target %s", netdev_get_ifindex(station->netdev),
>   			util_address_to_string(bss->addr));
> @@ -2334,12 +2350,10 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
>   	struct handshake_state *hs = netdev_get_handshake(station->netdev);
>   	struct scan_bss *current_bss = station->connected_bss;
>   	struct scan_bss *bss;
> -	struct scan_bss *best_bss = NULL;
> -	double best_bss_rank = 0.0;
> +	double cur_bss_rank = 0.0;
>   	static const double RANK_FT_FACTOR = 1.3;
>   	uint16_t mdid;
>   	enum security orig_security, security;
> -	bool seen = false;
>   
>   	if (err) {
>   		station_roam_failed(station);
> @@ -2358,6 +2372,21 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
>   		ie_parse_mobility_domain_from_data(hs->mde, hs->mde[1] + 2,
>   							&mdid, NULL, NULL);
>   
> +	station->roam_bss_list = l_queue_new();

That way you can create it only in station_new, free it in station_free()

Rest LGTM.

Regards,
-Denis

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

* Re: [PATCH v4 05/15] ft: netdev: prep for FT isolation into ft.c
  2022-09-22  2:40   ` Denis Kenzior
@ 2022-09-22 15:42     ` James Prestwood
  2022-09-22 16:18       ` Denis Kenzior
  0 siblings, 1 reply; 20+ messages in thread
From: James Prestwood @ 2022-09-22 15:42 UTC (permalink / raw)
  To: Denis Kenzior, iwd

On Wed, 2022-09-21 at 21:40 -0500, Denis Kenzior wrote:
> Hi James,
> 
> On 9/21/22 17:31, James Prestwood wrote:
> > Currently netdev handles caching FT auth information and uses FT
> > parsers/auth-proto to manage the protocol. This sets up to remove
> > this state machine from netdev and isolate it into ft.c.
> > 
> > This does not break the existing auth-proto (hence the slight
> > modifications, which will be removed soon).
> > 
> > Eventually the auth-proto will be removed from FT entirely,
> > replaced
> > just by an FT state machine, similar to how EAPoL works (netdev
> > hooks
> > to TX/RX frames).
> > ---
> >   src/ft.c     | 312
> > ++++++++++++++++++++++++++++++++++++++++++++++++---
> >   src/ft.h     |  22 +++-
> >   src/netdev.c |  28 +++--
> >   3 files changed, 329 insertions(+), 33 deletions(-)
> > 
> 
> <snip>
> 
> > @@ -45,6 +69,9 @@ struct ft_sm {
> >         void *user_data;
> >   
> >         bool over_ds : 1;
> > +
> > +       uint8_t prev_bssid[6];
> > +       struct l_queue *ft_auths;
> 
> Is ft_auths still needed?

Oops, this was carry over from earlier versions.

> 
> >   };
> >   
> >   /*
> 
> <snip>
> 
> > +int ft_associate(uint32_t ifindex, const uint8_t *addr)
> > +{
> > +       struct netdev *netdev = netdev_find(ifindex);
> > +       struct handshake_state *hs = netdev_get_handshake(netdev);
> > +       struct ft_info *info;
> > +       int ret;
> > +
> > +       /*
> > +        * TODO: Since FT-over-DS is done early, before the time of
> > roaming, it
> > +        *       may end up that a completely new BSS is the best
> > candidate and
> > +        *       we haven't yet authenticated. We could actually
> > authenticate
> > +        *       at this point, but for now just assume the caller
> > will choose
> > +        *       a different BSS.
> > +        */
> > +       info = ft_info_find(ifindex, addr);
> > +       if (!info)
> > +               return -ENOENT;
> > +
> > +       ft_prepare_handshake(info, hs);
> > +
> > +       ret = ft_tx_reassociate(ifindex, info->frequency, info-
> > >prev_bssid);
> > +
> > +       /* After this no previous auths will be valid */
> > +       l_queue_clear(info_list, ft_info_destroy);
> 
> So why are we clearing the entire list?  Shouldn't we be clearing it
> only for 
> the ifindex in question?

Yes, it should just be per-ifindex.

> 
> > +
> > +       return ret;
> > +}
> > +
> > +void ft_reset(uint32_t ifindex)
> 
> I think a more descriptive name is required.  Like 
> ft_purge_authentications_for_ifindex() or something? :)
> 
> > +{
> > +       struct ft_info *info;
> > +
> > +       while ((info = ft_info_find(ifindex, NULL))) {
> > +               l_queue_remove(info_list, info);
> 
> This looks like a O(n^2) loop instead of doing the purge in linear
> time?

True, I guess I could do l_queue_peek_head() in the while condition,
then pop/free if the ifindex matches.

I've run into this a few times. Would be nice if we had an API to
remove/free multiple entries based on a match function. Or if an
l_queue_entry loop allowed removal, maybe it does already.

> 
> > +               ft_info_destroy(info);
> > +       }
> > +}
> > +
> > +static int ft_init(void)
> > +{
> > +       info_list = l_queue_new();
> > +
> > +       return 0;
> > +}
> > +
> > +static void ft_exit(void)
> > +{
> > +       if (!l_queue_isempty(info_list))
> > +               l_warn("stale FT info objects found!");
> > +
> > +       l_queue_destroy(info_list, ft_info_destroy);
> > +}
> > +
> > +IWD_MODULE(ft, ft_init, ft_exit);
> 
> Does netdev need an IWD_MODULE_DEPENDS on ft now?

I don't think so? Since init/exit aren't using any netdev calls I think
its safe.

> 
> Regards,
> -Denis



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

* Re: [PATCH v4 05/15] ft: netdev: prep for FT isolation into ft.c
  2022-09-22 15:42     ` James Prestwood
@ 2022-09-22 16:18       ` Denis Kenzior
  0 siblings, 0 replies; 20+ messages in thread
From: Denis Kenzior @ 2022-09-22 16:18 UTC (permalink / raw)
  To: James Prestwood, iwd

Hi James,

> True, I guess I could do l_queue_peek_head() in the while condition,
> then pop/free if the ifindex matches.
> 
> I've run into this a few times. Would be nice if we had an API to
> remove/free multiple entries based on a match function. Or if an
> l_queue_entry loop allowed removal, maybe it does already.

We do have this, l_queue_foreach_remove().

>>> +IWD_MODULE(ft, ft_init, ft_exit);
>>
>> Does netdev need an IWD_MODULE_DEPENDS on ft now?
> 
> I don't think so? Since init/exit aren't using any netdev calls I think
> its safe.

No, the other way around.  netdev (maybe?) needs a dependency on ft, since 
netdev invokes __ft_set*

Regards,
-Denis

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

end of thread, other threads:[~2022-09-22 16:18 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-21 22:31 [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED James Prestwood
2022-09-21 22:31 ` [PATCH v4 02/15] nl80211util: include frame type with build_cmd_frame James Prestwood
2022-09-21 22:31 ` [PATCH v4 03/15] wiphy: add new work priority for FT James Prestwood
2022-09-21 22:31 ` [PATCH v4 04/15] offchannel: add priority to start call James Prestwood
2022-09-21 22:31 ` [PATCH v4 05/15] ft: netdev: prep for FT isolation into ft.c James Prestwood
2022-09-22  2:40   ` Denis Kenzior
2022-09-22 15:42     ` James Prestwood
2022-09-22 16:18       ` Denis Kenzior
2022-09-21 22:31 ` [PATCH v4 06/15] netdev: add FT TX frame hook James Prestwood
2022-09-21 22:31 ` [PATCH v4 07/15] ft: implement offchannel authentication James Prestwood
2022-09-21 22:31 ` [PATCH v4 08/15] station: create list of roam candidates James Prestwood
2022-09-22  3:09   ` Denis Kenzior
2022-09-21 22:31 ` [PATCH v4 09/15] netdev: hook in RX for FT-Action/Authentication/Association James Prestwood
2022-09-21 22:31 ` [PATCH v4 10/15] ft: update action response parsing to include header James Prestwood
2022-09-21 22:31 ` [PATCH v4 11/15] station: handle NETDEV_EVENT_FT_ROAMED James Prestwood
2022-09-21 22:31 ` [PATCH v4 12/15] station: try multiple roam candidates James Prestwood
2022-09-21 22:31 ` [PATCH v4 13/15] netdev: ft: complete FT refactor James Prestwood
2022-09-21 22:31 ` [PATCH v4 14/15] netdev: remove FT auth proto James Prestwood
2022-09-21 22:31 ` [PATCH v4 15/15] ft: remove auth-proto/ft_sm James Prestwood
2022-09-22  2:25 ` [PATCH v4 01/15] netdev: add NETDEV_EVENT_FT_ROAMED Denis Kenzior

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.