1053 lines · 27.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz> * Copyright 2007	Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014  Intel Mobile Communications GmbH * Copyright (C) 2015-2017	Intel Deutschland GmbH * Copyright (C) 2018-2024 Intel Corporation * * element parsing for mac80211 */ #include <net/mac80211.h>#include <linux/netdevice.h>#include <linux/export.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/skbuff.h>#include <linux/etherdevice.h>#include <linux/if_arp.h>#include <linux/bitmap.h>#include <linux/crc32.h>#include <net/net_namespace.h>#include <net/cfg80211.h>#include <net/rtnetlink.h>#include <kunit/visibility.h> #include "ieee80211_i.h"#include "driver-ops.h"#include "rate.h"#include "mesh.h"#include "wme.h"#include "led.h"#include "wep.h" struct ieee80211_elems_parse {	/* must be first for kfree to work */	struct ieee802_11_elems elems; 	/* The basic Multi-Link element in the original elements */	const struct element *ml_basic_elem; 	/* The reconfiguration Multi-Link element in the original elements */	const struct element *ml_reconf_elem; 	/*	 * scratch buffer that can be used for various element parsing related	 * tasks, e.g., element de-fragmentation etc.	 */	size_t scratch_len;	u8 *scratch_pos;	u8 scratch[] __counted_by(scratch_len);}; static voidieee80211_parse_extension_element(u32 *crc,				  const struct element *elem,				  struct ieee80211_elems_parse *elems_parse,				  struct ieee80211_elems_parse_params *params){	struct ieee802_11_elems *elems = &elems_parse->elems;	const void *data = elem->data + 1;	bool calc_crc = false;	u8 len; 	if (!elem->datalen)		return; 	len = elem->datalen - 1; 	switch (elem->data[0]) {	case WLAN_EID_EXT_HE_MU_EDCA:		if (params->mode < IEEE80211_CONN_MODE_HE)			break;		calc_crc = true;		if (len >= sizeof(*elems->mu_edca_param_set))			elems->mu_edca_param_set = data;		break;	case WLAN_EID_EXT_HE_CAPABILITY:		if (params->mode < IEEE80211_CONN_MODE_HE)			break;		if (ieee80211_he_capa_size_ok(data, len)) {			elems->he_cap = data;			elems->he_cap_len = len;		}		break;	case WLAN_EID_EXT_HE_OPERATION:		if (params->mode < IEEE80211_CONN_MODE_HE)			break;		calc_crc = true;		if (len >= sizeof(*elems->he_operation) &&		    len >= ieee80211_he_oper_size(data) - 1)			elems->he_operation = data;		break;	case WLAN_EID_EXT_UORA:		if (params->mode < IEEE80211_CONN_MODE_HE)			break;		if (len >= 1)			elems->uora_element = data;		break;	case WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME:		if (len == 3)			elems->max_channel_switch_time = data;		break;	case WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION:		if (len >= sizeof(*elems->mbssid_config_ie))			elems->mbssid_config_ie = data;		break;	case WLAN_EID_EXT_HE_SPR:		if (params->mode < IEEE80211_CONN_MODE_HE)			break;		if (len >= sizeof(*elems->he_spr) &&		    len >= ieee80211_he_spr_size(data) - 1)			elems->he_spr = data;		break;	case WLAN_EID_EXT_HE_6GHZ_CAPA:		if (params->mode < IEEE80211_CONN_MODE_HE)			break;		if (len >= sizeof(*elems->he_6ghz_capa))			elems->he_6ghz_capa = data;		break;	case WLAN_EID_EXT_EHT_CAPABILITY:		if (params->mode < IEEE80211_CONN_MODE_EHT)			break;		if (ieee80211_eht_capa_size_ok(elems->he_cap,					       data, len,					       params->from_ap)) {			elems->eht_cap = data;			elems->eht_cap_len = len;		}		break;	case WLAN_EID_EXT_EHT_OPERATION:		if (params->mode < IEEE80211_CONN_MODE_EHT)			break;		if (ieee80211_eht_oper_size_ok(data, len))			elems->eht_operation = data;		calc_crc = true;		break;	case WLAN_EID_EXT_EHT_MULTI_LINK:		if (params->mode < IEEE80211_CONN_MODE_EHT)			break;		calc_crc = true; 		if (ieee80211_mle_size_ok(data, len)) {			const struct ieee80211_multi_link_elem *mle =				(void *)data; 			switch (le16_get_bits(mle->control,					      IEEE80211_ML_CONTROL_TYPE)) {			case IEEE80211_ML_CONTROL_TYPE_BASIC:				if (elems_parse->ml_basic_elem) {					elems->parse_error |=						IEEE80211_PARSE_ERR_DUP_NEST_ML_BASIC;					break;				}				elems_parse->ml_basic_elem = elem;				break;			case IEEE80211_ML_CONTROL_TYPE_RECONF:				elems_parse->ml_reconf_elem = elem;				break;			default:				break;			}		}		break;	case WLAN_EID_EXT_BANDWIDTH_INDICATION:		if (params->mode < IEEE80211_CONN_MODE_EHT)			break;		if (ieee80211_bandwidth_indication_size_ok(data, len))			elems->bandwidth_indication = data;		calc_crc = true;		break;	case WLAN_EID_EXT_TID_TO_LINK_MAPPING:		if (params->mode < IEEE80211_CONN_MODE_EHT)			break;		calc_crc = true;		if (ieee80211_tid_to_link_map_size_ok(data, len) &&		    elems->ttlm_num < ARRAY_SIZE(elems->ttlm)) {			elems->ttlm[elems->ttlm_num] = (void *)data;			elems->ttlm_num++;		}		break;	} 	if (crc && calc_crc)		*crc = crc32_be(*crc, (void *)elem, elem->datalen + 2);} static void ieee80211_parse_tpe(struct ieee80211_parsed_tpe *tpe,				const u8 *data, u8 len){	const struct ieee80211_tx_pwr_env *env = (const void *)data;	u8 count, interpret, category;	u8 *out, N, *cnt_out = NULL, *N_out = NULL; 	if (!ieee80211_valid_tpe_element(data, len))		return; 	count = u8_get_bits(env->info, IEEE80211_TX_PWR_ENV_INFO_COUNT);	interpret = u8_get_bits(env->info, IEEE80211_TX_PWR_ENV_INFO_INTERPRET);	category = u8_get_bits(env->info, IEEE80211_TX_PWR_ENV_INFO_CATEGORY); 	switch (interpret) {	case IEEE80211_TPE_LOCAL_EIRP:		out = tpe->max_local[category].power;		cnt_out = &tpe->max_local[category].count;		tpe->max_local[category].valid = true;		break;	case IEEE80211_TPE_REG_CLIENT_EIRP:		out = tpe->max_reg_client[category].power;		cnt_out = &tpe->max_reg_client[category].count;		tpe->max_reg_client[category].valid = true;		break;	case IEEE80211_TPE_LOCAL_EIRP_PSD:		out = tpe->psd_local[category].power;		cnt_out = &tpe->psd_local[category].count;		N_out = &tpe->psd_local[category].n;		tpe->psd_local[category].valid = true;		break;	case IEEE80211_TPE_REG_CLIENT_EIRP_PSD:		out = tpe->psd_reg_client[category].power;		cnt_out = &tpe->psd_reg_client[category].count;		N_out = &tpe->psd_reg_client[category].n;		tpe->psd_reg_client[category].valid = true;		break;	} 	switch (interpret) {	case IEEE80211_TPE_LOCAL_EIRP:	case IEEE80211_TPE_REG_CLIENT_EIRP:		/* count was validated <= 3, plus 320 MHz */		BUILD_BUG_ON(IEEE80211_TPE_EIRP_ENTRIES_320MHZ < 5);		memcpy(out, env->variable, count + 1);		*cnt_out = count + 1;		/* separately take 320 MHz if present */		if (count == 3 && len > sizeof(*env) + count + 1) {			out[4] = env->variable[4];			*cnt_out = 5;		}		break;	case IEEE80211_TPE_LOCAL_EIRP_PSD:	case IEEE80211_TPE_REG_CLIENT_EIRP_PSD:		if (!count) {			memset(out, env->variable[0],			       IEEE80211_TPE_PSD_ENTRIES_320MHZ);			*cnt_out = IEEE80211_TPE_PSD_ENTRIES_320MHZ;			break;		} 		N = 1 << (count - 1);		memcpy(out, env->variable, N);		*cnt_out = N;		*N_out = N; 		if (len > sizeof(*env) + N) {			int K = u8_get_bits(env->variable[N],					    IEEE80211_TX_PWR_ENV_EXT_COUNT); 			K = min(K, IEEE80211_TPE_PSD_ENTRIES_320MHZ - N);			memcpy(out + N, env->variable + N + 1, K);			(*cnt_out) += K;		}		break;	}} static u32_ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,			     struct ieee80211_elems_parse *elems_parse,			     const struct element *check_inherit){	struct ieee802_11_elems *elems = &elems_parse->elems;	const struct element *elem;	bool calc_crc = params->filter != 0;	DECLARE_BITMAP(seen_elems, 256);	u32 crc = params->crc; 	bitmap_zero(seen_elems, 256); 	for_each_element(elem, params->start, params->len) {		const struct element *subelem;		u8 elem_parse_failed;		u8 id = elem->id;		u8 elen = elem->datalen;		const u8 *pos = elem->data; 		if (check_inherit &&		    !cfg80211_is_element_inherited(elem,						   check_inherit))			continue; 		switch (id) {		case WLAN_EID_SSID:		case WLAN_EID_SUPP_RATES:		case WLAN_EID_FH_PARAMS:		case WLAN_EID_DS_PARAMS:		case WLAN_EID_CF_PARAMS:		case WLAN_EID_TIM:		case WLAN_EID_IBSS_PARAMS:		case WLAN_EID_CHALLENGE:		case WLAN_EID_RSN:		case WLAN_EID_ERP_INFO:		case WLAN_EID_EXT_SUPP_RATES:		case WLAN_EID_HT_CAPABILITY:		case WLAN_EID_HT_OPERATION:		case WLAN_EID_VHT_CAPABILITY:		case WLAN_EID_VHT_OPERATION:		case WLAN_EID_MESH_ID:		case WLAN_EID_MESH_CONFIG:		case WLAN_EID_PEER_MGMT:		case WLAN_EID_PREQ:		case WLAN_EID_PREP:		case WLAN_EID_PERR:		case WLAN_EID_RANN:		case WLAN_EID_CHANNEL_SWITCH:		case WLAN_EID_EXT_CHANSWITCH_ANN:		case WLAN_EID_COUNTRY:		case WLAN_EID_PWR_CONSTRAINT:		case WLAN_EID_TIMEOUT_INTERVAL:		case WLAN_EID_SECONDARY_CHANNEL_OFFSET:		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:		case WLAN_EID_CHAN_SWITCH_PARAM:		case WLAN_EID_EXT_CAPABILITY:		case WLAN_EID_CHAN_SWITCH_TIMING:		case WLAN_EID_LINK_ID:		case WLAN_EID_BSS_MAX_IDLE_PERIOD:		case WLAN_EID_RSNX:		case WLAN_EID_S1G_BCN_COMPAT:		case WLAN_EID_S1G_CAPABILITIES:		case WLAN_EID_S1G_OPERATION:		case WLAN_EID_AID_RESPONSE:		case WLAN_EID_S1G_SHORT_BCN_INTERVAL:		/*		 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible		 * that if the content gets bigger it might be needed more than once		 */			if (test_bit(id, seen_elems)) {				elems->parse_error |=					IEEE80211_PARSE_ERR_DUP_ELEM;				continue;			}			break;		} 		if (calc_crc && id < 64 && (params->filter & (1ULL << id)))			crc = crc32_be(crc, pos - 2, elen + 2); 		elem_parse_failed = 0; 		switch (id) {		case WLAN_EID_LINK_ID:			if (elen + 2 < sizeof(struct ieee80211_tdls_lnkie)) {				elem_parse_failed =					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;				break;			}			elems->lnk_id = (void *)(pos - 2);			break;		case WLAN_EID_CHAN_SWITCH_TIMING:			if (elen < sizeof(struct ieee80211_ch_switch_timing)) {				elem_parse_failed =					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;				break;			}			elems->ch_sw_timing = (void *)pos;			break;		case WLAN_EID_EXT_CAPABILITY:			elems->ext_capab = pos;			elems->ext_capab_len = elen;			break;		case WLAN_EID_SSID:			elems->ssid = pos;			elems->ssid_len = elen;			break;		case WLAN_EID_SUPP_RATES:			elems->supp_rates = pos;			elems->supp_rates_len = elen;			break;		case WLAN_EID_DS_PARAMS:			if (elen >= 1)				elems->ds_params = pos;			else				elem_parse_failed =					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;			break;		case WLAN_EID_TIM:			if (elen >= sizeof(struct ieee80211_tim_ie)) {				elems->tim = (void *)pos;				elems->tim_len = elen;			} else				elem_parse_failed =					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;			break;		case WLAN_EID_VENDOR_SPECIFIC:			if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&			    pos[2] == 0xf2) {				/* Microsoft OUI (00:50:F2) */ 				if (calc_crc)					crc = crc32_be(crc, pos - 2, elen + 2); 				if (elen >= 5 && pos[3] == 2) {					/* OUI Type 2 - WMM IE */					if (pos[4] == 0) {						elems->wmm_info = pos;						elems->wmm_info_len = elen;					} else if (pos[4] == 1) {						elems->wmm_param = pos;						elems->wmm_param_len = elen;					}				}			}			break;		case WLAN_EID_RSN:			elems->rsn = pos;			elems->rsn_len = elen;			break;		case WLAN_EID_ERP_INFO:			if (elen >= 1)				elems->erp_info = pos;			else				elem_parse_failed =					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;			break;		case WLAN_EID_EXT_SUPP_RATES:			elems->ext_supp_rates = pos;			elems->ext_supp_rates_len = elen;			break;		case WLAN_EID_HT_CAPABILITY:			if (params->mode < IEEE80211_CONN_MODE_HT)				break;			if (elen >= sizeof(struct ieee80211_ht_cap))				elems->ht_cap_elem = (void *)pos;			else				elem_parse_failed =					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;			break;		case WLAN_EID_HT_OPERATION:			if (params->mode < IEEE80211_CONN_MODE_HT)				break;			if (elen >= sizeof(struct ieee80211_ht_operation))				elems->ht_operation = (void *)pos;			else				elem_parse_failed =					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;			break;		case WLAN_EID_VHT_CAPABILITY:			if (params->mode < IEEE80211_CONN_MODE_VHT)				break;			if (elen >= sizeof(struct ieee80211_vht_cap))				elems->vht_cap_elem = (void *)pos;			else				elem_parse_failed =					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;			break;		case WLAN_EID_VHT_OPERATION:			if (params->mode < IEEE80211_CONN_MODE_VHT)				break;			if (elen >= sizeof(struct ieee80211_vht_operation)) {				elems->vht_operation = (void *)pos;				if (calc_crc)					crc = crc32_be(crc, pos - 2, elen + 2);				break;			}			elem_parse_failed =				IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;			break;		case WLAN_EID_OPMODE_NOTIF:			if (params->mode < IEEE80211_CONN_MODE_VHT)				break;			if (elen > 0) {				elems->opmode_notif = pos;				if (calc_crc)					crc = crc32_be(crc, pos - 2, elen + 2);				break;			}			elem_parse_failed =				IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;			break;		case WLAN_EID_MESH_ID:			elems->mesh_id = pos;			elems->mesh_id_len = elen;			break;		case WLAN_EID_MESH_CONFIG:			if (elen >= sizeof(struct ieee80211_meshconf_ie))				elems->mesh_config = (void *)pos;			else				elem_parse_failed =					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;			break;		case WLAN_EID_PEER_MGMT:			elems->peering = pos;			elems->peering_len = elen;			break;		case WLAN_EID_MESH_AWAKE_WINDOW:			if (elen >= 2)				elems->awake_window = (void *)pos;			break;		case WLAN_EID_PREQ:

Lines 1–500 of 1053.