[PATCH v2] supplicant: Fix MLD related IE parsing.

greearb at candelatech.com greearb at candelatech.com
Sat Oct 28 16:24:35 PDT 2023


From: Ben Greear <greearb at candelatech.com>

Now supplicant can deal with beacons containing multiple TBTT
elements.  This lets MLO connections be attempted against TPLINK
be800 AP.

This also includes a good bit of logging changes, including
conversion to wpa_dbg so that wlan name is seen in the logs.

Signed-off-by: Ben Greear <greearb at candelatech.com>
---

v2:  Remove spurious debugging/logging changes.
A few logging changes I left in, felt that while adding new
info it was fine to change to wpa_dbg as that gives better
info when running multiple stations in same supplicant.

 src/common/ieee802_11_common.c |  28 ++++++++
 src/common/ieee802_11_common.h |   1 +
 wpa_supplicant/bss.c           |  15 ++++
 wpa_supplicant/bss.h           |   1 +
 wpa_supplicant/sme.c           | 127 +++++++++++++++++++--------------
 5 files changed, 119 insertions(+), 53 deletions(-)

diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 8ccb24d22..e4516f8f1 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -2498,6 +2498,34 @@ const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
 	return NULL;
 }
 
+/**
+ * get_ie - Fetch a specified information element from IEs buffer
+ * @ies: Information elements buffer
+ * @len: Information elements buffer length
+ * @eid: Information element identifier (WLAN_EID_*)
+ * @nth: Return the nth element of the requested type (2 returns the second)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the nth matching information element in the IEs
+ * buffer or %NULL in case the element is not found.
+ */
+const u8 * get_ie_nth(const u8 *ies, size_t len, u8 eid, int nth)
+{
+	const struct element *elem;
+	int sofar = 0;
+
+	if (!ies)
+		return NULL;
+
+	for_each_element_id(elem, eid, ies, len) {
+		if ((sofar + 1) == nth)
+			return &elem->id;
+		sofar++;
+	}
+
+	return NULL;
+}
+
 
 /**
  * get_ie_ext - Fetch a specified extended information element from IEs buffer
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index ef95c3591..b35ce777b 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -262,6 +262,7 @@ extern const struct oper_class_map global_op_class[];
 extern size_t global_op_class_size;
 
 const u8 * get_ie(const u8 *ies, size_t len, u8 eid);
+const u8 * get_ie_nth(const u8 *ies, size_t len, u8 eid, int nth);
 const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext);
 const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type);
 
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 3b1646dd2..0162c142d 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -1194,6 +1194,21 @@ const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
 	return get_ie(wpa_bss_ie_ptr(bss), bss->ie_len, ie);
 }
 
+/**
+ * wpa_bss_get_ie_nth - Fetch a specified information element from a BSS entry
+ * @bss: BSS table entry
+ * @ie: Information element identitifier (WLAN_EID_*)
+ * @nth: Return the nth element of the requested type (2 returns the second)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the nth matching information element in the BSS
+ * entry.
+ */
+const u8 * wpa_bss_get_ie_nth(const struct wpa_bss *bss, u8 ie, int nth)
+{
+	return get_ie_nth(wpa_bss_ie_ptr(bss), bss->ie_len, ie, nth);
+}
+
 
 /**
  * wpa_bss_get_ie_ext - Fetch a specified extended IE from a BSS entry
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 611da8844..4580e7981 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -160,6 +160,7 @@ struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id);
 struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
 				      unsigned int idf, unsigned int idl);
 const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie);
+const u8 * wpa_bss_get_ie_nth(const struct wpa_bss *bss, u8 ie, int nth);
 const u8 * wpa_bss_get_ie_ext(const struct wpa_bss *bss, u8 ext);
 const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
 const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index acb6981d9..c595014c5 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -400,6 +400,7 @@ static bool wpas_ml_element(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 			     BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
 			     BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA);
 	bool ret = false;
+	int rnr_idx = 0;
 
 	if (!(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO))
 		return false;
@@ -468,70 +469,90 @@ static bool wpas_ml_element(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 
 	wpa_s->valid_links = BIT(wpa_s->mlo_assoc_link_id);
 
-	rnr_ie = wpa_bss_get_ie(bss, WLAN_EID_REDUCED_NEIGHBOR_REPORT);
-	if (!rnr_ie) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No RNR element");
-		ret = true;
-		goto out;
-	}
+	/* Search for multiple neigh report IEs. */
+	while (true) {
+		rnr_ie = wpa_bss_get_ie_nth(bss, WLAN_EID_REDUCED_NEIGHBOR_REPORT, ++rnr_idx);
+		if (!rnr_ie) {
+			ret = true;
+			if (rnr_idx == 0) {
+				wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No RNR element");
+				goto out;
+			}
+			break;
+		}
 
-	rnr_ie_len = rnr_ie[1];
-	pos = rnr_ie + 2;
+		rnr_ie_len = rnr_ie[1];
+		pos = rnr_ie + 2;
 
-	while (rnr_ie_len > sizeof(struct ieee80211_neighbor_ap_info)) {
-		const struct ieee80211_neighbor_ap_info *ap_info =
-			(const struct ieee80211_neighbor_ap_info *) pos;
-		const u8 *data = ap_info->data;
-		size_t len = sizeof(struct ieee80211_neighbor_ap_info) +
-			ap_info->tbtt_info_len;
+		while (rnr_ie_len > sizeof(struct ieee80211_neighbor_ap_info)) {
+			const struct ieee80211_neighbor_ap_info *ap_info =
+				(const struct ieee80211_neighbor_ap_info *) pos;
+			const u8 *data = ap_info->data; /* start of tbtt elements */
+			const u8 tbtt_count = ap_info->tbtt_info_hdr >> 4;
+			size_t len = ap_info->tbtt_info_len * (tbtt_count + 1) +
+				sizeof(struct ieee80211_neighbor_ap_info);
+			int tbti;
 
-		wpa_printf(MSG_DEBUG, "MLD: op_class=%u, channel=%u",
-			   ap_info->op_class, ap_info->channel);
+			wpa_dbg(wpa_s, MSG_DEBUG, "MLD: rnr-idx: %d op_class=%u, channel=%u\n",
+				rnr_idx - 1, ap_info->op_class, ap_info->channel);
 
-		if (len > rnr_ie_len)
-			break;
+			if (len > rnr_ie_len)
+				break;
 
-		if (ap_info->tbtt_info_len < 16) {
-			rnr_ie_len -= len;
-			pos += len;
-			continue;
-		}
+			if (ap_info->tbtt_info_len < 16) {
+				rnr_ie_len -= len;
+				pos += len;
+				continue;
+			}
 
-		data += 13;
+			for (tbti = 0; tbti <= tbtt_count; tbti++) {
+				const u8* bssid = data + 1; /* one byte into the tbtt object */
 
-		wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
-			   *data, *(data + 1) & 0xF);
+				data += 13; /* advance to mld parameters */
 
-		if (*data) {
-			wpa_printf(MSG_DEBUG,
-				   "MLD: Reported link not part of MLD");
-		} else {
-			struct wpa_bss *neigh_bss =
-				wpa_bss_get_bssid(wpa_s, ap_info->data + 1);
-			u8 link_id = *(data + 1) & 0xF;
-
-			if (neigh_bss) {
-				if (wpa_scan_res_match(wpa_s, 0, neigh_bss,
-						       wpa_s->current_ssid,
-						       1, 0)) {
-					wpa_s->valid_links |= BIT(link_id);
-					os_memcpy(wpa_s->links[link_id].bssid,
-						  ap_info->data + 1, ETH_ALEN);
-					wpa_s->links[link_id].freq =
-						neigh_bss->freq;
-				} else {
+				wpa_dbg(wpa_s, MSG_DEBUG, "MLD: tbtt idx: %d mld ID=%u, link ID=%u  data: 0x%lx bssid=" MACSTR,
+					tbti, *data, *(data + 1) & 0xF, (unsigned long)data,
+					MAC2STR(bssid));
+
+				if (*data) {
 					wpa_printf(MSG_DEBUG,
-						   "MLD: Neighbor doesn't match current SSID - skip link");
+						   "MLD: Reported link not part of MLD");
+				} else {
+					struct wpa_bss *neigh_bss =
+						wpa_bss_get_bssid(wpa_s, bssid);
+					u8 link_id = *(data + 1) & 0xF;
+
+					/* Intel radios won't scan 6e at first.  Would need to force
+					 * the radio to scan multiple times to fix that limitations.
+					 */
+					if (neigh_bss) {
+						u8 bss_params = *(data + (1 + 6 + 4 - 13));
+						if (((bss_params & 0x2) /* same ssid */
+						     && (bss_params & (1<<6))) || /* co-located AP */
+						    wpa_scan_res_match(wpa_s, 0, neigh_bss,
+								       wpa_s->current_ssid,
+								       1, 0)) {
+							wpa_s->valid_links |= BIT(link_id);
+							os_memcpy(wpa_s->links[link_id].bssid,
+								  bssid, ETH_ALEN);
+							wpa_s->links[link_id].freq =
+								neigh_bss->freq;
+						} else {
+							wpa_printf(MSG_DEBUG,
+								   "MLD: Neighbor doesn't match current SSID - skip link");
+						}
+					} else {
+						wpa_printf(MSG_DEBUG,
+							   "MLD: Neighbor not found in scan");
+					}
 				}
-			} else {
-				wpa_printf(MSG_DEBUG,
-					   "MLD: Neighbor not found in scan");
-			}
-		}
+				data += (ap_info->tbtt_info_len - 13); /* consume rest of tbtt */
+			}/* for all tbtt */
 
-		rnr_ie_len -= len;
-		pos += len;
-	}
+			rnr_ie_len -= len;
+			pos += len;
+		}/* while more neigh info */
+	}/* for all neigh report IEs */
 
 	wpa_printf(MSG_DEBUG, "MLD: valid_links=0x%x", wpa_s->valid_links);
 
-- 
2.40.1




More information about the Hostap mailing list