Commit 5d24828d authored by Johannes Berg's avatar Johannes Berg
Browse files

mac80211: always allocate struct ieee802_11_elems

As the 802.11 spec evolves, we need to parse more and more
elements. This is causing the struct to grow, and we can no
longer get away with putting it on the stack.

Change the API to always dynamically allocate and return an
allocated pointer that must be kfree()d later.

As an alternative, I contemplated a scheme whereby we'd say
in the code which elements we needed, e.g.

    DECLARE_ELEMENT_PARSER(elems,
                           SUPPORTED_CHANNELS,
                           CHANNEL_SWITCH,
                           EXT(KEY_DELIVERY));

    ieee802_11_parse_elems(..., &elems, ...);

and while I think this is possible and will save us a lot
since most individual places only care about a small subset
of the elements, it ended up being a bit more work since a
lot of places do the parsing and then pass the struct to
other functions, sometimes with multiple levels.

Link: https://lore.kernel.org/r/20210920154009.26caff6b5998.I05ae58768e990e611aee8eca8abefd9d7bc15e05@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 49a765d6
Loading
Loading
Loading
Loading
+6 −5
Original line number Diff line number Diff line
@@ -477,7 +477,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
				     size_t len)
{
	u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
	struct ieee802_11_elems elems = { };
	struct ieee802_11_elems *elems = NULL;
	u8 dialog_token;
	int ies_len;

@@ -495,16 +495,17 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
	ies_len = len - offsetof(struct ieee80211_mgmt,
				 u.action.u.addba_req.variable);
	if (ies_len) {
		ieee802_11_parse_elems(mgmt->u.action.u.addba_req.variable,
                                ies_len, true, &elems, mgmt->bssid, NULL);
		if (elems.parse_error)
		elems = ieee802_11_parse_elems(mgmt->u.action.u.addba_req.variable,
					       ies_len, true, mgmt->bssid, NULL);
		if (!elems || elems->parse_error)
			return;
	}

	__ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
					start_seq_num, ba_policy, tid,
					buf_size, true, false,
					elems.addba_ext_ie);
					elems ? elems->addba_ext_ie : NULL);
	kfree(elems);
}

void ieee80211_manage_rx_ba_offl(struct ieee80211_vif *vif,
+15 −10
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@
 * Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
 * Copyright 2013-2014  Intel Mobile Communications GmbH
 * Copyright(c) 2016 Intel Deutschland GmbH
 * Copyright(c) 2018-2020 Intel Corporation
 * Copyright(c) 2018-2021 Intel Corporation
 */

#include <linux/delay.h>
@@ -1589,7 +1589,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
				    struct ieee80211_rx_status *rx_status)
{
	size_t baselen;
	struct ieee802_11_elems elems;
	struct ieee802_11_elems *elems;

	BUILD_BUG_ON(offsetof(typeof(mgmt->u.probe_resp), variable) !=
		     offsetof(typeof(mgmt->u.beacon), variable));
@@ -1602,10 +1602,14 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
	if (baselen > len)
		return;

	ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
			       false, &elems, mgmt->bssid, NULL);
	elems = ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
				       len - baselen, false,
				       mgmt->bssid, NULL);

	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
	if (elems) {
		ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, elems);
		kfree(elems);
	}
}

void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
@@ -1614,7 +1618,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
	struct ieee80211_rx_status *rx_status;
	struct ieee80211_mgmt *mgmt;
	u16 fc;
	struct ieee802_11_elems elems;
	struct ieee802_11_elems *elems;
	int ies_len;

	rx_status = IEEE80211_SKB_RXCB(skb);
@@ -1651,15 +1655,16 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
			if (ies_len < 0)
				break;

			ieee802_11_parse_elems(
			elems = ieee802_11_parse_elems(
				mgmt->u.action.u.chan_switch.variable,
				ies_len, true, &elems, mgmt->bssid, NULL);
				ies_len, true, mgmt->bssid, NULL);

			if (elems.parse_error)
			if (!elems || elems->parse_error)
				break;

			ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt, skb->len,
							rx_status, &elems);
							rx_status, elems);
			kfree(elems);
			break;
		}
	}
+11 −11
Original line number Diff line number Diff line
@@ -2192,17 +2192,17 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
	ieee80211_tx_skb_tid(sdata, skb, 7);
}

void ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
				struct ieee802_11_elems *elems,
				u64 filter, u32 crc, u8 *transmitter_bssid,
				u8 *bss_bssid);
static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
struct ieee802_11_elems *ieee802_11_parse_elems_crc(const u8 *start, size_t len,
						    bool action,
					  struct ieee802_11_elems *elems,
					  u8 *transmitter_bssid,
					  u8 *bss_bssid)
						    u64 filter, u32 crc,
						    const u8 *transmitter_bssid,
						    const u8 *bss_bssid);
static inline struct ieee802_11_elems *
ieee802_11_parse_elems(const u8 *start, size_t len, bool action,
		       const u8 *transmitter_bssid,
		       const u8 *bss_bssid)
{
	ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0,
	return ieee802_11_parse_elems_crc(start, len, action, 0, 0,
					  transmitter_bssid, bss_bssid);
}

+49 −36
Original line number Diff line number Diff line
@@ -1246,7 +1246,7 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
	struct sk_buff *presp;
	struct beacon_data *bcn;
	struct ieee80211_mgmt *hdr;
	struct ieee802_11_elems elems;
	struct ieee802_11_elems *elems;
	size_t baselen;
	u8 *pos;

@@ -1255,22 +1255,24 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
	if (baselen > len)
		return;

	ieee802_11_parse_elems(pos, len - baselen, false, &elems, mgmt->bssid,
	elems = ieee802_11_parse_elems(pos, len - baselen, false, mgmt->bssid,
				       NULL);

	if (!elems.mesh_id)
	if (!elems)
		return;

	if (!elems->mesh_id)
		goto free;

	/* 802.11-2012 10.1.4.3.2 */
	if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
	     !is_broadcast_ether_addr(mgmt->da)) ||
	    elems.ssid_len != 0)
		return;
	    elems->ssid_len != 0)
		goto free;

	if (elems.mesh_id_len != 0 &&
	    (elems.mesh_id_len != ifmsh->mesh_id_len ||
	     memcmp(elems.mesh_id, ifmsh->mesh_id, ifmsh->mesh_id_len)))
		return;
	if (elems->mesh_id_len != 0 &&
	    (elems->mesh_id_len != ifmsh->mesh_id_len ||
	     memcmp(elems->mesh_id, ifmsh->mesh_id, ifmsh->mesh_id_len)))
		goto free;

	rcu_read_lock();
	bcn = rcu_dereference(ifmsh->beacon);
@@ -1294,6 +1296,8 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
	ieee80211_tx_skb(sdata, presp);
out:
	rcu_read_unlock();
free:
	kfree(elems);
}

static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
@@ -1304,7 +1308,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
{
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
	struct ieee802_11_elems elems;
	struct ieee802_11_elems *elems;
	struct ieee80211_channel *channel;
	size_t baselen;
	int freq;
@@ -1319,42 +1323,47 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
	if (baselen > len)
		return;

	ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
			       false, &elems, mgmt->bssid, NULL);
	elems = ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
				       len - baselen,
				       false, mgmt->bssid, NULL);
	if (!elems)
		return;

	/* ignore non-mesh or secure / unsecure mismatch */
	if ((!elems.mesh_id || !elems.mesh_config) ||
	    (elems.rsn && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) ||
	    (!elems.rsn && sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE))
		return;
	if ((!elems->mesh_id || !elems->mesh_config) ||
	    (elems->rsn && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) ||
	    (!elems->rsn && sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE))
		goto free;

	if (elems.ds_params)
		freq = ieee80211_channel_to_frequency(elems.ds_params[0], band);
	if (elems->ds_params)
		freq = ieee80211_channel_to_frequency(elems->ds_params[0], band);
	else
		freq = rx_status->freq;

	channel = ieee80211_get_channel(local->hw.wiphy, freq);

	if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
		return;
		goto free;

	if (mesh_matches_local(sdata, &elems)) {
	if (mesh_matches_local(sdata, elems)) {
		mpl_dbg(sdata, "rssi_threshold=%d,rx_status->signal=%d\n",
			sdata->u.mesh.mshcfg.rssi_threshold, rx_status->signal);
		if (!sdata->u.mesh.user_mpm ||
		    sdata->u.mesh.mshcfg.rssi_threshold == 0 ||
		    sdata->u.mesh.mshcfg.rssi_threshold < rx_status->signal)
			mesh_neighbour_update(sdata, mgmt->sa, &elems,
			mesh_neighbour_update(sdata, mgmt->sa, elems,
					      rx_status);

		if (ifmsh->csa_role != IEEE80211_MESH_CSA_ROLE_INIT &&
		    !sdata->vif.csa_active)
			ieee80211_mesh_process_chnswitch(sdata, &elems, true);
			ieee80211_mesh_process_chnswitch(sdata, elems, true);
	}

	if (ifmsh->sync_ops)
		ifmsh->sync_ops->rx_bcn_presp(sdata, stype, mgmt, len,
					      elems.mesh_config, rx_status);
					      elems->mesh_config, rx_status);
free:
	kfree(elems);
}

int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
@@ -1446,7 +1455,7 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
			      struct ieee80211_mgmt *mgmt, size_t len)
{
	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
	struct ieee802_11_elems elems;
	struct ieee802_11_elems *elems;
	u16 pre_value;
	bool fwd_csa = true;
	size_t baselen;
@@ -1459,33 +1468,37 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
	pos = mgmt->u.action.u.chan_switch.variable;
	baselen = offsetof(struct ieee80211_mgmt,
			   u.action.u.chan_switch.variable);
	ieee802_11_parse_elems(pos, len - baselen, true, &elems,
	elems = ieee802_11_parse_elems(pos, len - baselen, true,
				       mgmt->bssid, NULL);

	if (!mesh_matches_local(sdata, &elems))
	if (!elems)
		return;

	ifmsh->chsw_ttl = elems.mesh_chansw_params_ie->mesh_ttl;
	if (!mesh_matches_local(sdata, elems))
		goto free;

	ifmsh->chsw_ttl = elems->mesh_chansw_params_ie->mesh_ttl;
	if (!--ifmsh->chsw_ttl)
		fwd_csa = false;

	pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value);
	pre_value = le16_to_cpu(elems->mesh_chansw_params_ie->mesh_pre_value);
	if (ifmsh->pre_value >= pre_value)
		return;
		goto free;

	ifmsh->pre_value = pre_value;

	if (!sdata->vif.csa_active &&
	    !ieee80211_mesh_process_chnswitch(sdata, &elems, false)) {
	    !ieee80211_mesh_process_chnswitch(sdata, elems, false)) {
		mcsa_dbg(sdata, "Failed to process CSA action frame");
		return;
		goto free;
	}

	/* forward or re-broadcast the CSA frame */
	if (fwd_csa) {
		if (mesh_fwd_csa_frame(sdata, mgmt, len, &elems) < 0)
		if (mesh_fwd_csa_frame(sdata, mgmt, len, elems) < 0)
			mcsa_dbg(sdata, "Failed to forward the CSA frame");
	}
free:
	kfree(elems);
}

static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
+24 −20
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2008, 2009 open80211s Ltd.
 * Copyright (C) 2019 Intel Corporation
 * Copyright (C) 2019, 2021 Intel Corporation
 * Author:     Luis Carlos Cobo <luisca@cozybit.com>
 */

@@ -908,7 +908,7 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
			    struct ieee80211_mgmt *mgmt, size_t len)
{
	struct ieee802_11_elems elems;
	struct ieee802_11_elems *elems;
	size_t baselen;
	u32 path_metric;
	struct sta_info *sta;
@@ -926,37 +926,41 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
	rcu_read_unlock();

	baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;
	ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
			       len - baselen, false, &elems, mgmt->bssid, NULL);
	elems = ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
				       len - baselen, false, mgmt->bssid, NULL);
	if (!elems)
		return;

	if (elems.preq) {
		if (elems.preq_len != 37)
	if (elems->preq) {
		if (elems->preq_len != 37)
			/* Right now we support just 1 destination and no AE */
			return;
		path_metric = hwmp_route_info_get(sdata, mgmt, elems.preq,
			goto free;
		path_metric = hwmp_route_info_get(sdata, mgmt, elems->preq,
						  MPATH_PREQ);
		if (path_metric)
			hwmp_preq_frame_process(sdata, mgmt, elems.preq,
			hwmp_preq_frame_process(sdata, mgmt, elems->preq,
						path_metric);
	}
	if (elems.prep) {
		if (elems.prep_len != 31)
	if (elems->prep) {
		if (elems->prep_len != 31)
			/* Right now we support no AE */
			return;
		path_metric = hwmp_route_info_get(sdata, mgmt, elems.prep,
			goto free;
		path_metric = hwmp_route_info_get(sdata, mgmt, elems->prep,
						  MPATH_PREP);
		if (path_metric)
			hwmp_prep_frame_process(sdata, mgmt, elems.prep,
			hwmp_prep_frame_process(sdata, mgmt, elems->prep,
						path_metric);
	}
	if (elems.perr) {
		if (elems.perr_len != 15)
	if (elems->perr) {
		if (elems->perr_len != 15)
			/* Right now we support only one destination per PERR */
			return;
		hwmp_perr_frame_process(sdata, mgmt, elems.perr);
			goto free;
		hwmp_perr_frame_process(sdata, mgmt, elems->perr);
	}
	if (elems.rann)
		hwmp_rann_frame_process(sdata, mgmt, elems.rann);
	if (elems->rann)
		hwmp_rann_frame_process(sdata, mgmt, elems->rann);
free:
	kfree(elems);
}

/**
Loading