[RFC 2/2] MLO: Add support for open connection when driver's SME in use

Veerendranath Jakkam quic_vjakkam at quicinc.com
Sat Jul 9 19:46:52 PDT 2022


From: Shivani Baranwal <quic_shivbara at quicinc.com>

This commit does below
- Allow driver's SME to enable MLO(Multi-Link Operation) connection when
  non-WPA open mode selected for the connection.
- Read mult-link connection info from NL80211_CMD_CONNECT event and copy
  to corresponding wpa_s instance.
- Hold the all the associated link BSSes scan results till the
  connection is active.
- Authorize supplicant port for AP MLD address after successful
  connection.

Signed-off-by: Shivani Baranwal <quic_shivbara at quicinc.com>
Co-developed-by: Veerendranath Jakkam <quic_vjakkam at quicinc.com>
Signed-off-by: Veerendranath Jakkam <quic_vjakkam at quicinc.com>
---
 src/common/defs.h                  |  2 +
 src/drivers/driver.h               | 22 ++++++++++
 src/drivers/driver_nl80211.c       | 80 +++++++++++++++++++++++++++++++---
 src/drivers/driver_nl80211.h       |  2 +
 src/drivers/driver_nl80211_event.c | 78 +++++++++++++++++++++++++++++++--
 wpa_supplicant/bss.c               | 22 ++++++++--
 wpa_supplicant/driver_i.h          | 10 +++++
 wpa_supplicant/events.c            | 89 ++++++++++++++++++++++++++++++++++++++
 wpa_supplicant/wpa_supplicant_i.h  |  7 +++
 9 files changed, 300 insertions(+), 12 deletions(-)

diff --git a/src/common/defs.h b/src/common/defs.h
index 4e63053..91ac4f8 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -498,4 +498,6 @@ enum frame_encryption {
 	FRAME_ENCRYPTED = 1
 };
 
+#define MAX_NUM_MLD_LINKS 15
+
 #endif /* DEFS_H */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 52ee8df..0685eb0 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -2621,6 +2621,15 @@ struct weighted_pcl {
 	u32 flag; /* bitmap for WEIGHTED_PCL_* */
 };
 
+struct driver_sta_mlo_info {
+	u16 valid_links;
+	u8 ap_mld_addr[ETH_ALEN];
+	struct {
+		u8 addr[ETH_ALEN];
+		u8 bssid[ETH_ALEN];
+	} links[MAX_NUM_MLD_LINKS];
+};
+
 /**
  * struct wpa_driver_ops - Driver interface API definition
  *
@@ -4669,6 +4678,19 @@ struct wpa_driver_ops {
 			      const u8 *match, size_t match_len,
 			      bool multicast);
 #endif /* CONFIG_TESTING_OPTIONS */
+
+
+	/**
+	 * get_sta_mlo_info - Get the current multi-link associtaion info
+	 * @priv: private driver interface data
+	 * @mlo: pointer to fill multi-link associtaion info
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This callback is used to fetch multi-link of the current association.
+	 */
+	int (*get_sta_mlo_info)(void *priv,
+				struct driver_sta_mlo_info *mlo_info);
+
 };
 
 /**
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 78c0658..5fd058b 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -270,6 +270,7 @@ void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv)
 	drv->associated = 0;
 	os_memset(drv->bssid, 0, ETH_ALEN);
 	drv->first_bss->freq = 0;
+	drv->sta_mlo_info.valid_links = 0;
 }
 
 
@@ -1013,6 +1014,19 @@ static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid)
 }
 
 
+static int nl80211_get_sta_mlo_info(void *priv,
+				    struct driver_sta_mlo_info *mlo_info)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (!drv->associated)
+		return -1;
+
+	os_memcpy(mlo_info, &drv->sta_mlo_info, sizeof(*mlo_info));
+	return 0;
+}
+
 static void wpa_driver_nl80211_event_newlink(
 	struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
 	int ifindex, const char *ifname)
@@ -1447,9 +1461,11 @@ static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg)
 		[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
 		[NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
 		[NL80211_BSS_STATUS] = { .type = NLA_U32 },
+		[NL80211_BSS_MLO_LINK_ID] = { .type = NLA_U8 },
 	};
 	struct nl80211_get_assoc_freq_arg *ctx = arg;
 	enum nl80211_bss_status status;
+	struct wpa_driver_nl80211_data *drv = ctx->drv;
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
 		  genlmsg_attrlen(gnlh, 0), NULL);
@@ -1461,7 +1477,11 @@ static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg)
 
 	status = nla_get_u32(bss[NL80211_BSS_STATUS]);
 	if (status == NL80211_BSS_STATUS_ASSOCIATED &&
-	    bss[NL80211_BSS_FREQUENCY]) {
+	    bss[NL80211_BSS_FREQUENCY] &&
+	    (!drv->sta_mlo_info.valid_links ||
+	     (bss[NL80211_BSS_MLO_LINK_ID] &&
+	      drv->mlo_assoc_link_id ==
+	      nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID])))) {
 		ctx->assoc_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
 		wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
 			   ctx->assoc_freq);
@@ -1473,7 +1493,11 @@ static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg)
 			   ctx->ibss_freq);
 	}
 	if (status == NL80211_BSS_STATUS_ASSOCIATED &&
-	    bss[NL80211_BSS_BSSID]) {
+	    bss[NL80211_BSS_BSSID] &&
+	    (!drv->sta_mlo_info.valid_links ||
+	     (bss[NL80211_BSS_MLO_LINK_ID] &&
+	      drv->mlo_assoc_link_id ==
+	      nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID])))) {
 		os_memcpy(ctx->assoc_bssid,
 			  nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
 		wpa_printf(MSG_DEBUG, "nl80211: Associated with "
@@ -6395,6 +6419,17 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
 	    nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
 		return -1;
 
+	/*
+	 * Indicate driver's SME to enable MLO connection only for non-WPA open
+	 * connections.
+	 */
+	if ((!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) &&
+	    params->key_mgmt_suite == WPA_KEY_MGMT_NONE &&
+	    params->pairwise_suite == WPA_CIPHER_NONE &&
+	    params->group_suite == WPA_CIPHER_NONE &&
+	    nla_put_flag(msg, NL80211_ATTR_MLO_SUPPORT))
+		return -1;
+
 	return 0;
 }
 
@@ -6861,14 +6896,16 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
 	struct nl_msg *msg;
 	struct nl80211_sta_flag_update upd;
 	int ret;
+	const u8 *connected_addr = drv->sta_mlo_info.valid_links ?
+				   drv->sta_mlo_info.ap_mld_addr : drv->bssid;
 
-	if (!drv->associated && is_zero_ether_addr(drv->bssid) && !authorized) {
+	if (!drv->associated && is_zero_ether_addr(connected_addr) && !authorized) {
 		wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated");
 		return 0;
 	}
 
 	wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for "
-		   MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid));
+		   MACSTR, authorized ? "" : "un", MAC2STR(connected_addr));
 
 	os_memset(&upd, 0, sizeof(upd));
 	upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED);
@@ -6876,7 +6913,7 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
 		upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
 
 	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
-	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, connected_addr) ||
 	    nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
@@ -9742,6 +9779,38 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
 		return pos - buf;
 	pos += res;
 
+	if (drv->sta_mlo_info.valid_links) {
+		int i;
+		struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info;
+
+		res = os_snprintf(pos, end - pos,
+				  "ap_mld_addr=" MACSTR "\n",
+				   MAC2STR(mlo->ap_mld_addr));
+
+		if (os_snprintf_error(end - pos, res))
+			return pos - buf;
+		pos += res;
+
+
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(mlo->valid_links & BIT(i)))
+				continue;
+
+			res = os_snprintf(pos, end - pos,
+					  "link_addr[%u]=" MACSTR "\n"
+					  "link_bssid[%u]=" MACSTR "\n",
+					  i,
+					  MAC2STR(mlo->links[i].addr),
+					  i,
+					  MAC2STR(mlo->links[i].bssid));
+
+			if (os_snprintf_error(end - pos, res))
+				return pos - buf;
+
+			pos += res;
+		}
+	}
+
 	if (drv->has_capability) {
 		res = os_snprintf(pos, end - pos,
 				  "capa.key_mgmt=0x%x\n"
@@ -12482,4 +12551,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.register_frame = testing_nl80211_register_frame,
 	.radio_disable = testing_nl80211_radio_disable,
 #endif /* CONFIG_TESTING_OPTIONS */
+	.get_sta_mlo_info = nl80211_get_sta_mlo_info,
 };
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 80d4564..f359663 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -128,6 +128,8 @@ struct wpa_driver_nl80211_data {
 	u8 bssid[ETH_ALEN];
 	u8 prev_bssid[ETH_ALEN];
 	int associated;
+	int mlo_assoc_link_id;
+	struct driver_sta_mlo_info sta_mlo_info;
 	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
 	enum nl80211_iftype nlmode;
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 369337f..3183508 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -420,6 +420,71 @@ convert_connect_fail_reason_codes(enum qca_sta_connect_fail_reason_codes
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
+static void nl80211_parse_mlo_info(struct wpa_driver_nl80211_data *drv,
+				   struct nlattr *addr,
+				   struct nlattr *mlo_links,
+				   struct nlattr *resp_ie)
+{
+	struct nlattr *link;
+	int rem_links;
+	const u8 *ml_ie;
+	struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info;
+
+	if (!addr || !mlo_links || !resp_ie)
+		return;
+
+	ml_ie = get_ie_ext(nla_data(resp_ie), nla_len(resp_ie),
+			   WLAN_EID_EXT_MULTI_LINK);
+
+#define ML_IE_SELF_LINK_ID_OFFSET \
+		(3 + /* IE header */ \
+		 2 + /* Control field */ \
+		 1 + /* Common info length field */ \
+		 6) /* MLD mac address */
+	if (!ml_ie || nla_len(resp_ie) <= ML_IE_SELF_LINK_ID_OFFSET)
+		return;
+
+	drv->mlo_assoc_link_id = ml_ie[ML_IE_SELF_LINK_ID_OFFSET];
+	os_memcpy(mlo->ap_mld_addr, nla_data(addr), ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "ap_mld_addr " MACSTR, MAC2STR(mlo->ap_mld_addr));
+
+	nla_for_each_nested(link, mlo_links, rem_links) {
+		struct nlattr *tb[NL80211_ATTR_MAX + 1];
+		int link_id;
+
+		nla_parse(tb, NL80211_ATTR_MAX, nla_data(link), nla_len(link),
+			  NULL);
+
+		if (!tb[NL80211_ATTR_MLO_LINK_ID] || !tb[NL80211_ATTR_MAC] ||
+		    !tb[NL80211_ATTR_BSSID])
+			continue;
+
+		link_id = nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+		mlo->valid_links |= BIT(link_id);
+		os_memcpy(mlo->links[link_id].addr,
+			  nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+		os_memcpy(mlo->links[link_id].bssid,
+			  nla_data(tb[NL80211_ATTR_BSSID]), ETH_ALEN);
+		wpa_printf(MSG_DEBUG, "link[%u].addr " MACSTR, link_id,
+			   MAC2STR(mlo->links[link_id].addr));
+		wpa_printf(MSG_DEBUG, "link[%u].bssid " MACSTR, link_id,
+			   MAC2STR(mlo->links[link_id].bssid));
+	}
+
+	if (drv->mlo_assoc_link_id >= MAX_NUM_MLD_LINKS ||
+	    !(mlo->valid_links & BIT(drv->mlo_assoc_link_id))) {
+		wpa_printf(MSG_ERROR, "Invalid MLO assoc link ID %d",
+			   drv->mlo_assoc_link_id);
+		mlo->valid_links = 0;
+		return;
+	}
+
+	os_memcpy(drv->bssid,
+		  mlo->links[drv->mlo_assoc_link_id].bssid, ETH_ALEN);
+	os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
+}
+
+
 static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
 			       enum nl80211_commands cmd, struct nlattr *status,
 			       struct nlattr *addr, struct nlattr *req_ie,
@@ -433,7 +498,8 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
 			       struct nlattr *subnet_status,
 			       struct nlattr *fils_erp_next_seq_num,
 			       struct nlattr *fils_pmk,
-			       struct nlattr *fils_pmkid)
+			       struct nlattr *fils_pmkid,
+			       struct nlattr *mlo_links)
 {
 	union wpa_event_data event;
 	const u8 *ssid = NULL;
@@ -525,7 +591,9 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
 	}
 
 	drv->associated = 1;
-	if (addr) {
+	drv->sta_mlo_info.valid_links = 0;
+	nl80211_parse_mlo_info(drv, addr, mlo_links, resp_ie);
+	if (!drv->sta_mlo_info.valid_links && addr) {
 		os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
 		os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
 	}
@@ -2111,7 +2179,8 @@ static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv,
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK],
-			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID]);
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID],
+			   NULL);
 }
 
 
@@ -3113,7 +3182,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
 				   NULL,
 				   tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM],
 				   tb[NL80211_ATTR_PMK],
-				   tb[NL80211_ATTR_PMKID]);
+				   tb[NL80211_ATTR_PMKID],
+				   tb[NL80211_ATTR_MLO_LINKS]);
 		break;
 	case NL80211_CMD_CH_SWITCH_STARTED_NOTIFY:
 		mlme_event_ch_switch(drv,
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index eb97a61..7dcdb99 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -379,6 +379,8 @@ static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 
 static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
+	int i;
+
 	if (bss == wpa_s->current_bss)
 		return 1;
 
@@ -388,9 +390,23 @@ static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 		       bss->ssid_len) != 0))
 		return 0; /* SSID has changed */
 
-	return !is_zero_ether_addr(bss->bssid) &&
-		(os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
-		 os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0);
+	if (!is_zero_ether_addr(bss->bssid) &&
+	    (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
+	     os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0))
+		return 1;
+
+	if (!wpa_s->valid_links)
+		return 0;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(wpa_s->valid_links & BIT(i)))
+			continue;
+
+		if (os_memcmp(bss->bssid, wpa_s->links[i].bssid, ETH_ALEN) == 0)
+			return 1;
+	}
+
+	return 0;
 }
 
 
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index b0af1cd..e202590 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -1117,4 +1117,14 @@ static inline int wpa_drv_dpp_listen(struct wpa_supplicant *wpa_s, bool enable)
 	return wpa_s->driver->dpp_listen(wpa_s->drv_priv, enable);
 }
 
+static inline int
+wpas_drv_get_sta_mlo_info(struct wpa_supplicant *wpa_s,
+			  struct driver_sta_mlo_info *mlo_info)
+{
+	if (!wpa_s->driver->get_sta_mlo_info)
+		return 0;
+
+	return wpa_s->driver->get_sta_mlo_info(wpa_s->drv_priv, mlo_info);
+}
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index ec56cfd..0eab93d 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -166,6 +166,20 @@ wpa_supplicant_update_current_bss(struct wpa_supplicant *wpa_s, const u8 *bssid)
 	return bss;
 }
 
+static void wpa_supplicant_update_link_bss(struct wpa_supplicant *wpa_s,
+					   u8 link_id,
+					   const u8 *bssid)
+{
+	struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
+
+	if (!bss) {
+		wpa_supplicant_update_scan_results(wpa_s);
+		bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
+	}
+
+	if (bss)
+		wpa_s->links[link_id].bss = bss;
+}
 
 static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
 {
@@ -285,6 +299,19 @@ void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx)
 }
 
 
+static void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s)
+{
+	int i;
+
+	if (!wpa_s->valid_links)
+		return;
+
+	wpa_s->valid_links = 0;
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
+		wpa_s->links[i].bss = NULL;
+}
+
+
 void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
 {
 	int bssid_changed;
@@ -351,6 +378,8 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
 
 	if (wpa_s->enabled_4addr_mode && wpa_drv_set_4addr_mode(wpa_s, 0) == 0)
 		wpa_s->enabled_4addr_mode = 0;
+
+	wpas_reset_mlo_info(wpa_s);
 }
 
 
@@ -3302,6 +3331,62 @@ static void wpas_fst_update_mb_assoc(struct wpa_supplicant *wpa_s,
 }
 
 
+static int wpas_update_mlo_info(struct wpa_supplicant *wpa_s)
+{
+	struct driver_sta_mlo_info mlo;
+	int i;
+
+	mlo.valid_links = 0;
+	if (wpas_drv_get_sta_mlo_info(wpa_s, &mlo)) {
+		wpa_dbg(wpa_s, MSG_ERROR, "Failed to get MLO link info");
+		wpa_supplicant_deauthenticate(
+			wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+		return -1;
+	}
+
+	if (wpa_s->valid_links == mlo.valid_links) {
+		bool match = true;
+
+		if (!mlo.valid_links)
+			return 0;
+
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(mlo.valid_links & BIT(i)))
+				continue;
+
+			if (os_memcmp(wpa_s->links[i].addr, mlo.links[i].addr,
+				      ETH_ALEN)) {
+				match = false;
+				break;
+			}
+
+			if (os_memcmp(wpa_s->links[i].bssid, mlo.links[i].bssid,
+				      ETH_ALEN)) {
+				match = false;
+				break;
+			}
+		}
+
+		if (match &&
+		    !os_memcmp(wpa_s->ap_mld_addr, mlo.ap_mld_addr, ETH_ALEN))
+			return 0;
+	}
+
+	wpa_s->valid_links = mlo.valid_links;
+	os_memcpy(wpa_s->ap_mld_addr, mlo.ap_mld_addr, ETH_ALEN);
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(wpa_s->valid_links & BIT(i)))
+			continue;
+
+		os_memcpy(wpa_s->links[i].addr, mlo.links[i].addr, ETH_ALEN);
+		os_memcpy(wpa_s->links[i].bssid, mlo.links[i].bssid, ETH_ALEN);
+		wpa_supplicant_update_link_bss(wpa_s, i, mlo.links[i].bssid);
+	}
+
+	return 0;
+}
+
+
 static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
 				       union wpa_event_data *data)
 {
@@ -3402,6 +3487,10 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
 				"WPA/RSN IEs not updated");
 	}
 
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+	    wpas_update_mlo_info(wpa_s) < 0)
+		return;
+
 	wpas_fst_update_mb_assoc(wpa_s, data);
 
 #ifdef CONFIG_SME
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index a349d65..23d418e 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -739,6 +739,13 @@ struct wpa_supplicant {
 	struct wpa_bss *current_bss;
 	int ap_ies_from_associnfo;
 	unsigned int assoc_freq;
+	u8 ap_mld_addr[ETH_ALEN];
+	u8 valid_links;
+	struct {
+		u8 addr[ETH_ALEN];
+		u8 bssid[ETH_ALEN];
+		struct wpa_bss *bss;
+	} links[MAX_NUM_MLD_LINKS];
 	u8 *last_con_fail_realm;
 	size_t last_con_fail_realm_len;
 
-- 
2.7.4




More information about the Hostap mailing list