Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

wifi: mac80211: apply advertised TTLM from association response

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>

authored by

Benjamin Berg and committed by
Johannes Berg
aebc29de 3fa2886d

+119 -99
-2
net/mac80211/ieee80211_i.h
··· 451 451 struct ieee80211_conn_settings conn; 452 452 453 453 u16 status; 454 - 455 - bool disabled; 456 454 } link[IEEE80211_MLD_MAX_NUM_LINKS]; 457 455 458 456 u8 ap_addr[ETH_ALEN] __aligned(2);
+119 -97
net/mac80211/mlme.c
··· 6161 6161 return true; 6162 6162 } 6163 6163 6164 + static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data) 6165 + { 6166 + if (bm_size == 1) 6167 + return *data; 6168 + 6169 + return get_unaligned_le16(data); 6170 + } 6171 + 6172 + static int 6173 + ieee80211_parse_adv_t2l(struct ieee80211_sub_if_data *sdata, 6174 + const struct ieee80211_ttlm_elem *ttlm, 6175 + struct ieee80211_adv_ttlm_info *ttlm_info) 6176 + { 6177 + /* The element size was already validated in 6178 + * ieee80211_tid_to_link_map_size_ok() 6179 + */ 6180 + u8 control, link_map_presence, map_size, tid; 6181 + u8 *pos; 6182 + 6183 + memset(ttlm_info, 0, sizeof(*ttlm_info)); 6184 + pos = (void *)ttlm->optional; 6185 + control = ttlm->control; 6186 + 6187 + if ((control & IEEE80211_TTLM_CONTROL_DIRECTION) != 6188 + IEEE80211_TTLM_DIRECTION_BOTH) { 6189 + sdata_info(sdata, "Invalid advertised T2L map direction\n"); 6190 + return -EINVAL; 6191 + } 6192 + 6193 + link_map_presence = *pos; 6194 + pos++; 6195 + 6196 + if (control & IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT) { 6197 + ttlm_info->switch_time = get_unaligned_le16(pos); 6198 + 6199 + /* Since ttlm_info->switch_time == 0 means no switch time, bump 6200 + * it by 1. 6201 + */ 6202 + if (!ttlm_info->switch_time) 6203 + ttlm_info->switch_time = 1; 6204 + 6205 + pos += 2; 6206 + } 6207 + 6208 + if (control & IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT) { 6209 + ttlm_info->duration = pos[0] | pos[1] << 8 | pos[2] << 16; 6210 + pos += 3; 6211 + } 6212 + 6213 + if (control & IEEE80211_TTLM_CONTROL_DEF_LINK_MAP) { 6214 + ttlm_info->map = 0xffff; 6215 + return 0; 6216 + } 6217 + 6218 + if (control & IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE) 6219 + map_size = 1; 6220 + else 6221 + map_size = 2; 6222 + 6223 + /* According to Draft P802.11be_D3.0 clause 35.3.7.1.7, an AP MLD shall 6224 + * not advertise a TID-to-link mapping that does not map all TIDs to the 6225 + * same link set, reject frame if not all links have mapping 6226 + */ 6227 + if (link_map_presence != 0xff) { 6228 + sdata_info(sdata, 6229 + "Invalid advertised T2L mapping presence indicator\n"); 6230 + return -EINVAL; 6231 + } 6232 + 6233 + ttlm_info->map = ieee80211_get_ttlm(map_size, pos); 6234 + if (!ttlm_info->map) { 6235 + sdata_info(sdata, 6236 + "Invalid advertised T2L map for TID 0\n"); 6237 + return -EINVAL; 6238 + } 6239 + 6240 + pos += map_size; 6241 + 6242 + for (tid = 1; tid < 8; tid++) { 6243 + u16 map = ieee80211_get_ttlm(map_size, pos); 6244 + 6245 + if (map != ttlm_info->map) { 6246 + sdata_info(sdata, "Invalid advertised T2L map for tid %d\n", 6247 + tid); 6248 + return -EINVAL; 6249 + } 6250 + 6251 + pos += map_size; 6252 + } 6253 + return 0; 6254 + } 6255 + 6164 6256 static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, 6165 6257 struct ieee80211_mgmt *mgmt, 6166 6258 struct ieee802_11_elems *elems, ··· 6284 6192 continue; 6285 6193 6286 6194 valid_links |= BIT(link_id); 6287 - if (assoc_data->link[link_id].disabled) 6288 - dormant_links |= BIT(link_id); 6289 6195 6290 6196 if (link_id != assoc_data->assoc_link_id) { 6291 6197 err = ieee80211_sta_allocate_link(sta, link_id); 6292 6198 if (err) 6293 6199 goto out_err; 6294 6200 } 6201 + } 6202 + 6203 + /* 6204 + * We do not support setting a negotiated TTLM during 6205 + * association. As such, we can assume that if there is a TTLM, 6206 + * then it is the currently active advertised TTLM. 6207 + * In that case, there must be exactly one TTLM that does not 6208 + * have a switch time set. This mapping should also leave us 6209 + * with at least one usable link. 6210 + */ 6211 + if (elems->ttlm_num > 1) { 6212 + sdata_info(sdata, 6213 + "More than one advertised TTLM in association response\n"); 6214 + goto out_err; 6215 + } else if (elems->ttlm_num == 1) { 6216 + if (ieee80211_parse_adv_t2l(sdata, elems->ttlm[0], 6217 + &sdata->u.mgd.ttlm_info) || 6218 + sdata->u.mgd.ttlm_info.switch_time != 0 || 6219 + !(valid_links & sdata->u.mgd.ttlm_info.map)) { 6220 + sdata_info(sdata, 6221 + "Invalid advertised TTLM in association response\n"); 6222 + goto out_err; 6223 + } 6224 + 6225 + sdata->u.mgd.ttlm_info.active = true; 6226 + dormant_links = 6227 + valid_links & ~sdata->u.mgd.ttlm_info.map; 6295 6228 } 6296 6229 6297 6230 ieee80211_vif_set_links(sdata, valid_links, dormant_links); ··· 7107 6990 7108 6991 sdata->u.mgd.ttlm_info.active = true; 7109 6992 sdata->u.mgd.ttlm_info.switch_time = 0; 7110 - } 7111 - 7112 - static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data) 7113 - { 7114 - if (bm_size == 1) 7115 - return *data; 7116 - else 7117 - return get_unaligned_le16(data); 7118 - } 7119 - 7120 - static int 7121 - ieee80211_parse_adv_t2l(struct ieee80211_sub_if_data *sdata, 7122 - const struct ieee80211_ttlm_elem *ttlm, 7123 - struct ieee80211_adv_ttlm_info *ttlm_info) 7124 - { 7125 - /* The element size was already validated in 7126 - * ieee80211_tid_to_link_map_size_ok() 7127 - */ 7128 - u8 control, link_map_presence, map_size, tid; 7129 - u8 *pos; 7130 - 7131 - memset(ttlm_info, 0, sizeof(*ttlm_info)); 7132 - pos = (void *)ttlm->optional; 7133 - control = ttlm->control; 7134 - 7135 - if ((control & IEEE80211_TTLM_CONTROL_DIRECTION) != 7136 - IEEE80211_TTLM_DIRECTION_BOTH) { 7137 - sdata_info(sdata, "Invalid advertised T2L map direction\n"); 7138 - return -EINVAL; 7139 - } 7140 - 7141 - link_map_presence = *pos; 7142 - pos++; 7143 - 7144 - if (control & IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT) { 7145 - ttlm_info->switch_time = get_unaligned_le16(pos); 7146 - 7147 - /* Since ttlm_info->switch_time == 0 means no switch time, bump 7148 - * it by 1. 7149 - */ 7150 - if (!ttlm_info->switch_time) 7151 - ttlm_info->switch_time = 1; 7152 - 7153 - pos += 2; 7154 - } 7155 - 7156 - if (control & IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT) { 7157 - ttlm_info->duration = pos[0] | pos[1] << 8 | pos[2] << 16; 7158 - pos += 3; 7159 - } 7160 - 7161 - if (control & IEEE80211_TTLM_CONTROL_DEF_LINK_MAP) { 7162 - ttlm_info->map = 0xffff; 7163 - return 0; 7164 - } 7165 - 7166 - if (control & IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE) 7167 - map_size = 1; 7168 - else 7169 - map_size = 2; 7170 - 7171 - /* According to Draft P802.11be_D3.0 clause 35.3.7.1.7, an AP MLD shall 7172 - * not advertise a TID-to-link mapping that does not map all TIDs to the 7173 - * same link set, reject frame if not all links have mapping 7174 - */ 7175 - if (link_map_presence != 0xff) { 7176 - sdata_info(sdata, 7177 - "Invalid advertised T2L mapping presence indicator\n"); 7178 - return -EINVAL; 7179 - } 7180 - 7181 - ttlm_info->map = ieee80211_get_ttlm(map_size, pos); 7182 - if (!ttlm_info->map) { 7183 - sdata_info(sdata, 7184 - "Invalid advertised T2L map for TID 0\n"); 7185 - return -EINVAL; 7186 - } 7187 - 7188 - pos += map_size; 7189 - 7190 - for (tid = 1; tid < 8; tid++) { 7191 - u16 map = ieee80211_get_ttlm(map_size, pos); 7192 - 7193 - if (map != ttlm_info->map) { 7194 - sdata_info(sdata, "Invalid advertised T2L map for tid %d\n", 7195 - tid); 7196 - return -EINVAL; 7197 - } 7198 - 7199 - pos += map_size; 7200 - } 7201 - return 0; 7202 6993 } 7203 6994 7204 6995 static void ieee80211_process_adv_ttlm(struct ieee80211_sub_if_data *sdata, ··· 9765 9740 req, true, i, 9766 9741 &assoc_data->link[i].conn); 9767 9742 assoc_data->link[i].bss = link_cbss; 9768 - assoc_data->link[i].disabled = req->links[i].disabled; 9769 9743 9770 9744 if (!bss->uapsd_supported) 9771 9745 uapsd_supported = false; ··· 10746 10722 &data->link[link_id].conn); 10747 10723 10748 10724 data->link[link_id].bss = link_cbss; 10749 - data->link[link_id].disabled = 10750 - req->add_links[link_id].disabled; 10751 10725 data->link[link_id].elems = 10752 10726 (u8 *)req->add_links[link_id].elems; 10753 10727 data->link[link_id].elems_len =