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, ...@@ -2801,6 +2801,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
ieee80211_hw_set(hw, TDLS_WIDER_BW); ieee80211_hw_set(hw, TDLS_WIDER_BW);
if (rctbl) if (rctbl)
ieee80211_hw_set(hw, SUPPORTS_RC_TABLE); ieee80211_hw_set(hw, SUPPORTS_RC_TABLE);
ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH
* Copyright (c) 2016 - 2017 Intel Deutschland 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 * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -2475,6 +2475,7 @@ enum ieee80211_eid_ext { ...@@ -2475,6 +2475,7 @@ enum ieee80211_eid_ext {
WLAN_EID_EXT_HE_OPERATION = 36, WLAN_EID_EXT_HE_OPERATION = 36,
WLAN_EID_EXT_UORA = 37, WLAN_EID_EXT_UORA = 37,
WLAN_EID_EXT_HE_MU_EDCA = 38, WLAN_EID_EXT_HE_MU_EDCA = 38,
WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION = 55,
}; };
/* Action category code */ /* Action category code */
...@@ -2656,6 +2657,11 @@ enum ieee80211_tdls_actioncode { ...@@ -2656,6 +2657,11 @@ enum ieee80211_tdls_actioncode {
*/ */
#define WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING BIT(2) #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 */ /* 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_BUFFER_STA BIT(4)
#define WLAN_EXT_CAPA4_TDLS_PEER_PSM BIT(5) #define WLAN_EXT_CAPA4_TDLS_PEER_PSM BIT(5)
...@@ -2691,6 +2697,9 @@ enum ieee80211_tdls_actioncode { ...@@ -2691,6 +2697,9 @@ enum ieee80211_tdls_actioncode {
#define WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT BIT(5) #define WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT BIT(5)
#define WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT BIT(6) #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 */ /* TDLS specific payload type in the LLC/SNAP header */
#define WLAN_TDLS_SNAP_RFTYPE 0x2 #define WLAN_TDLS_SNAP_RFTYPE 0x2
...@@ -2882,6 +2891,34 @@ enum ieee80211_sa_query_action { ...@@ -2882,6 +2891,34 @@ enum ieee80211_sa_query_action {
WLAN_ACTION_SA_QUERY_RESPONSE = 1, 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)) #define SUITE(oui, id) (((oui) << 8) | (id))
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2015-2017 Intel Deutschland 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 * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -2035,9 +2035,15 @@ struct cfg80211_bss_ies { ...@@ -2035,9 +2035,15 @@ struct cfg80211_bss_ies {
* a BSS that hides the SSID in its beacon, this points to the BSS struct * 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 * 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. * 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) * @signal: signal strength value (type depends on the wiphy's signal_type)
* @chains: bitmask for filled values in @chain_signal. * @chains: bitmask for filled values in @chain_signal.
* @chain_signal: per-chain signal strength of last received BSS in dBm. * @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 * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes
*/ */
struct cfg80211_bss { struct cfg80211_bss {
...@@ -2049,6 +2055,8 @@ struct cfg80211_bss { ...@@ -2049,6 +2055,8 @@ struct cfg80211_bss {
const struct cfg80211_bss_ies __rcu *proberesp_ies; const struct cfg80211_bss_ies __rcu *proberesp_ies;
struct cfg80211_bss *hidden_beacon_bss; struct cfg80211_bss *hidden_beacon_bss;
struct cfg80211_bss *transmitted_bss;
struct list_head nontrans_list;
s32 signal; s32 signal;
...@@ -2059,6 +2067,9 @@ struct cfg80211_bss { ...@@ -2059,6 +2067,9 @@ struct cfg80211_bss {
u8 chains; u8 chains;
s8 chain_signal[IEEE80211_MAX_CHAINS]; s8 chain_signal[IEEE80211_MAX_CHAINS];
u8 bssid_index;
u8 max_bssid_indicator;
u8 priv[0] __aligned(sizeof(void *)); u8 priv[0] __aligned(sizeof(void *));
}; };
...@@ -4313,6 +4324,11 @@ struct cfg80211_pmsr_capabilities { ...@@ -4313,6 +4324,11 @@ struct cfg80211_pmsr_capabilities {
* @txq_memory_limit: configuration internal TX queue memory limit * @txq_memory_limit: configuration internal TX queue memory limit
* @txq_quantum: configuration of internal TX queue scheduler quantum * @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 * @pmsr_capa: peer measurement capabilities
*/ */
struct wiphy { struct wiphy {
...@@ -4453,6 +4469,9 @@ struct wiphy { ...@@ -4453,6 +4469,9 @@ struct wiphy {
u32 txq_memory_limit; u32 txq_memory_limit;
u32 txq_quantum; u32 txq_quantum;
u8 support_mbssid:1,
support_only_he_mbssid:1;
const struct cfg80211_pmsr_capabilities *pmsr_capa; const struct cfg80211_pmsr_capabilities *pmsr_capa;
char priv[0] __aligned(NETDEV_ALIGN); char priv[0] __aligned(NETDEV_ALIGN);
...@@ -5451,6 +5470,29 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, ...@@ -5451,6 +5470,29 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
return cfg80211_inform_bss_frame_data(wiphy, &data, mgmt, len, gfp); 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 * 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 * @CFG80211_BSS_FTYPE_UNKNOWN: driver doesn't know whether the data is
......
...@@ -591,6 +591,14 @@ struct ieee80211_ftm_responder_params { ...@@ -591,6 +591,14 @@ struct ieee80211_ftm_responder_params {
* @ftm_responder: whether to enable or disable fine timing measurement FTM * @ftm_responder: whether to enable or disable fine timing measurement FTM
* responder functionality. * responder functionality.
* @ftmr_params: configurable lci/civic parameter when enabling FTM responder. * @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 { struct ieee80211_bss_conf {
const u8 *bssid; const u8 *bssid;
...@@ -644,6 +652,13 @@ struct ieee80211_bss_conf { ...@@ -644,6 +652,13 @@ struct ieee80211_bss_conf {
bool protected_keep_alive; bool protected_keep_alive;
bool ftm_responder; bool ftm_responder;
struct ieee80211_ftm_responder_params *ftmr_params; 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 { ...@@ -2219,6 +2234,11 @@ struct ieee80211_txq {
* @IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN: Driver does not report accurate A-MPDU * @IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN: Driver does not report accurate A-MPDU
* length in tx status information * 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 * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/ */
enum ieee80211_hw_flags { enum ieee80211_hw_flags {
...@@ -2268,6 +2288,8 @@ enum ieee80211_hw_flags { ...@@ -2268,6 +2288,8 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_VHT_EXT_NSS_BW, IEEE80211_HW_SUPPORTS_VHT_EXT_NSS_BW,
IEEE80211_HW_STA_MMPDU_TXQ, IEEE80211_HW_STA_MMPDU_TXQ,
IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN, IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
IEEE80211_HW_SUPPORTS_MULTI_BSSID,
IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
/* keep last, obviously */ /* keep last, obviously */
NUM_IEEE80211_HW_FLAGS NUM_IEEE80211_HW_FLAGS
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net> * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2018 Intel Corporation * Copyright (C) 2018 - 2019 Intel Corporation
* *
* GPLv2 * GPLv2
* *
...@@ -219,6 +219,8 @@ static const char *hw_flag_names[] = { ...@@ -219,6 +219,8 @@ static const char *hw_flag_names[] = {
FLAG(SUPPORTS_VHT_EXT_NSS_BW), FLAG(SUPPORTS_VHT_EXT_NSS_BW),
FLAG(STA_MMPDU_TXQ), FLAG(STA_MMPDU_TXQ),
FLAG(TX_STATUS_NO_AMPDU_LEN), FLAG(TX_STATUS_NO_AMPDU_LEN),
FLAG(SUPPORTS_MULTI_BSSID),
FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
#undef FLAG #undef FLAG
}; };
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* Copyright 2009, Johannes Berg <johannes@sipsolutions.net> * Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright(c) 2016 Intel Deutschland 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 * 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 * 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, ...@@ -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); ieee80211_update_sta_info(sdata, mgmt, len, rx_status, elems, channel);
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, channel);
channel);
if (!bss) if (!bss)
return; return;
...@@ -1604,7 +1604,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata, ...@@ -1604,7 +1604,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
return; return;
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, 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); 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, ...@@ -1654,7 +1654,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
ieee802_11_parse_elems( ieee802_11_parse_elems(
mgmt->u.action.u.chan_switch.variable, mgmt->u.action.u.chan_switch.variable,
ies_len, true, &elems); ies_len, true, &elems, mgmt->bssid, NULL);
if (elems.parse_error) if (elems.parse_error)
break; break;
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2015 Intel Mobile Communications GmbH * 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 * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -1495,6 +1495,12 @@ struct ieee802_11_elems { ...@@ -1495,6 +1495,12 @@ struct ieee802_11_elems {
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie; 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_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 */ /* length of them, respectively */
u8 ext_capab_len; u8 ext_capab_len;
...@@ -1513,6 +1519,7 @@ struct ieee802_11_elems { ...@@ -1513,6 +1519,7 @@ struct ieee802_11_elems {
u8 prep_len; u8 prep_len;
u8 perr_len; u8 perr_len;
u8 country_elem_len; u8 country_elem_len;
u8 bssid_index_len;
/* whether a parse error occurred while retrieving these elements */ /* whether a parse error occurred while retrieving these elements */
bool parse_error; bool parse_error;
...@@ -1672,7 +1679,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local, ...@@ -1672,7 +1679,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
struct ieee80211_rx_status *rx_status, struct ieee80211_rx_status *rx_status,
struct ieee80211_mgmt *mgmt, struct ieee80211_mgmt *mgmt,
size_t len, size_t len,
struct ieee802_11_elems *elems,
struct ieee80211_channel *channel); struct ieee80211_channel *channel);
void ieee80211_rx_bss_put(struct ieee80211_local *local, void ieee80211_rx_bss_put(struct ieee80211_local *local,
struct ieee80211_bss *bss); struct ieee80211_bss *bss);
...@@ -1956,12 +1962,16 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, ...@@ -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, u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
struct ieee802_11_elems *elems, 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, static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
bool action, 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 @@ ...@@ -4,7 +4,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2017 Intel Deutschland 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 * 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 * 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) ...@@ -1112,6 +1112,17 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (ieee80211_hw_check(&local->hw, CHANCTX_STA_CSA)) if (ieee80211_hw_check(&local->hw, CHANCTX_STA_CSA))
local->ext_capa[0] |= WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING; 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; local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM;
result = wiphy_register(local->hw.wiphy); result = wiphy_register(local->hw.wiphy);
......
/* /*
* Copyright (c) 2008, 2009 open80211s Ltd. * Copyright (c) 2008, 2009 open80211s Ltd.
* Copyright (C) 2018 Intel Corporation * Copyright (C) 2018 - 2019 Intel Corporation
* Authors: Luis Carlos Cobo <luisca@cozybit.com> * Authors: Luis Carlos Cobo <luisca@cozybit.com>
* Javier Cardona <javier@cozybit.com> * Javier Cardona <javier@cozybit.com>
* *
...@@ -1106,7 +1106,8 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata, ...@@ -1106,7 +1106,8 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
if (baselen > len) if (baselen > len)
return; 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) if (!elems.mesh_id)
return; return;
...@@ -1170,7 +1171,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, ...@@ -1170,7 +1171,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
return; return;
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, 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 */ /* ignore non-mesh or secure / unsecure mismatch */
if ((!elems.mesh_id || !elems.mesh_config) || if ((!elems.mesh_id || !elems.mesh_config) ||
...@@ -1306,7 +1307,8 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata, ...@@ -1306,7 +1307,8 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
pos = mgmt->u.action.u.chan_switch.variable; pos = mgmt->u.action.u.chan_switch.variable;
baselen = offsetof(struct ieee80211_mgmt, baselen = offsetof(struct ieee80211_mgmt,
u.action.u.chan_switch.variable); 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; ifmsh->chsw_ttl = elems.mesh_chansw_params_ie->mesh_ttl;
if (!--ifmsh->chsw_ttl) if (!--ifmsh->chsw_ttl)
......
/* /*
* Copyright (c) 2008, 2009 open80211s Ltd. * Copyright (c) 2008, 2009 open80211s Ltd.
* Copyright (C) 2019 Intel Corporation
* Author: Luis Carlos Cobo <luisca@cozybit.com> * Author: Luis Carlos Cobo <luisca@cozybit.com>
* *
* This program is free software; you can redistribute it and/or modify * 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, ...@@ -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; baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;
ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable, 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) {
if (elems.preq_len != 37) if (elems.preq_len != 37)
......
/* /*
* Copyright (c) 2008, 2009 open80211s Ltd. * Copyright (c) 2008, 2009 open80211s Ltd.
* Copyright (C) 2019 Intel Corporation
* Author: Luis Carlos Cobo <luisca@cozybit.com> * Author: Luis Carlos Cobo <luisca@cozybit.com>
* *
* This program is free software; you can redistribute it and/or modify * 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, ...@@ -1214,6 +1215,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
if (baselen > len) if (baselen > len)
return; 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); mesh_process_plink_frame(sdata, mgmt, &elems, rx_status);
} }
...@@ -813,6 +813,21 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) ...@@ -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 present, add any custom IEs that go before HT */
if (assoc_data->ie_len) { if (assoc_data->ie_len) {
static const u8 before_ht[] = { static const u8 before_ht[] = {
...@@ -2762,7 +2777,8 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, ...@@ -2762,7 +2777,8 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
u32 tx_flags = 0; u32 tx_flags = 0;
pos = mgmt->u.auth.variable; 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) if (!elems.challenge)
return; return;
auth_data->expected_transaction = 4; auth_data->expected_transaction = 4;
...@@ -3130,7 +3146,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, ...@@ -3130,7 +3146,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
} }
pos = mgmt->u.assoc_resp.variable; 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) { if (!elems.supp_rates) {
sdata_info(sdata, "no SuppRates element in AssocResp\n"); sdata_info(sdata, "no SuppRates element in AssocResp\n");
...@@ -3167,7 +3184,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, ...@@ -3167,7 +3184,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
return false; return false;
ieee802_11_parse_elems(bss_ies->data, bss_ies->len, 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 && if (assoc_data->wmm &&
!elems.wmm_param && bss_elems.wmm_param) { !elems.wmm_param && bss_elems.wmm_param) {
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, ...@@ -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? */ /* 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 * Some APs, e.g. Netgear WNDR3700, report invalid HT operation data
* in their association response, so ignore that data for our own * 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, ...@@ -3464,7 +3491,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
return; return;
pos = mgmt->u.assoc_resp.variable; 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 && if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
elems.timeout_int && elems.timeout_int &&
...@@ -3521,8 +3549,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ...@@ -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, static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_rx_status *rx_status, struct ieee80211_rx_status *rx_status)
struct ieee802_11_elems *elems)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_bss *bss; struct ieee80211_bss *bss;
...@@ -3534,8 +3561,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ...@@ -3534,8 +3561,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (!channel) if (!channel)
return; return;
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, channel);
channel);
if (bss) { if (bss) {
sdata->vif.bss_conf.beacon_rate = bss->beacon_rate; sdata->vif.bss_conf.beacon_rate = bss->beacon_rate;
ieee80211_rx_bss_put(local, bss); ieee80211_rx_bss_put(local, bss);
...@@ -3550,7 +3576,6 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ...@@ -3550,7 +3576,6 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd; struct ieee80211_if_managed *ifmgd;
struct ieee80211_rx_status *rx_status = (void *) skb->cb; struct ieee80211_rx_status *rx_status = (void *) skb->cb;
size_t baselen, len = skb->len; size_t baselen, len = skb->len;
struct ieee802_11_elems elems;
ifmgd = &sdata->u.mgd; ifmgd = &sdata->u.mgd;
...@@ -3563,10 +3588,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ...@@ -3563,10 +3588,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
if (baselen > len) if (baselen > len)
return; return;
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, ieee80211_rx_bss_info(sdata, mgmt, len, rx_status);
false, &elems);
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
if (ifmgd->associated && if (ifmgd->associated &&
ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
...@@ -3693,6 +3715,16 @@ static void ieee80211_handle_beacon_sig(struct ieee80211_sub_if_data *sdata, ...@@ -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, static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_rx_status *rx_status) struct ieee80211_rx_status *rx_status)
...@@ -3734,15 +3766,16 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ...@@ -3734,15 +3766,16 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock(); rcu_read_unlock();
if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon && 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, 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); ieee80211_rx_bss_info(sdata, mgmt, len, rx_status);
if (elems.tim && !elems.parse_error) {
const struct ieee80211_tim_ie *tim_ie = elems.tim; if (elems.dtim_period)
ifmgd->dtim_period = tim_ie->dtim_period; ifmgd->dtim_period = elems.dtim_period;
}
ifmgd->have_beacon = true; ifmgd->have_beacon = true;
ifmgd->assoc_data->need_beacon = false; ifmgd->assoc_data->need_beacon = false;
if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) { 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, ...@@ -3750,12 +3783,17 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
le64_to_cpu(mgmt->u.beacon.timestamp); le64_to_cpu(mgmt->u.beacon.timestamp);
sdata->vif.bss_conf.sync_device_ts = sdata->vif.bss_conf.sync_device_ts =
rx_status->device_timestamp; rx_status->device_timestamp;
if (elems.tim) sdata->vif.bss_conf.sync_dtim_count = elems.dtim_count;
sdata->vif.bss_conf.sync_dtim_count =
elems.tim->dtim_count;
else
sdata->vif.bss_conf.sync_dtim_count = 0;
} }
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 */ /* continue assoc process */
ifmgd->assoc_data->timeout = jiffies; ifmgd->assoc_data->timeout = jiffies;
ifmgd->assoc_data->timeout_started = true; ifmgd->assoc_data->timeout_started = true;
...@@ -3764,7 +3802,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ...@@ -3764,7 +3802,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
} }
if (!ifmgd->associated || if (!ifmgd->associated ||
!ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) !ieee80211_rx_our_beacon(mgmt->bssid, ifmgd->associated))
return; return;
bssid = ifmgd->associated->bssid; bssid = ifmgd->associated->bssid;
...@@ -3787,7 +3825,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ...@@ -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 = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable, ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
len - baselen, false, &elems, len - baselen, false, &elems,
care_about_ies, ncrc); care_about_ies, ncrc,
mgmt->bssid, bssid);
if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) && if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
ieee80211_check_tim(elems.tim, elems.tim_len, ifmgd->aid)) { 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, ...@@ -3859,11 +3898,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
le64_to_cpu(mgmt->u.beacon.timestamp); le64_to_cpu(mgmt->u.beacon.timestamp);
sdata->vif.bss_conf.sync_device_ts = sdata->vif.bss_conf.sync_device_ts =
rx_status->device_timestamp; rx_status->device_timestamp;
if (elems.tim) sdata->vif.bss_conf.sync_dtim_count = elems.dtim_count;
sdata->vif.bss_conf.sync_dtim_count =
elems.tim->dtim_count;
else
sdata->vif.bss_conf.sync_dtim_count = 0;
} }
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) 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, ...@@ -3871,7 +3906,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ifmgd->beacon_crc = ncrc; ifmgd->beacon_crc = ncrc;
ifmgd->beacon_crc_valid = true; 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, ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
rx_status->device_timestamp, rx_status->device_timestamp,
...@@ -3889,10 +3924,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ...@@ -3889,10 +3924,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
*/ */
if (!ifmgd->have_beacon) { if (!ifmgd->have_beacon) {
/* a few bogus AP send dtim_period = 0 or no TIM IE */ /* a few bogus AP send dtim_period = 0 or no TIM IE */
if (elems.tim) bss_conf->dtim_period = elems.dtim_period ?: 1;
bss_conf->dtim_period = elems.tim->dtim_period ?: 1;
else
bss_conf->dtim_period = 1;
changed |= BSS_CHANGED_BEACON_INFO; changed |= BSS_CHANGED_BEACON_INFO;
ifmgd->have_beacon = true; ifmgd->have_beacon = true;
...@@ -3992,9 +4024,10 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ...@@ -3992,9 +4024,10 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
if (ies_len < 0) if (ies_len < 0)
break; break;
/* CSA IE cannot be overridden, no need for BSSID */
ieee802_11_parse_elems( ieee802_11_parse_elems(
mgmt->u.action.u.chan_switch.variable, mgmt->u.action.u.chan_switch.variable,
ies_len, true, &elems); ies_len, true, &elems, mgmt->bssid, NULL);
if (elems.parse_error) if (elems.parse_error)
break; break;
...@@ -4011,9 +4044,13 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ...@@ -4011,9 +4044,13 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
if (ies_len < 0) if (ies_len < 0)
break; break;
/*
* extended CSA IE can't be overridden, no need for
* BSSID
*/
ieee802_11_parse_elems( ieee802_11_parse_elems(
mgmt->u.action.u.ext_chan_switch.variable, mgmt->u.action.u.ext_chan_switch.variable,
ies_len, true, &elems); ies_len, true, &elems, mgmt->bssid, NULL);
if (elems.parse_error) if (elems.parse_error)
break; break;
...@@ -4754,6 +4791,40 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ...@@ -4754,6 +4791,40 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
return ret; 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, static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *cbss, bool assoc, struct cfg80211_bss *cbss, bool assoc,
bool override) bool override)
...@@ -4845,17 +4916,13 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, ...@@ -4845,17 +4916,13 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
rcu_read_lock(); rcu_read_lock();
ies = rcu_dereference(cbss->beacon_ies); ies = rcu_dereference(cbss->beacon_ies);
if (ies) { if (ies) {
const u8 *tim_ie;
sdata->vif.bss_conf.sync_tsf = ies->tsf; sdata->vif.bss_conf.sync_tsf = ies->tsf;
sdata->vif.bss_conf.sync_device_ts = sdata->vif.bss_conf.sync_device_ts =
bss->device_ts_beacon; bss->device_ts_beacon;
tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
ies->data, ies->len); ieee80211_get_dtim(ies,
if (tim_ie && tim_ie[1] >= 2) &sdata->vif.bss_conf.sync_dtim_count,
sdata->vif.bss_conf.sync_dtim_count = tim_ie[2]; NULL);
else
sdata->vif.bss_conf.sync_dtim_count = 0;
} else if (!ieee80211_hw_check(&sdata->local->hw, } else if (!ieee80211_hw_check(&sdata->local->hw,
TIMING_BEACON_ONLY)) { TIMING_BEACON_ONLY)) {
ies = rcu_dereference(cbss->proberesp_ies); ies = rcu_dereference(cbss->proberesp_ies);
...@@ -5325,17 +5392,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, ...@@ -5325,17 +5392,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
assoc_data->timeout_started = true; assoc_data->timeout_started = true;
assoc_data->need_beacon = true; assoc_data->need_beacon = true;
} else if (beacon_ies) { } else if (beacon_ies) {
const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, const u8 *ie;
beacon_ies->data,
beacon_ies->len);
u8 dtim_count = 0; u8 dtim_count = 0;
if (tim_ie && tim_ie[1] >= sizeof(struct ieee80211_tim_ie)) { ieee80211_get_dtim(beacon_ies, &dtim_count,
const struct ieee80211_tim_ie *tim; &ifmgd->dtim_period);
tim = (void *)(tim_ie + 2);
ifmgd->dtim_period = tim->dtim_period;
dtim_count = tim->dtim_count;
}
ifmgd->have_beacon = true; ifmgd->have_beacon = true;
assoc_data->timeout = jiffies; assoc_data->timeout = jiffies;
assoc_data->timeout_started = true; assoc_data->timeout_started = true;
...@@ -5346,6 +5408,17 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, ...@@ -5346,6 +5408,17 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
bss->device_ts_beacon; bss->device_ts_beacon;
sdata->vif.bss_conf.sync_dtim_count = dtim_count; 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 { } else {
assoc_data->timeout = jiffies; assoc_data->timeout = jiffies;
assoc_data->timeout_started = true; assoc_data->timeout_started = true;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net> * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2013-2015 Intel Mobile Communications GmbH * Copyright 2013-2015 Intel Mobile Communications GmbH
* Copyright 2016-2017 Intel Deutschland 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 * 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 * 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) ...@@ -57,62 +58,14 @@ static bool is_uapsd_supported(struct ieee802_11_elems *elems)
return qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD; return qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD;
} }
struct ieee80211_bss * static void
ieee80211_bss_info_update(struct ieee80211_local *local, ieee80211_update_bss_from_elems(struct ieee80211_local *local,
struct ieee80211_rx_status *rx_status, struct ieee80211_bss *bss,
struct ieee80211_mgmt *mgmt, size_t len, struct ieee802_11_elems *elems,
struct ieee802_11_elems *elems, struct ieee80211_rx_status *rx_status,
struct ieee80211_channel *channel) bool beacon)
{ {
bool beacon = ieee80211_is_beacon(mgmt->frame_control);
struct cfg80211_bss *cbss;
struct ieee80211_bss *bss;
int clen, srlen; 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) if (beacon)
bss->device_ts_beacon = rx_status->device_timestamp; bss->device_ts_beacon = rx_status->device_timestamp;
...@@ -182,6 +135,89 @@ ieee80211_bss_info_update(struct ieee80211_local *local, ...@@ -182,6 +135,89 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
bss->beacon_rate = bss->beacon_rate =
&sband->bitrates[rx_status->rate_idx]; &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; return bss;
} }
...@@ -206,10 +242,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) ...@@ -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_sub_if_data *sdata1, *sdata2;
struct ieee80211_mgmt *mgmt = (void *)skb->data; struct ieee80211_mgmt *mgmt = (void *)skb->data;
struct ieee80211_bss *bss; struct ieee80211_bss *bss;
u8 *elements;
struct ieee80211_channel *channel; struct ieee80211_channel *channel;
size_t baselen;
struct ieee802_11_elems elems;
if (skb->len < 24 || if (skb->len < 24 ||
(!ieee80211_is_probe_resp(mgmt->frame_control) && (!ieee80211_is_probe_resp(mgmt->frame_control) &&
...@@ -244,26 +277,15 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) ...@@ -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, !ieee80211_scan_accept_presp(sdata2, sched_scan_req_flags,
mgmt->da)) mgmt->da))
return; 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); channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
return; return;
bss = ieee80211_bss_info_update(local, rx_status, bss = ieee80211_bss_info_update(local, rx_status,
mgmt, skb->len, &elems, mgmt, skb->len,
channel); channel);
if (bss) if (bss)
ieee80211_rx_bss_put(local, bss); ieee80211_rx_bss_put(local, bss);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* Copyright 2014, Intel Corporation * Copyright 2014, Intel Corporation
* Copyright 2014 Intel Mobile Communications GmbH * Copyright 2014 Intel Mobile Communications GmbH
* Copyright 2015 - 2016 Intel Deutschland GmbH * Copyright 2015 - 2016 Intel Deutschland GmbH
* Copyright (C) 2019 Intel Corporation
* *
* This file is GPLv2 as found in COPYING. * This file is GPLv2 as found in COPYING.
*/ */
...@@ -1716,7 +1717,8 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata, ...@@ -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, 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) { if (elems.parse_error) {
tdls_dbg(sdata, "Invalid IEs in TDLS channel switch resp\n"); tdls_dbg(sdata, "Invalid IEs in TDLS channel switch resp\n");
ret = -EINVAL; ret = -EINVAL;
...@@ -1828,7 +1830,7 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata, ...@@ -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, 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) { if (elems.parse_error) {
tdls_dbg(sdata, "Invalid IEs in TDLS channel switch req\n"); tdls_dbg(sdata, "Invalid IEs in TDLS channel switch req\n");
return -EINVAL; return -EINVAL;
......
...@@ -891,19 +891,18 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, ...@@ -891,19 +891,18 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
} }
EXPORT_SYMBOL(ieee80211_queue_delayed_work); EXPORT_SYMBOL(ieee80211_queue_delayed_work);
u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, static u32
struct ieee802_11_elems *elems, _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
u64 filter, u32 crc) 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; bool calc_crc = filter != 0;
DECLARE_BITMAP(seen_elems, 256); DECLARE_BITMAP(seen_elems, 256);
const u8 *ie; const u8 *ie;
bitmap_zero(seen_elems, 256); bitmap_zero(seen_elems, 256);
memset(elems, 0, sizeof(*elems));
elems->ie_start = start;
elems->total_len = len;
for_each_element(elem, start, len) { for_each_element(elem, start, len) {
bool elem_parse_failed; bool elem_parse_failed;
...@@ -1209,6 +1208,57 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, ...@@ -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)) if (elen >= sizeof(*elems->max_idle_period_ie))
elems->max_idle_period_ie = (void *)pos; elems->max_idle_period_ie = (void *)pos;
break; 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: case WLAN_EID_EXTENSION:
if (pos[0] == WLAN_EID_EXT_HE_MU_EDCA && if (pos[0] == WLAN_EID_EXT_HE_MU_EDCA &&
elen >= (sizeof(*elems->mu_edca_param_set) + 1)) { 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, ...@@ -1224,6 +1274,10 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
elems->he_operation = (void *)&pos[1]; elems->he_operation = (void *)&pos[1];
} else if (pos[0] == WLAN_EID_EXT_UORA && elen >= 1) { } else if (pos[0] == WLAN_EID_EXT_UORA && elen >= 1) {
elems->uora_element = (void *)&pos[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; break;
default: default:
...@@ -1242,6 +1296,48 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, ...@@ -1242,6 +1296,48 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
return crc; 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, void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
struct ieee80211_tx_queue_params struct ieee80211_tx_queue_params
*qparam, int ac) *qparam, int ac)
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* Wireless configuration interface internals. * Wireless configuration interface internals.
* *
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright (C) 2018 Intel Corporation * Copyright (C) 2018-2019 Intel Corporation
*/ */
#ifndef __NET_WIRELESS_CORE_H #ifndef __NET_WIRELESS_CORE_H
#define __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 ...@@ -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) static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss)
{ {
atomic_inc(&bss->hold); 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) static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss)
{ {
int r = atomic_dec_return(&bss->hold); int r = atomic_dec_return(&bss->hold);
WARN_ON(r < 0); 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 @@ ...@@ -5,6 +5,7 @@
* Copyright 2008 Johannes Berg <johannes@sipsolutions.net> * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2016 Intel Deutschland GmbH * Copyright 2016 Intel Deutschland GmbH
* Copyright (C) 2018-2019 Intel Corporation
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -109,6 +110,12 @@ static inline void bss_ref_get(struct cfg80211_registered_device *rdev, ...@@ -109,6 +110,12 @@ static inline void bss_ref_get(struct cfg80211_registered_device *rdev,
pub); pub);
bss->refcount++; 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, 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, ...@@ -125,6 +132,18 @@ static inline void bss_ref_put(struct cfg80211_registered_device *rdev,
if (hbss->refcount == 0) if (hbss->refcount == 0)
bss_free(hbss); 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--; bss->refcount--;
if (bss->refcount == 0) if (bss->refcount == 0)
bss_free(bss); bss_free(bss);
...@@ -150,6 +169,7 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev, ...@@ -150,6 +169,7 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,
} }
list_del_init(&bss->list); list_del_init(&bss->list);
list_del_init(&bss->pub.nontrans_list);
rb_erase(&bss->rbn, &rdev->bss_tree); rb_erase(&bss->rbn, &rdev->bss_tree);
rdev->bss_entries--; rdev->bss_entries--;
WARN_ONCE((rdev->bss_entries == 0) ^ list_empty(&rdev->bss_list), 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, ...@@ -159,6 +179,162 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,
return true; 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, static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev,
unsigned long expire_time) unsigned long expire_time)
{ {
...@@ -518,29 +694,6 @@ const struct element *cfg80211_find_vendor_elem(unsigned int oui, int oui_type, ...@@ -518,29 +694,6 @@ const struct element *cfg80211_find_vendor_elem(unsigned int oui, int oui_type,
} }
EXPORT_SYMBOL(cfg80211_find_vendor_elem); 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 * enum bss_compare_mode - BSS compare mode
* @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find) * @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, ...@@ -875,6 +1028,12 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev,
return true; 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. */ /* Returned bss is reference counted and must be cleaned up appropriately. */
static struct cfg80211_internal_bss * static struct cfg80211_internal_bss *
cfg80211_bss_update(struct cfg80211_registered_device *rdev, cfg80211_bss_update(struct cfg80211_registered_device *rdev,
...@@ -978,6 +1137,8 @@ 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, memcpy(found->pub.chain_signal, tmp->pub.chain_signal,
IEEE80211_MAX_CHAINS); IEEE80211_MAX_CHAINS);
ether_addr_copy(found->parent_bssid, tmp->parent_bssid); 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 { } else {
struct cfg80211_internal_bss *new; struct cfg80211_internal_bss *new;
struct cfg80211_internal_bss *hidden; struct cfg80211_internal_bss *hidden;
...@@ -1002,6 +1163,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, ...@@ -1002,6 +1163,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
memcpy(new, tmp, sizeof(*new)); memcpy(new, tmp, sizeof(*new));
new->refcount = 1; new->refcount = 1;
INIT_LIST_HEAD(&new->hidden_list); INIT_LIST_HEAD(&new->hidden_list);
INIT_LIST_HEAD(&new->pub.nontrans_list);
if (rcu_access_pointer(tmp->pub.proberesp_ies)) { if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN); hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN);
...@@ -1035,6 +1197,17 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, ...@@ -1035,6 +1197,17 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
goto drop; 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); list_add_tail(&new->list, &rdev->bss_list);
rdev->bss_entries++; rdev->bss_entries++;
rb_insert_bss(rdev, new); rb_insert_bss(rdev, new);
...@@ -1123,14 +1296,16 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen, ...@@ -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. */ /* Returned bss is reference counted and must be cleaned up appropriately. */
struct cfg80211_bss * static struct cfg80211_bss *
cfg80211_inform_bss_data(struct wiphy *wiphy, cfg80211_inform_single_bss_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data, struct cfg80211_inform_bss *data,
enum cfg80211_bss_frame_type ftype, enum cfg80211_bss_frame_type ftype,
const u8 *bssid, u64 tsf, u16 capability, const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen, u16 beacon_interval, const u8 *ie, size_t ielen,
gfp_t gfp) 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 cfg80211_bss_ies *ies;
struct ieee80211_channel *channel; struct ieee80211_channel *channel;
struct cfg80211_internal_bss tmp = {}, *res; struct cfg80211_internal_bss tmp = {}, *res;
...@@ -1156,6 +1331,11 @@ cfg80211_inform_bss_data(struct wiphy *wiphy, ...@@ -1156,6 +1331,11 @@ cfg80211_inform_bss_data(struct wiphy *wiphy,
tmp.pub.beacon_interval = beacon_interval; tmp.pub.beacon_interval = beacon_interval;
tmp.pub.capability = capability; tmp.pub.capability = capability;
tmp.ts_boottime = data->boottime_ns; 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 * 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, ...@@ -1202,19 +1382,247 @@ cfg80211_inform_bss_data(struct wiphy *wiphy,
regulatory_hint_found_beacon(wiphy, channel, gfp); 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); trace_cfg80211_return_bss(&res->pub);
/* cfg80211_bss_update gives us a referenced result */ /* cfg80211_bss_update gives us a referenced result */
return &res->pub; 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 * struct cfg80211_bss *
cfg80211_inform_bss_frame_data(struct wiphy *wiphy, cfg80211_inform_bss_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data, struct cfg80211_inform_bss *data,
struct ieee80211_mgmt *mgmt, size_t len, enum cfg80211_bss_frame_type ftype,
gfp_t gfp) 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_internal_bss tmp = {}, *res;
struct cfg80211_bss_ies *ies; struct cfg80211_bss_ies *ies;
...@@ -1272,6 +1680,11 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy, ...@@ -1272,6 +1680,11 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
tmp.pub.chains = data->chains; tmp.pub.chains = data->chains;
memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS); memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS);
ether_addr_copy(tmp.parent_bssid, data->parent_bssid); 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) <= signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
wiphy->max_adj_channel_rssi_comp; wiphy->max_adj_channel_rssi_comp;
...@@ -1293,6 +1706,53 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy, ...@@ -1293,6 +1706,53 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
/* cfg80211_bss_update gives us a referenced result */ /* cfg80211_bss_update gives us a referenced result */
return &res->pub; 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); EXPORT_SYMBOL(cfg80211_inform_bss_frame_data);
void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
...@@ -1330,7 +1790,8 @@ EXPORT_SYMBOL(cfg80211_put_bss); ...@@ -1330,7 +1790,8 @@ EXPORT_SYMBOL(cfg80211_put_bss);
void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
{ {
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 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)) if (WARN_ON(!pub))
return; return;
...@@ -1338,10 +1799,21 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) ...@@ -1338,10 +1799,21 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
bss = container_of(pub, struct cfg80211_internal_bss, pub); bss = container_of(pub, struct cfg80211_internal_bss, pub);
spin_lock_bh(&rdev->bss_lock); spin_lock_bh(&rdev->bss_lock);
if (!list_empty(&bss->list)) { if (list_empty(&bss->list))
if (__cfg80211_unlink_bss(rdev, bss)) 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++; rdev->bss_generation++;
} }
if (__cfg80211_unlink_bss(rdev, bss))
rdev->bss_generation++;
out:
spin_unlock_bh(&rdev->bss_lock); spin_unlock_bh(&rdev->bss_lock);
} }
EXPORT_SYMBOL(cfg80211_unlink_bss); 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