Commit aebc29de authored by Benjamin Berg's avatar Benjamin Berg Committed by Johannes Berg
Browse files

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: 6d543b34 ("wifi: mac80211: Support disabled links during association")
Signed-off-by: default avatarBenjamin Berg <benjamin.berg@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260118093904.43c861424543.I067f702ac46b84ac3f8b4ea16fb0db9cbbfae7e2@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 3fa2886d
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -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);
+119 −97
Original line number Diff line number Diff line
@@ -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 =