// 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 void
ieee80211_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