All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/7] netdev: add user_data to netdev_send_action_frame[v]
@ 2021-04-27 19:49 James Prestwood
  2021-04-27 19:49 ` [PATCH 2/7] ft: factor out various parsing routines James Prestwood
                   ` (6 more replies)
  0 siblings, 7 replies; 10+ messages in thread
From: James Prestwood @ 2021-04-27 19:49 UTC (permalink / raw)
  To: iwd

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

This makes this internal API a bit more usable by removing the
restriction of always having netdev as the user_data.
---
 src/netdev.c | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/src/netdev.c b/src/netdev.c
index 1190399d..2dae07df 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -3524,7 +3524,8 @@ static uint32_t netdev_send_action_framev(struct netdev *netdev,
 					const uint8_t *to,
 					struct iovec *iov, size_t iov_len,
 					uint32_t freq,
-					l_genl_msg_func_t callback)
+					l_genl_msg_func_t callback,
+					void *user_data)
 {
 	uint32_t id;
 	struct l_genl_msg *msg = nl80211_build_cmd_frame(netdev->index,
@@ -3532,7 +3533,7 @@ static uint32_t netdev_send_action_framev(struct netdev *netdev,
 								to, freq,
 								iov, iov_len);
 
-	id = l_genl_family_send(nl80211, msg, callback, netdev, NULL);
+	id = l_genl_family_send(nl80211, msg, callback, user_data, NULL);
 
 	if (!id)
 		l_genl_msg_unref(msg);
@@ -3544,14 +3545,16 @@ static uint32_t netdev_send_action_frame(struct netdev *netdev,
 					const uint8_t *to,
 					const uint8_t *body, size_t body_len,
 					uint32_t freq,
-					l_genl_msg_func_t callback)
+					l_genl_msg_func_t callback,
+					void *user_data)
 {
 	struct iovec iov[1];
 
 	iov[0].iov_base = (void *)body;
 	iov[0].iov_len = body_len;
 
-	return netdev_send_action_framev(netdev, to, iov, 1, freq, callback);
+	return netdev_send_action_framev(netdev, to, iov, 1, freq, callback,
+						user_data);
 }
 
 static void netdev_cmd_authenticate_ft_cb(struct l_genl_msg *msg,
@@ -3754,7 +3757,8 @@ static void netdev_ft_over_ds_tx_authenticate(struct iovec *iov,
 
 	netdev_send_action_framev(netdev, netdev->prev_bssid, iovs, iov_len + 1,
 					netdev->prev_frequency,
-					netdev_ft_request_cb);
+					netdev_ft_request_cb,
+					netdev);
 }
 
 static bool netdev_ft_work_ready(struct wiphy_radio_work_item *item)
@@ -3920,7 +3924,8 @@ int netdev_neighbor_report_req(struct netdev *netdev,
 	if (!netdev_send_action_frame(netdev, netdev->handshake->aa,
 					action_frame, sizeof(action_frame),
 					netdev->frequency,
-					netdev_neighbor_report_req_cb))
+					netdev_neighbor_report_req_cb,
+					netdev))
 		return -EIO;
 
 	netdev->neighbor_report_cb = cb;
@@ -4007,7 +4012,7 @@ static void netdev_sa_query_req_frame_event(const struct mmpdu_header *hdr,
 	if (!netdev_send_action_frame(netdev, netdev->handshake->aa,
 			sa_resp, sizeof(sa_resp),
 			netdev->frequency,
-			netdev_sa_query_resp_cb)) {
+			netdev_sa_query_resp_cb, netdev)) {
 		l_error("error sending SA Query response");
 		return;
 	}
@@ -4146,7 +4151,7 @@ static void netdev_unprot_disconnect_event(struct l_genl_msg *msg,
 	if (!netdev_send_action_frame(netdev, netdev->handshake->aa,
 			action_frame, sizeof(action_frame),
 			netdev->frequency,
-			netdev_sa_query_req_cb)) {
+			netdev_sa_query_req_cb, netdev)) {
 		l_error("error sending SA Query action frame");
 		return;
 	}
-- 
2.26.2

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

* [PATCH 2/7] ft: factor out various parsing routines
  2021-04-27 19:49 [PATCH 1/7] netdev: add user_data to netdev_send_action_frame[v] James Prestwood
@ 2021-04-27 19:49 ` James Prestwood
  2021-04-27 19:49 ` [PATCH 3/7] ft: expose ft_build_authenticate_ies James Prestwood
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: James Prestwood @ 2021-04-27 19:49 UTC (permalink / raw)
  To: iwd

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

Break up the rather large code block which parses out IEs,
verifies, and sets into the handshake. FT-over-DS needs these
steps broken up in order to parse the action frame response
without modifying the handshake.
---
 src/ft.c | 141 ++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 93 insertions(+), 48 deletions(-)

diff --git a/src/ft.c b/src/ft.c
index 2c58787d..698170cc 100644
--- a/src/ft.c
+++ b/src/ft.c
@@ -329,19 +329,14 @@ error:
 	return -EINVAL;
 }
 
-static int ft_process_ies(struct handshake_state *hs, const uint8_t *ies,
-			size_t ies_len)
+static int ft_parse_ies(const uint8_t *ies, size_t ies_len,
+			const uint8_t **rsne_out, const uint8_t **mde_out,
+			const uint8_t **fte_out)
 {
 	struct ie_tlv_iter iter;
 	const uint8_t *rsne = NULL;
 	const uint8_t *mde = NULL;
 	const uint8_t *fte = NULL;
-	uint32_t kck_len = handshake_state_get_kck_len(hs);
-	bool is_rsn;
-
-	/* Check 802.11r IEs */
-	if (!ies)
-		goto ft_error;
 
 	ie_tlv_iter_init(&iter, ies, ies_len);
 
@@ -370,8 +365,24 @@ static int ft_process_ies(struct handshake_state *hs, const uint8_t *ies,
 		}
 	}
 
-	is_rsn = hs->supplicant_ie != NULL;
+	if (rsne_out)
+		*rsne_out = rsne;
+
+	if (mde_out)
+		*mde_out = mde;
+
+	if (fte_out)
+		*fte_out = fte;
+
+	return 0;
 
+ft_error:
+	return -EINVAL;
+}
+
+static bool ft_verify_rsne(const uint8_t *rsne, const uint8_t *pmk_r0_name,
+				const uint8_t *authenticator_ie)
+{
 	/*
 	 * In an RSN, check for an RSNE containing the PMK-R0-Name and
 	 * the remaining fields same as in the advertised RSNE.
@@ -385,36 +396,31 @@ static int ft_process_ies(struct handshake_state *hs, const uint8_t *ies,
 	 * — All other fields shall be identical to the contents of the
 	 *   RSNE advertised by the AP in Beacon and Probe Response frames."
 	 */
-	if (is_rsn) {
-		struct ie_rsn_info msg2_rsne;
 
-		if (!rsne)
-			goto ft_error;
+	struct ie_rsn_info msg2_rsne;
 
-		if (ie_parse_rsne_from_data(rsne, rsne[1] + 2,
+	if (!rsne)
+		return false;
+
+	if (ie_parse_rsne_from_data(rsne, rsne[1] + 2,
 						&msg2_rsne) < 0)
-			goto ft_error;
+		return false;
 
-		if (msg2_rsne.num_pmkids != 1 ||
-				memcmp(msg2_rsne.pmkids, hs->pmk_r0_name, 16))
-			goto ft_error;
+	if (msg2_rsne.num_pmkids != 1 ||
+				memcmp(msg2_rsne.pmkids, pmk_r0_name, 16))
+		return false;
 
-		if (!handshake_util_ap_ie_matches(rsne, hs->authenticator_ie,
-							false))
-			goto ft_error;
-	} else if (rsne)
-		goto ft_error;
+	if (!handshake_util_ap_ie_matches(rsne, authenticator_ie, false))
+		return false;
 
-	/*
-	 * Check for an MD IE identical to the one we sent in message 1
-	 *
-	 * 12.8.3: "The MDE shall contain the MDID and FT Capability and
-	 * Policy fields. This element shall be the same as the MDE
-	 * advertised by the target AP in Beacon and Probe Response frames."
-	 */
-	if (!mde || memcmp(hs->mde, mde, hs->mde[1] + 2))
-		goto ft_error;
+	return true;
+}
 
+static bool ft_parse_fte(struct handshake_state *hs,
+				const uint8_t *snonce,
+				const uint8_t *fte,
+				struct ie_ft_info *ft_info)
+{
 	/*
 	 * In an RSN, check for an FT IE with the same R0KH-ID and the same
 	 * SNonce that we sent, and check that the R1KH-ID and the ANonce
@@ -433,28 +439,67 @@ static int ft_process_ies(struct handshake_state *hs, const uint8_t *ies,
 	 *   of this sequence.
 	 * — All other fields shall be set to 0."
 	 */
-	if (is_rsn) {
-		struct ie_ft_info ft_info;
-		uint8_t zeros[24] = {};
+	uint8_t zeros[24] = {};
+	uint32_t kck_len = handshake_state_get_kck_len(hs);
 
-		if (!fte)
-			goto ft_error;
+	if (!fte)
+		return false;
 
-		if (ie_parse_fast_bss_transition_from_data(fte, fte[1] + 2,
-						kck_len, &ft_info) < 0)
-			goto ft_error;
+	if (ie_parse_fast_bss_transition_from_data(fte, fte[1] + 2,
+					kck_len, ft_info) < 0)
+		return false;
+	if (ft_info->mic_element_count != 0 ||
+			memcmp(ft_info->mic, zeros, kck_len))
+		return false;
+	if (hs->r0khid_len != ft_info->r0khid_len ||
+			memcmp(hs->r0khid, ft_info->r0khid,
+				hs->r0khid_len) ||
+			!ft_info->r1khid_present)
+		return false;
+	if (memcmp(ft_info->snonce, snonce, 32))
+		return false;
 
-		if (ft_info.mic_element_count != 0 ||
-				memcmp(ft_info.mic, zeros, kck_len))
-			goto ft_error;
+	return true;
+}
 
-		if (hs->r0khid_len != ft_info.r0khid_len ||
-				memcmp(hs->r0khid, ft_info.r0khid,
-					hs->r0khid_len) ||
-				!ft_info.r1khid_present)
+static int ft_process_ies(struct handshake_state *hs, const uint8_t *ies,
+			size_t ies_len)
+{
+	const uint8_t *rsne = NULL;
+	const uint8_t *mde = NULL;
+	const uint8_t *fte = NULL;
+	bool is_rsn;
+
+	/* Check 802.11r IEs */
+	if (!ies)
+		goto ft_error;
+
+	if (ft_parse_ies(ies, ies_len, &rsne, &mde, &fte) < 0)
+		goto ft_error;
+
+	is_rsn = hs->supplicant_ie != NULL;
+
+	if (is_rsn) {
+		if (!ft_verify_rsne(rsne, hs->pmk_r0_name,
+					hs->authenticator_ie))
 			goto ft_error;
+	} else if (rsne)
+		goto ft_error;
 
-		if (memcmp(ft_info.snonce, hs->snonce, 32))
+	/*
+	 * Check for an MD IE identical to the one we sent in message 1
+	 *
+	 * 12.8.3: "The MDE shall contain the MDID and FT Capability and
+	 * Policy fields. This element shall be the same as the MDE
+	 * advertised by the target AP in Beacon and Probe Response frames."
+	 */
+	if (!mde || memcmp(hs->mde, mde, hs->mde[1] + 2))
+		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);
-- 
2.26.2

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

* [PATCH 3/7] ft: expose ft_build_authenticate_ies
  2021-04-27 19:49 [PATCH 1/7] netdev: add user_data to netdev_send_action_frame[v] James Prestwood
  2021-04-27 19:49 ` [PATCH 2/7] ft: factor out various parsing routines James Prestwood
@ 2021-04-27 19:49 ` James Prestwood
  2021-04-27 19:49 ` [PATCH 4/7] ft: create cache for FT-over-DS targets James Prestwood
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: James Prestwood @ 2021-04-27 19:49 UTC (permalink / raw)
  To: iwd

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

The building of the FT IEs for Action/Authenticate
frames will need to be shared between ft and netdev
once FT-over-DS is refactored.

The building was refactored to work off the callers
buffer rather than internal stack buffers. An argument
'new_snonce' was included as FT-over-DS will generate
a new snonce for the initial action frame, hence the
handshakes snonce cannot be used.
---
 src/ft.c | 62 +++++++++++++++++++++++++++++++-------------------------
 src/ft.h |  4 ++++
 2 files changed, 38 insertions(+), 28 deletions(-)

diff --git a/src/ft.c b/src/ft.c
index 698170cc..80782a58 100644
--- a/src/ft.c
+++ b/src/ft.c
@@ -724,19 +724,16 @@ static void ft_sm_free(struct auth_proto *ap)
 	l_free(ft);
 }
 
-static bool ft_start(struct auth_proto *ap)
+bool ft_build_authenticate_ies(struct handshake_state *hs,
+				const uint8_t *new_snonce, uint8_t *buf,
+				size_t *len)
 {
-	struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
-	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 mde[5];
-	struct iovec iov[3];
-	size_t iov_elems = 0;
+	uint8_t *ptr = buf;
 
 	if (is_rsn) {
 		struct ie_rsn_info rsn_info;
-		uint8_t *rsne;
 
 		/*
 		 * Rebuild the RSNE to include the PMKR0Name and append
@@ -757,26 +754,18 @@ static bool ft_start(struct auth_proto *ap)
 		rsn_info.num_pmkids = 1;
 		rsn_info.pmkids = hs->pmk_r0_name;
 
-		rsne = alloca(256);
-		ie_build_rsne(&rsn_info, rsne);
-
-		iov[iov_elems].iov_base = rsne;
-		iov[iov_elems].iov_len = rsne[1] + 2;
-		iov_elems += 1;
+		ie_build_rsne(&rsn_info, ptr);
+		ptr += ptr[1] + 2;
 	}
 
 	/* The MDE advertised by the BSS must be passed verbatim */
-	mde[0] = IE_TYPE_MOBILITY_DOMAIN;
-	mde[1] = 3;
-	memcpy(mde + 2, hs->mde + 2, 3);
-
-	iov[iov_elems].iov_base = mde;
-	iov[iov_elems].iov_len = 5;
-	iov_elems += 1;
+	ptr[0] = IE_TYPE_MOBILITY_DOMAIN;
+	ptr[1] = 3;
+	memcpy(ptr + 2, hs->mde + 2, 3);
+	ptr += 5;
 
 	if (is_rsn) {
 		struct ie_ft_info ft_info;
-		uint8_t *fte;
 
 		/*
 		 * 12.8.2: "If present, the FTE shall be set as follows:
@@ -793,17 +782,34 @@ static bool ft_start(struct auth_proto *ap)
 		memcpy(ft_info.r0khid, hs->r0khid, hs->r0khid_len);
 		ft_info.r0khid_len = hs->r0khid_len;
 
-		memcpy(ft_info.snonce, hs->snonce, 32);
+		memcpy(ft_info.snonce, new_snonce, 32);
 
-		fte = alloca(256);
-		ie_build_fast_bss_transition(&ft_info, kck_len, fte);
+		ie_build_fast_bss_transition(&ft_info, kck_len, ptr);
 
-		iov[iov_elems].iov_base = fte;
-		iov[iov_elems].iov_len = fte[1] + 2;
-		iov_elems += 1;
+		ptr += ptr[1] + 2;
 	}
 
-	ft->tx_auth(iov, iov_elems, ft->user_data);
+	if (len)
+		*len = ptr - buf;
+
+	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->snonce, buf, &len))
+		return false;
+
+	iov.iov_base = buf;
+	iov.iov_len = len;
+
+	ft->tx_auth(&iov, 1, ft->user_data);
 
 	return true;
 }
diff --git a/src/ft.h b/src/ft.h
index 6f6a7fd5..f24b3b5e 100644
--- a/src/ft.h
+++ b/src/ft.h
@@ -25,6 +25,10 @@ typedef void (*ft_tx_authenticate_func_t)(struct iovec *iov, size_t iov_len,
 typedef void (*ft_tx_associate_func_t)(struct iovec *ie_iov, size_t iov_len,
 					void *user_data);
 
+bool ft_build_authenticate_ies(struct handshake_state *hs,
+				const uint8_t *new_snonce, uint8_t *buf,
+				size_t *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,
-- 
2.26.2

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

* [PATCH 4/7] ft: create cache for FT-over-DS targets
  2021-04-27 19:49 [PATCH 1/7] netdev: add user_data to netdev_send_action_frame[v] James Prestwood
  2021-04-27 19:49 ` [PATCH 2/7] ft: factor out various parsing routines James Prestwood
  2021-04-27 19:49 ` [PATCH 3/7] ft: expose ft_build_authenticate_ies James Prestwood
@ 2021-04-27 19:49 ` James Prestwood
  2021-04-28 19:30   ` Denis Kenzior
  2021-04-27 19:49 ` [PATCH 5/7] ft: refactor FT-over-DS into two stages James Prestwood
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 10+ messages in thread
From: James Prestwood @ 2021-04-27 19:49 UTC (permalink / raw)
  To: iwd

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

FT-over-DS is being separated into two independent stages. The
first of which is the processing of the action frame response.
The cache will hold all the parsed information from the action
frame and allow it to be retrieved at a later time when IWD
needs to roam.

Initial cache entires should be created when the action frame is
being sent out. Once a response is received it can be parsed and
the matching entry (if found) will be updated with the parsed
IEs.

ft_over_ds_entry_create will create a new entry and push it into
the queue. Entries are found based on the supplicant address and
authenticator address. From an API standpoint the current handshake
and target BSS are passed in. Neither the handshake nor scan_bss
are saved directly, but only the information needed to do FT.

ft_over_ds_parse_action_response parses the action response from
the AP, verifies, and updates the matching entry with the parsed
IEs.

ft_over_ds_prepare_handshake is the final step prior to
Reassociation. This sets all the stored IEs, anonce, and KH IDs
into the handshake and derives the new PTK.
---
 src/ft.c | 296 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/ft.h |  22 +++++
 2 files changed, 318 insertions(+)

diff --git a/src/ft.c b/src/ft.c
index 80782a58..d9387e9b 100644
--- a/src/ft.c
+++ b/src/ft.c
@@ -32,6 +32,25 @@
 #include "src/ft.h"
 #include "src/mpdu.h"
 #include "src/auth-proto.h"
+#include "src/scan.h"
+#include "src/util.h"
+#include "src/netdev.h"
+#include "src/module.h"
+
+struct ft_ds_info {
+	uint32_t ifindex;
+
+	uint8_t spa[6];
+	uint8_t aa[6];
+	uint8_t snonce[32];
+	uint8_t mde[3];
+	uint8_t *authenticator_ie;
+	uint8_t *fte;
+	void *user_data;
+	ft_over_ds_destroy_func_t destroy;
+
+	struct ie_ft_info ft_info;
+};
 
 struct ft_sm {
 	struct auth_proto ap;
@@ -43,6 +62,8 @@ struct ft_sm {
 	void *user_data;
 };
 
+static struct l_queue *ft_over_ds_entries;
+
 /*
  * 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.
@@ -462,6 +483,52 @@ static bool ft_parse_fte(struct handshake_state *hs,
 	return true;
 }
 
+static bool ft_over_ds_process_ies(struct handshake_state *hs,
+					struct ft_ds_info *info,
+					const uint8_t *ies,
+					size_t ies_len)
+{
+	const uint8_t *rsne = NULL;
+	const uint8_t *mde = NULL;
+	const uint8_t *fte = NULL;
+	bool is_rsn;
+
+	if (ft_parse_ies(ies, ies_len, &rsne, &mde, &fte) < 0)
+		return false;
+
+	is_rsn = hs->supplicant_ie != NULL;
+
+	if (is_rsn) {
+		if (!ft_verify_rsne(rsne, hs->pmk_r0_name,
+					info->authenticator_ie))
+			goto ft_error;
+	} else if (rsne)
+		goto ft_error;
+
+	/*
+	 * Check for an MD IE identical to the one we sent in message 1
+	 *
+	 * 12.8.3: "The MDE shall contain the MDID and FT Capability and
+	 * Policy fields. This element shall be the same as the MDE
+	 * advertised by the target AP in Beacon and Probe Response frames."
+	 */
+	if (!mde || memcmp(info->mde, mde + 2, sizeof(info->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)
 {
@@ -520,6 +587,221 @@ ft_error:
 	return -EBADMSG;
 }
 
+struct ft_ds_finder {
+	const uint8_t *spa;
+	const uint8_t *aa;
+};
+
+static bool ft_over_ds_match(const void *a, const void *b)
+{
+	const struct ft_ds_info *info = a;
+	const struct ft_ds_finder *finder = b;
+
+	if (memcmp(finder->spa, info->spa, 6) != 0)
+		return false;
+
+	/* handle only SPA match for remove all */
+	if (!finder->aa)
+		return true;
+
+	if (memcmp(finder->aa, info->aa, 6) != 0)
+		return false;
+
+	return true;
+}
+
+static struct ft_ds_info *ft_over_ds_find(const uint8_t *spa, const uint8_t *aa)
+{
+	struct ft_ds_finder finder;
+
+	finder.spa = spa;
+	finder.aa = aa;
+
+	return l_queue_find(ft_over_ds_entries, ft_over_ds_match, &finder);
+}
+
+/*
+ * Creates an initial over-DS entry to be updated later when IE's are added
+ */
+bool ft_over_ds_entry_create(uint32_t ifindex, struct handshake_state *hs,
+				struct scan_bss *target,
+				const uint8_t *snonce, void *user_data,
+				ft_over_ds_destroy_func_t destroy)
+{
+	struct ft_ds_info *info;
+
+	if (ft_over_ds_find(hs->spa, target->addr))
+		return false;
+
+	info = l_new(struct ft_ds_info, 1);
+
+	info->ifindex = ifindex;
+	memcpy(info->spa, hs->spa, 6);
+	memcpy(info->aa, target->addr, 6);
+	memcpy(info->mde, target->mde, sizeof(info->mde));
+	memcpy(info->snonce, snonce, 32);
+	info->user_data = user_data;
+	info->destroy = destroy;
+
+	info->authenticator_ie = l_memdup(hs->authenticator_ie,
+						hs->authenticator_ie[1] + 2);
+
+	l_queue_push_head(ft_over_ds_entries, info);
+
+	l_debug("Created FT entry SPA: "MAC" AA: "MAC, MAC_STR(info->spa),
+			MAC_STR(info->aa));
+
+	return true;
+}
+
+static void ft_over_ds_info_free(void *data)
+{
+	struct ft_ds_info *info = data;
+
+	if (info->destroy)
+		info->destroy(info->user_data);
+
+	if (info->authenticator_ie)
+		l_free(info->authenticator_ie);
+
+	if (info->fte)
+		l_free(info->fte);
+
+	l_free(info);
+}
+
+static bool ft_over_ds_remove(void *data, void *user_data)
+{
+	struct ft_ds_info *info = data;
+
+	if (memcmp(info->spa, user_data, 6) != 0)
+		return false;
+
+	ft_over_ds_info_free(info);
+
+	return true;
+}
+
+bool ft_over_ds_entry_remove_all(const uint8_t *spa)
+{
+	l_queue_foreach_remove(ft_over_ds_entries, ft_over_ds_remove,
+					(void *)spa);
+
+	return true;
+}
+
+bool ft_over_ds_entry_remove(const uint8_t *spa, const uint8_t *aa)
+{
+	struct ft_ds_finder finder;
+
+	finder.spa = spa;
+	finder.aa = aa;
+
+	l_queue_remove_if(ft_over_ds_entries, ft_over_ds_match, &finder);
+
+	return true;
+}
+
+void *ft_over_ds_entry_get_userdata(const uint8_t *spa, const uint8_t *aa)
+{
+	struct ft_ds_info *info = ft_over_ds_find(spa, aa);
+
+	if (!info)
+		return NULL;
+
+	return info->user_data;
+}
+
+/*
+ * Updates an over-DS entry with the action response IE's. This will parse and
+ * verify the IEs, and remove the entry if invalid.
+ */
+static bool ft_over_ds_entry_update(struct ft_ds_info *info,
+				const uint8_t *ies, size_t ies_len)
+{
+	struct netdev *netdev;
+	struct handshake_state *hs;
+
+	netdev = netdev_find(info->ifindex);
+	if (!netdev) {
+		/* Netdev went away, remove all cached entries */
+		ft_over_ds_entry_remove_all(info->spa);
+		return false;
+	}
+
+	hs = netdev_get_handshake(netdev);
+
+	if (!ft_over_ds_process_ies(hs, info, ies, ies_len)) {
+		l_queue_remove(ft_over_ds_entries, info);
+		ft_over_ds_info_free(info);
+		return false;
+	}
+
+	return true;
+}
+
+int ft_over_ds_parse_action_response(uint8_t *spa, const uint8_t *frame,
+				size_t frame_len, const uint8_t **aa_out)
+{
+	struct ft_ds_info *info;
+	uint16_t status = 0;
+	const uint8_t *aa;
+
+	if (frame_len < 16)
+		goto parse_error;
+
+	/* Category FT */
+	if (frame[0] != 6)
+		goto parse_error;
+
+	/* FT Action */
+	if (frame[1] != 2)
+		goto parse_error;
+
+	if (memcmp(frame + 2, spa, 6))
+		goto parse_error;
+
+	aa = frame + 8;
+
+	info = ft_over_ds_find(spa, aa);
+	if (!info)
+		return -ENOENT;
+
+	if (!ft_over_ds_entry_update(info, frame + 16, frame_len - 16))
+		goto parse_error;
+
+	if (aa_out)
+		*aa_out = aa;
+
+	return 0;
+
+parse_error:
+	return (int)status;
+}
+
+bool ft_over_ds_prepare_handshake(struct handshake_state *hs,
+					struct scan_bss *target)
+{
+	struct ft_ds_info *info = ft_over_ds_find(hs->spa, target->addr);
+
+	if (!info)
+		return false;
+
+	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;
+}
+
 static int ft_rx_action(struct auth_proto *ap, const uint8_t *frame,
 				size_t frame_len)
 {
@@ -850,3 +1132,17 @@ struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
 {
 	return ft_sm_new(hs, tx_auth, tx_assoc, false, user_data);
 }
+
+static int ft_init(void)
+{
+	ft_over_ds_entries = l_queue_new();
+
+	return 0;
+}
+
+static void ft_exit(void)
+{
+	l_queue_destroy(ft_over_ds_entries, ft_over_ds_info_free);
+}
+
+IWD_MODULE(ft, ft_init, ft_exit);
diff --git a/src/ft.h b/src/ft.h
index f24b3b5e..27d91dd4 100644
--- a/src/ft.h
+++ b/src/ft.h
@@ -20,15 +20,25 @@
  *
  */
 
+
+struct scan_bss;
+struct netdev;
+struct handshake_state;
+
 typedef void (*ft_tx_authenticate_func_t)(struct iovec *iov, size_t iov_len,
 					void *user_data);
 typedef void (*ft_tx_associate_func_t)(struct iovec *ie_iov, size_t iov_len,
 					void *user_data);
 
+typedef void (*ft_over_ds_destroy_func_t)(void *user_data);
+
 bool ft_build_authenticate_ies(struct handshake_state *hs,
 				const uint8_t *new_snonce, uint8_t *buf,
 				size_t *len);
 
+int ft_over_ds_parse_action_response(uint8_t *spa, const uint8_t *frame,
+				size_t frame_len, const uint8_t **aa_out);
+
 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,
@@ -38,3 +48,15 @@ struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
 				ft_tx_authenticate_func_t tx_auth,
 				ft_tx_associate_func_t tx_assoc,
 				void *user_data);
+
+bool ft_over_ds_entry_create(uint32_t ifindex, struct handshake_state *hs,
+				struct scan_bss *target,
+				const uint8_t *snonce, void *user_data,
+				ft_over_ds_destroy_func_t destroy);
+bool ft_over_ds_entry_remove_all(const uint8_t *spa);
+bool ft_over_ds_entry_remove(const uint8_t *spa, const uint8_t *aa);
+
+void *ft_over_ds_entry_get_userdata(const uint8_t *spa, const uint8_t *aa);
+
+bool ft_over_ds_prepare_handshake(struct handshake_state *hs,
+					struct scan_bss *target);
-- 
2.26.2

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

* [PATCH 5/7] ft: refactor FT-over-DS into two stages
  2021-04-27 19:49 [PATCH 1/7] netdev: add user_data to netdev_send_action_frame[v] James Prestwood
                   ` (2 preceding siblings ...)
  2021-04-27 19:49 ` [PATCH 4/7] ft: create cache for FT-over-DS targets James Prestwood
@ 2021-04-27 19:49 ` James Prestwood
  2021-04-28 20:04   ` Denis Kenzior
  2021-04-27 19:49 ` [PATCH 6/7] station: separate FT-over-DS stages James Prestwood
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 10+ messages in thread
From: James Prestwood @ 2021-04-27 19:49 UTC (permalink / raw)
  To: iwd

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

FT-over-DS followed the same pattern as FT-over-Air which worked,
but really limited how the protocol could be used. FT-over-DS is
unique in that we can authenticate to many APs by sending out
FT action frames and parsing the results. Once parsed IWD can
immediately Reassociate, or do so at a later time.

To take advantage of this IWD need to separate FT-over-DS into
two stages: action frame and reassociation.

The initial action frame stage is started by netdev. The target
BSS is sent an FT action frame and a new cache entry is created
in ft.c. Once the response is received the entry is updated
with all the needed data to Reassociate. To limit the record
keeping on netdev each FT-over-DS entry holds a userdata pointer
so netdev doesn't need to maintain its own list of data for
callbacks.

Once the action response is parsed netdev will call back signalling
the action frame sequence was completed (either successfully or not).
At this point the 'normal' FT procedure can start using the
FT-over-DS auth-proto.
---
 src/ft.c      |  95 ++++--------------
 src/ft.h      |   1 -
 src/netdev.c  | 262 ++++++++++++++++++++++++++++++++++++--------------
 src/netdev.h  |  11 ++-
 src/station.c |   3 +-
 5 files changed, 224 insertions(+), 148 deletions(-)

diff --git a/src/ft.c b/src/ft.c
index d9387e9b..9a204dae 100644
--- a/src/ft.c
+++ b/src/ft.c
@@ -177,40 +177,6 @@ static bool ft_parse_authentication_resp_frame(const uint8_t *data, size_t len,
 	return true;
 }
 
-static bool ft_parse_action_resp_frame(const uint8_t *frame, size_t frame_len,
-					const uint8_t *spa, const uint8_t *aa,
-					uint16_t *out_status,
-					const uint8_t **out_ies,
-					size_t *out_ies_len)
-{
-	uint16_t status = 0;
-
-	/* Category FT */
-	if (frame[0] != 6)
-		return false;
-
-	/* FT Action */
-	if (frame[1] != 2)
-		return false;
-
-	if (memcmp(frame + 2, spa, 6))
-		return false;
-	if (memcmp(frame + 8, aa, 6))
-		return false;
-
-	status = l_get_le16(frame + 14);
-
-	if (out_status)
-		*out_status = status;
-
-	if (status == 0 && out_ies) {
-		*out_ies = frame + 16;
-		*out_ies_len = frame_len - 16;
-	}
-
-	return true;
-}
-
 static bool ft_parse_associate_resp_frame(const uint8_t *frame, size_t frame_len,
 				uint16_t *out_status, const uint8_t **rsne,
 				const uint8_t **mde, const uint8_t **fte)
@@ -802,34 +768,6 @@ bool ft_over_ds_prepare_handshake(struct handshake_state *hs,
 	return true;
 }
 
-static int ft_rx_action(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;
-
-	if (!ft_parse_action_resp_frame(frame, frame_len, ft->hs->spa,
-						ft->hs->aa, &status_code,
-						&ies, &ies_len))
-		return -EBADMSG;
-
-	/* 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_tx_reassociate(ft);
-
-auth_error:
-	return (int)status_code;
-}
-
 static int ft_rx_authenticate(struct auth_proto *ap, const uint8_t *frame,
 				size_t frame_len)
 {
@@ -1006,6 +944,13 @@ static void ft_sm_free(struct auth_proto *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) == 0;
+}
+
 bool ft_build_authenticate_ies(struct handshake_state *hs,
 				const uint8_t *new_snonce, uint8_t *buf,
 				size_t *len)
@@ -1096,10 +1041,9 @@ static bool ft_start(struct auth_proto *ap)
 	return true;
 }
 
-static struct auth_proto *ft_sm_new(struct handshake_state *hs,
+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,
-				bool over_air,
 				void *user_data)
 {
 	struct ft_sm *ft = l_new(struct ft_sm, 1);
@@ -1109,7 +1053,7 @@ static struct auth_proto *ft_sm_new(struct handshake_state *hs,
 	ft->hs = hs;
 	ft->user_data = user_data;
 
-	ft->ap.rx_authenticate = (over_air) ? ft_rx_authenticate : ft_rx_action;
+	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;
@@ -1117,20 +1061,21 @@ static struct auth_proto *ft_sm_new(struct handshake_state *hs,
 	return &ft->ap;
 }
 
-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,
-				void *user_data)
-{
-	return ft_sm_new(hs, tx_auth, tx_assoc, true, user_data);
-}
-
 struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
-				ft_tx_authenticate_func_t tx_auth,
 				ft_tx_associate_func_t tx_assoc,
 				void *user_data)
 {
-	return ft_sm_new(hs, tx_auth, tx_assoc, false, 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->ap.rx_associate = ft_rx_associate;
+	ft->ap.start = ft_over_ds_start;
+	ft->ap.free = ft_sm_free;
+
+	return &ft->ap;
 }
 
 static int ft_init(void)
diff --git a/src/ft.h b/src/ft.h
index 27d91dd4..be52abaa 100644
--- a/src/ft.h
+++ b/src/ft.h
@@ -45,7 +45,6 @@ struct auth_proto *ft_over_air_sm_new(struct handshake_state *hs,
 				void *user_data);
 
 struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
-				ft_tx_authenticate_func_t tx_auth,
 				ft_tx_associate_func_t tx_assoc,
 				void *user_data);
 
diff --git a/src/netdev.c b/src/netdev.c
index 2dae07df..d0f9cae8 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -187,6 +187,14 @@ struct netdev_watch {
 	void *user_data;
 };
 
+struct netdev_ft_over_ds_info {
+	struct netdev *netdev;
+	uint8_t addr[ETH_ALEN];
+	struct l_timeout *timeout;
+	netdev_ft_over_ds_cb_t cb;
+	void *user_data;
+};
+
 static struct l_netlink *rtnl = NULL;
 static struct l_genl_family *nl80211;
 static struct l_queue *netdev_list;
@@ -742,6 +750,8 @@ static void netdev_connect_free(struct netdev *netdev)
 		l_genl_family_cancel(nl80211, netdev->disconnect_cmd_id);
 		netdev->disconnect_cmd_id = 0;
 	}
+
+	ft_over_ds_entry_remove_all(netdev->addr);
 }
 
 static void netdev_connect_failed(struct netdev *netdev,
@@ -1259,6 +1269,12 @@ static void netdev_connect_ok(struct netdev *netdev)
 
 	netdev->operational = true;
 
+	/*
+	 * Remove all entries in case station sends out a batch of FT-over-DS
+	 * action frames after connecting.
+	 */
+	ft_over_ds_entry_remove_all(netdev->addr);
+
 	if (netdev->fw_roam_bss) {
 		if (netdev->event_filter)
 			netdev->event_filter(netdev, NETDEV_EVENT_ROAMED,
@@ -3630,10 +3646,25 @@ static void prepare_ft(struct netdev *netdev, 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);
+
 	memcpy(netdev->prev_bssid, netdev->handshake->aa, 6);
 	netdev->prev_frequency = netdev->frequency;
 	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->associated = false;
 	netdev->operational = false;
 	netdev->in_ft = true;
@@ -3676,44 +3707,59 @@ static void prepare_ft(struct netdev *netdev, struct scan_bss *target_bss)
 	}
 }
 
-static void netdev_ft_request_cb(struct l_genl_msg *msg, void *user_data)
+static void netdev_ft_over_ds_auth_failed(struct netdev_ft_over_ds_info *info,
+						uint16_t status)
 {
-	struct netdev *netdev = user_data;
-	int err = l_genl_msg_get_error(msg);
-
-	if (err < 0) {
-		l_error("Could not send CMD_FRAME (%d)", err);
-		netdev_connect_failed(netdev,
-					NETDEV_RESULT_AUTHENTICATION_FAILED,
-					MMPDU_STATUS_CODE_UNSPECIFIED);
+	if (info->timeout) {
+		l_timeout_remove(info->timeout);
+		info->timeout = NULL;
 	}
+
+	if (info->cb)
+		info->cb(info->netdev, status, info->addr, info->user_data);
+
+	ft_over_ds_entry_remove(info->netdev->addr, info->addr);
 }
 
 static void netdev_ft_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;
+	struct netdev_ft_over_ds_info *info = NULL;
 	int ret;
 	uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
-	struct netdev *netdev = user_data;
+	const uint8_t *aa;
+
+	ret = ft_over_ds_parse_action_response(netdev->addr, body,
+						body_len, &aa);
+	if (ret < 0)
+		return;
 
-	if (!netdev->ap || !netdev->in_ft)
+	info = ft_over_ds_entry_get_userdata(netdev->addr, aa);
+	if (!info)
 		return;
 
-	ret = auth_proto_rx_authenticate(netdev->ap, body, body_len);
-	if (ret < 0)
-		goto ft_error;
-	else if (ret > 0) {
+	if (info->timeout) {
+		l_timeout_remove(info->timeout);
+		info->timeout = NULL;
+	}
+
+	/* Now make sure the packet contained a success status code */
+	if (ret > 0) {
 		status_code = (uint16_t)ret;
 		goto ft_error;
 	}
 
+	if (info->cb)
+		info->cb(netdev, 0, info->addr, info->user_data);
+
 	return;
 
 ft_error:
-	netdev_connect_failed(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED,
-				status_code);
-	return;
+	l_debug("FT-over-DS to "MAC" failed", MAC_STR(info->addr));
+
+	netdev_ft_over_ds_auth_failed(info, status_code);
 }
 
 static void netdev_qos_map_frame_event(const struct mmpdu_header *hdr,
@@ -3738,29 +3784,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 void netdev_ft_over_ds_tx_authenticate(struct iovec *iov,
-					size_t iov_len, void *user_data)
-{
-	struct netdev *netdev = user_data;
-	uint8_t ft_req[14];
-	struct handshake_state *hs = netdev->handshake;
-	struct iovec iovs[iov_len + 1];
-
-	ft_req[0] = 6; /* FT category */
-	ft_req[1] = 1; /* FT Request action */
-	memcpy(ft_req + 2, netdev->addr, 6);
-	memcpy(ft_req + 8, hs->aa, 6);
-
-	iovs[0].iov_base = ft_req;
-	iovs[0].iov_len = sizeof(ft_req);
-	memcpy(iovs + 1, iov, sizeof(*iov) * iov_len);
-
-	netdev_send_action_framev(netdev, netdev->prev_bssid, iovs, iov_len + 1,
-					netdev->prev_frequency,
-					netdev_ft_request_cb,
-					netdev);
-}
-
 static bool netdev_ft_work_ready(struct wiphy_radio_work_item *item)
 {
 	struct netdev *netdev = l_container_of(item, struct netdev, work);
@@ -3781,8 +3804,7 @@ static const struct wiphy_radio_work_item_ops ft_work_ops = {
 	.do_work = netdev_ft_work_ready,
 };
 
-static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
-				bool over_air,
+int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
 				netdev_connect_cb_t cb)
 {
 	if (!netdev->operational)
@@ -3795,32 +3817,13 @@ static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
 
 	prepare_ft(netdev, target_bss);
 
-	/*
-	 * 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);
 	handshake_state_new_snonce(netdev->handshake);
 
-	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->connect_cb = cb;
 
-	if (over_air)
-		netdev->ap = ft_over_air_sm_new(netdev->handshake,
+	netdev->ap = ft_over_air_sm_new(netdev->handshake,
 					netdev_ft_tx_authenticate,
 					netdev_ft_tx_associate, netdev);
-	else
-		netdev->ap = ft_over_ds_sm_new(netdev->handshake,
-					netdev_ft_over_ds_tx_authenticate,
-					netdev_ft_tx_associate, netdev);
 
 	wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
 				&ft_work_ops);
@@ -3828,17 +3831,136 @@ static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
 	return 0;
 }
 
-int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
-				netdev_connect_cb_t cb)
+int netdev_fast_transition_over_ds(struct netdev *netdev,
+					struct scan_bss *target_bss,
+					netdev_connect_cb_t cb,
+					void *user_data)
+{
+	struct netdev_ft_over_ds_info *info;
+
+	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;
+
+	info = ft_over_ds_entry_get_userdata(netdev->addr, target_bss->addr);
+	if (!info)
+		return -ENOENT;
+
+	prepare_ft(netdev, target_bss);
+
+	if (!ft_over_ds_prepare_handshake(netdev->handshake, target_bss))
+		return -ENOENT;
+
+	netdev->connect_cb = cb;
+	netdev->user_data = user_data;
+
+	netdev->ap = ft_over_ds_sm_new(netdev->handshake,
+					netdev_ft_tx_associate,
+					netdev);
+
+	wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
+				&ft_work_ops);
+
+	return 0;
+}
+
+static void netdev_ft_request_cb(struct l_genl_msg *msg, void *user_data)
 {
-	return fast_transition(netdev, target_bss, true, cb);
+	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);
+	}
 }
 
-int netdev_fast_transition_over_ds(struct netdev *netdev,
-					struct scan_bss *target_bss,
-					netdev_connect_cb_t cb)
+static void netdev_ft_over_ds_timeout(struct l_timeout *timeout,
+					void *user_data)
+{
+	struct netdev_ft_over_ds_info *info = user_data;
+
+	netdev_ft_over_ds_auth_failed(info, MMPDU_STATUS_CODE_UNSPECIFIED);
+}
+
+static void netdev_ft_over_ds_info_destroy(void *user_data)
 {
-	return fast_transition(netdev, target_bss, false, cb);
+	struct netdev_ft_over_ds_info *info = user_data;
+
+	if (info->timeout)
+		l_timeout_remove(info->timeout);
+
+	l_free(info);
+}
+
+int netdev_fast_transition_over_ds_action(struct netdev *netdev,
+						struct scan_bss *target_bss,
+						netdev_ft_over_ds_cb_t cb,
+						void *user_data)
+{
+	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;
+	uint8_t snonce[32];
+
+	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("");
+
+	l_getrandom(snonce, 32);
+
+	info = l_new(struct netdev_ft_over_ds_info, 1);
+	info->netdev = netdev;
+	memcpy(info->addr, target_bss->addr, ETH_ALEN);
+	info->cb = cb;
+	info->user_data = user_data;
+
+	if (!ft_over_ds_entry_create(netdev->index, hs, target_bss, snonce,
+					info, netdev_ft_over_ds_info_destroy)) {
+		l_free(info);
+		return -EALREADY;
+	}
+
+	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->addr, 6);
+
+	iovs[0].iov_base = ft_req;
+	iovs[0].iov_len = sizeof(ft_req);
+
+	if (!ft_build_authenticate_ies(hs, snonce, buf, &len))
+		goto failed;
+
+	iovs[1].iov_base = buf;
+	iovs[1].iov_len = len;
+
+	info->timeout = l_timeout_create_ms(300, netdev_ft_over_ds_timeout,
+						info, NULL);
+
+	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)
diff --git a/src/netdev.h b/src/netdev.h
index 7b321bfb..bebde242 100644
--- a/src/netdev.h
+++ b/src/netdev.h
@@ -124,6 +124,10 @@ typedef void (*netdev_get_station_cb_t)(
 
 const char *netdev_iftype_to_string(uint32_t iftype);
 
+typedef void (*netdev_ft_over_ds_cb_t)(struct netdev *netdev,
+					uint16_t status, const uint8_t *bssid,
+					void *user_data);
+
 struct wiphy *netdev_get_wiphy(struct netdev *netdev);
 const uint8_t *netdev_get_address(struct netdev *netdev);
 uint32_t netdev_get_ifindex(struct netdev *netdev);
@@ -157,9 +161,14 @@ int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss,
 			netdev_connect_cb_t cb, void *user_data);
 int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
 				netdev_connect_cb_t cb);
+int netdev_fast_transition_over_ds_action(struct netdev *netdev,
+					struct scan_bss *target_bss,
+					netdev_ft_over_ds_cb_t cb,
+					void *user_data);
 int netdev_fast_transition_over_ds(struct netdev *netdev,
 					struct scan_bss *target_bss,
-					netdev_connect_cb_t cb);
+					netdev_connect_cb_t cb,
+					void *user_data);
 int netdev_preauthenticate(struct netdev *netdev, struct scan_bss *target_bss,
 				netdev_preauthenticate_cb_t cb,
 				void *user_data);
diff --git a/src/station.c b/src/station.c
index 064872c6..9254ec90 100644
--- a/src/station.c
+++ b/src/station.c
@@ -1840,7 +1840,8 @@ static void station_transition_start(struct station *station,
 		if ((hs->mde[4] & 1) && (station->ap_directed_roaming ||
 				station->signal_low)) {
 			if (netdev_fast_transition_over_ds(station->netdev, bss,
-					station_fast_transition_cb) < 0) {
+					station_fast_transition_cb,
+					station) < 0) {
 				station_roam_failed(station);
 				return;
 			}
-- 
2.26.2

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

* [PATCH 6/7] station: separate FT-over-DS stages
  2021-04-27 19:49 [PATCH 1/7] netdev: add user_data to netdev_send_action_frame[v] James Prestwood
                   ` (3 preceding siblings ...)
  2021-04-27 19:49 ` [PATCH 5/7] ft: refactor FT-over-DS into two stages James Prestwood
@ 2021-04-27 19:49 ` James Prestwood
  2021-04-27 19:49 ` [PATCH 7/7] ft: netdev: add return value to tx_associate James Prestwood
  2021-04-28 19:16 ` [PATCH 1/7] netdev: add user_data to netdev_send_action_frame[v] Denis Kenzior
  6 siblings, 0 replies; 10+ messages in thread
From: James Prestwood @ 2021-04-27 19:49 UTC (permalink / raw)
  To: iwd

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

FT-over-DS was refactored to separate the FT action frame and
reassociation. From stations standpoint IWD needs to call
netdev_fast_transition_over_ds_action prior to actually roaming.
For now these two stages are being combined and the action
roam happens immediately after the action response callback.
---
 src/station.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 52 insertions(+), 4 deletions(-)

diff --git a/src/station.c b/src/station.c
index 9254ec90..79ba23dc 100644
--- a/src/station.c
+++ b/src/station.c
@@ -112,6 +112,7 @@ struct station {
 	bool ap_directed_roaming : 1;
 	bool scanning : 1;
 	bool autoconnect : 1;
+	bool ft_over_ds : 1;
 };
 
 struct anqp_entry {
@@ -1625,10 +1626,17 @@ static void station_roam_failed(struct station *station)
 	l_debug("%u", netdev_get_ifindex(station->netdev));
 
 	/*
-	 * If we attempted a reassociation or a fast transition, and ended up
-	 * here then we are now disconnected.
+	 * If we attempted a reassociation or a fast transition (except DS),
+	 * and ended up here then we are now disconnected. In the case of
+	 * FT over DS we can remain connected to the AP even if the transition
+	 * fails.
 	 */
 	if (station->state == STATION_STATE_ROAMING) {
+		if (station->ft_over_ds) {
+			station_enter_state(station, STATION_STATE_CONNECTED);
+			goto delayed_retry;
+		}
+
 		station_disassociated(station);
 		return;
 	}
@@ -1657,6 +1665,7 @@ delayed_retry:
 	station->preparing_roam = false;
 	station->roam_scan_full = false;
 	station->ap_directed_roaming = false;
+	station->ft_over_ds = false;
 
 	if (station->signal_low)
 		station_roam_timeout_rearm(station, roam_retry_interval);
@@ -1742,6 +1751,42 @@ static bool bss_match_bssid(const void *a, const void *b)
 	return !memcmp(bss->addr, bssid, sizeof(bss->addr));
 }
 
+static void station_fast_transition_ds_cb(struct netdev *netdev,
+					uint16_t status, const uint8_t *bssid,
+					void *user_data)
+{
+	struct station *station = user_data;
+	struct scan_bss *bss;
+
+	if (status != 0)
+		goto failed;
+
+	/*
+	 * TODO: In the future it may be desired to start sending out these
+	 * FT-over-DS action frames at the time of connecting then be able to
+	 * roam immediately when required. If this is being done we can simply
+	 * bail out now as ft already caches the entires. But since this was
+	 * initiated due to a need to roam, do so now.
+	 */
+
+	/* Make sure we still have our BSS */
+	bss = l_queue_find(station->bss_list, bss_match_bssid, bssid);
+	if (!bss)
+		goto failed;
+
+	l_debug("Starting FT-over-DS roam");
+
+	if (netdev_fast_transition_over_ds(station->netdev, bss,
+					station_fast_transition_cb,
+					station) < 0)
+		goto failed;
+
+	return;
+
+failed:
+	station_roam_failed(station);
+}
+
 static void station_preauthenticate_cb(struct netdev *netdev,
 					enum netdev_result result,
 					const uint8_t *pmk, void *user_data)
@@ -1839,8 +1884,11 @@ static void station_transition_start(struct station *station,
 		/* FT-over-DS can be better suited for these situations */
 		if ((hs->mde[4] & 1) && (station->ap_directed_roaming ||
 				station->signal_low)) {
-			if (netdev_fast_transition_over_ds(station->netdev, bss,
-					station_fast_transition_cb,
+			station->ft_over_ds = true;
+
+			if (netdev_fast_transition_over_ds_action(
+					station->netdev, bss,
+					station_fast_transition_ds_cb,
 					station) < 0) {
 				station_roam_failed(station);
 				return;
-- 
2.26.2

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

* [PATCH 7/7] ft: netdev: add return value to tx_associate
  2021-04-27 19:49 [PATCH 1/7] netdev: add user_data to netdev_send_action_frame[v] James Prestwood
                   ` (4 preceding siblings ...)
  2021-04-27 19:49 ` [PATCH 6/7] station: separate FT-over-DS stages James Prestwood
@ 2021-04-27 19:49 ` James Prestwood
  2021-04-28 19:16 ` [PATCH 1/7] netdev: add user_data to netdev_send_action_frame[v] Denis Kenzior
  6 siblings, 0 replies; 10+ messages in thread
From: James Prestwood @ 2021-04-27 19:49 UTC (permalink / raw)
  To: iwd

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

Prior to this, an error sending the FT Reassociation was treated
as fatal, which is correct for FT-over-Air but not for FT-over-DS.
If the actual l_genl_family_send call fails for FT-over-DS the
existing connection can be maintained and there is no need to
call netdev_connect_failed.

Adding a return to the tx_associate function works for both FT
types. In the FT-over-Air case this return will ultimately get
sent back up to auth_proto_rx_authenticate in which case will
call netdev_connect_failed. For FT-over-DS tx_associate is
actually called from the 'start' operation which can fail and
still maintain the existing connection.
---
 src/ft.c     | 4 +---
 src/ft.h     | 2 +-
 src/netdev.c | 8 ++++----
 3 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/src/ft.c b/src/ft.c
index 9a204dae..8e745eed 100644
--- a/src/ft.c
+++ b/src/ft.c
@@ -308,9 +308,7 @@ static int ft_tx_reassociate(struct ft_sm *ft)
 		iov_elems += 1;
 	}
 
-	ft->tx_assoc(iov, iov_elems, ft->user_data);
-
-	return 0;
+	return ft->tx_assoc(iov, iov_elems, ft->user_data);
 
 error:
 	return -EINVAL;
diff --git a/src/ft.h b/src/ft.h
index be52abaa..51a4d5f1 100644
--- a/src/ft.h
+++ b/src/ft.h
@@ -27,7 +27,7 @@ struct handshake_state;
 
 typedef void (*ft_tx_authenticate_func_t)(struct iovec *iov, size_t iov_len,
 					void *user_data);
-typedef void (*ft_tx_associate_func_t)(struct iovec *ie_iov, size_t iov_len,
+typedef int (*ft_tx_associate_func_t)(struct iovec *ie_iov, size_t iov_len,
 					void *user_data);
 
 typedef void (*ft_over_ds_destroy_func_t)(void *user_data);
diff --git a/src/netdev.c b/src/netdev.c
index d0f9cae8..fc893e92 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -3618,7 +3618,7 @@ restore_snonce:
 					MMPDU_STATUS_CODE_UNSPECIFIED);
 }
 
-static void netdev_ft_tx_associate(struct iovec *ie_iov, size_t iov_len,
+static int netdev_ft_tx_associate(struct iovec *ie_iov, size_t iov_len,
 					void *user_data)
 {
 	struct netdev *netdev = user_data;
@@ -3636,10 +3636,10 @@ static void netdev_ft_tx_associate(struct iovec *ie_iov, size_t iov_len,
 	if (!netdev->connect_cmd_id) {
 		l_genl_msg_unref(msg);
 
-		netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
-					MMPDU_STATUS_CODE_UNSPECIFIED);
-		return;
+		return -EIO;
 	}
+
+	return 0;
 }
 
 static void prepare_ft(struct netdev *netdev, struct scan_bss *target_bss)
-- 
2.26.2

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

* Re: [PATCH 1/7] netdev: add user_data to netdev_send_action_frame[v]
  2021-04-27 19:49 [PATCH 1/7] netdev: add user_data to netdev_send_action_frame[v] James Prestwood
                   ` (5 preceding siblings ...)
  2021-04-27 19:49 ` [PATCH 7/7] ft: netdev: add return value to tx_associate James Prestwood
@ 2021-04-28 19:16 ` Denis Kenzior
  6 siblings, 0 replies; 10+ messages in thread
From: Denis Kenzior @ 2021-04-28 19:16 UTC (permalink / raw)
  To: iwd

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

Hi James,

On 4/27/21 2:49 PM, James Prestwood wrote:
> This makes this internal API a bit more usable by removing the
> restriction of always having netdev as the user_data.
> ---
>   src/netdev.c | 21 +++++++++++++--------
>   1 file changed, 13 insertions(+), 8 deletions(-)
> 

Patch 1-3 applied, thanks.

Regards,
-Denis

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

* Re: [PATCH 4/7] ft: create cache for FT-over-DS targets
  2021-04-27 19:49 ` [PATCH 4/7] ft: create cache for FT-over-DS targets James Prestwood
@ 2021-04-28 19:30   ` Denis Kenzior
  0 siblings, 0 replies; 10+ messages in thread
From: Denis Kenzior @ 2021-04-28 19:30 UTC (permalink / raw)
  To: iwd

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

Hi James,

On 4/27/21 2:49 PM, James Prestwood wrote:
> FT-over-DS is being separated into two independent stages. The
> first of which is the processing of the action frame response.
> The cache will hold all the parsed information from the action
> frame and allow it to be retrieved at a later time when IWD
> needs to roam.
> 
> Initial cache entires should be created when the action frame is
> being sent out. Once a response is received it can be parsed and
> the matching entry (if found) will be updated with the parsed
> IEs.
> 
> ft_over_ds_entry_create will create a new entry and push it into
> the queue. Entries are found based on the supplicant address and
> authenticator address. From an API standpoint the current handshake
> and target BSS are passed in. Neither the handshake nor scan_bss
> are saved directly, but only the information needed to do FT.
> 
> ft_over_ds_parse_action_response parses the action response from
> the AP, verifies, and updates the matching entry with the parsed
> IEs.
> 
> ft_over_ds_prepare_handshake is the final step prior to
> Reassociation. This sets all the stored IEs, anonce, and KH IDs
> into the handshake and derives the new PTK.
> ---
>   src/ft.c | 296 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   src/ft.h |  22 +++++
>   2 files changed, 318 insertions(+)
> 
> diff --git a/src/ft.c b/src/ft.c
> index 80782a58..d9387e9b 100644
> --- a/src/ft.c
> +++ b/src/ft.c
> @@ -32,6 +32,25 @@
>   #include "src/ft.h"
>   #include "src/mpdu.h"
>   #include "src/auth-proto.h"
> +#include "src/scan.h"
> +#include "src/util.h"
> +#include "src/netdev.h"
> +#include "src/module.h"
> +
> +struct ft_ds_info {
> +	uint32_t ifindex;

Why do you need the ifindex and spa?  I would have thought one or the other 
would be sufficient.

> +
> +	uint8_t spa[6];
> +	uint8_t aa[6];
> +	uint8_t snonce[32];
> +	uint8_t mde[3];
> +	uint8_t *authenticator_ie;

Why do you need to store this?  Can't you just use the one from the 
netdev->handshake_state?  Maybe just pass it into 
ft_over_ds_parse_action_response()?

> +	uint8_t *fte;
> +	void *user_data;
> +	ft_over_ds_destroy_func_t destroy;

Whats this for?

> +
> +	struct ie_ft_info ft_info;
> +};
>   
>   struct ft_sm {
>   	struct auth_proto ap;
> @@ -43,6 +62,8 @@ struct ft_sm {
>   	void *user_data;
>   };
>   
> +static struct l_queue *ft_over_ds_entries;
> +
>   /*
>    * 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.
> @@ -462,6 +483,52 @@ static bool ft_parse_fte(struct handshake_state *hs,
>   	return true;
>   }
>   
> +static bool ft_over_ds_process_ies(struct handshake_state *hs,
> +					struct ft_ds_info *info,
> +					const uint8_t *ies,
> +					size_t ies_len)
> +{
> +	const uint8_t *rsne = NULL;
> +	const uint8_t *mde = NULL;
> +	const uint8_t *fte = NULL;
> +	bool is_rsn;
> +
> +	if (ft_parse_ies(ies, ies_len, &rsne, &mde, &fte) < 0)
> +		return false;
> +
> +	is_rsn = hs->supplicant_ie != NULL;
> +
> +	if (is_rsn) {
> +		if (!ft_verify_rsne(rsne, hs->pmk_r0_name,
> +					info->authenticator_ie))
> +			goto ft_error;
> +	} else if (rsne)
> +		goto ft_error;
> +
> +	/*
> +	 * Check for an MD IE identical to the one we sent in message 1
> +	 *
> +	 * 12.8.3: "The MDE shall contain the MDID and FT Capability and
> +	 * Policy fields. This element shall be the same as the MDE
> +	 * advertised by the target AP in Beacon and Probe Response frames."
> +	 */
> +	if (!mde || memcmp(info->mde, mde + 2, sizeof(info->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)
>   {
> @@ -520,6 +587,221 @@ ft_error:
>   	return -EBADMSG;
>   }
>   
> +struct ft_ds_finder {
> +	const uint8_t *spa;
> +	const uint8_t *aa;
> +};
> +
> +static bool ft_over_ds_match(const void *a, const void *b)
> +{
> +	const struct ft_ds_info *info = a;
> +	const struct ft_ds_finder *finder = b;
> +
> +	if (memcmp(finder->spa, info->spa, 6) != 0)
> +		return false;
> +
> +	/* handle only SPA match for remove all */
> +	if (!finder->aa)
> +		return true;
> +
> +	if (memcmp(finder->aa, info->aa, 6) != 0)
> +		return false;
> +
> +	return true;
> +}
> +
> +static struct ft_ds_info *ft_over_ds_find(const uint8_t *spa, const uint8_t *aa)
> +{
> +	struct ft_ds_finder finder;
> +
> +	finder.spa = spa;
> +	finder.aa = aa;
> +
> +	return l_queue_find(ft_over_ds_entries, ft_over_ds_match, &finder);
> +}
> +
> +/*
> + * Creates an initial over-DS entry to be updated later when IE's are added
> + */
> +bool ft_over_ds_entry_create(uint32_t ifindex, struct handshake_state *hs,
> +				struct scan_bss *target,
> +				const uint8_t *snonce, void *user_data,
> +				ft_over_ds_destroy_func_t destroy)

This seems super-complicated since you already have struct 
netdev_ft_over_ds_info().  Maybe this structure could be a 'pure cache' entry. 
That way you can create it when sending the action frame, and only once a valid 
response has been received, would you actually put the entry into the cache.

> +{
> +	struct ft_ds_info *info;
> +
> +	if (ft_over_ds_find(hs->spa, target->addr))
> +		return false;
> +
> +	info = l_new(struct ft_ds_info, 1);
> +
> +	info->ifindex = ifindex;
> +	memcpy(info->spa, hs->spa, 6);
> +	memcpy(info->aa, target->addr, 6);
> +	memcpy(info->mde, target->mde, sizeof(info->mde));
> +	memcpy(info->snonce, snonce, 32);
> +	info->user_data = user_data;
> +	info->destroy = destroy;
> +
> +	info->authenticator_ie = l_memdup(hs->authenticator_ie,
> +						hs->authenticator_ie[1] + 2);
> +
> +	l_queue_push_head(ft_over_ds_entries, info);

So instead of doing this here, add a ft_over_ds_cache_put();

> +
> +	l_debug("Created FT entry SPA: "MAC" AA: "MAC, MAC_STR(info->spa),
> +			MAC_STR(info->aa));
> +
> +	return true;
> +}
> +
> +static void ft_over_ds_info_free(void *data)
> +{
> +	struct ft_ds_info *info = data;
> +
> +	if (info->destroy)
> +		info->destroy(info->user_data);
> +
> +	if (info->authenticator_ie)
> +		l_free(info->authenticator_ie);
> +
> +	if (info->fte)
> +		l_free(info->fte);
> +
> +	l_free(info);
> +}
> +
> +static bool ft_over_ds_remove(void *data, void *user_data)

Then you wouldn't need this at all, since the cache entries would be wiped by 
ifindex or the spa.

> +{
> +	struct ft_ds_info *info = data;
> +
> +	if (memcmp(info->spa, user_data, 6) != 0)
> +		return false;
> +
> +	ft_over_ds_info_free(info);
> +
> +	return true;
> +}
> +
> +bool ft_over_ds_entry_remove_all(const uint8_t *spa)
> +{
> +	l_queue_foreach_remove(ft_over_ds_entries, ft_over_ds_remove,
> +					(void *)spa);
> +
> +	return true;
> +}
> +
> +bool ft_over_ds_entry_remove(const uint8_t *spa, const uint8_t *aa)
> +{
> +	struct ft_ds_finder finder;
> +
> +	finder.spa = spa;
> +	finder.aa = aa;
> +
> +	l_queue_remove_if(ft_over_ds_entries, ft_over_ds_match, &finder);
> +
> +	return true;
> +}
> +
> +void *ft_over_ds_entry_get_userdata(const uint8_t *spa, const uint8_t *aa)
> +{
> +	struct ft_ds_info *info = ft_over_ds_find(spa, aa);
> +
> +	if (!info)
> +		return NULL;
> +
> +	return info->user_data;
> +}
> +
> +/*
> + * Updates an over-DS entry with the action response IE's. This will parse and
> + * verify the IEs, and remove the entry if invalid.
> + */
> +static bool ft_over_ds_entry_update(struct ft_ds_info *info,
> +				const uint8_t *ies, size_t ies_len)
> +{
> +	struct netdev *netdev;
> +	struct handshake_state *hs;
> +
> +	netdev = netdev_find(info->ifindex);
> +	if (!netdev) {
> +		/* Netdev went away, remove all cached entries */
> +		ft_over_ds_entry_remove_all(info->spa);
> +		return false;
> +	}
> +
> +	hs = netdev_get_handshake(netdev);
> +
> +	if (!ft_over_ds_process_ies(hs, info, ies, ies_len)) {
> +		l_queue_remove(ft_over_ds_entries, info);
> +		ft_over_ds_info_free(info);
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +int ft_over_ds_parse_action_response(uint8_t *spa, const uint8_t *frame,
> +				size_t frame_len, const uint8_t **aa_out) > +{
> +	struct ft_ds_info *info;
> +	uint16_t status = 0;
> +	const uint8_t *aa;
> +
> +	if (frame_len < 16)
> +		goto parse_error;
> +
> +	/* Category FT */
> +	if (frame[0] != 6)
> +		goto parse_error;
> +
> +	/* FT Action */
> +	if (frame[1] != 2)
> +		goto parse_error;
> +
> +	if (memcmp(frame + 2, spa, 6))
> +		goto parse_error;
> +
> +	aa = frame + 8;
> +
> +	info = ft_over_ds_find(spa, aa);
> +	if (!info)
> +		return -ENOENT;
> +
> +	if (!ft_over_ds_entry_update(info, frame + 16, frame_len - 16))
> +		goto parse_error;

Hmm, why not have this function receive the frame + handshake + ft_over_ds_entry 
itself and if the parsing succeeds, then the (updated) entry is put into the cache?

> +
> +	if (aa_out)
> +		*aa_out = aa;
> +
> +	return 0;
> +
> +parse_error:
> +	return (int)status;
> +}
> +
> +bool ft_over_ds_prepare_handshake(struct handshake_state *hs,
> +					struct scan_bss *target)

target is only used for the address, so you might as well just pass in the mac 
directly and avoid adding a dependency onto scan.h

> +{
> +	struct ft_ds_info *info = ft_over_ds_find(hs->spa, target->addr);
> +
> +	if (!info)
> +		return false;
> +
> +	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;
> +}
> +
>   static int ft_rx_action(struct auth_proto *ap, const uint8_t *frame,
>   				size_t frame_len)
>   {

<snip>

Regards,
-Denis

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

* Re: [PATCH 5/7] ft: refactor FT-over-DS into two stages
  2021-04-27 19:49 ` [PATCH 5/7] ft: refactor FT-over-DS into two stages James Prestwood
@ 2021-04-28 20:04   ` Denis Kenzior
  0 siblings, 0 replies; 10+ messages in thread
From: Denis Kenzior @ 2021-04-28 20:04 UTC (permalink / raw)
  To: iwd

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

Hi James,

On 4/27/21 2:49 PM, James Prestwood wrote:
> FT-over-DS followed the same pattern as FT-over-Air which worked,
> but really limited how the protocol could be used. FT-over-DS is
> unique in that we can authenticate to many APs by sending out
> FT action frames and parsing the results. Once parsed IWD can
> immediately Reassociate, or do so at a later time.
> 
> To take advantage of this IWD need to separate FT-over-DS into
> two stages: action frame and reassociation.
> 
> The initial action frame stage is started by netdev. The target
> BSS is sent an FT action frame and a new cache entry is created
> in ft.c. Once the response is received the entry is updated
> with all the needed data to Reassociate. To limit the record
> keeping on netdev each FT-over-DS entry holds a userdata pointer
> so netdev doesn't need to maintain its own list of data for
> callbacks.
> 
> Once the action response is parsed netdev will call back signalling
> the action frame sequence was completed (either successfully or not).
> At this point the 'normal' FT procedure can start using the
> FT-over-DS auth-proto.
> ---
>   src/ft.c      |  95 ++++--------------
>   src/ft.h      |   1 -
>   src/netdev.c  | 262 ++++++++++++++++++++++++++++++++++++--------------
>   src/netdev.h  |  11 ++-

I wonder if the netdev changes can be broken up some more to make it easier to 
see what's happening?

>   src/station.c |   3 +-
>   5 files changed, 224 insertions(+), 148 deletions(-)
> 

<snip>

> diff --git a/src/netdev.c b/src/netdev.c
> index 2dae07df..d0f9cae8 100644
> --- a/src/netdev.c
> +++ b/src/netdev.c
> @@ -187,6 +187,14 @@ struct netdev_watch {
>   	void *user_data;
>   };
>   
> +struct netdev_ft_over_ds_info {
> +	struct netdev *netdev;
> +	uint8_t addr[ETH_ALEN];
> +	struct l_timeout *timeout;
> +	netdev_ft_over_ds_cb_t cb;
> +	void *user_data;
> +};
> +

So what about declaring this something like:

struct netdev_ft_over_ds_info {
	struct ft_ds_info super;
	...
}

and book-keeping it directly in netdev?  That way you won't need much of the 
cache/finding/removal API in ft.[ch].  But you can keep most of the parsing in 
ft.c for convenience.

<snip>

> diff --git a/src/netdev.h b/src/netdev.h
> index 7b321bfb..bebde242 100644
> --- a/src/netdev.h
> +++ b/src/netdev.h
> @@ -124,6 +124,10 @@ typedef void (*netdev_get_station_cb_t)(
>   
>   const char *netdev_iftype_to_string(uint32_t iftype);
>   
> +typedef void (*netdev_ft_over_ds_cb_t)(struct netdev *netdev,
> +					uint16_t status, const uint8_t *bssid,
> +					void *user_data);
> +
>   struct wiphy *netdev_get_wiphy(struct netdev *netdev);
>   const uint8_t *netdev_get_address(struct netdev *netdev);
>   uint32_t netdev_get_ifindex(struct netdev *netdev);
> @@ -157,9 +161,14 @@ int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss,
>   			netdev_connect_cb_t cb, void *user_data);
>   int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
>   				netdev_connect_cb_t cb);
> +int netdev_fast_transition_over_ds_action(struct netdev *netdev,
> +					struct scan_bss *target_bss,
> +					netdev_ft_over_ds_cb_t cb,
> +					void *user_data);
>   int netdev_fast_transition_over_ds(struct netdev *netdev,
>   					struct scan_bss *target_bss,
> -					netdev_connect_cb_t cb);
> +					netdev_connect_cb_t cb,
> +					void *user_data);

Why does this have a user_data and netdev_fast_transition does not?

>   int netdev_preauthenticate(struct netdev *netdev, struct scan_bss *target_bss,
>   				netdev_preauthenticate_cb_t cb,
>   				void *user_data);

Regards,
-Denis

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

end of thread, other threads:[~2021-04-28 20:04 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-27 19:49 [PATCH 1/7] netdev: add user_data to netdev_send_action_frame[v] James Prestwood
2021-04-27 19:49 ` [PATCH 2/7] ft: factor out various parsing routines James Prestwood
2021-04-27 19:49 ` [PATCH 3/7] ft: expose ft_build_authenticate_ies James Prestwood
2021-04-27 19:49 ` [PATCH 4/7] ft: create cache for FT-over-DS targets James Prestwood
2021-04-28 19:30   ` Denis Kenzior
2021-04-27 19:49 ` [PATCH 5/7] ft: refactor FT-over-DS into two stages James Prestwood
2021-04-28 20:04   ` Denis Kenzior
2021-04-27 19:49 ` [PATCH 6/7] station: separate FT-over-DS stages James Prestwood
2021-04-27 19:49 ` [PATCH 7/7] ft: netdev: add return value to tx_associate James Prestwood
2021-04-28 19:16 ` [PATCH 1/7] netdev: add user_data to netdev_send_action_frame[v] 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.