]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: mac80211: send extended MLD capa/ops if AP has it
authorJohannes Berg <johannes.berg@intel.com>
Mon, 9 Jun 2025 18:35:25 +0000 (21:35 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 9 Jul 2025 09:52:36 +0000 (11:52 +0200)
Currently the code only sends extended MLD capa/ops in
strict mode, but if the AP has it then it should also
be able to parse it. There could be cases where the AP
doesn't have it but we would want to advertise it (e.g.
if the AP supports nothing but we want to have BTM.),
but given the broken deployed APs out there right now
this is the best we can do.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Reviewed-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20250609213232.c9b8b3a6ca77.I1153d4283d1fbb9e5db60e7b939cc133a6345db5@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/mlme.c

index 75dfbb06dff2204ff6a7f6b035f711c7591daab8..1bedd8d6e891039f19d266bbcf3f97cf49a16c57 100644 (file)
@@ -1939,14 +1939,7 @@ ieee80211_assoc_add_ml_elem(struct ieee80211_sub_if_data *sdata,
        }
        skb_put_data(skb, &mld_capa_ops, sizeof(mld_capa_ops));
 
-       /* Many APs have broken parsing of the extended MLD capa/ops field,
-        * dropping (re-)association request frames or replying with association
-        * response with a failure status if it's present. Without a clear
-        * indication as to whether the AP supports parsing this field or not do
-        * not include it in the common information unless strict mode is set.
-        */
-       if (ieee80211_hw_check(&local->hw, STRICT) &&
-           assoc_data->ext_mld_capa_ops) {
+       if (assoc_data->ext_mld_capa_ops) {
                ml_elem->control |=
                        cpu_to_le16(IEEE80211_MLC_BASIC_PRES_EXT_MLD_CAPA_OP);
                common->len += 2;
@@ -9357,6 +9350,39 @@ out_rcu:
        return err;
 }
 
+static bool
+ieee80211_mgd_assoc_bss_has_mld_ext_capa_ops(struct cfg80211_assoc_request *req)
+{
+       const struct cfg80211_bss_ies *ies;
+       struct cfg80211_bss *bss;
+       const struct element *ml;
+
+       /* not an MLO connection if link_id < 0, so irrelevant */
+       if (req->link_id < 0)
+               return false;
+
+       bss = req->links[req->link_id].bss;
+
+       guard(rcu)();
+       ies = rcu_dereference(bss->ies);
+       for_each_element_extid(ml, WLAN_EID_EXT_EHT_MULTI_LINK,
+                              ies->data, ies->len) {
+               const struct ieee80211_multi_link_elem *mle;
+
+               if (!ieee80211_mle_type_ok(ml->data + 1,
+                                          IEEE80211_ML_CONTROL_TYPE_BASIC,
+                                          ml->datalen - 1))
+                       continue;
+
+               mle = (void *)(ml->data + 1);
+               if (mle->control & cpu_to_le16(IEEE80211_MLC_BASIC_PRES_EXT_MLD_CAPA_OP))
+                       return true;
+       }
+
+       return false;
+
+}
+
 int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                        struct cfg80211_assoc_request *req)
 {
@@ -9409,7 +9435,17 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        else
                memcpy(assoc_data->ap_addr, cbss->bssid, ETH_ALEN);
 
-       assoc_data->ext_mld_capa_ops = cpu_to_le16(req->ext_mld_capa_ops);
+       /*
+        * Many APs have broken parsing of the extended MLD capa/ops field,
+        * dropping (re-)association request frames or replying with association
+        * response with a failure status if it's present.
+        * Set our value from the userspace request only in strict mode or if
+        * the AP also had that field present.
+        */
+       if (ieee80211_hw_check(&local->hw, STRICT) ||
+           ieee80211_mgd_assoc_bss_has_mld_ext_capa_ops(req))
+               assoc_data->ext_mld_capa_ops =
+                       cpu_to_le16(req->ext_mld_capa_ops);
 
        if (ifmgd->associated) {
                u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];