From: Johannes Berg <johannes.berg@xxxxxxxxx> For MLO, we will need to build these elements per link, so factor out the code that does this, returning the capability, to simplify building the multi-link element in the future. Signed-off-by: Johannes Berg <johannes.berg@xxxxxxxxx> --- net/mac80211/mlme.c | 278 ++++++++++++++++++++++++++------------------ 1 file changed, 162 insertions(+), 116 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 999eead2129e..26c354e1b042 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -936,17 +936,158 @@ static size_t ieee80211_add_before_he_elems(struct sk_buff *skb, return noffset; } +static size_t ieee80211_assoc_link_elems(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, u16 *capab, + const struct element *ext_capa, + const u8 *extra_elems, + size_t extra_elems_len, + struct ieee80211_link_data *link) +{ + enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif); + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; + struct cfg80211_bss *cbss = assoc_data->bss; + struct ieee80211_channel *chan = cbss->channel; + const struct ieee80211_sband_iftype_data *iftd; + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20; + struct ieee80211_chanctx_conf *chanctx_conf; + size_t offset = 0; + u8 *pos; + int i; + + /* + * 5/10 MHz scenarios are only viable without MLO, in which + * case this pointer should be used ... All of this is a bit + * unclear though, not sure this even works at all. + */ + rcu_read_lock(); + chanctx_conf = rcu_dereference(link->conf->chanctx_conf); + if (chanctx_conf) + width = chanctx_conf->def.width; + rcu_read_unlock(); + + sband = local->hw.wiphy->bands[chan->band]; + iftd = ieee80211_get_sband_iftype_data(sband, iftype); + + if (sband->band == NL80211_BAND_2GHZ) { + *capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; + *capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; + } + + if ((cbss->capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && + ieee80211_hw_check(&local->hw, SPECTRUM_MGMT)) + *capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; + + if (sband->band != NL80211_BAND_S1GHZ) + ieee80211_assoc_add_rates(skb, width, sband, assoc_data); + + if (*capab & WLAN_CAPABILITY_SPECTRUM_MGMT || + *capab & WLAN_CAPABILITY_RADIO_MEASURE) { + struct cfg80211_chan_def chandef = { + .width = width, + .chan = chan, + }; + + pos = skb_put(skb, 4); + *pos++ = WLAN_EID_PWR_CAPABILITY; + *pos++ = 2; + *pos++ = 0; /* min tx power */ + /* max tx power */ + *pos++ = ieee80211_chandef_max_power(&chandef); + } + + /* + * Per spec, we shouldn't include the list of channels if we advertise + * support for extended channel switching, but we've always done that; + * (for now?) apply this restriction only on the (new) 6 GHz band. + */ + if (*capab & WLAN_CAPABILITY_SPECTRUM_MGMT && + (sband->band != NL80211_BAND_6GHZ || + !ext_capa || ext_capa->datalen < 1 || + !(ext_capa->data[0] & WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING))) { + /* TODO: get this in reg domain format */ + pos = skb_put(skb, 2 * sband->n_channels + 2); + *pos++ = WLAN_EID_SUPPORTED_CHANNELS; + *pos++ = 2 * sband->n_channels; + for (i = 0; i < sband->n_channels; i++) { + int cf = sband->channels[i].center_freq; + + *pos++ = ieee80211_frequency_to_channel(cf); + *pos++ = 1; /* one channel in the subband*/ + } + } + + /* if present, add any custom IEs that go before HT */ + offset = ieee80211_add_before_ht_elems(skb, extra_elems, + extra_elems_len, + offset); + + if (sband->band != NL80211_BAND_6GHZ && + !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) + ieee80211_add_ht_ie(sdata, skb, + assoc_data->ap_ht_param, + sband, chan, link->smps_mode, + link->u.mgd.conn_flags); + + /* if present, add any custom IEs that go before VHT */ + offset = ieee80211_add_before_vht_elems(skb, extra_elems, + extra_elems_len, + offset); + + if (sband->band != NL80211_BAND_6GHZ && + !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) + link->conf->mu_mimo_owner = + ieee80211_add_vht_ie(sdata, skb, sband, + &assoc_data->ap_vht_cap, + link->u.mgd.conn_flags); + + /* + * If AP doesn't support HT, mark HE and EHT as disabled. + * If on the 5GHz band, make sure it supports VHT. + */ + if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT || + (sband->band == NL80211_BAND_5GHZ && + link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) + link->u.mgd.conn_flags |= + IEEE80211_CONN_DISABLE_HE | + IEEE80211_CONN_DISABLE_EHT; + + /* if present, add any custom IEs that go before HE */ + offset = ieee80211_add_before_he_elems(skb, extra_elems, + extra_elems_len, + offset); + + if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE)) + ieee80211_add_he_ie(sdata, skb, sband, + link->u.mgd.conn_flags); + + if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT)) + ieee80211_add_eht_ie(sdata, skb, sband); + + if (sband->band == NL80211_BAND_S1GHZ) { + ieee80211_add_aid_request_ie(sdata, skb); + ieee80211_add_s1g_capab_ie(sdata, &sband->s1g_cap, skb); + } + + if (iftd && iftd->vendor_elems.data && iftd->vendor_elems.len) + skb_put_data(skb, iftd->vendor_elems.data, iftd->vendor_elems.len); + + return offset; +} + static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; + struct ieee80211_link_data *link = &sdata->deflink; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; u8 *pos, qos_info, *ie_start; - size_t offset = 0, noffset; - int i; - u16 capab; + size_t offset, noffset; + u16 capab = WLAN_CAPABILITY_ESS; struct ieee80211_supported_band *sband; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_channel *chan; @@ -955,7 +1096,7 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif); const struct ieee80211_sband_iftype_data *iftd; struct ieee80211_prep_tx_info info = {}; - struct ieee80211_link_data *link = &sdata->deflink; + void *capab_pos; int ret; /* we know it's writable, cast away the const */ @@ -1003,23 +1144,18 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) skb_reserve(skb, local->hw.extra_tx_headroom); - capab = WLAN_CAPABILITY_ESS; - - if (sband->band == NL80211_BAND_2GHZ) { - capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; - capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; - } - if (assoc_data->capability & WLAN_CAPABILITY_PRIVACY) capab |= WLAN_CAPABILITY_PRIVACY; - if ((assoc_data->capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && - ieee80211_hw_check(&local->hw, SPECTRUM_MGMT)) - capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; - if (ifmgd->flags & IEEE80211_STA_ENABLE_RRM) capab |= WLAN_CAPABILITY_RADIO_MEASURE; + /* Set MBSSID support for HE AP if needed */ + if (ieee80211_hw_check(&local->hw, SUPPORTS_ONLY_HE_MULTI_BSSID) && + !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && + ext_capa && ext_capa->datalen >= 3) + ext_capa->data[2] |= WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT; + mgmt = skb_put_zero(skb, 24); memcpy(mgmt->da, assoc_data->bss->bssid, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); @@ -1032,7 +1168,7 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) skb_put(skb, 10); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ); - mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab); + capab_pos = &mgmt->u.reassoc_req.capab_info; mgmt->u.reassoc_req.listen_interval = listen_int; memcpy(mgmt->u.reassoc_req.current_ap, assoc_data->prev_bssid, ETH_ALEN); @@ -1041,7 +1177,7 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) skb_put(skb, 4); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ); - mgmt->u.assoc_req.capab_info = cpu_to_le16(capab); + capab_pos = &mgmt->u.assoc_req.capab_info; mgmt->u.assoc_req.listen_interval = listen_int; info.subtype = IEEE80211_STYPE_ASSOC_REQ; } @@ -1053,97 +1189,15 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) *pos++ = assoc_data->ssid_len; memcpy(pos, assoc_data->ssid, assoc_data->ssid_len); - if (sband->band != NL80211_BAND_S1GHZ) - ieee80211_assoc_add_rates(skb, chanctx_conf->def.width, - sband, assoc_data); - - if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT || - capab & WLAN_CAPABILITY_RADIO_MEASURE) { - pos = skb_put(skb, 4); - *pos++ = WLAN_EID_PWR_CAPABILITY; - *pos++ = 2; - *pos++ = 0; /* min tx power */ - /* max tx power */ - *pos++ = ieee80211_chandef_max_power(&chanctx_conf->def); - } - - /* - * Per spec, we shouldn't include the list of channels if we advertise - * support for extended channel switching, but we've always done that; - * (for now?) apply this restriction only on the (new) 6 GHz band. - */ - if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT && - (sband->band != NL80211_BAND_6GHZ || - !ext_capa || ext_capa->datalen < 1 || - !(ext_capa->data[0] & WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING))) { - /* TODO: get this in reg domain format */ - pos = skb_put(skb, 2 * sband->n_channels + 2); - *pos++ = WLAN_EID_SUPPORTED_CHANNELS; - *pos++ = 2 * sband->n_channels; - for (i = 0; i < sband->n_channels; i++) { - int cf = sband->channels[i].center_freq; - - *pos++ = ieee80211_frequency_to_channel(cf); - *pos++ = 1; /* one channel in the subband*/ - } - } - - /* Set MBSSID support for HE AP if needed */ - if (ieee80211_hw_check(&local->hw, SUPPORTS_ONLY_HE_MULTI_BSSID) && - !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && - ext_capa && ext_capa->datalen >= 3) - ext_capa->data[2] |= WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT; - - /* if present, add any custom IEs that go before HT */ - offset = ieee80211_add_before_ht_elems(skb, assoc_data->ie, - assoc_data->ie_len, - offset); - - if (WARN_ON_ONCE((link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) && - !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT))) - link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_VHT; - - if (sband->band != NL80211_BAND_6GHZ && - !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) - ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, - sband, chan, link->smps_mode, - link->u.mgd.conn_flags); - - /* if present, add any custom IEs that go before VHT */ - offset = ieee80211_add_before_vht_elems(skb, assoc_data->ie, - assoc_data->ie_len, - offset); - - if (sband->band != NL80211_BAND_6GHZ && - !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) - link->conf->mu_mimo_owner = - ieee80211_add_vht_ie(sdata, skb, sband, - &assoc_data->ap_vht_cap, - link->u.mgd.conn_flags); - - /* - * If AP doesn't support HT, mark HE and EHT as disabled. - * If on the 5GHz band, make sure it supports VHT. - */ - if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT || - (sband->band == NL80211_BAND_5GHZ && - link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) - link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HE | - IEEE80211_CONN_DISABLE_EHT; - - /* if present, add any custom IEs that go before HE */ - offset = ieee80211_add_before_he_elems(skb, assoc_data->ie, - assoc_data->ie_len, - offset); - - if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE)) { - ieee80211_add_he_ie(sdata, skb, sband, link->u.mgd.conn_flags); - - if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT)) - ieee80211_add_eht_ie(sdata, skb, sband); - } - - /* if present, add any custom non-vendor IEs that go after HE */ + /* add the elements for the assoc (main) link */ + offset = ieee80211_assoc_link_elems(sdata, skb, &capab, + ext_capa, + assoc_data->ie, + assoc_data->ie_len, + link); + put_unaligned_le16(capab, capab_pos); + + /* if present, add any custom non-vendor IEs */ if (assoc_data->ie_len) { noffset = ieee80211_ie_split_vendor(assoc_data->ie, assoc_data->ie_len, @@ -1164,14 +1218,6 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) pos = ieee80211_add_wmm_info_ie(skb_put(skb, 9), qos_info); } - if (sband->band == NL80211_BAND_S1GHZ) { - ieee80211_add_aid_request_ie(sdata, skb); - ieee80211_add_s1g_capab_ie(sdata, &sband->s1g_cap, skb); - } - - if (iftd && iftd->vendor_elems.data && iftd->vendor_elems.len) - skb_put_data(skb, iftd->vendor_elems.data, iftd->vendor_elems.len); - /* add any remaining custom (i.e. vendor specific here) IEs */ if (assoc_data->ie_len) { noffset = assoc_data->ie_len; -- 2.36.1