]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
AP MLD: Handle new STA event when using SME offload to the driver
authorVeerendranath Jakkam <quic_vjakkam@quicinc.com>
Mon, 6 Mar 2023 03:03:53 +0000 (08:33 +0530)
committerJouni Malinen <j@w1.fi>
Fri, 21 Jul 2023 16:57:25 +0000 (19:57 +0300)
Parse link id and station MLD address received from the driver in the
NL80211_CMD_NEW_STA event.

Set MLO information of the station to the sta_info and wpa_sm.

Co-authored-by: Manaswini Paluri <quic_mpaluri@quicinc.com>
Signed-off-by: Veerendranath Jakkam <quic_vjakkam@quicinc.com>
src/ap/drv_callbacks.c
src/ap/hostapd.h
src/common/ieee802_11_defs.h
src/drivers/driver.h
src/drivers/driver_atheros.c
src/drivers/driver_bsd.c
src/drivers/driver_nl80211_event.c
wpa_supplicant/events.c

index fce0922cdb0580e3c975eda8a157f74be656dfc5..612802b1169c67d9cf52ff03d2098e1b41c30515 100644 (file)
@@ -132,8 +132,122 @@ static bool check_sa_query_need(struct hostapd_data *hapd, struct sta_info *sta)
 }
 
 
+#ifdef CONFIG_IEEE80211BE
+static int hostapd_update_sta_links_status(struct hostapd_data *hapd,
+                                          struct sta_info *sta,
+                                          const u8 *resp_ies,
+                                          size_t resp_ies_len)
+{
+       struct mld_info *info = &sta->mld_info;
+       struct wpabuf *mlebuf;
+       const u8 *mle, *pos;
+       struct ieee802_11_elems elems;
+       size_t mle_len, rem_len;
+       int ret = 0;
+
+       if (!resp_ies) {
+               wpa_printf(MSG_DEBUG,
+                          "MLO: (Re)Association Response frame elements not available");
+               return -1;
+       }
+
+       if (ieee802_11_parse_elems(resp_ies, resp_ies_len, &elems, 0) ==
+           ParseFailed) {
+               wpa_printf(MSG_DEBUG,
+                          "MLO: Failed to parse (Re)Association Response frame elements");
+               return -1;
+       }
+
+       mlebuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+       if (!mlebuf) {
+               wpa_printf(MSG_ERROR,
+                          "MLO: Basic Multi-Link element not found in (Re)Association Response frame");
+               return -1;
+       }
+
+       mle = wpabuf_head(mlebuf);
+       mle_len = wpabuf_len(mlebuf);
+       if (mle_len < MULTI_LINK_CONTROL_LEN + 1 ||
+           mle_len - MULTI_LINK_CONTROL_LEN < mle[MULTI_LINK_CONTROL_LEN]) {
+               wpa_printf(MSG_ERROR,
+                          "MLO: Invalid Multi-Link element in (Re)Association Response frame");
+               ret = -1;
+               goto out;
+       }
+
+       /* Skip Common Info */
+       pos = mle + MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN];
+       rem_len = mle_len -
+               (MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN]);
+
+       /* Parse Subelements */
+       while (rem_len > 2) {
+               size_t ie_len = 2 + pos[1];
+
+               if (rem_len < ie_len)
+                       break;
+
+               if (pos[0] == MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
+                       u8 link_id;
+                       const u8 *sta_profile;
+                       size_t sta_profile_len;
+                       u16 sta_ctrl;
+
+                       if (pos[1] < BASIC_MLE_STA_CTRL_LEN + 1) {
+                               wpa_printf(MSG_DEBUG,
+                                          "MLO: Invalid per-STA profile IE");
+                               goto next_subelem;
+                       }
+
+                       sta_profile_len = pos[1];
+                       sta_profile = &pos[2];
+                       sta_ctrl = WPA_GET_LE16(sta_profile);
+                       link_id = sta_ctrl & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+                       if (link_id >= MAX_NUM_MLD_LINKS) {
+                               wpa_printf(MSG_DEBUG,
+                                          "MLO: Invalid link ID in per-STA profile IE");
+                               goto next_subelem;
+                       }
+
+                       /* Skip STA Control and STA Info */
+                       if (sta_profile_len - BASIC_MLE_STA_CTRL_LEN <
+                           sta_profile[BASIC_MLE_STA_CTRL_LEN]) {
+                               wpa_printf(MSG_DEBUG,
+                                          "MLO: Invalid STA info in per-STA profile IE");
+                               goto next_subelem;
+                       }
+
+                       sta_profile_len = sta_profile_len -
+                               (BASIC_MLE_STA_CTRL_LEN +
+                                sta_profile[BASIC_MLE_STA_CTRL_LEN]);
+                       sta_profile = sta_profile + BASIC_MLE_STA_CTRL_LEN +
+                               sta_profile[BASIC_MLE_STA_CTRL_LEN];
+
+                       /* Skip Capabilities Information field */
+                       if (sta_profile_len < 2)
+                               goto next_subelem;
+                       sta_profile_len -= 2;
+                       sta_profile += 2;
+
+                       /* Get status of the link */
+                       info->links[link_id].status = WPA_GET_LE16(sta_profile);
+               }
+next_subelem:
+               pos += ie_len;
+               rem_len -= ie_len;
+       }
+
+out:
+       wpabuf_free(mlebuf);
+       return ret;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
-                       const u8 *req_ies, size_t req_ies_len, int reassoc)
+                       const u8 *req_ies, size_t req_ies_len,
+                       const u8 *resp_ies, size_t resp_ies_len,
+                       const u8 *link_addr, int reassoc)
 {
        struct sta_info *sta;
        int new_assoc;
@@ -226,6 +340,45 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
                        return -1;
                }
        }
+
+#ifdef CONFIG_IEEE80211BE
+       if (link_addr) {
+               struct mld_info *info = &sta->mld_info;
+               int i, num_valid_links = 0;
+               u8 link_id = hapd->mld_link_id;
+
+               info->mld_sta = true;
+               sta->mld_assoc_link_id = link_id;
+               os_memcpy(info->common_info.mld_addr, addr, ETH_ALEN);
+               info->links[link_id].valid = true;
+               os_memcpy(info->links[link_id].peer_addr, link_addr, ETH_ALEN);
+               os_memcpy(info->links[link_id].local_addr, hapd->own_addr,
+                         ETH_ALEN);
+
+               if (!elems.basic_mle ||
+                   hostapd_process_ml_assoc_req(hapd, &elems, sta) !=
+                   WLAN_STATUS_SUCCESS) {
+                       reason = WLAN_REASON_UNSPECIFIED;
+                       wpa_printf(MSG_DEBUG,
+                                  "Failed to get STA non-assoc links info");
+                       goto fail;
+               }
+
+               for (i = 0 ; i < MAX_NUM_MLD_LINKS; i++) {
+                       if (info->links[i].valid)
+                               num_valid_links++;
+               }
+               if (num_valid_links > 1 &&
+                   hostapd_update_sta_links_status(hapd, sta, resp_ies,
+                                                   resp_ies_len)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Failed to get STA non-assoc links status info");
+                       reason = WLAN_REASON_UNSPECIFIED;
+                       goto fail;
+               }
+       }
+#endif /* CONFIG_IEEE80211BE */
+
        sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
 
        /*
@@ -365,6 +518,15 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
                                   "Failed to initialize WPA state machine");
                        return -1;
                }
+#ifdef CONFIG_IEEE80211BE
+               if (sta->mld_info.mld_sta) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLD: Set ML info in RSN Authenticator");
+                       wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld_addr,
+                                            sta->mld_assoc_link_id,
+                                            &sta->mld_info);
+               }
+#endif /* CONFIG_IEEE80211BE */
                res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
                                          hapd->iface->freq,
                                          ie, ielen,
@@ -2120,9 +2282,23 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
        case EVENT_ASSOC:
                if (!data)
                        return;
+#ifdef CONFIG_IEEE80211BE
+               if (data->assoc_info.assoc_link_id != -1) {
+                       hapd = hostapd_mld_get_link_bss(
+                               hapd, data->assoc_info.assoc_link_id);
+                       if (!hapd) {
+                               wpa_printf(MSG_ERROR,
+                                          "MLD: Failed to get link BSS for EVENT_ASSOC");
+                               return;
+                       }
+               }
+#endif /* CONFIG_IEEE80211BE */
                hostapd_notif_assoc(hapd, data->assoc_info.addr,
                                    data->assoc_info.req_ies,
                                    data->assoc_info.req_ies_len,
+                                   data->assoc_info.resp_ies,
+                                   data->assoc_info.resp_ies_len,
+                                   data->assoc_info.link_addr,
                                    data->assoc_info.reassoc);
                break;
 #ifdef CONFIG_OWE
index 6e0ce3dea697a3da911d6377a3ceff096179992a..2994b2788c2db2e29ec93885eace535ba3bbe213 100644 (file)
@@ -730,7 +730,8 @@ void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr,
 void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
                                      struct sta_info *sta);
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
-                       const u8 *ie, size_t ielen, int reassoc);
+                       const u8 *req_ie, size_t req_ielen, const u8 *resp_ie,
+                       size_t resp_ielen, const u8 *link_addr, int reassoc);
 void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
 void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
 void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
index 571ace2f5100aa08a560710f859341d1fbc436a9..381613d14e4db1e030cade5a5e3544986e7ad3b0 100644 (file)
@@ -2607,6 +2607,7 @@ struct ieee80211_eht_capabilities {
  * STA Control field definitions of Per-STA Profile subelement in Basic
  * Multi-Link element as described in Figure 9-1002n: STA Control field format.
  */
+#define BASIC_MLE_STA_CTRL_LEN                         2
 #define BASIC_MLE_STA_CTRL_LINK_ID_MASK                        0x000F
 #define BASIC_MLE_STA_CTRL_COMPLETE_PROFILE            0x0010
 #define BASIC_MLE_STA_CTRL_PRES_STA_MAC                        0x0020
index ece5f1259652450ca4cd1542b9cdaa0676ddc191..5957567a940e956b60d6fcf518f70bd150c347d5 100644 (file)
@@ -5953,6 +5953,17 @@ union wpa_event_data {
                 * fils_pmkid - PMKID used or generated in FILS authentication
                 */
                const u8 *fils_pmkid;
+
+               /**
+                * link_addr - Link address of the STA
+                */
+               const u8 *link_addr;
+
+               /**
+                * assoc_link_id - Association link id of the MLO association or
+                *      -1 if MLO is not used
+                */
+               int assoc_link_id;
        } assoc_info;
 
        /**
@@ -6674,15 +6685,21 @@ void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
  * event indication for some of the common events.
  */
 
-static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie,
-                                  size_t ielen, int reassoc)
+static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *req_ies,
+                                  size_t req_ielen, const u8 *resp_ies,
+                                  size_t resp_ielen, const u8 *link_addr,
+                                  int assoc_link_id, int reassoc)
 {
        union wpa_event_data event;
        os_memset(&event, 0, sizeof(event));
        event.assoc_info.reassoc = reassoc;
-       event.assoc_info.req_ies = ie;
-       event.assoc_info.req_ies_len = ielen;
+       event.assoc_info.req_ies = req_ies;
+       event.assoc_info.req_ies_len = req_ielen;
+       event.assoc_info.resp_ies = resp_ies;
+       event.assoc_info.resp_ies_len = resp_ielen;
        event.assoc_info.addr = addr;
+       event.assoc_info.link_addr = link_addr;
+       event.assoc_info.assoc_link_id = assoc_link_id;
        wpa_supplicant_event(ctx, EVENT_ASSOC, &event);
 }
 
index 08ff915acfb611a0bbefa210e26703aaa63d85ed..59f65b810bacc901cee0cb3eead55e46ed8c6706 100644 (file)
@@ -913,14 +913,16 @@ static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
                        break;
                ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
                iebuf = mgmt->u.assoc_req.variable;
-               drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0);
+               drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, NULL, 0,
+                               NULL, -1, 0);
                break;
        case WLAN_FC_STYPE_REASSOC_REQ:
                if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req))
                        break;
                ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
                iebuf = mgmt->u.reassoc_req.variable;
-               drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1);
+               drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, NULL, 0,
+                               NULL, -1, 1);
                break;
        case WLAN_FC_STYPE_AUTH:
                if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
@@ -1222,7 +1224,7 @@ atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
                ielen += 2;
 
 no_ie:
-       drv_event_assoc(hapd, addr, iebuf, ielen, 0);
+       drv_event_assoc(hapd, addr, iebuf, ielen, NULL, 0, NULL, -1, 0);
 
        if (os_memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
                /* Cached accounting data is not valid anymore. */
index c32d05e6520058f2ff0b0204c31d481e787bb689..850637f0db2e012d2633b1a2d80941e84aa385a3 100644 (file)
@@ -553,7 +553,7 @@ bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
                ielen += 2;
 
 no_ie:
-       drv_event_assoc(ctx, addr, iebuf, ielen, 0);
+       drv_event_assoc(ctx, addr, iebuf, ielen, NULL, 0, NULL, -1, 0);
 }
 
 static int
index d6e15053979ebdc44737448c6e4c0dcab045f50f..03b395bca611593fd57aa5df0275de7126d532c2 100644 (file)
@@ -2165,23 +2165,60 @@ static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
                                      struct i802_bss *bss,
                                      struct nlattr **tb)
 {
-       u8 *addr;
+       u8 *peer_addr;
        union wpa_event_data data;
 
        if (tb[NL80211_ATTR_MAC] == NULL)
                return;
-       addr = nla_data(tb[NL80211_ATTR_MAC]);
-       wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr));
+       peer_addr = nla_data(tb[NL80211_ATTR_MAC]);
+       wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR,
+                  MAC2STR(peer_addr));
 
        if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
-               u8 *ies = NULL;
-               size_t ies_len = 0;
+               u8 *link_addr = NULL;
+               int assoc_link_id = -1;
+               u8 *req_ies = NULL, *resp_ies = NULL;
+               size_t req_ies_len = 0, resp_ies_len = 0;
+
+               if (bss->links[0].link_id == NL80211_DRV_LINK_ID_NA &&
+                   (tb[NL80211_ATTR_MLO_LINK_ID] ||
+                    tb[NL80211_ATTR_MLD_ADDR])) {
+                       wpa_printf(MSG_ERROR,
+                                  "nl80211: MLO info not expected for new station event for non-MLD AP");
+                       return;
+               }
+
+               if (tb[NL80211_ATTR_MLO_LINK_ID]) {
+                       assoc_link_id =
+                               nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+                       wpa_printf(MSG_DEBUG, "nl80211: STA assoc link ID %d",
+                                  assoc_link_id);
+                       if (tb[NL80211_ATTR_MLD_ADDR]) {
+                               peer_addr = nla_data(tb[NL80211_ATTR_MLD_ADDR]);
+                               link_addr = nla_data(tb[NL80211_ATTR_MAC]);
+                               wpa_printf(MSG_DEBUG,
+                                          "nl80211: STA MLD address " MACSTR,
+                                          MAC2STR(peer_addr));
+                       }
+               }
+
                if (tb[NL80211_ATTR_IE]) {
-                       ies = nla_data(tb[NL80211_ATTR_IE]);
-                       ies_len = nla_len(tb[NL80211_ATTR_IE]);
+                       req_ies = nla_data(tb[NL80211_ATTR_IE]);
+                       req_ies_len = nla_len(tb[NL80211_ATTR_IE]);
+                       wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs",
+                                   req_ies, req_ies_len);
                }
-               wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len);
-               drv_event_assoc(bss->ctx, addr, ies, ies_len, 0);
+
+               if (tb[NL80211_ATTR_RESP_IE]) {
+                       resp_ies = nla_data(tb[NL80211_ATTR_RESP_IE]);
+                       resp_ies_len = nla_len(tb[NL80211_ATTR_RESP_IE]);
+                       wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Resp IEs",
+                                   resp_ies, resp_ies_len);
+               }
+
+               drv_event_assoc(bss->ctx, peer_addr, req_ies, req_ies_len,
+                               resp_ies, resp_ies_len, link_addr,
+                               assoc_link_id, 0);
                return;
        }
 
@@ -2189,7 +2226,7 @@ static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
                return;
 
        os_memset(&data, 0, sizeof(data));
-       os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN);
+       os_memcpy(data.ibss_rsn_start.peer, peer_addr, ETH_ALEN);
        wpa_supplicant_event(bss->ctx, EVENT_IBSS_RSN_START, &data);
 }
 
index 9db6c14a3d999cba26a631e5103e86779f8baa61..41e25f0771ecefa2879c9b90fd78cac13be78e56 100644 (file)
@@ -3583,8 +3583,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
                hostapd_notif_assoc(wpa_s->ap_iface->bss[0],
                                    data->assoc_info.addr,
                                    data->assoc_info.req_ies,
-                                   data->assoc_info.req_ies_len,
-                                   data->assoc_info.reassoc);
+                                   data->assoc_info.req_ies_len, NULL, 0,
+                                   NULL, data->assoc_info.reassoc);
                return;
        }
 #endif /* CONFIG_AP */