]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: mac80211: apply advertised TTLM from association response
authorBenjamin Berg <benjamin.berg@intel.com>
Sun, 18 Jan 2026 07:51:14 +0000 (09:51 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 20 Jan 2026 09:02:01 +0000 (10:02 +0100)
When the AP has a disabled link that the station can include in the
association, the fact that the link is dormant needs to be advertised
in the TID to Link Mapping (TTLM). Section 35.3.7.2.3 ("Negotiation of
TTLM") of Draft P802.11REVmf_D1.0 also states that the mapping needs to
be included in the association response frame.

As such, we can simply rely on the TTLM from the association response.
Before this change mac80211 would not properly track that an advertised
TTLM was effectively active, resulting in it not enabling the link once
it became available again.

For the link reconfiguration case, the data was not used at all. This
behaviour is actually correct because Draft P802.11REVmf_D1.0 states in
section 35.3.6.4 that we "shall operate with all the TIDs mapped to the
newly added links ..."

Fixes: 6d543b34dbcf ("wifi: mac80211: Support disabled links during association")
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260118093904.43c861424543.I067f702ac46b84ac3f8b4ea16fb0db9cbbfae7e2@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/ieee80211_i.h
net/mac80211/mlme.c

index 9d9313eee59f8150734fa3abf1246161cd525621..bd573f8e61fb99f87bda3e44f8b0290e6b198d66 100644 (file)
@@ -451,8 +451,6 @@ struct ieee80211_mgd_assoc_data {
                struct ieee80211_conn_settings conn;
 
                u16 status;
-
-               bool disabled;
        } link[IEEE80211_MLD_MAX_NUM_LINKS];
 
        u8 ap_addr[ETH_ALEN] __aligned(2);
index 3f6bbe4e0175405c8c581bbbe53b277a8e3e50bc..b72345c779c0f3821d9eec2fe4356c7f4839c989 100644 (file)
@@ -6161,6 +6161,98 @@ static bool ieee80211_get_dtim(const struct cfg80211_bss_ies *ies,
        return true;
 }
 
+static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data)
+{
+       if (bm_size == 1)
+               return *data;
+
+       return get_unaligned_le16(data);
+}
+
+static int
+ieee80211_parse_adv_t2l(struct ieee80211_sub_if_data *sdata,
+                       const struct ieee80211_ttlm_elem *ttlm,
+                       struct ieee80211_adv_ttlm_info *ttlm_info)
+{
+       /* The element size was already validated in
+        * ieee80211_tid_to_link_map_size_ok()
+        */
+       u8 control, link_map_presence, map_size, tid;
+       u8 *pos;
+
+       memset(ttlm_info, 0, sizeof(*ttlm_info));
+       pos = (void *)ttlm->optional;
+       control = ttlm->control;
+
+       if ((control & IEEE80211_TTLM_CONTROL_DIRECTION) !=
+           IEEE80211_TTLM_DIRECTION_BOTH) {
+               sdata_info(sdata, "Invalid advertised T2L map direction\n");
+               return -EINVAL;
+       }
+
+       link_map_presence = *pos;
+       pos++;
+
+       if (control & IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT) {
+               ttlm_info->switch_time = get_unaligned_le16(pos);
+
+               /* Since ttlm_info->switch_time == 0 means no switch time, bump
+                * it by 1.
+                */
+               if (!ttlm_info->switch_time)
+                       ttlm_info->switch_time = 1;
+
+               pos += 2;
+       }
+
+       if (control & IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT) {
+               ttlm_info->duration = pos[0] | pos[1] << 8 | pos[2] << 16;
+               pos += 3;
+       }
+
+       if (control & IEEE80211_TTLM_CONTROL_DEF_LINK_MAP) {
+               ttlm_info->map = 0xffff;
+               return 0;
+       }
+
+       if (control & IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE)
+               map_size = 1;
+       else
+               map_size = 2;
+
+       /* According to Draft P802.11be_D3.0 clause 35.3.7.1.7, an AP MLD shall
+        * not advertise a TID-to-link mapping that does not map all TIDs to the
+        * same link set, reject frame if not all links have mapping
+        */
+       if (link_map_presence != 0xff) {
+               sdata_info(sdata,
+                          "Invalid advertised T2L mapping presence indicator\n");
+               return -EINVAL;
+       }
+
+       ttlm_info->map = ieee80211_get_ttlm(map_size, pos);
+       if (!ttlm_info->map) {
+               sdata_info(sdata,
+                          "Invalid advertised T2L map for TID 0\n");
+               return -EINVAL;
+       }
+
+       pos += map_size;
+
+       for (tid = 1; tid < 8; tid++) {
+               u16 map = ieee80211_get_ttlm(map_size, pos);
+
+               if (map != ttlm_info->map) {
+                       sdata_info(sdata, "Invalid advertised T2L map for tid %d\n",
+                                  tid);
+                       return -EINVAL;
+               }
+
+               pos += map_size;
+       }
+       return 0;
+}
+
 static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                                    struct ieee80211_mgmt *mgmt,
                                    struct ieee802_11_elems *elems,
@@ -6192,8 +6284,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                                continue;
 
                        valid_links |= BIT(link_id);
-                       if (assoc_data->link[link_id].disabled)
-                               dormant_links |= BIT(link_id);
 
                        if (link_id != assoc_data->assoc_link_id) {
                                err = ieee80211_sta_allocate_link(sta, link_id);
@@ -6202,6 +6292,33 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                        }
                }
 
+               /*
+                * We do not support setting a negotiated TTLM during
+                * association. As such, we can assume that if there is a TTLM,
+                * then it is the currently active advertised TTLM.
+                * In that case, there must be exactly one TTLM that does not
+                * have a switch time set. This mapping should also leave us
+                * with at least one usable link.
+                */
+               if (elems->ttlm_num > 1) {
+                       sdata_info(sdata,
+                                  "More than one advertised TTLM in association response\n");
+                       goto out_err;
+               } else if (elems->ttlm_num == 1) {
+                       if (ieee80211_parse_adv_t2l(sdata, elems->ttlm[0],
+                                                   &sdata->u.mgd.ttlm_info) ||
+                           sdata->u.mgd.ttlm_info.switch_time != 0 ||
+                           !(valid_links & sdata->u.mgd.ttlm_info.map)) {
+                               sdata_info(sdata,
+                                          "Invalid advertised TTLM in association response\n");
+                               goto out_err;
+                       }
+
+                       sdata->u.mgd.ttlm_info.active = true;
+                       dormant_links =
+                               valid_links & ~sdata->u.mgd.ttlm_info.map;
+               }
+
                ieee80211_vif_set_links(sdata, valid_links, dormant_links);
        }
 
@@ -6992,98 +7109,6 @@ static void ieee80211_tid_to_link_map_work(struct wiphy *wiphy,
        sdata->u.mgd.ttlm_info.switch_time = 0;
 }
 
-static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data)
-{
-       if (bm_size == 1)
-               return *data;
-       else
-               return get_unaligned_le16(data);
-}
-
-static int
-ieee80211_parse_adv_t2l(struct ieee80211_sub_if_data *sdata,
-                       const struct ieee80211_ttlm_elem *ttlm,
-                       struct ieee80211_adv_ttlm_info *ttlm_info)
-{
-       /* The element size was already validated in
-        * ieee80211_tid_to_link_map_size_ok()
-        */
-       u8 control, link_map_presence, map_size, tid;
-       u8 *pos;
-
-       memset(ttlm_info, 0, sizeof(*ttlm_info));
-       pos = (void *)ttlm->optional;
-       control = ttlm->control;
-
-       if ((control & IEEE80211_TTLM_CONTROL_DIRECTION) !=
-           IEEE80211_TTLM_DIRECTION_BOTH) {
-               sdata_info(sdata, "Invalid advertised T2L map direction\n");
-               return -EINVAL;
-       }
-
-       link_map_presence = *pos;
-       pos++;
-
-       if (control & IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT) {
-               ttlm_info->switch_time = get_unaligned_le16(pos);
-
-               /* Since ttlm_info->switch_time == 0 means no switch time, bump
-                * it by 1.
-                */
-               if (!ttlm_info->switch_time)
-                       ttlm_info->switch_time = 1;
-
-               pos += 2;
-       }
-
-       if (control & IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT) {
-               ttlm_info->duration = pos[0] | pos[1] << 8 | pos[2] << 16;
-               pos += 3;
-       }
-
-       if (control & IEEE80211_TTLM_CONTROL_DEF_LINK_MAP) {
-               ttlm_info->map = 0xffff;
-               return 0;
-       }
-
-       if (control & IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE)
-               map_size = 1;
-       else
-               map_size = 2;
-
-       /* According to Draft P802.11be_D3.0 clause 35.3.7.1.7, an AP MLD shall
-        * not advertise a TID-to-link mapping that does not map all TIDs to the
-        * same link set, reject frame if not all links have mapping
-        */
-       if (link_map_presence != 0xff) {
-               sdata_info(sdata,
-                          "Invalid advertised T2L mapping presence indicator\n");
-               return -EINVAL;
-       }
-
-       ttlm_info->map = ieee80211_get_ttlm(map_size, pos);
-       if (!ttlm_info->map) {
-               sdata_info(sdata,
-                          "Invalid advertised T2L map for TID 0\n");
-               return -EINVAL;
-       }
-
-       pos += map_size;
-
-       for (tid = 1; tid < 8; tid++) {
-               u16 map = ieee80211_get_ttlm(map_size, pos);
-
-               if (map != ttlm_info->map) {
-                       sdata_info(sdata, "Invalid advertised T2L map for tid %d\n",
-                                  tid);
-                       return -EINVAL;
-               }
-
-               pos += map_size;
-       }
-       return 0;
-}
-
 static void ieee80211_process_adv_ttlm(struct ieee80211_sub_if_data *sdata,
                                          struct ieee802_11_elems *elems,
                                          u64 beacon_ts)
@@ -9740,7 +9765,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                                                               req, true, i,
                                                               &assoc_data->link[i].conn);
                        assoc_data->link[i].bss = link_cbss;
-                       assoc_data->link[i].disabled = req->links[i].disabled;
 
                        if (!bss->uapsd_supported)
                                uapsd_supported = false;
@@ -10722,8 +10746,6 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata,
                                                         &data->link[link_id].conn);
 
                        data->link[link_id].bss = link_cbss;
-                       data->link[link_id].disabled =
-                               req->add_links[link_id].disabled;
                        data->link[link_id].elems =
                                (u8 *)req->add_links[link_id].elems;
                        data->link[link_id].elems_len =