}
+#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;
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);
/*
"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,
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
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,
* 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
* 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;
/**
* 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);
}
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))
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. */
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
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;
}
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);
}
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 */