Commit db33aa7e authored by Johannes Berg's avatar Johannes Berg

Merge branch 'cfg80211-mac80211-multi-bssid' into mac80211-next

This finally merges the multi-BSSID code. This is the result of a
long collaboration between the team at Qualcomm (Peng and Jouni)
and our team at Intel (mostly Sara).
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parents 9f308616 851ae31d
......@@ -2801,6 +2801,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
ieee80211_hw_set(hw, TDLS_WIDER_BW);
if (rctbl)
ieee80211_hw_set(hw, SUPPORTS_RC_TABLE);
ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
......
......@@ -8,7 +8,7 @@
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH
* Copyright (c) 2016 - 2017 Intel Deutschland GmbH
* Copyright (c) 2018 Intel Corporation
* Copyright (c) 2018 - 2019 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -2475,6 +2475,7 @@ enum ieee80211_eid_ext {
WLAN_EID_EXT_HE_OPERATION = 36,
WLAN_EID_EXT_UORA = 37,
WLAN_EID_EXT_HE_MU_EDCA = 38,
WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION = 55,
};
/* Action category code */
......@@ -2656,6 +2657,11 @@ enum ieee80211_tdls_actioncode {
*/
#define WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING BIT(2)
/* Multiple BSSID capability is set in the 6th bit of 3rd byte of the
* @WLAN_EID_EXT_CAPABILITY information element
*/
#define WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT BIT(6)
/* TDLS capabilities in the the 4th byte of @WLAN_EID_EXT_CAPABILITY */
#define WLAN_EXT_CAPA4_TDLS_BUFFER_STA BIT(4)
#define WLAN_EXT_CAPA4_TDLS_PEER_PSM BIT(5)
......@@ -2691,6 +2697,9 @@ enum ieee80211_tdls_actioncode {
#define WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT BIT(5)
#define WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT BIT(6)
/* Defines support for enhanced multi-bssid advertisement*/
#define WLAN_EXT_CAPA11_EMA_SUPPORT BIT(1)
/* TDLS specific payload type in the LLC/SNAP header */
#define WLAN_TDLS_SNAP_RFTYPE 0x2
......@@ -2882,6 +2891,34 @@ enum ieee80211_sa_query_action {
WLAN_ACTION_SA_QUERY_RESPONSE = 1,
};
/**
* struct ieee80211_bssid_index
*
* This structure refers to "Multiple BSSID-index element"
*
* @bssid_index: BSSID index
* @dtim_period: optional, overrides transmitted BSS dtim period
* @dtim_count: optional, overrides transmitted BSS dtim count
*/
struct ieee80211_bssid_index {
u8 bssid_index;
u8 dtim_period;
u8 dtim_count;
};
/**
* struct ieee80211_multiple_bssid_configuration
*
* This structure refers to "Multiple BSSID Configuration element"
*
* @bssid_count: total number of active BSSIDs in the set
* @profile_periodicity: the least number of beacon frames need to be received
* in order to discover all the nontransmitted BSSIDs in the set.
*/
struct ieee80211_multiple_bssid_configuration {
u8 bssid_count;
u8 profile_periodicity;
};
#define SUITE(oui, id) (((oui) << 8) | (id))
......
......@@ -6,7 +6,7 @@
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2015-2017 Intel Deutschland GmbH
* Copyright (C) 2018 Intel Corporation
* Copyright (C) 2018-2019 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -2035,9 +2035,15 @@ struct cfg80211_bss_ies {
* a BSS that hides the SSID in its beacon, this points to the BSS struct
* that holds the beacon data. @beacon_ies is still valid, of course, and
* points to the same data as hidden_beacon_bss->beacon_ies in that case.
* @transmitted_bss: pointer to the transmitted BSS, if this is a
* non-transmitted one (multi-BSSID support)
* @nontrans_list: list of non-transmitted BSS, if this is a transmitted one
* (multi-BSSID support)
* @signal: signal strength value (type depends on the wiphy's signal_type)
* @chains: bitmask for filled values in @chain_signal.
* @chain_signal: per-chain signal strength of last received BSS in dBm.
* @bssid_index: index in the multiple BSS set
* @max_bssid_indicator: max number of members in the BSS set
* @priv: private area for driver use, has at least wiphy->bss_priv_size bytes
*/
struct cfg80211_bss {
......@@ -2049,6 +2055,8 @@ struct cfg80211_bss {
const struct cfg80211_bss_ies __rcu *proberesp_ies;
struct cfg80211_bss *hidden_beacon_bss;
struct cfg80211_bss *transmitted_bss;
struct list_head nontrans_list;
s32 signal;
......@@ -2059,6 +2067,9 @@ struct cfg80211_bss {
u8 chains;
s8 chain_signal[IEEE80211_MAX_CHAINS];
u8 bssid_index;
u8 max_bssid_indicator;
u8 priv[0] __aligned(sizeof(void *));
};
......@@ -4313,6 +4324,11 @@ struct cfg80211_pmsr_capabilities {
* @txq_memory_limit: configuration internal TX queue memory limit
* @txq_quantum: configuration of internal TX queue scheduler quantum
*
* @support_mbssid: can HW support association with nontransmitted AP
* @support_only_he_mbssid: don't parse MBSSID elements if it is not
* HE AP, in order to avoid compatibility issues.
* @support_mbssid must be set for this to have any effect.
*
* @pmsr_capa: peer measurement capabilities
*/
struct wiphy {
......@@ -4453,6 +4469,9 @@ struct wiphy {
u32 txq_memory_limit;
u32 txq_quantum;
u8 support_mbssid:1,
support_only_he_mbssid:1;
const struct cfg80211_pmsr_capabilities *pmsr_capa;
char priv[0] __aligned(NETDEV_ALIGN);
......@@ -5451,6 +5470,29 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
return cfg80211_inform_bss_frame_data(wiphy, &data, mgmt, len, gfp);
}
/**
* cfg80211_gen_new_bssid - generate a nontransmitted BSSID for multi-BSSID
* @bssid: transmitter BSSID
* @max_bssid: max BSSID indicator, taken from Multiple BSSID element
* @mbssid_index: BSSID index, taken from Multiple BSSID index element
* @new_bssid_addr: address of the resulting BSSID
*/
static inline void cfg80211_gen_new_bssid(const u8 *bssid, u8 max_bssid,
u8 mbssid_index, u8 *new_bssid_addr)
{
u64 bssid_tmp, new_bssid;
u64 lsb_n;
bssid_tmp = ether_addr_to_u64(bssid);
lsb_n = bssid_tmp & ((1 << max_bssid) - 1);
new_bssid = bssid_tmp;
new_bssid &= ~((1 << max_bssid) - 1);
new_bssid |= (lsb_n + mbssid_index) % (1 << max_bssid);
u64_to_ether_addr(new_bssid, new_bssid_addr);
}
/**
* enum cfg80211_bss_frame_type - frame type that the BSS data came from
* @CFG80211_BSS_FTYPE_UNKNOWN: driver doesn't know whether the data is
......
......@@ -591,6 +591,14 @@ struct ieee80211_ftm_responder_params {
* @ftm_responder: whether to enable or disable fine timing measurement FTM
* responder functionality.
* @ftmr_params: configurable lci/civic parameter when enabling FTM responder.
* @nontransmitted: this BSS is a nontransmitted BSS profile
* @transmitter_bssid: the address of transmitter AP
* @bssid_index: index inside the multiple BSSID set
* @bssid_indicator: 2^bssid_indicator is the maximum number of APs in set
* @ema_ap: AP supports enhancements of discovery and advertisement of
* nontransmitted BSSIDs
* @profile_periodicity: the least number of beacon frames need to be received
* in order to discover all the nontransmitted BSSIDs in the set.
*/
struct ieee80211_bss_conf {
const u8 *bssid;
......@@ -644,6 +652,13 @@ struct ieee80211_bss_conf {
bool protected_keep_alive;
bool ftm_responder;
struct ieee80211_ftm_responder_params *ftmr_params;
/* Multiple BSSID data */
bool nontransmitted;
u8 transmitter_bssid[ETH_ALEN];
u8 bssid_index;
u8 bssid_indicator;
bool ema_ap;
u8 profile_periodicity;
};
/**
......@@ -2219,6 +2234,11 @@ struct ieee80211_txq {
* @IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN: Driver does not report accurate A-MPDU
* length in tx status information
*
* @IEEE80211_HW_SUPPORTS_MULTI_BSSID: Hardware supports multi BSSID
*
* @IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID: Hardware supports multi BSSID
* only for HE APs. Applies if @IEEE80211_HW_SUPPORTS_MULTI_BSSID is set.
*
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/
enum ieee80211_hw_flags {
......@@ -2268,6 +2288,8 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_VHT_EXT_NSS_BW,
IEEE80211_HW_STA_MMPDU_TXQ,
IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
IEEE80211_HW_SUPPORTS_MULTI_BSSID,
IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
/* keep last, obviously */
NUM_IEEE80211_HW_FLAGS
......
......@@ -3,7 +3,7 @@
*
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2018 Intel Corporation
* Copyright (C) 2018 - 2019 Intel Corporation
*
* GPLv2
*
......@@ -219,6 +219,8 @@ static const char *hw_flag_names[] = {
FLAG(SUPPORTS_VHT_EXT_NSS_BW),
FLAG(STA_MMPDU_TXQ),
FLAG(TX_STATUS_NO_AMPDU_LEN),
FLAG(SUPPORTS_MULTI_BSSID),
FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
#undef FLAG
};
......
......@@ -8,6 +8,7 @@
* Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright(c) 2016 Intel Deutschland GmbH
* Copyright(c) 2018-2019 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -1124,8 +1125,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
ieee80211_update_sta_info(sdata, mgmt, len, rx_status, elems, channel);
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
channel);
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, channel);
if (!bss)
return;
......@@ -1604,7 +1604,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
return;
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
false, &elems);
false, &elems, mgmt->bssid, NULL);
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
}
......@@ -1654,7 +1654,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
ieee802_11_parse_elems(
mgmt->u.action.u.chan_switch.variable,
ies_len, true, &elems);
ies_len, true, &elems, mgmt->bssid, NULL);
if (elems.parse_error)
break;
......
......@@ -4,7 +4,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2018 Intel Corporation
* Copyright (C) 2018-2019 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -1495,6 +1495,12 @@ struct ieee802_11_elems {
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie;
const struct ieee80211_bss_max_idle_period_ie *max_idle_period_ie;
const struct ieee80211_multiple_bssid_configuration *mbssid_config_ie;
const struct ieee80211_bssid_index *bssid_index;
const u8 *nontransmitted_bssid_profile;
u8 max_bssid_indicator;
u8 dtim_count;
u8 dtim_period;
/* length of them, respectively */
u8 ext_capab_len;
......@@ -1513,6 +1519,7 @@ struct ieee802_11_elems {
u8 prep_len;
u8 perr_len;
u8 country_elem_len;
u8 bssid_index_len;
/* whether a parse error occurred while retrieving these elements */
bool parse_error;
......@@ -1672,7 +1679,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
struct ieee80211_rx_status *rx_status,
struct ieee80211_mgmt *mgmt,
size_t len,
struct ieee802_11_elems *elems,
struct ieee80211_channel *channel);
void ieee80211_rx_bss_put(struct ieee80211_local *local,
struct ieee80211_bss *bss);
......@@ -1956,12 +1962,16 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
struct ieee802_11_elems *elems,
u64 filter, u32 crc);
u64 filter, u32 crc, u8 *transmitter_bssid,
u8 *bss_bssid);
static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
bool action,
struct ieee802_11_elems *elems)
struct ieee802_11_elems *elems,
u8 *transmitter_bssid,
u8 *bss_bssid)
{
ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0);
ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0,
transmitter_bssid, bss_bssid);
}
......
......@@ -4,7 +4,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2017 Intel Deutschland GmbH
* Copyright (C) 2018 Intel Corporation
* Copyright (C) 2018 - 2019 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -1112,6 +1112,17 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (ieee80211_hw_check(&local->hw, CHANCTX_STA_CSA))
local->ext_capa[0] |= WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING;
/* mac80211 supports multi BSSID, if the driver supports it */
if (ieee80211_hw_check(&local->hw, SUPPORTS_MULTI_BSSID)) {
local->hw.wiphy->support_mbssid = true;
if (ieee80211_hw_check(&local->hw,
SUPPORTS_ONLY_HE_MULTI_BSSID))
local->hw.wiphy->support_only_he_mbssid = true;
else
local->ext_capa[2] |=
WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT;
}
local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM;
result = wiphy_register(local->hw.wiphy);
......
/*
* Copyright (c) 2008, 2009 open80211s Ltd.
* Copyright (C) 2018 Intel Corporation
* Copyright (C) 2018 - 2019 Intel Corporation
* Authors: Luis Carlos Cobo <luisca@cozybit.com>
* Javier Cardona <javier@cozybit.com>
*
......@@ -1106,7 +1106,8 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
if (baselen > len)
return;
ieee802_11_parse_elems(pos, len - baselen, false, &elems);
ieee802_11_parse_elems(pos, len - baselen, false, &elems, mgmt->bssid,
NULL);
if (!elems.mesh_id)
return;
......@@ -1170,7 +1171,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
return;
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
false, &elems);
false, &elems, mgmt->bssid, NULL);
/* ignore non-mesh or secure / unsecure mismatch */
if ((!elems.mesh_id || !elems.mesh_config) ||
......@@ -1306,7 +1307,8 @@ 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);
ieee802_11_parse_elems(pos, len - baselen, true, &elems,
mgmt->bssid, NULL);
ifmsh->chsw_ttl = elems.mesh_chansw_params_ie->mesh_ttl;
if (!--ifmsh->chsw_ttl)
......
/*
* Copyright (c) 2008, 2009 open80211s Ltd.
* Copyright (C) 2019 Intel Corporation
* Author: Luis Carlos Cobo <luisca@cozybit.com>
*
* This program is free software; you can redistribute it and/or modify
......@@ -926,7 +927,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
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);
len - baselen, false, &elems, mgmt->bssid, NULL);
if (elems.preq) {
if (elems.preq_len != 37)
......
/*
* Copyright (c) 2008, 2009 open80211s Ltd.
* Copyright (C) 2019 Intel Corporation
* Author: Luis Carlos Cobo <luisca@cozybit.com>
*
* This program is free software; you can redistribute it and/or modify
......@@ -1214,6 +1215,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
if (baselen > len)
return;
}
ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems);
ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems,
mgmt->bssid, NULL);
mesh_process_plink_frame(sdata, mgmt, &elems, rx_status);
}
......@@ -813,6 +813,21 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
}
}
/* Set MBSSID support for HE AP if needed */
if (ieee80211_hw_check(&local->hw, SUPPORTS_ONLY_HE_MULTI_BSSID) &&
!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) && assoc_data->ie_len) {
struct element *elem;
/* we know it's writable, cast away the const */
elem = (void *)cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY,
assoc_data->ie,
assoc_data->ie_len);
/* We can probably assume both always true */
if (elem && elem->datalen >= 3)
elem->data[2] |= WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT;
}
/* if present, add any custom IEs that go before HT */
if (assoc_data->ie_len) {
static const u8 before_ht[] = {
......@@ -2762,7 +2777,8 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
u32 tx_flags = 0;
pos = mgmt->u.auth.variable;
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, &elems,
mgmt->bssid, auth_data->bss->bssid);
if (!elems.challenge)
return;
auth_data->expected_transaction = 4;
......@@ -3130,7 +3146,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
}
pos = mgmt->u.assoc_resp.variable;
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, &elems,
mgmt->bssid, assoc_data->bss->bssid);
if (!elems.supp_rates) {
sdata_info(sdata, "no SuppRates element in AssocResp\n");
......@@ -3167,7 +3184,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
return false;
ieee802_11_parse_elems(bss_ies->data, bss_ies->len,
false, &bss_elems);
false, &bss_elems,
mgmt->bssid,
assoc_data->bss->bssid);
if (assoc_data->wmm &&
!elems.wmm_param && bss_elems.wmm_param) {
elems.wmm_param = bss_elems.wmm_param;
......@@ -3304,6 +3323,14 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
/* TODO: OPEN: what happens if BSS color disable is set? */
}
if (cbss->transmitted_bss) {
bss_conf->nontransmitted = true;
ether_addr_copy(bss_conf->transmitter_bssid,
cbss->transmitted_bss->bssid);
bss_conf->bssid_indicator = cbss->max_bssid_indicator;
bss_conf->bssid_index = cbss->bssid_index;
}
/*
* Some APs, e.g. Netgear WNDR3700, report invalid HT operation data
* in their association response, so ignore that data for our own
......@@ -3464,7 +3491,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
return;
pos = mgmt->u.assoc_resp.variable;
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, &elems,
mgmt->bssid, assoc_data->bss->bssid);
if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
elems.timeout_int &&
......@@ -3521,8 +3549,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_rx_status *rx_status,
struct ieee802_11_elems *elems)
struct ieee80211_rx_status *rx_status)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_bss *bss;
......@@ -3534,8 +3561,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (!channel)
return;
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
channel);
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, channel);
if (bss) {
sdata->vif.bss_conf.beacon_rate = bss->beacon_rate;
ieee80211_rx_bss_put(local, bss);
......@@ -3550,7 +3576,6 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd;
struct ieee80211_rx_status *rx_status = (void *) skb->cb;
size_t baselen, len = skb->len;
struct ieee802_11_elems elems;
ifmgd = &sdata->u.mgd;
......@@ -3563,10 +3588,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
if (baselen > len)
return;
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
false, &elems);
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status);
if (ifmgd->associated &&
ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
......@@ -3693,6 +3715,16 @@ static void ieee80211_handle_beacon_sig(struct ieee80211_sub_if_data *sdata,
}
}
static bool ieee80211_rx_our_beacon(const u8 *tx_bssid,
struct cfg80211_bss *bss)
{
if (ether_addr_equal(tx_bssid, bss->bssid))
return true;
if (!bss->transmitted_bss)
return false;
return ether_addr_equal(tx_bssid, bss->transmitted_bss->bssid);
}
static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_rx_status *rx_status)
......@@ -3734,15 +3766,16 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {
ieee80211_rx_our_beacon(mgmt->bssid, ifmgd->assoc_data->bss)) {
ieee802_11_parse_elems(mgmt->u.beacon.variable,
len - baselen, false, &elems);
len - baselen, false, &elems,
mgmt->bssid,
ifmgd->assoc_data->bss->bssid);
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
if (elems.tim && !elems.parse_error) {
const struct ieee80211_tim_ie *tim_ie = elems.tim;
ifmgd->dtim_period = tim_ie->dtim_period;
}
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status);
if (elems.dtim_period)
ifmgd->dtim_period = elems.dtim_period;
ifmgd->have_beacon = true;
ifmgd->assoc_data->need_beacon = false;
if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
......@@ -3750,12 +3783,17 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
le64_to_cpu(mgmt->u.beacon.timestamp);
sdata->vif.bss_conf.sync_device_ts =
rx_status->device_timestamp;
if (elems.tim)
sdata->vif.bss_conf.sync_dtim_count =
elems.tim->dtim_count;
else
sdata->vif.bss_conf.sync_dtim_count = 0;
sdata->vif.bss_conf.sync_dtim_count = elems.dtim_count;
}
if (elems.mbssid_config_ie)
bss_conf->profile_periodicity =
elems.mbssid_config_ie->profile_periodicity;
if (elems.ext_capab_len >= 11 &&
(elems.ext_capab[10] & WLAN_EXT_CAPA11_EMA_SUPPORT))
bss_conf->ema_ap = true;
/* continue assoc process */
ifmgd->assoc_data->timeout = jiffies;
ifmgd->assoc_data->timeout_started = true;
......@@ -3764,7 +3802,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
if (!ifmgd->associated ||
!ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
!ieee80211_rx_our_beacon(mgmt->bssid, ifmgd->associated))
return;
bssid = ifmgd->associated->bssid;
......@@ -3787,7 +3825,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
len - baselen, false, &elems,
care_about_ies, ncrc);
care_about_ies, ncrc,
mgmt->bssid, bssid);
if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
ieee80211_check_tim(elems.tim, elems.tim_len, ifmgd->aid)) {
......@@ -3859,11 +3898,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
le64_to_cpu(mgmt->u.beacon.timestamp);
sdata->vif.bss_conf.sync_device_ts =
rx_status->device_timestamp;
if (elems.tim)
sdata->vif.bss_conf.sync_dtim_count =
elems.tim->dtim_count;
else
sdata->vif.bss_conf.sync_dtim_count = 0;
sdata->vif.bss_conf.sync_dtim_count = elems.dtim_count;
}
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
......@@ -3871,7 +3906,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ifmgd->beacon_crc = ncrc;
ifmgd->beacon_crc_valid = true;
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status);
ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
rx_status->device_timestamp,
......@@ -3889,10 +3924,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
*/
if (!ifmgd->have_beacon) {
/* a few bogus AP send dtim_period = 0 or no TIM IE */
if (elems.tim)
bss_conf->dtim_period = elems.tim->dtim_period ?: 1;
else
bss_conf->dtim_period = 1;
bss_conf->dtim_period = elems.dtim_period ?: 1;
changed |= BSS_CHANGED_BEACON_INFO;
ifmgd->have_beacon = true;
......@@ -3992,9 +4024,10 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
if (ies_len < 0)
break;
/* CSA IE cannot be overridden, no need for BSSID */
ieee802_11_parse_elems(
mgmt->u.action.u.chan_switch.variable,
ies_len, true, &elems);
ies_len, true, &elems, mgmt->bssid, NULL);
if (elems.parse_error)
break;
......@@ -4011,9 +4044,13 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
if (ies_len < 0)
break;
/*
* extended CSA IE can't be overridden, no need for
* BSSID
*/
ieee802_11_parse_elems(
mgmt->u.action.u.ext_chan_switch.variable,
ies_len, true, &elems);
ies_len, true, &elems, mgmt->bssid, NULL);
if (elems.parse_error)
break;
......@@ -4754,6 +4791,40 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
return ret;
}
static bool ieee80211_get_dtim(const struct cfg80211_bss_ies *ies,
u8 *dtim_count, u8 *dtim_period)
{
const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies->data, ies->len);
const u8 *idx_ie = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, ies->data,
ies->len);
const struct ieee80211_tim_ie *tim = NULL;
const struct ieee80211_bssid_index *idx;
bool valid = tim_ie && tim_ie[1] >= 2;
if (valid)
tim = (void *)(tim_ie + 2);
if (dtim_count)
*dtim_count = valid ? tim->dtim_count : 0;
if (dtim_period)
*dtim_period = valid ? tim->dtim_period : 0;
/* Check if value is overridden by non-transmitted profile */
if (!idx_ie || idx_ie[1] < 3)
return valid;
idx = (void *)(idx_ie + 2);
if (dtim_count)
*dtim_count = idx->dtim_count;
if (dtim_period)
*dtim_period = idx->dtim_period;
return true;
}
static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *cbss, bool assoc,
bool override)
......@@ -4845,17 +4916,13 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
rcu_read_lock();
ies = rcu_dereference(cbss->beacon_ies);
if (ies) {
const u8 *tim_ie;
sdata->vif.bss_conf.sync_tsf = ies->tsf;
sdata->vif.bss_conf.sync_device_ts =
bss->device_ts_beacon;
tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
ies->data, ies->len);
if (tim_ie && tim_ie[1] >= 2)
sdata->vif.bss_conf.sync_dtim_count = tim_ie[2];
else
sdata->vif.bss_conf.sync_dtim_count = 0;
ieee80211_get_dtim(ies,
&sdata->vif.bss_conf.sync_dtim_count,
NULL);
} else if (!ieee80211_hw_check(&sdata->local->hw,
TIMING_BEACON_ONLY)) {
ies = rcu_dereference(cbss->proberesp_ies);
......@@ -5325,17 +5392,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
assoc_data->timeout_started = true;
assoc_data->need_beacon = true;
} else if (beacon_ies) {
const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
beacon_ies->data,
beacon_ies->len);
const u8 *ie;
u8 dtim_count = 0;
if (tim_ie && tim_ie[1] >= sizeof(struct ieee80211_tim_ie)) {
const struct ieee80211_tim_ie *tim;
tim = (void *)(tim_ie + 2);
ifmgd->dtim_period = tim->dtim_period;
dtim_count = tim->dtim_count;
}
ieee80211_get_dtim(beacon_ies, &dtim_count,
&ifmgd->dtim_period);
ifmgd->have_beacon = true;
assoc_data->timeout = jiffies;
assoc_data->timeout_started = true;
......@@ -5346,6 +5408,17 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
bss->device_ts_beacon;
sdata->vif.bss_conf.sync_dtim_count = dtim_count;
}
ie = cfg80211_find_ext_ie(WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION,
beacon_ies->data, beacon_ies->len);
if (ie && ie[1] >= 3)
sdata->vif.bss_conf.profile_periodicity = ie[4];
ie = cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY,
beacon_ies->data, beacon_ies->len);
if (ie && ie[1] >= 11 &&
(ie[10] & WLAN_EXT_CAPA11_EMA_SUPPORT))
sdata->vif.bss_conf.ema_ap = true;
} else {
assoc_data->timeout = jiffies;
assoc_data->timeout_started = true;
......
......@@ -8,6 +8,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2013-2015 Intel Mobile Communications GmbH
* Copyright 2016-2017 Intel Deutschland GmbH
* Copyright (C) 2018-2019 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -57,62 +58,14 @@ static bool is_uapsd_supported(struct ieee802_11_elems *elems)
return qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD;
}
struct ieee80211_bss *
ieee80211_bss_info_update(struct ieee80211_local *local,
struct ieee80211_rx_status *rx_status,
struct ieee80211_mgmt *mgmt, size_t len,
struct ieee802_11_elems *elems,
struct ieee80211_channel *channel)
static void
ieee80211_update_bss_from_elems(struct ieee80211_local *local,
struct ieee80211_bss *bss,
struct ieee802_11_elems *elems,
struct ieee80211_rx_status *rx_status,
bool beacon)
{
bool beacon = ieee80211_is_beacon(mgmt->frame_control);
struct cfg80211_bss *cbss;
struct ieee80211_bss *bss;
int clen, srlen;
struct cfg80211_inform_bss bss_meta = {
.boottime_ns = rx_status->boottime_ns,
};
bool signal_valid;
struct ieee80211_sub_if_data *scan_sdata;
if (rx_status->flag & RX_FLAG_NO_SIGNAL_VAL)
bss_meta.signal = 0; /* invalid signal indication */
else if (ieee80211_hw_check(&local->hw, SIGNAL_DBM))
bss_meta.signal = rx_status->signal * 100;
else if (ieee80211_hw_check(&local->hw, SIGNAL_UNSPEC))
bss_meta.signal = (rx_status->signal * 100) / local->hw.max_signal;
bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_20;
if (rx_status->bw == RATE_INFO_BW_5)
bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_5;
else if (rx_status->bw == RATE_INFO_BW_10)
bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_10;
bss_meta.chan = channel;
rcu_read_lock();
scan_sdata = rcu_dereference(local->scan_sdata);
if (scan_sdata && scan_sdata->vif.type == NL80211_IFTYPE_STATION &&
scan_sdata->vif.bss_conf.assoc &&
ieee80211_have_rx_timestamp(rx_status)) {
bss_meta.parent_tsf =
ieee80211_calculate_rx_timestamp(local, rx_status,
len + FCS_LEN, 24);
ether_addr_copy(bss_meta.parent_bssid,
scan_sdata->vif.bss_conf.bssid);
}
rcu_read_unlock();
cbss = cfg80211_inform_bss_frame_data(local->hw.wiphy, &bss_meta,
mgmt, len, GFP_ATOMIC);
if (!cbss)
return NULL;
/* In case the signal is invalid update the status */
signal_valid = abs(channel->center_freq - cbss->channel->center_freq)
<= local->hw.wiphy->max_adj_channel_rssi_comp;
if (!signal_valid)
rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
bss = (void *)cbss->priv;
if (beacon)
bss->device_ts_beacon = rx_status->device_timestamp;
......@@ -182,6 +135,89 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
bss->beacon_rate =
&sband->bitrates[rx_status->rate_idx];
}
}
struct ieee80211_bss *
ieee80211_bss_info_update(struct ieee80211_local *local,
struct ieee80211_rx_status *rx_status,
struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_channel *channel)
{
bool beacon = ieee80211_is_beacon(mgmt->frame_control);
struct cfg80211_bss *cbss, *non_tx_cbss;
struct ieee80211_bss *bss, *non_tx_bss;
struct cfg80211_inform_bss bss_meta = {
.boottime_ns = rx_status->boottime_ns,
};
bool signal_valid;
struct ieee80211_sub_if_data *scan_sdata;
struct ieee802_11_elems elems;
size_t baselen;
u8 *elements;
if (rx_status->flag & RX_FLAG_NO_SIGNAL_VAL)
bss_meta.signal = 0; /* invalid signal indication */
else if (ieee80211_hw_check(&local->hw, SIGNAL_DBM))
bss_meta.signal = rx_status->signal * 100;
else if (ieee80211_hw_check(&local->hw, SIGNAL_UNSPEC))
bss_meta.signal = (rx_status->signal * 100) / local->hw.max_signal;
bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_20;
if (rx_status->bw == RATE_INFO_BW_5)
bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_5;
else if (rx_status->bw == RATE_INFO_BW_10)
bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_10;
bss_meta.chan = channel;
rcu_read_lock();
scan_sdata = rcu_dereference(local->scan_sdata);
if (scan_sdata && scan_sdata->vif.type == NL80211_IFTYPE_STATION &&
scan_sdata->vif.bss_conf.assoc &&
ieee80211_have_rx_timestamp(rx_status)) {
bss_meta.parent_tsf =
ieee80211_calculate_rx_timestamp(local, rx_status,
len + FCS_LEN, 24);
ether_addr_copy(bss_meta.parent_bssid,
scan_sdata->vif.bss_conf.bssid);
}
rcu_read_unlock();
cbss = cfg80211_inform_bss_frame_data(local->hw.wiphy, &bss_meta,
mgmt, len, GFP_ATOMIC);
if (!cbss)
return NULL;
if (ieee80211_is_probe_resp(mgmt->frame_control)) {
elements = mgmt->u.probe_resp.variable;
baselen = offsetof(struct ieee80211_mgmt,
u.probe_resp.variable);
} else {
baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
elements = mgmt->u.beacon.variable;
}
if (baselen > len)
return NULL;
ieee802_11_parse_elems(elements, len - baselen, false, &elems,
mgmt->bssid, cbss->bssid);
/* In case the signal is invalid update the status */
signal_valid = abs(channel->center_freq - cbss->channel->center_freq)
<= local->hw.wiphy->max_adj_channel_rssi_comp;
if (!signal_valid)
rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
bss = (void *)cbss->priv;
ieee80211_update_bss_from_elems(local, bss, &elems, rx_status, beacon);
list_for_each_entry(non_tx_cbss, &cbss->nontrans_list, nontrans_list) {
non_tx_bss = (void *)non_tx_cbss->priv;
ieee80211_update_bss_from_elems(local, non_tx_bss, &elems,
rx_status, beacon);
}
return bss;
}
......@@ -206,10 +242,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
struct ieee80211_sub_if_data *sdata1, *sdata2;
struct ieee80211_mgmt *mgmt = (void *)skb->data;
struct ieee80211_bss *bss;
u8 *elements;
struct ieee80211_channel *channel;
size_t baselen;
struct ieee802_11_elems elems;
if (skb->len < 24 ||
(!ieee80211_is_probe_resp(mgmt->frame_control) &&
......@@ -244,26 +277,15 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
!ieee80211_scan_accept_presp(sdata2, sched_scan_req_flags,
mgmt->da))
return;
elements = mgmt->u.probe_resp.variable;
baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
} else {
baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
elements = mgmt->u.beacon.variable;
}
if (baselen > skb->len)
return;
ieee802_11_parse_elems(elements, skb->len - baselen, false, &elems);
channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
return;
bss = ieee80211_bss_info_update(local, rx_status,
mgmt, skb->len, &elems,
mgmt, skb->len,
channel);
if (bss)
ieee80211_rx_bss_put(local, bss);
......
......@@ -5,6 +5,7 @@
* Copyright 2014, Intel Corporation
* Copyright 2014 Intel Mobile Communications GmbH
* Copyright 2015 - 2016 Intel Deutschland GmbH
* Copyright (C) 2019 Intel Corporation
*
* This file is GPLv2 as found in COPYING.
*/
......@@ -1716,7 +1717,8 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata,
}
ieee802_11_parse_elems(tf->u.chan_switch_resp.variable,
skb->len - baselen, false, &elems);
skb->len - baselen, false, &elems,
NULL, NULL);
if (elems.parse_error) {
tdls_dbg(sdata, "Invalid IEs in TDLS channel switch resp\n");
ret = -EINVAL;
......@@ -1828,7 +1830,7 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
}
ieee802_11_parse_elems(tf->u.chan_switch_req.variable,
skb->len - baselen, false, &elems);
skb->len - baselen, false, &elems, NULL, NULL);
if (elems.parse_error) {
tdls_dbg(sdata, "Invalid IEs in TDLS channel switch req\n");
return -EINVAL;
......
......@@ -891,19 +891,18 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_queue_delayed_work);
u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
struct ieee802_11_elems *elems,
u64 filter, u32 crc)
static u32
_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)
{
struct element *elem;
const struct element *elem, *sub;
bool calc_crc = filter != 0;
DECLARE_BITMAP(seen_elems, 256);
const u8 *ie;
bitmap_zero(seen_elems, 256);
memset(elems, 0, sizeof(*elems));
elems->ie_start = start;
elems->total_len = len;
for_each_element(elem, start, len) {
bool elem_parse_failed;
......@@ -1209,6 +1208,57 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
if (elen >= sizeof(*elems->max_idle_period_ie))
elems->max_idle_period_ie = (void *)pos;
break;
case WLAN_EID_MULTIPLE_BSSID:
if (!bss_bssid || !transmitter_bssid || elen < 4)
break;
elems->max_bssid_indicator = pos[0];
for_each_element(sub, pos + 1, elen - 1) {
u8 sub_len = sub->datalen;
u8 new_bssid[ETH_ALEN];
const u8 *index;
/*
* we only expect the "non-transmitted BSSID
* profile" subelement (subelement id 0)
*/
if (sub->id != 0 || sub->datalen < 4) {
/* not a valid BSS profile */
continue;
}
if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP ||
sub->data[1] != 2) {
/* The first element of the
* Nontransmitted BSSID Profile is not
* the Nontransmitted BSSID Capability
* element.
*/
continue;
}
/* found a Nontransmitted BSSID Profile */
index = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX,
sub->data, sub_len);
if (!index || index[1] < 1 || index[2] == 0) {
/* Invalid MBSSID Index element */
continue;
}
cfg80211_gen_new_bssid(transmitter_bssid,
pos[0],
index[2],
new_bssid);
if (ether_addr_equal(new_bssid, bss_bssid)) {
elems->nontransmitted_bssid_profile =
(void *)sub;
elems->bssid_index_len = index[1];
elems->bssid_index = (void *)&index[2];
break;
}
}
break;
case WLAN_EID_EXTENSION:
if (pos[0] == WLAN_EID_EXT_HE_MU_EDCA &&
elen >= (sizeof(*elems->mu_edca_param_set) + 1)) {
......@@ -1224,6 +1274,10 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
elems->he_operation = (void *)&pos[1];
} else if (pos[0] == WLAN_EID_EXT_UORA && elen >= 1) {
elems->uora_element = (void *)&pos[1];
} else if (pos[0] ==
WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION &&
elen == 3) {
elems->mbssid_config_ie = (void *)&pos[1];
}
break;
default:
......@@ -1242,6 +1296,48 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
return crc;
}
u32 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)
{
memset(elems, 0, sizeof(*elems));
elems->ie_start = start;
elems->total_len = len;
crc = _ieee802_11_parse_elems_crc(start, len, action, elems, filter,
crc, transmitter_bssid, bss_bssid);
/* Override with nontransmitted profile, if found */
if (transmitter_bssid && elems->nontransmitted_bssid_profile) {
const u8 *profile = elems->nontransmitted_bssid_profile;
_ieee802_11_parse_elems_crc(&profile[2], profile[1],
action, elems, 0, 0,
transmitter_bssid, bss_bssid);
}
if (elems->tim && !elems->parse_error) {
const struct ieee80211_tim_ie *tim_ie = elems->tim;
elems->dtim_period = tim_ie->dtim_period;
elems->dtim_count = tim_ie->dtim_count;
}
/* Override DTIM period and count if needed */
if (elems->bssid_index &&
elems->bssid_index_len >=
offsetofend(struct ieee80211_bssid_index, dtim_period))
elems->dtim_period = elems->bssid_index->dtim_period;
if (elems->bssid_index &&
elems->bssid_index_len >=
offsetofend(struct ieee80211_bssid_index, dtim_count))
elems->dtim_count = elems->bssid_index->dtim_count;
return crc;
}
void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
struct ieee80211_tx_queue_params
*qparam, int ac)
......
......@@ -3,7 +3,7 @@
* Wireless configuration interface internals.
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright (C) 2018 Intel Corporation
* Copyright (C) 2018-2019 Intel Corporation
*/
#ifndef __NET_WIRELESS_CORE_H
#define __NET_WIRELESS_CORE_H
......@@ -182,12 +182,23 @@ static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pu
static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss)
{
atomic_inc(&bss->hold);
if (bss->pub.transmitted_bss) {
bss = container_of(bss->pub.transmitted_bss,
struct cfg80211_internal_bss, pub);
atomic_inc(&bss->hold);
}
}
static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss)
{
int r = atomic_dec_return(&bss->hold);
WARN_ON(r < 0);
if (bss->pub.transmitted_bss) {
bss = container_of(bss->pub.transmitted_bss,
struct cfg80211_internal_bss, pub);
r = atomic_dec_return(&bss->hold);
WARN_ON(r < 0);
}
}
......
......@@ -5,6 +5,7 @@
* Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2016 Intel Deutschland GmbH
* Copyright (C) 2018-2019 Intel Corporation
*/
#include <linux/kernel.h>
#include <linux/slab.h>
......@@ -109,6 +110,12 @@ static inline void bss_ref_get(struct cfg80211_registered_device *rdev,
pub);
bss->refcount++;
}
if (bss->pub.transmitted_bss) {
bss = container_of(bss->pub.transmitted_bss,
struct cfg80211_internal_bss,
pub);
bss->refcount++;
}
}
static inline void bss_ref_put(struct cfg80211_registered_device *rdev,
......@@ -125,6 +132,18 @@ static inline void bss_ref_put(struct cfg80211_registered_device *rdev,
if (hbss->refcount == 0)
bss_free(hbss);
}
if (bss->pub.transmitted_bss) {
struct cfg80211_internal_bss *tbss;
tbss = container_of(bss->pub.transmitted_bss,
struct cfg80211_internal_bss,
pub);
tbss->refcount--;
if (tbss->refcount == 0)
bss_free(tbss);
}
bss->refcount--;
if (bss->refcount == 0)
bss_free(bss);
......@@ -150,6 +169,7 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,
}
list_del_init(&bss->list);
list_del_init(&bss->pub.nontrans_list);
rb_erase(&bss->rbn, &rdev->bss_tree);
rdev->bss_entries--;
WARN_ONCE((rdev->bss_entries == 0) ^ list_empty(&rdev->bss_list),
......@@ -159,6 +179,162 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,
return true;
}
static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen,
const u8 *subelement, size_t subie_len,
u8 *new_ie, gfp_t gfp)
{
u8 *pos, *tmp;
const u8 *tmp_old, *tmp_new;
u8 *sub_copy;
/* copy subelement as we need to change its content to
* mark an ie after it is processed.
*/
sub_copy = kmalloc(subie_len, gfp);
if (!sub_copy)
return 0;
memcpy(sub_copy, subelement, subie_len);
pos = &new_ie[0];
/* set new ssid */
tmp_new = cfg80211_find_ie(WLAN_EID_SSID, sub_copy, subie_len);
if (tmp_new) {
memcpy(pos, tmp_new, tmp_new[1] + 2);
pos += (tmp_new[1] + 2);
}
/* go through IEs in ie (skip SSID) and subelement,
* merge them into new_ie
*/
tmp_old = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
tmp_old = (tmp_old) ? tmp_old + tmp_old[1] + 2 : ie;
while (tmp_old + tmp_old[1] + 2 - ie <= ielen) {
if (tmp_old[0] == 0) {
tmp_old++;
continue;
}
if (tmp_old[0] == WLAN_EID_EXTENSION)
tmp = (u8 *)cfg80211_find_ext_ie(tmp_old[2], sub_copy,
subie_len);
else
tmp = (u8 *)cfg80211_find_ie(tmp_old[0], sub_copy,
subie_len);
if (!tmp) {
/* ie in old ie but not in subelement */
if (tmp_old[0] != WLAN_EID_MULTIPLE_BSSID) {
memcpy(pos, tmp_old, tmp_old[1] + 2);
pos += tmp_old[1] + 2;
}
} else {
/* ie in transmitting ie also in subelement,
* copy from subelement and flag the ie in subelement
* as copied (by setting eid field to WLAN_EID_SSID,
* which is skipped anyway).
* For vendor ie, compare OUI + type + subType to
* determine if they are the same ie.
*/
if (tmp_old[0] == WLAN_EID_VENDOR_SPECIFIC) {
if (!memcmp(tmp_old + 2, tmp + 2, 5)) {
/* same vendor ie, copy from
* subelement
*/
memcpy(pos, tmp, tmp[1] + 2);
pos += tmp[1] + 2;
tmp[0] = WLAN_EID_SSID;
} else {
memcpy(pos, tmp_old, tmp_old[1] + 2);
pos += tmp_old[1] + 2;
}
} else {
/* copy ie from subelement into new ie */
memcpy(pos, tmp, tmp[1] + 2);
pos += tmp[1] + 2;
tmp[0] = WLAN_EID_SSID;
}
}
if (tmp_old + tmp_old[1] + 2 - ie == ielen)
break;
tmp_old += tmp_old[1] + 2;
}
/* go through subelement again to check if there is any ie not
* copied to new ie, skip ssid, capability, bssid-index ie
*/
tmp_new = sub_copy;
while (tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) {
if (!(tmp_new[0] == WLAN_EID_NON_TX_BSSID_CAP ||
tmp_new[0] == WLAN_EID_SSID ||
tmp_new[0] == WLAN_EID_MULTI_BSSID_IDX)) {
memcpy(pos, tmp_new, tmp_new[1] + 2);
pos += tmp_new[1] + 2;
}
if (tmp_new + tmp_new[1] + 2 - sub_copy == subie_len)
break;
tmp_new += tmp_new[1] + 2;
}
kfree(sub_copy);
return pos - new_ie;
}
static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
const u8 *ssid, size_t ssid_len)
{
const struct cfg80211_bss_ies *ies;
const u8 *ssidie;
if (bssid && !ether_addr_equal(a->bssid, bssid))
return false;
if (!ssid)
return true;
ies = rcu_access_pointer(a->ies);
if (!ies)
return false;
ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
if (!ssidie)
return false;
if (ssidie[1] != ssid_len)
return false;
return memcmp(ssidie + 2, ssid, ssid_len) == 0;
}
static int
cfg80211_add_nontrans_list(struct cfg80211_bss *trans_bss,
struct cfg80211_bss *nontrans_bss)
{
const u8 *ssid;
size_t ssid_len;
struct cfg80211_bss *bss = NULL;
rcu_read_lock();
ssid = ieee80211_bss_get_ie(nontrans_bss, WLAN_EID_SSID);
if (!ssid) {
rcu_read_unlock();
return -EINVAL;
}
ssid_len = ssid[1];
ssid = ssid + 2;
rcu_read_unlock();
/* check if nontrans_bss is in the list */
list_for_each_entry(bss, &trans_bss->nontrans_list, nontrans_list) {
if (is_bss(bss, nontrans_bss->bssid, ssid, ssid_len))
return 0;
}
/* add to the list */
list_add_tail(&nontrans_bss->nontrans_list, &trans_bss->nontrans_list);
return 0;
}
static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev,
unsigned long expire_time)
{
......@@ -518,29 +694,6 @@ const struct element *cfg80211_find_vendor_elem(unsigned int oui, int oui_type,
}
EXPORT_SYMBOL(cfg80211_find_vendor_elem);
static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
const u8 *ssid, size_t ssid_len)
{
const struct cfg80211_bss_ies *ies;
const u8 *ssidie;
if (bssid && !ether_addr_equal(a->bssid, bssid))
return false;
if (!ssid)
return true;
ies = rcu_access_pointer(a->ies);
if (!ies)
return false;
ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
if (!ssidie)
return false;
if (ssidie[1] != ssid_len)
return false;
return memcmp(ssidie + 2, ssid, ssid_len) == 0;
}
/**
* enum bss_compare_mode - BSS compare mode
* @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find)
......@@ -875,6 +1028,12 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev,
return true;
}
struct cfg80211_non_tx_bss {
struct cfg80211_bss *tx_bss;
u8 max_bssid_indicator;
u8 bssid_index;
};
/* Returned bss is reference counted and must be cleaned up appropriately. */
static struct cfg80211_internal_bss *
cfg80211_bss_update(struct cfg80211_registered_device *rdev,
......@@ -978,6 +1137,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
memcpy(found->pub.chain_signal, tmp->pub.chain_signal,
IEEE80211_MAX_CHAINS);
ether_addr_copy(found->parent_bssid, tmp->parent_bssid);
found->pub.max_bssid_indicator = tmp->pub.max_bssid_indicator;
found->pub.bssid_index = tmp->pub.bssid_index;
} else {
struct cfg80211_internal_bss *new;
struct cfg80211_internal_bss *hidden;
......@@ -1002,6 +1163,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
memcpy(new, tmp, sizeof(*new));
new->refcount = 1;
INIT_LIST_HEAD(&new->hidden_list);
INIT_LIST_HEAD(&new->pub.nontrans_list);
if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN);
......@@ -1035,6 +1197,17 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
goto drop;
}
/* This must be before the call to bss_ref_get */
if (tmp->pub.transmitted_bss) {
struct cfg80211_internal_bss *pbss =
container_of(tmp->pub.transmitted_bss,
struct cfg80211_internal_bss,
pub);
new->pub.transmitted_bss = tmp->pub.transmitted_bss;
bss_ref_get(rdev, pbss);
}
list_add_tail(&new->list, &rdev->bss_list);
rdev->bss_entries++;
rb_insert_bss(rdev, new);
......@@ -1123,14 +1296,16 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
}
/* Returned bss is reference counted and must be cleaned up appropriately. */
struct cfg80211_bss *
cfg80211_inform_bss_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data,
enum cfg80211_bss_frame_type ftype,
const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen,
gfp_t gfp)
static struct cfg80211_bss *
cfg80211_inform_single_bss_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data,
enum cfg80211_bss_frame_type ftype,
const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen,
struct cfg80211_non_tx_bss *non_tx_data,
gfp_t gfp)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
struct cfg80211_bss_ies *ies;
struct ieee80211_channel *channel;
struct cfg80211_internal_bss tmp = {}, *res;
......@@ -1156,6 +1331,11 @@ cfg80211_inform_bss_data(struct wiphy *wiphy,
tmp.pub.beacon_interval = beacon_interval;
tmp.pub.capability = capability;
tmp.ts_boottime = data->boottime_ns;
if (non_tx_data) {
tmp.pub.transmitted_bss = non_tx_data->tx_bss;
tmp.pub.bssid_index = non_tx_data->bssid_index;
tmp.pub.max_bssid_indicator = non_tx_data->max_bssid_indicator;
}
/*
* If we do not know here whether the IEs are from a Beacon or Probe
......@@ -1202,19 +1382,247 @@ cfg80211_inform_bss_data(struct wiphy *wiphy,
regulatory_hint_found_beacon(wiphy, channel, gfp);
}
if (non_tx_data && non_tx_data->tx_bss) {
/* this is a nontransmitting bss, we need to add it to
* transmitting bss' list if it is not there
*/
if (cfg80211_add_nontrans_list(non_tx_data->tx_bss,
&res->pub)) {
if (__cfg80211_unlink_bss(rdev, res))
rdev->bss_generation++;
}
}
trace_cfg80211_return_bss(&res->pub);
/* cfg80211_bss_update gives us a referenced result */
return &res->pub;
}
EXPORT_SYMBOL(cfg80211_inform_bss_data);
/* cfg80211_inform_bss_width_frame helper */
static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data,
enum cfg80211_bss_frame_type ftype,
const u8 *bssid, u64 tsf,
u16 beacon_interval, const u8 *ie,
size_t ielen,
struct cfg80211_non_tx_bss *non_tx_data,
gfp_t gfp)
{
const u8 *mbssid_index_ie;
const struct element *elem, *sub;
size_t new_ie_len;
u8 new_bssid[ETH_ALEN];
u8 *new_ie;
u16 capability;
struct cfg80211_bss *bss;
if (!non_tx_data)
return;
if (!cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
return;
if (!wiphy->support_mbssid)
return;
if (wiphy->support_only_he_mbssid &&
!cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen))
return;
new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp);
if (!new_ie)
return;
for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ielen) {
if (elem->datalen < 4)
continue;
for_each_element(sub, elem->data + 1, elem->datalen - 1) {
if (sub->id != 0 || sub->datalen < 4) {
/* not a valid BSS profile */
continue;
}
if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP ||
sub->data[1] != 2) {
/* The first element within the Nontransmitted
* BSSID Profile is not the Nontransmitted
* BSSID Capability element.
*/
continue;
}
/* found a Nontransmitted BSSID Profile */
mbssid_index_ie = cfg80211_find_ie
(WLAN_EID_MULTI_BSSID_IDX,
sub->data, sub->datalen);
if (!mbssid_index_ie || mbssid_index_ie[1] < 1 ||
mbssid_index_ie[2] == 0) {
/* No valid Multiple BSSID-Index element */
continue;
}
non_tx_data->bssid_index = mbssid_index_ie[2];
non_tx_data->max_bssid_indicator = elem->data[0];
cfg80211_gen_new_bssid(bssid,
non_tx_data->max_bssid_indicator,
non_tx_data->bssid_index,
new_bssid);
memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
new_ie_len = cfg80211_gen_new_ie(ie, ielen, sub->data,
sub->datalen, new_ie,
gfp);
if (!new_ie_len)
continue;
capability = get_unaligned_le16(sub->data + 2);
bss = cfg80211_inform_single_bss_data(wiphy, data,
ftype,
new_bssid, tsf,
capability,
beacon_interval,
new_ie,
new_ie_len,
non_tx_data,
gfp);
if (!bss)
break;
cfg80211_put_bss(wiphy, bss);
}
}
kfree(new_ie);
}
struct cfg80211_bss *
cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data,
struct ieee80211_mgmt *mgmt, size_t len,
gfp_t gfp)
cfg80211_inform_bss_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data,
enum cfg80211_bss_frame_type ftype,
const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen,
gfp_t gfp)
{
struct cfg80211_bss *res;
struct cfg80211_non_tx_bss non_tx_data;
res = cfg80211_inform_single_bss_data(wiphy, data, ftype, bssid, tsf,
capability, beacon_interval, ie,
ielen, NULL, gfp);
non_tx_data.tx_bss = res;
cfg80211_parse_mbssid_data(wiphy, data, ftype, bssid, tsf,
beacon_interval, ie, ielen, &non_tx_data,
gfp);
return res;
}
EXPORT_SYMBOL(cfg80211_inform_bss_data);
static void
cfg80211_parse_mbssid_frame_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data,
struct ieee80211_mgmt *mgmt, size_t len,
struct cfg80211_non_tx_bss *non_tx_data,
gfp_t gfp)
{
enum cfg80211_bss_frame_type ftype;
const u8 *ie = mgmt->u.probe_resp.variable;
size_t ielen = len - offsetof(struct ieee80211_mgmt,
u.probe_resp.variable);
ftype = ieee80211_is_beacon(mgmt->frame_control) ?
CFG80211_BSS_FTYPE_BEACON : CFG80211_BSS_FTYPE_PRESP;
cfg80211_parse_mbssid_data(wiphy, data, ftype, mgmt->bssid,
le64_to_cpu(mgmt->u.probe_resp.timestamp),
le16_to_cpu(mgmt->u.probe_resp.beacon_int),
ie, ielen, non_tx_data, gfp);
}
static void
cfg80211_update_notlisted_nontrans(struct wiphy *wiphy,
struct cfg80211_bss *nontrans_bss,
struct ieee80211_mgmt *mgmt, size_t len,
gfp_t gfp)
{
u8 *ie, *new_ie, *pos;
const u8 *nontrans_ssid, *trans_ssid, *mbssid;
size_t ielen = len - offsetof(struct ieee80211_mgmt,
u.probe_resp.variable);
size_t new_ie_len;
struct cfg80211_bss_ies *new_ies;
const struct cfg80211_bss_ies *old;
u8 cpy_len;
ie = mgmt->u.probe_resp.variable;
new_ie_len = ielen;
trans_ssid = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
if (!trans_ssid)
return;
new_ie_len -= trans_ssid[1];
mbssid = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen);
if (!mbssid)
return;
new_ie_len -= mbssid[1];
rcu_read_lock();
nontrans_ssid = ieee80211_bss_get_ie(nontrans_bss, WLAN_EID_SSID);
if (!nontrans_ssid) {
rcu_read_unlock();
return;
}
new_ie_len += nontrans_ssid[1];
rcu_read_unlock();
/* generate new ie for nontrans BSS
* 1. replace SSID with nontrans BSS' SSID
* 2. skip MBSSID IE
*/
new_ie = kzalloc(new_ie_len, gfp);
if (!new_ie)
return;
new_ies = kzalloc(sizeof(*new_ies) + new_ie_len, gfp);
if (!new_ies)
goto out_free;
pos = new_ie;
/* copy the nontransmitted SSID */
cpy_len = nontrans_ssid[1] + 2;
memcpy(pos, nontrans_ssid, cpy_len);
pos += cpy_len;
/* copy the IEs between SSID and MBSSID */
cpy_len = trans_ssid[1] + 2;
memcpy(pos, (trans_ssid + cpy_len), (mbssid - (trans_ssid + cpy_len)));
pos += (mbssid - (trans_ssid + cpy_len));
/* copy the IEs after MBSSID */
cpy_len = mbssid[1] + 2;
memcpy(pos, mbssid + cpy_len, ((ie + ielen) - (mbssid + cpy_len)));
/* update ie */
new_ies->len = new_ie_len;
new_ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
new_ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control);
memcpy(new_ies->data, new_ie, new_ie_len);
if (ieee80211_is_probe_resp(mgmt->frame_control)) {
old = rcu_access_pointer(nontrans_bss->proberesp_ies);
rcu_assign_pointer(nontrans_bss->proberesp_ies, new_ies);
rcu_assign_pointer(nontrans_bss->ies, new_ies);
if (old)
kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
} else {
old = rcu_access_pointer(nontrans_bss->beacon_ies);
rcu_assign_pointer(nontrans_bss->beacon_ies, new_ies);
rcu_assign_pointer(nontrans_bss->ies, new_ies);
if (old)
kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
}
out_free:
kfree(new_ie);
}
/* cfg80211_inform_bss_width_frame helper */
static struct cfg80211_bss *
cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data,
struct ieee80211_mgmt *mgmt, size_t len,
struct cfg80211_non_tx_bss *non_tx_data,
gfp_t gfp)
{
struct cfg80211_internal_bss tmp = {}, *res;
struct cfg80211_bss_ies *ies;
......@@ -1272,6 +1680,11 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
tmp.pub.chains = data->chains;
memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS);
ether_addr_copy(tmp.parent_bssid, data->parent_bssid);
if (non_tx_data) {
tmp.pub.transmitted_bss = non_tx_data->tx_bss;
tmp.pub.bssid_index = non_tx_data->bssid_index;
tmp.pub.max_bssid_indicator = non_tx_data->max_bssid_indicator;
}
signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
wiphy->max_adj_channel_rssi_comp;
......@@ -1293,6 +1706,53 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
/* cfg80211_bss_update gives us a referenced result */
return &res->pub;
}
struct cfg80211_bss *
cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data,
struct ieee80211_mgmt *mgmt, size_t len,
gfp_t gfp)
{
struct cfg80211_bss *res, *tmp_bss;
const u8 *ie = mgmt->u.probe_resp.variable;
const struct cfg80211_bss_ies *ies1, *ies2;
size_t ielen = len - offsetof(struct ieee80211_mgmt,
u.probe_resp.variable);
struct cfg80211_non_tx_bss non_tx_data;
res = cfg80211_inform_single_bss_frame_data(wiphy, data, mgmt,
len, NULL, gfp);
if (!res || !wiphy->support_mbssid ||
!cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
return res;
if (wiphy->support_only_he_mbssid &&
!cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen))
return res;
non_tx_data.tx_bss = res;
/* process each non-transmitting bss */
cfg80211_parse_mbssid_frame_data(wiphy, data, mgmt, len,
&non_tx_data, gfp);
/* check if the res has other nontransmitting bss which is not
* in MBSSID IE
*/
ies1 = rcu_access_pointer(res->ies);
/* go through nontrans_list, if the timestamp of the BSS is
* earlier than the timestamp of the transmitting BSS then
* update it
*/
list_for_each_entry(tmp_bss, &res->nontrans_list,
nontrans_list) {
ies2 = rcu_access_pointer(tmp_bss->ies);
if (ies2->tsf < ies1->tsf)
cfg80211_update_notlisted_nontrans(wiphy, tmp_bss,
mgmt, len, gfp);
}
return res;
}
EXPORT_SYMBOL(cfg80211_inform_bss_frame_data);
void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
......@@ -1330,7 +1790,8 @@ EXPORT_SYMBOL(cfg80211_put_bss);
void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
struct cfg80211_internal_bss *bss;
struct cfg80211_internal_bss *bss, *tmp1;
struct cfg80211_bss *nontrans_bss, *tmp;
if (WARN_ON(!pub))
return;
......@@ -1338,10 +1799,21 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
bss = container_of(pub, struct cfg80211_internal_bss, pub);
spin_lock_bh(&rdev->bss_lock);
if (!list_empty(&bss->list)) {
if (__cfg80211_unlink_bss(rdev, bss))
if (list_empty(&bss->list))
goto out;
list_for_each_entry_safe(nontrans_bss, tmp,
&pub->nontrans_list,
nontrans_list) {
tmp1 = container_of(nontrans_bss,
struct cfg80211_internal_bss, pub);
if (__cfg80211_unlink_bss(rdev, tmp1))
rdev->bss_generation++;
}
if (__cfg80211_unlink_bss(rdev, bss))
rdev->bss_generation++;
out:
spin_unlock_bh(&rdev->bss_lock);
}
EXPORT_SYMBOL(cfg80211_unlink_bss);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment