Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

Conflicts:
	drivers/net/wireless/mwifiex/sta_ioctl.c
	net/mac80211/scan.c
This commit is contained in:
John W. Linville 2012-12-06 14:58:41 -05:00
commit 403e16731f
35 changed files with 1134 additions and 546 deletions

View File

@ -298,6 +298,7 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss)
const u8 *rates_eid, *ext_rates_eid; const u8 *rates_eid, *ext_rates_eid;
int n = 0; int n = 0;
rcu_read_lock();
rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
@ -325,6 +326,7 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss)
*tlv++ = 0x96; *tlv++ = 0x96;
n = 4; n = 4;
} }
rcu_read_unlock();
rate_tlv->header.len = cpu_to_le16(n); rate_tlv->header.len = cpu_to_le16(n);
return sizeof(rate_tlv->header) + n; return sizeof(rate_tlv->header) + n;
@ -1140,11 +1142,13 @@ static int lbs_associate(struct lbs_private *priv,
cmd->capability = cpu_to_le16(bss->capability); cmd->capability = cpu_to_le16(bss->capability);
/* add SSID TLV */ /* add SSID TLV */
rcu_read_lock();
ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
if (ssid_eid) if (ssid_eid)
pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]); pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]);
else else
lbs_deb_assoc("no SSID\n"); lbs_deb_assoc("no SSID\n");
rcu_read_unlock();
/* add DS param TLV */ /* add DS param TLV */
if (bss->channel) if (bss->channel)
@ -1782,7 +1786,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv,
struct cfg80211_ibss_params *params, struct cfg80211_ibss_params *params,
struct cfg80211_bss *bss) struct cfg80211_bss *bss)
{ {
const u8 *rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); const u8 *rates_eid;
struct cmd_ds_802_11_ad_hoc_join cmd; struct cmd_ds_802_11_ad_hoc_join cmd;
u8 preamble = RADIO_PREAMBLE_SHORT; u8 preamble = RADIO_PREAMBLE_SHORT;
int ret = 0; int ret = 0;
@ -1841,6 +1845,8 @@ static int lbs_ibss_join_existing(struct lbs_private *priv,
/* set rates to the intersection of our rates and the rates in the /* set rates to the intersection of our rates and the rates in the
bss */ bss */
rcu_read_lock();
rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
if (!rates_eid) { if (!rates_eid) {
lbs_add_rates(cmd.bss.rates); lbs_add_rates(cmd.bss.rates);
} else { } else {
@ -1860,6 +1866,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv,
} }
} }
} }
rcu_read_unlock();
/* Only v8 and below support setting this */ /* Only v8 and below support setting this */
if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) {

View File

@ -1347,9 +1347,14 @@ static void hw_scan_work(struct work_struct *work)
hwsim->hw_scan_vif, hwsim->hw_scan_vif,
req->ssids[i].ssid, req->ssids[i].ssid,
req->ssids[i].ssid_len, req->ssids[i].ssid_len,
req->ie, req->ie_len); req->ie_len);
if (!probe) if (!probe)
continue; continue;
if (req->ie_len)
memcpy(skb_put(probe, req->ie_len), req->ie,
req->ie_len);
local_bh_disable(); local_bh_disable();
mac80211_hwsim_tx_frame(hwsim->hw, probe, mac80211_hwsim_tx_frame(hwsim->hw, probe,
hwsim->tmp_chan); hwsim->tmp_chan);

View File

@ -160,11 +160,21 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
{ {
int ret; int ret;
u8 *beacon_ie; u8 *beacon_ie;
size_t beacon_ie_len;
struct mwifiex_bss_priv *bss_priv = (void *)bss->priv; struct mwifiex_bss_priv *bss_priv = (void *)bss->priv;
size_t beacon_ie_len = bss->len_information_elements; const struct cfg80211_bss_ies *ies;
rcu_read_lock();
ies = rcu_dereference(bss->ies);
if (WARN_ON(!ies)) {
/* should never happen */
rcu_read_unlock();
return -EINVAL;
}
beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC);
beacon_ie_len = ies->len;
rcu_read_unlock();
beacon_ie = kmemdup(bss->information_elements, beacon_ie_len,
GFP_KERNEL);
if (!beacon_ie) { if (!beacon_ie) {
dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n"); dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n");
return -ENOMEM; return -ENOMEM;
@ -199,18 +209,23 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
static int mwifiex_process_country_ie(struct mwifiex_private *priv, static int mwifiex_process_country_ie(struct mwifiex_private *priv,
struct cfg80211_bss *bss) struct cfg80211_bss *bss)
{ {
u8 *country_ie, country_ie_len; const u8 *country_ie;
u8 country_ie_len;
struct mwifiex_802_11d_domain_reg *domain_info = struct mwifiex_802_11d_domain_reg *domain_info =
&priv->adapter->domain_reg; &priv->adapter->domain_reg;
country_ie = (u8 *)ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); rcu_read_lock();
country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
if (!country_ie) if (!country_ie) {
rcu_read_unlock();
return 0; return 0;
}
country_ie_len = country_ie[1]; country_ie_len = country_ie[1];
if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) {
rcu_read_unlock();
return 0; return 0;
}
domain_info->country_code[0] = country_ie[2]; domain_info->country_code[0] = country_ie[2];
domain_info->country_code[1] = country_ie[3]; domain_info->country_code[1] = country_ie[3];
@ -224,6 +239,8 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv,
memcpy((u8 *)domain_info->triplet, memcpy((u8 *)domain_info->triplet,
&country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len);
rcu_read_unlock();
if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
HostCmd_ACT_GEN_SET, 0, NULL)) { HostCmd_ACT_GEN_SET, 0, NULL)) {
wiphy_err(priv->adapter->wiphy, wiphy_err(priv->adapter->wiphy,

View File

@ -896,11 +896,13 @@ static int wl1251_op_hw_scan(struct ieee80211_hw *hw,
goto out; goto out;
skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len,
req->ie, req->ie_len); req->ie_len);
if (!skb) { if (!skb) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
} }
if (req->ie_len)
memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len);
ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, skb->data, ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, skb->data,
skb->len); skb->len);

View File

@ -1038,11 +1038,13 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u16 template_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5; u16 template_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len, skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
ie, ie_len); ie_len);
if (!skb) { if (!skb) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
} }
if (ie_len)
memcpy(skb_put(skb, ie_len), ie, ie_len);
wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len); wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len);

View File

@ -1212,6 +1212,21 @@ struct ieee80211_vht_cap {
struct ieee80211_vht_mcs_info supp_mcs; struct ieee80211_vht_mcs_info supp_mcs;
} __packed; } __packed;
/**
* enum ieee80211_vht_chanwidth - VHT channel width
* @IEEE80211_VHT_CHANWIDTH_USE_HT: use the HT operation IE to
* determine the channel width (20 or 40 MHz)
* @IEEE80211_VHT_CHANWIDTH_80MHZ: 80 MHz bandwidth
* @IEEE80211_VHT_CHANWIDTH_160MHZ: 160 MHz bandwidth
* @IEEE80211_VHT_CHANWIDTH_80P80MHZ: 80+80 MHz bandwidth
*/
enum ieee80211_vht_chanwidth {
IEEE80211_VHT_CHANWIDTH_USE_HT = 0,
IEEE80211_VHT_CHANWIDTH_80MHZ = 1,
IEEE80211_VHT_CHANWIDTH_160MHZ = 2,
IEEE80211_VHT_CHANWIDTH_80P80MHZ = 3,
};
/** /**
* struct ieee80211_vht_operation - VHT operation IE * struct ieee80211_vht_operation - VHT operation IE
* *

View File

@ -58,6 +58,8 @@
* structures here describe these capabilities in detail. * structures here describe these capabilities in detail.
*/ */
struct wiphy;
/* /*
* wireless hardware capability structures * wireless hardware capability structures
*/ */
@ -387,6 +389,22 @@ const struct cfg80211_chan_def *
cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1, cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1,
const struct cfg80211_chan_def *chandef2); const struct cfg80211_chan_def *chandef2);
/**
* cfg80211_chandef_valid - check if a channel definition is valid
* @chandef: the channel definition to check
*/
bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef);
/**
* cfg80211_chandef_usable - check if secondary channels can be used
* @wiphy: the wiphy to validate against
* @chandef: the channel definition to check
* @prohibited_flags: the regulatory chanenl flags that must not be set
*/
bool cfg80211_chandef_usable(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef,
u32 prohibited_flags);
/** /**
* enum survey_info_flags - survey information flags * enum survey_info_flags - survey information flags
* *
@ -520,6 +538,8 @@ struct cfg80211_beacon_data {
* @privacy: the BSS uses privacy * @privacy: the BSS uses privacy
* @auth_type: Authentication type (algorithm) * @auth_type: Authentication type (algorithm)
* @inactivity_timeout: time in seconds to determine station's inactivity. * @inactivity_timeout: time in seconds to determine station's inactivity.
* @p2p_ctwindow: P2P CT Window
* @p2p_opp_ps: P2P opportunistic PS
*/ */
struct cfg80211_ap_settings { struct cfg80211_ap_settings {
struct cfg80211_chan_def chandef; struct cfg80211_chan_def chandef;
@ -534,6 +554,8 @@ struct cfg80211_ap_settings {
bool privacy; bool privacy;
enum nl80211_auth_type auth_type; enum nl80211_auth_type auth_type;
int inactivity_timeout; int inactivity_timeout;
u8 p2p_ctwindow;
bool p2p_opp_ps;
}; };
/** /**
@ -895,6 +917,8 @@ struct mpath_info {
* @ap_isolate: do not forward packets between connected stations * @ap_isolate: do not forward packets between connected stations
* @ht_opmode: HT Operation mode * @ht_opmode: HT Operation mode
* (u16 = opmode, -1 = do not change) * (u16 = opmode, -1 = do not change)
* @p2p_ctwindow: P2P CT Window (-1 = no change)
* @p2p_opp_ps: P2P opportunistic PS (-1 = no change)
*/ */
struct bss_parameters { struct bss_parameters {
int use_cts_prot; int use_cts_prot;
@ -904,6 +928,7 @@ struct bss_parameters {
u8 basic_rates_len; u8 basic_rates_len;
int ap_isolate; int ap_isolate;
int ht_opmode; int ht_opmode;
s8 p2p_ctwindow, p2p_opp_ps;
}; };
/** /**
@ -1045,9 +1070,6 @@ struct ieee80211_txq_params {
u8 aifs; u8 aifs;
}; };
/* from net/wireless.h */
struct wiphy;
/** /**
* DOC: Scanning and BSS list handling * DOC: Scanning and BSS list handling
* *
@ -1183,6 +1205,18 @@ enum cfg80211_signal_type {
CFG80211_SIGNAL_TYPE_UNSPEC, CFG80211_SIGNAL_TYPE_UNSPEC,
}; };
/**
* struct cfg80211_bss_ie_data - BSS entry IE data
* @rcu_head: internal use, for freeing
* @len: length of the IEs
* @data: IE data
*/
struct cfg80211_bss_ies {
struct rcu_head rcu_head;
int len;
u8 data[];
};
/** /**
* struct cfg80211_bss - BSS description * struct cfg80211_bss - BSS description
* *
@ -1194,36 +1228,34 @@ enum cfg80211_signal_type {
* @tsf: timestamp of last received update * @tsf: timestamp of last received update
* @beacon_interval: the beacon interval as from the frame * @beacon_interval: the beacon interval as from the frame
* @capability: the capability field in host byte order * @capability: the capability field in host byte order
* @information_elements: the information elements (Note that there * @ies: the information elements (Note that there
* is no guarantee that these are well-formed!); this is a pointer to * is no guarantee that these are well-formed!); this is a pointer to
* either the beacon_ies or proberesp_ies depending on whether Probe * either the beacon_ies or proberesp_ies depending on whether Probe
* Response frame has been received * Response frame has been received
* @len_information_elements: total length of the information elements
* @beacon_ies: the information elements from the last Beacon frame * @beacon_ies: the information elements from the last Beacon frame
* @len_beacon_ies: total length of the beacon_ies
* @proberesp_ies: the information elements from the last Probe Response frame * @proberesp_ies: the information elements from the last Probe Response frame
* @len_proberesp_ies: total length of the proberesp_ies
* @signal: signal strength value (type depends on the wiphy's signal_type) * @signal: signal strength value (type depends on the wiphy's signal_type)
* @free_priv: function pointer to free private data * @free_priv: function pointer to free private data
* @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 {
u64 tsf;
struct ieee80211_channel *channel; struct ieee80211_channel *channel;
u8 bssid[ETH_ALEN]; const struct cfg80211_bss_ies __rcu *ies;
u64 tsf; const struct cfg80211_bss_ies __rcu *beacon_ies;
u16 beacon_interval; const struct cfg80211_bss_ies __rcu *proberesp_ies;
u16 capability;
u8 *information_elements; void (*free_priv)(struct cfg80211_bss *bss);
size_t len_information_elements;
u8 *beacon_ies;
size_t len_beacon_ies;
u8 *proberesp_ies;
size_t len_proberesp_ies;
s32 signal; s32 signal;
void (*free_priv)(struct cfg80211_bss *bss); u16 beacon_interval;
u16 capability;
u8 bssid[ETH_ALEN];
u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); u8 priv[0] __attribute__((__aligned__(sizeof(void *))));
}; };
@ -1231,6 +1263,9 @@ struct cfg80211_bss {
* ieee80211_bss_get_ie - find IE with given ID * ieee80211_bss_get_ie - find IE with given ID
* @bss: the bss to search * @bss: the bss to search
* @ie: the IE ID * @ie: the IE ID
*
* Note that the return value is an RCU-protected pointer, so
* rcu_read_lock() must be held when calling this function.
* Returns %NULL if not found. * Returns %NULL if not found.
*/ */
const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie); const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie);

View File

@ -186,6 +186,10 @@ struct ieee80211_radiotap_header {
* IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitless * IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitless
* *
* Contains the AMPDU information for the subframe. * Contains the AMPDU information for the subframe.
*
* IEEE80211_RADIOTAP_VHT u16, u8, u8, u8[4], u8, u8, u16
*
* Contains VHT information about this frame.
*/ */
enum ieee80211_radiotap_type { enum ieee80211_radiotap_type {
IEEE80211_RADIOTAP_TSFT = 0, IEEE80211_RADIOTAP_TSFT = 0,
@ -209,6 +213,7 @@ enum ieee80211_radiotap_type {
IEEE80211_RADIOTAP_MCS = 19, IEEE80211_RADIOTAP_MCS = 19,
IEEE80211_RADIOTAP_AMPDU_STATUS = 20, IEEE80211_RADIOTAP_AMPDU_STATUS = 20,
IEEE80211_RADIOTAP_VHT = 21,
/* valid in every it_present bitmap, even vendor namespaces */ /* valid in every it_present bitmap, even vendor namespaces */
IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29,
@ -282,6 +287,25 @@ enum ieee80211_radiotap_type {
#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010 #define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010
#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020 #define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020
/* For IEEE80211_RADIOTAP_VHT */
#define IEEE80211_RADIOTAP_VHT_KNOWN_STBC 0x0001
#define IEEE80211_RADIOTAP_VHT_KNOWN_TXOP_PS_NA 0x0002
#define IEEE80211_RADIOTAP_VHT_KNOWN_GI 0x0004
#define IEEE80211_RADIOTAP_VHT_KNOWN_SGI_NSYM_DIS 0x0008
#define IEEE80211_RADIOTAP_VHT_KNOWN_LDPC_EXTRA_OFDM_SYM 0x0010
#define IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED 0x0020
#define IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH 0x0040
#define IEEE80211_RADIOTAP_VHT_KNOWN_GROUP_ID 0x0080
#define IEEE80211_RADIOTAP_VHT_KNOWN_PARTIAL_AID 0x0100
#define IEEE80211_RADIOTAP_VHT_FLAG_STBC 0x01
#define IEEE80211_RADIOTAP_VHT_FLAG_TXOP_PS_NA 0x02
#define IEEE80211_RADIOTAP_VHT_FLAG_SGI 0x04
#define IEEE80211_RADIOTAP_VHT_FLAG_SGI_NSYM_M10_9 0x08
#define IEEE80211_RADIOTAP_VHT_FLAG_LDPC_EXTRA_OFDM_SYM 0x10
#define IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED 0x20
/* helpers */ /* helpers */
static inline int ieee80211_get_radiotap_len(unsigned char *data) static inline int ieee80211_get_radiotap_len(unsigned char *data)
{ {

View File

@ -164,7 +164,7 @@ enum ieee80211_chanctx_change {
* active on the channel to receive MIMO transmissions * active on the channel to receive MIMO transmissions
* @rx_chains_dynamic: The number of RX chains that must be enabled * @rx_chains_dynamic: The number of RX chains that must be enabled
* after RTS/CTS handshake to receive SMPS MIMO transmissions; * after RTS/CTS handshake to receive SMPS MIMO transmissions;
* this will always be >= @rx_chains_always. * this will always be >= @rx_chains_static.
* @drv_priv: data area for driver use, will always be aligned to * @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *), size is determined in hw information. * sizeof(void *), size is determined in hw information.
*/ */
@ -1473,6 +1473,10 @@ enum ieee80211_hw_flags {
* include _FMT. Use %IEEE80211_RADIOTAP_MCS_HAVE_* values, only * include _FMT. Use %IEEE80211_RADIOTAP_MCS_HAVE_* values, only
* adding _BW is supported today. * adding _BW is supported today.
* *
* @radiotap_vht_details: lists which VHT MCS information the HW reports,
* the default is _GI | _BANDWIDTH.
* Use the %IEEE80211_RADIOTAP_VHT_KNOWN_* values.
*
* @netdev_features: netdev features to be set in each netdev created * @netdev_features: netdev features to be set in each netdev created
* from this HW. Note only HW checksum features are currently * from this HW. Note only HW checksum features are currently
* compatible with mac80211. Other feature bits will be rejected. * compatible with mac80211. Other feature bits will be rejected.
@ -1499,6 +1503,7 @@ struct ieee80211_hw {
u8 max_tx_aggregation_subframes; u8 max_tx_aggregation_subframes;
u8 offchannel_tx_hw_queue; u8 offchannel_tx_hw_queue;
u8 radiotap_mcs_details; u8 radiotap_mcs_details;
u16 radiotap_vht_details;
netdev_features_t netdev_features; netdev_features_t netdev_features;
}; };
@ -3139,8 +3144,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
* @vif: &struct ieee80211_vif pointer from the add_interface callback. * @vif: &struct ieee80211_vif pointer from the add_interface callback.
* @ssid: SSID buffer * @ssid: SSID buffer
* @ssid_len: length of SSID * @ssid_len: length of SSID
* @ie: buffer containing all IEs except SSID for the template * @tailroom: tailroom to reserve at end of SKB for IEs
* @ie_len: length of the IE buffer
* *
* Creates a Probe Request template which can, for example, be uploaded to * Creates a Probe Request template which can, for example, be uploaded to
* hardware. * hardware.
@ -3148,7 +3152,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
const u8 *ssid, size_t ssid_len, const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len); size_t tailroom);
/** /**
* ieee80211_rts_get - RTS frame generation function * ieee80211_rts_get - RTS frame generation function

View File

@ -1303,6 +1303,13 @@ enum nl80211_commands {
* *
* @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32) * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32)
* *
* @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with
* the START_AP and SET_BSS commands
* @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the
* START_AP and SET_BSS commands. This can have the values 0 or 1;
* if not given in START_AP 0 is assumed, if not given in SET_BSS
* no change is made.
*
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
*/ */
@ -1570,6 +1577,9 @@ enum nl80211_attrs {
NL80211_ATTR_CENTER_FREQ1, NL80211_ATTR_CENTER_FREQ1,
NL80211_ATTR_CENTER_FREQ2, NL80211_ATTR_CENTER_FREQ2,
NL80211_ATTR_P2P_CTWINDOW,
NL80211_ATTR_P2P_OPPPS,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
@ -3126,6 +3136,10 @@ enum nl80211_ap_sme_features {
* @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform
* OBSS scans and generate 20/40 BSS coex reports. This flag is used only * OBSS scans and generate 20/40 BSS coex reports. This flag is used only
* for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied. * for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied.
* @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window
* setting
* @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic
* powersave
*/ */
enum nl80211_feature_flags { enum nl80211_feature_flags {
NL80211_FEATURE_SK_TX_STATUS = 1 << 0, NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
@ -3139,6 +3153,8 @@ enum nl80211_feature_flags {
NL80211_FEATURE_AP_SCAN = 1 << 8, NL80211_FEATURE_AP_SCAN = 1 << 8,
NL80211_FEATURE_VIF_TXPOWER = 1 << 9, NL80211_FEATURE_VIF_TXPOWER = 1 << 9,
NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10,
NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11,
NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12,
}; };
/** /**

View File

@ -398,6 +398,38 @@ void sta_set_rate_info_tx(struct sta_info *sta,
rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
} }
void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
{
rinfo->flags = 0;
if (sta->last_rx_rate_flag & RX_FLAG_HT) {
rinfo->flags |= RATE_INFO_FLAGS_MCS;
rinfo->mcs = sta->last_rx_rate_idx;
} else if (sta->last_rx_rate_flag & RX_FLAG_VHT) {
rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
rinfo->nss = sta->last_rx_rate_vht_nss;
rinfo->mcs = sta->last_rx_rate_idx;
} else {
struct ieee80211_supported_band *sband;
sband = sta->local->hw.wiphy->bands[
ieee80211_get_sdata_band(sta->sdata)];
rinfo->legacy =
sband->bitrates[sta->last_rx_rate_idx].bitrate;
}
if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
if (sta->last_rx_rate_flag & RX_FLAG_80MHZ)
rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ)
rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
if (sta->last_rx_rate_flag & RX_FLAG_160MHZ)
rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
}
static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
{ {
struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_sub_if_data *sdata = sta->sdata;
@ -444,34 +476,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
} }
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
sta_set_rate_info_rx(sta, &sinfo->rxrate);
sinfo->rxrate.flags = 0;
if (sta->last_rx_rate_flag & RX_FLAG_HT) {
sinfo->rxrate.flags |= RATE_INFO_FLAGS_MCS;
sinfo->rxrate.mcs = sta->last_rx_rate_idx;
} else if (sta->last_rx_rate_flag & RX_FLAG_VHT) {
sinfo->rxrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
sinfo->rxrate.nss = sta->last_rx_rate_vht_nss;
sinfo->rxrate.mcs = sta->last_rx_rate_idx;
} else {
struct ieee80211_supported_band *sband;
sband = sta->local->hw.wiphy->bands[
ieee80211_get_sdata_band(sta->sdata)];
sinfo->rxrate.legacy =
sband->bitrates[sta->last_rx_rate_idx].bitrate;
}
if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
sinfo->rxrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
if (sta->last_rx_rate_flag & RX_FLAG_80MHZ)
sinfo->rxrate.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ)
sinfo->rxrate.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
if (sta->last_rx_rate_flag & RX_FLAG_160MHZ)
sinfo->rxrate.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
if (ieee80211_vif_is_mesh(&sdata->vif)) { if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
@ -893,7 +898,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
u32 changed = BSS_CHANGED_BEACON_INT | u32 changed = BSS_CHANGED_BEACON_INT |
BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_BEACON | BSS_CHANGED_BEACON |
BSS_CHANGED_SSID; BSS_CHANGED_SSID |
BSS_CHANGED_P2P_PS;
int err; int err;
old = rtnl_dereference(sdata->u.ap.beacon); old = rtnl_dereference(sdata->u.ap.beacon);
@ -932,6 +938,9 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
sdata->vif.bss_conf.hidden_ssid = sdata->vif.bss_conf.hidden_ssid =
(params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE); (params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE);
sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow;
sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps;
err = ieee80211_assign_beacon(sdata, &params->beacon); err = ieee80211_assign_beacon(sdata, &params->beacon);
if (err < 0) if (err < 0)
return err; return err;
@ -1807,6 +1816,16 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
changed |= BSS_CHANGED_HT; changed |= BSS_CHANGED_HT;
} }
if (params->p2p_ctwindow >= 0) {
sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow;
changed |= BSS_CHANGED_P2P_PS;
}
if (params->p2p_opp_ps >= 0) {
sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps;
changed |= BSS_CHANGED_P2P_PS;
}
ieee80211_bss_info_change_notify(sdata, changed); ieee80211_bss_info_change_notify(sdata, changed);
return 0; return 0;

View File

@ -53,6 +53,7 @@ static const struct file_operations sta_ ##name## _ops = { \
STA_FILE(aid, sta.aid, D); STA_FILE(aid, sta.aid, D);
STA_FILE(dev, sdata->name, S); STA_FILE(dev, sdata->name, S);
STA_FILE(last_signal, last_signal, D); STA_FILE(last_signal, last_signal, D);
STA_FILE(last_ack_signal, last_ack_signal, D);
static ssize_t sta_flags_read(struct file *file, char __user *userbuf, static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
@ -321,6 +322,38 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
} }
STA_OPS(ht_capa); STA_OPS(ht_capa);
static ssize_t sta_current_tx_rate_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct sta_info *sta = file->private_data;
struct rate_info rinfo;
u16 rate;
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo);
rate = cfg80211_calculate_bitrate(&rinfo);
return mac80211_format_buffer(userbuf, count, ppos,
"%d.%d MBit/s\n",
rate/10, rate%10);
}
STA_OPS(current_tx_rate);
static ssize_t sta_last_rx_rate_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct sta_info *sta = file->private_data;
struct rate_info rinfo;
u16 rate;
sta_set_rate_info_rx(sta, &rinfo);
rate = cfg80211_calculate_bitrate(&rinfo);
return mac80211_format_buffer(userbuf, count, ppos,
"%d.%d MBit/s\n",
rate/10, rate%10);
}
STA_OPS(last_rx_rate);
#define DEBUGFS_ADD(name) \ #define DEBUGFS_ADD(name) \
debugfs_create_file(#name, 0400, \ debugfs_create_file(#name, 0400, \
sta->debugfs.dir, sta, &sta_ ##name## _ops); sta->debugfs.dir, sta, &sta_ ##name## _ops);
@ -369,6 +402,9 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD(dev); DEBUGFS_ADD(dev);
DEBUGFS_ADD(last_signal); DEBUGFS_ADD(last_signal);
DEBUGFS_ADD(ht_capa); DEBUGFS_ADD(ht_capa);
DEBUGFS_ADD(last_ack_signal);
DEBUGFS_ADD(current_tx_rate);
DEBUGFS_ADD(last_rx_rate);
DEBUGFS_ADD_COUNTER(rx_packets, rx_packets); DEBUGFS_ADD_COUNTER(rx_packets, rx_packets);
DEBUGFS_ADD_COUNTER(tx_packets, tx_packets); DEBUGFS_ADD_COUNTER(tx_packets, tx_packets);

View File

@ -371,6 +371,8 @@ enum ieee80211_sta_flags {
IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9), IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9),
IEEE80211_STA_DISABLE_40MHZ = BIT(10), IEEE80211_STA_DISABLE_40MHZ = BIT(10),
IEEE80211_STA_DISABLE_VHT = BIT(11), IEEE80211_STA_DISABLE_VHT = BIT(11),
IEEE80211_STA_DISABLE_80P80MHZ = BIT(12),
IEEE80211_STA_DISABLE_160MHZ = BIT(13),
}; };
struct ieee80211_mgd_auth_data { struct ieee80211_mgd_auth_data {
@ -1032,6 +1034,7 @@ struct ieee80211_local {
enum ieee80211_band hw_scan_band; enum ieee80211_band hw_scan_band;
int scan_channel_idx; int scan_channel_idx;
int scan_ies_len; int scan_ies_len;
int hw_scan_ies_bufsize;
struct work_struct sched_scan_stopped_work; struct work_struct sched_scan_stopped_work;
struct ieee80211_sub_if_data __rcu *sched_scan_sdata; struct ieee80211_sub_if_data __rcu *sched_scan_sdata;
@ -1573,7 +1576,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, u16 stype, u16 reason, const u8 *bssid, u16 stype, u16 reason,
bool send_frame, u8 *frame_buf); bool send_frame, u8 *frame_buf);
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
const u8 *ie, size_t ie_len, size_t buffer_len, const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask, enum ieee80211_band band, u32 rate_mask,
u8 channel); u8 channel);
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,

View File

@ -223,6 +223,47 @@ static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
return 0; return 0;
} }
static int ieee80211_verify_mac(struct ieee80211_local *local, u8 *addr)
{
struct ieee80211_sub_if_data *sdata;
u64 new, mask, tmp;
u8 *m;
int ret = 0;
if (is_zero_ether_addr(local->hw.wiphy->addr_mask))
return 0;
m = addr;
new = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
m = local->hw.wiphy->addr_mask;
mask = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
continue;
m = sdata->vif.addr;
tmp = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
if ((new & ~mask) != (tmp & ~mask)) {
ret = -EINVAL;
break;
}
}
mutex_unlock(&local->iflist_mtx);
return ret;
}
static int ieee80211_change_mac(struct net_device *dev, void *addr) static int ieee80211_change_mac(struct net_device *dev, void *addr)
{ {
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@ -232,6 +273,10 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr)
if (ieee80211_sdata_running(sdata)) if (ieee80211_sdata_running(sdata))
return -EBUSY; return -EBUSY;
ret = ieee80211_verify_mac(sdata->local, sa->sa_data);
if (ret)
return ret;
ret = eth_mac_addr(dev, sa); ret = eth_mac_addr(dev, sa);
if (ret == 0) if (ret == 0)

View File

@ -474,7 +474,8 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
.tx = 0xffff, .tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) | .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) |
BIT(IEEE80211_STYPE_DEAUTH >> 4), BIT(IEEE80211_STYPE_DEAUTH >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
}, },
[NL80211_IFTYPE_STATION] = { [NL80211_IFTYPE_STATION] = {
.tx = 0xffff, .tx = 0xffff,
@ -638,6 +639,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS | local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
IEEE80211_RADIOTAP_MCS_HAVE_GI | IEEE80211_RADIOTAP_MCS_HAVE_GI |
IEEE80211_RADIOTAP_MCS_HAVE_BW; IEEE80211_RADIOTAP_MCS_HAVE_BW;
local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
local->user_power_level = IEEE80211_UNSET_POWER_LEVEL; local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;

View File

@ -391,7 +391,8 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
sta->ch_width = chandef.width; sta->ch_width = chandef.width;
} }
rate_control_rate_init(sta); if (insert)
rate_control_rate_init(sta);
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->lock);
if (insert && sta_info_insert(sta)) if (insert && sta_info_insert(sta))

View File

@ -195,11 +195,15 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
ifmsh->sync_offset_clockdrift_max); ifmsh->sync_offset_clockdrift_max);
set_bit(MESH_WORK_DRIFT_ADJUST, set_bit(MESH_WORK_DRIFT_ADJUST,
&ifmsh->wrkq_flags); &ifmsh->wrkq_flags);
ifmsh->adjusting_tbtt = true;
} else { } else {
msync_dbg(sdata, msync_dbg(sdata,
"TBTT : max clockdrift=%lld; too small to adjust\n", "TBTT : max clockdrift=%lld; too small to adjust\n",
(long long)ifmsh->sync_offset_clockdrift_max); (long long)ifmsh->sync_offset_clockdrift_max);
ifmsh->sync_offset_clockdrift_max = 0; ifmsh->sync_offset_clockdrift_max = 0;
ifmsh->adjusting_tbtt = false;
} }
spin_unlock_bh(&ifmsh->sync_offset_lock); spin_unlock_bh(&ifmsh->sync_offset_lock);
} }

View File

@ -354,6 +354,16 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
/* determine capability flags */ /* determine capability flags */
cap = vht_cap.cap; cap = vht_cap.cap;
if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) {
cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
}
if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) {
cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160;
cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
}
/* reserve and fill IE */ /* reserve and fill IE */
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
@ -543,6 +553,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
offset = noffset; offset = noffset;
} }
if (WARN_ON_ONCE((ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)))
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,
sband, chan, sdata->smps_mode); sband, chan, sdata->smps_mode);
@ -775,6 +789,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
"not handling channel switch with channel contexts\n"); "not handling channel switch with channel contexts\n");
ieee80211_queue_work(&sdata->local->hw, ieee80211_queue_work(&sdata->local->hw,
&ifmgd->csa_connection_drop_work); &ifmgd->csa_connection_drop_work);
return;
} }
mutex_lock(&sdata->local->chanctx_mtx); mutex_lock(&sdata->local->chanctx_mtx);
@ -1368,19 +1383,26 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
if (sdata->vif.p2p) { if (sdata->vif.p2p) {
u8 noa[2]; const struct cfg80211_bss_ies *ies;
int ret;
ret = cfg80211_get_p2p_attr(cbss->information_elements, rcu_read_lock();
cbss->len_information_elements, ies = rcu_dereference(cbss->ies);
IEEE80211_P2P_ATTR_ABSENCE_NOTICE, if (ies) {
noa, sizeof(noa)); u8 noa[2];
if (ret >= 2) { int ret;
bss_conf->p2p_oppps = noa[1] & 0x80;
bss_conf->p2p_ctwindow = noa[1] & 0x7f; ret = cfg80211_get_p2p_attr(
bss_info_changed |= BSS_CHANGED_P2P_PS; ies->data, ies->len,
sdata->u.mgd.p2p_noa_index = noa[0]; IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
noa, sizeof(noa));
if (ret >= 2) {
bss_conf->p2p_oppps = noa[1] & 0x80;
bss_conf->p2p_ctwindow = noa[1] & 0x7f;
bss_info_changed |= BSS_CHANGED_P2P_PS;
sdata->u.mgd.p2p_noa_index = noa[0];
}
} }
rcu_read_unlock();
} }
/* just to be sure */ /* just to be sure */
@ -1645,6 +1667,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
} else { } else {
int ssid_len; int ssid_len;
rcu_read_lock();
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
if (WARN_ON_ONCE(ssid == NULL)) if (WARN_ON_ONCE(ssid == NULL))
ssid_len = 0; ssid_len = 0;
@ -1654,6 +1677,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL, ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
0, (u32) -1, true, false, 0, (u32) -1, true, false,
ifmgd->associated->channel, false); ifmgd->associated->channel, false);
rcu_read_unlock();
} }
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
@ -1749,6 +1773,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
else else
return NULL; return NULL;
rcu_read_lock();
ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID); ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID);
if (WARN_ON_ONCE(ssid == NULL)) if (WARN_ON_ONCE(ssid == NULL))
ssid_len = 0; ssid_len = 0;
@ -1759,6 +1784,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
(u32) -1, cbss->channel, (u32) -1, cbss->channel,
ssid + 2, ssid_len, ssid + 2, ssid_len,
NULL, 0, true); NULL, 0, true);
rcu_read_unlock();
return skb; return skb;
} }
@ -2844,9 +2870,12 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
auth_data->bss->bssid, auth_data->tries, auth_data->bss->bssid, auth_data->tries,
IEEE80211_AUTH_MAX_TRIES); IEEE80211_AUTH_MAX_TRIES);
rcu_read_lock();
ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID); ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID);
if (!ssidie) if (!ssidie) {
rcu_read_unlock();
return -EINVAL; return -EINVAL;
}
/* /*
* Direct probe is sent to broadcast address as some APs * Direct probe is sent to broadcast address as some APs
* will not answer to direct packet in unassociated state. * will not answer to direct packet in unassociated state.
@ -2854,6 +2883,7 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1],
NULL, 0, (u32) -1, true, false, NULL, 0, (u32) -1, true, false,
auth_data->bss->channel, false); auth_data->bss->channel, false);
rcu_read_unlock();
} }
auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
@ -3183,106 +3213,313 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
return 0; return 0;
} }
static u32 chandef_downgrade(struct cfg80211_chan_def *c)
{
u32 ret;
int tmp;
switch (c->width) {
case NL80211_CHAN_WIDTH_20:
c->width = NL80211_CHAN_WIDTH_20_NOHT;
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
break;
case NL80211_CHAN_WIDTH_40:
c->width = NL80211_CHAN_WIDTH_20;
c->center_freq1 = c->chan->center_freq;
ret = IEEE80211_STA_DISABLE_40MHZ |
IEEE80211_STA_DISABLE_VHT;
break;
case NL80211_CHAN_WIDTH_80:
tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
/* n_P40 */
tmp /= 2;
/* freq_P40 */
c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
c->width = NL80211_CHAN_WIDTH_40;
ret = IEEE80211_STA_DISABLE_VHT;
break;
case NL80211_CHAN_WIDTH_80P80:
c->center_freq2 = 0;
c->width = NL80211_CHAN_WIDTH_80;
ret = IEEE80211_STA_DISABLE_80P80MHZ |
IEEE80211_STA_DISABLE_160MHZ;
break;
case NL80211_CHAN_WIDTH_160:
/* n_P20 */
tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
/* n_P80 */
tmp /= 4;
c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
c->width = NL80211_CHAN_WIDTH_80;
ret = IEEE80211_STA_DISABLE_80P80MHZ |
IEEE80211_STA_DISABLE_160MHZ;
break;
default:
case NL80211_CHAN_WIDTH_20_NOHT:
WARN_ON_ONCE(1);
c->width = NL80211_CHAN_WIDTH_20_NOHT;
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
break;
}
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
return ret;
}
static u32
ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
struct ieee80211_channel *channel,
const struct ieee80211_ht_operation *ht_oper,
const struct ieee80211_vht_operation *vht_oper,
struct cfg80211_chan_def *chandef)
{
struct cfg80211_chan_def vht_chandef;
u32 ht_cfreq, ret;
chandef->chan = channel;
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
chandef->center_freq1 = channel->center_freq;
chandef->center_freq2 = 0;
if (!ht_oper || !sband->ht_cap.ht_supported) {
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
goto out;
}
chandef->width = NL80211_CHAN_WIDTH_20;
ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
channel->band);
/* check that channel matches the right operating channel */
if (channel->center_freq != ht_cfreq) {
/*
* It's possible that some APs are confused here;
* Netgear WNDR3700 sometimes reports 4 higher than
* the actual channel in association responses, but
* since we look at probe response/beacon data here
* it should be OK.
*/
sdata_info(sdata,
"Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
channel->center_freq, ht_cfreq,
ht_oper->primary_chan, channel->band);
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
goto out;
}
/* check 40 MHz support, if we have it */
if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
chandef->width = NL80211_CHAN_WIDTH_40;
chandef->center_freq1 += 10;
break;
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
chandef->width = NL80211_CHAN_WIDTH_40;
chandef->center_freq1 -= 10;
break;
}
} else {
/* 40 MHz (and 80 MHz) must be supported for VHT */
ret = IEEE80211_STA_DISABLE_VHT;
goto out;
}
if (!vht_oper || !sband->vht_cap.vht_supported) {
ret = IEEE80211_STA_DISABLE_VHT;
goto out;
}
vht_chandef.chan = channel;
vht_chandef.center_freq1 =
ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx,
channel->band);
vht_chandef.center_freq2 = 0;
if (vht_oper->center_freq_seg2_idx)
vht_chandef.center_freq2 =
ieee80211_channel_to_frequency(
vht_oper->center_freq_seg2_idx,
channel->band);
switch (vht_oper->chan_width) {
case IEEE80211_VHT_CHANWIDTH_USE_HT:
vht_chandef.width = chandef->width;
break;
case IEEE80211_VHT_CHANWIDTH_80MHZ:
vht_chandef.width = NL80211_CHAN_WIDTH_80;
break;
case IEEE80211_VHT_CHANWIDTH_160MHZ:
vht_chandef.width = NL80211_CHAN_WIDTH_160;
break;
case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
break;
default:
sdata_info(sdata,
"AP VHT operation IE has invalid channel width (%d), disable VHT\n",
vht_oper->chan_width);
ret = IEEE80211_STA_DISABLE_VHT;
goto out;
}
if (!cfg80211_chandef_valid(&vht_chandef)) {
sdata_info(sdata,
"AP VHT information is invalid, disable VHT\n");
ret = IEEE80211_STA_DISABLE_VHT;
goto out;
}
if (cfg80211_chandef_identical(chandef, &vht_chandef)) {
ret = 0;
goto out;
}
if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) {
sdata_info(sdata,
"AP VHT information doesn't match HT, disable VHT\n");
ret = IEEE80211_STA_DISABLE_VHT;
goto out;
}
*chandef = vht_chandef;
ret = 0;
while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
IEEE80211_CHAN_DISABLED)) {
if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) {
ret = IEEE80211_STA_DISABLE_HT |
IEEE80211_STA_DISABLE_VHT;
goto out;
}
ret = chandef_downgrade(chandef);
}
if (chandef->width != vht_chandef.width)
sdata_info(sdata,
"local regulatory prevented using AP HT/VHT configuration, downgraded\n");
out:
WARN_ON_ONCE(!cfg80211_chandef_valid(chandef));
return ret;
}
static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *cbss)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
const u8 *ht_cap_ie, *vht_cap_ie;
const struct ieee80211_ht_cap *ht_cap;
const struct ieee80211_vht_cap *vht_cap;
u8 chains = 1;
if (ifmgd->flags & IEEE80211_STA_DISABLE_HT)
return chains;
ht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY);
if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) {
ht_cap = (void *)(ht_cap_ie + 2);
chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
/*
* TODO: use "Tx Maximum Number Spatial Streams Supported" and
* "Tx Unequal Modulation Supported" fields.
*/
}
if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
return chains;
vht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY);
if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) {
u8 nss;
u16 tx_mcs_map;
vht_cap = (void *)(vht_cap_ie + 2);
tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
for (nss = 8; nss > 0; nss--) {
if (((tx_mcs_map >> (2 * (nss - 1))) & 3) !=
IEEE80211_VHT_MCS_NOT_SUPPORTED)
break;
}
/* TODO: use "Tx Highest Supported Long GI Data Rate" field? */
chains = max(chains, nss);
}
return chains;
}
static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *cbss) struct cfg80211_bss *cbss)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
int ht_cfreq;
enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
const u8 *ht_oper_ie;
const struct ieee80211_ht_operation *ht_oper = NULL; const struct ieee80211_ht_operation *ht_oper = NULL;
const struct ieee80211_vht_operation *vht_oper = NULL;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
struct cfg80211_chan_def chandef; struct cfg80211_chan_def chandef;
int ret;
sband = local->hw.wiphy->bands[cbss->channel->band]; sband = local->hw.wiphy->bands[cbss->channel->band];
ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ; ifmgd->flags &= ~(IEEE80211_STA_DISABLE_40MHZ |
IEEE80211_STA_DISABLE_80P80MHZ |
IEEE80211_STA_DISABLE_160MHZ);
if (sband->ht_cap.ht_supported) { rcu_read_lock();
ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
cbss->information_elements, if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
cbss->len_information_elements); sband->ht_cap.ht_supported) {
const u8 *ht_oper_ie;
ht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_OPERATION);
if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper)) if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper))
ht_oper = (void *)(ht_oper_ie + 2); ht_oper = (void *)(ht_oper_ie + 2);
} }
if (ht_oper) { if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, sband->vht_cap.vht_supported) {
cbss->channel->band); const u8 *vht_oper_ie;
/* check that channel matches the right operating channel */
if (cbss->channel->center_freq != ht_cfreq) { vht_oper_ie = ieee80211_bss_get_ie(cbss,
/* WLAN_EID_VHT_OPERATION);
* It's possible that some APs are confused here; if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper))
* Netgear WNDR3700 sometimes reports 4 higher than vht_oper = (void *)(vht_oper_ie + 2);
* the actual channel in association responses, but if (vht_oper && !ht_oper) {
* since we look at probe response/beacon data here vht_oper = NULL;
* it should be OK.
*/
sdata_info(sdata, sdata_info(sdata,
"Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", "AP advertised VHT without HT, disabling both\n");
cbss->channel->center_freq, sdata->flags |= IEEE80211_STA_DISABLE_HT;
ht_cfreq, ht_oper->primary_chan, sdata->flags |= IEEE80211_STA_DISABLE_VHT;
cbss->channel->band);
ht_oper = NULL;
} }
} }
if (ht_oper) { ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
/* cbss->channel,
* cfg80211 already verified that the channel itself can ht_oper, vht_oper,
* be used, but it didn't check that we can do the right &chandef);
* HT type, so do that here as well. If HT40 isn't allowed
* on this channel, disable 40 MHz operation.
*/
const u8 *ht_cap_ie;
const struct ieee80211_ht_cap *ht_cap;
u8 chains = 1;
channel_type = NL80211_CHAN_HT20; sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
local->rx_chains);
if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { rcu_read_unlock();
switch (ht_oper->ht_param &
IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
if (cbss->channel->flags &
IEEE80211_CHAN_NO_HT40PLUS)
ifmgd->flags |=
IEEE80211_STA_DISABLE_40MHZ;
else
channel_type = NL80211_CHAN_HT40PLUS;
break;
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
if (cbss->channel->flags &
IEEE80211_CHAN_NO_HT40MINUS)
ifmgd->flags |=
IEEE80211_STA_DISABLE_40MHZ;
else
channel_type = NL80211_CHAN_HT40MINUS;
break;
}
}
ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY,
cbss->information_elements,
cbss->len_information_elements);
if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) {
ht_cap = (void *)(ht_cap_ie + 2);
chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
}
sdata->needed_rx_chains = min(chains, local->rx_chains);
} else {
sdata->needed_rx_chains = 1;
sdata->u.mgd.flags |= IEEE80211_STA_DISABLE_HT;
}
/* will change later if needed */ /* will change later if needed */
sdata->smps_mode = IEEE80211_SMPS_OFF; sdata->smps_mode = IEEE80211_SMPS_OFF;
ieee80211_vif_release_channel(sdata); /*
cfg80211_chandef_create(&chandef, cbss->channel, channel_type); * If this fails (possibly due to channel context sharing
return ieee80211_vif_use_channel(sdata, &chandef, * on incompatible channels, e.g. 80+80 and 160 sharing the
IEEE80211_CHANCTX_SHARED); * same control channel) try to use a smaller bandwidth.
*/
ret = ieee80211_vif_use_channel(sdata, &chandef,
IEEE80211_CHANCTX_SHARED);
while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
ifmgd->flags |= chandef_downgrade(&chandef);
return ret;
} }
static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
@ -3510,14 +3747,21 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
const u8 *ssidie, *ht_ie; const u8 *ssidie, *ht_ie;
int i, err; int i, err;
ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
if (!ssidie)
return -EINVAL;
assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL);
if (!assoc_data) if (!assoc_data)
return -ENOMEM; return -ENOMEM;
rcu_read_lock();
ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
if (!ssidie) {
rcu_read_unlock();
kfree(assoc_data);
return -EINVAL;
}
memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]);
assoc_data->ssid_len = ssidie[1];
rcu_read_unlock();
mutex_lock(&ifmgd->mtx); mutex_lock(&ifmgd->mtx);
if (ifmgd->associated) if (ifmgd->associated)
@ -3612,12 +3856,14 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
assoc_data->supp_rates = bss->supp_rates; assoc_data->supp_rates = bss->supp_rates;
assoc_data->supp_rates_len = bss->supp_rates_len; assoc_data->supp_rates_len = bss->supp_rates_len;
rcu_read_lock();
ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION); ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION);
if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation)) if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation))
assoc_data->ap_ht_param = assoc_data->ap_ht_param =
((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param; ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param;
else else
ifmgd->flags |= IEEE80211_STA_DISABLE_HT; ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
rcu_read_unlock();
if (bss->wmm_used && bss->uapsd_supported && if (bss->wmm_used && bss->uapsd_supported &&
(sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
@ -3628,9 +3874,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED; ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED;
} }
memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]);
assoc_data->ssid_len = ssidie[1];
if (req->prev_bssid) if (req->prev_bssid)
memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN); memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN);

View File

@ -389,9 +389,9 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_tx_rate *ar = info->status.rates; struct ieee80211_tx_rate *ar = info->status.rates;
struct minstrel_rate_stats *rate, *rate2; struct minstrel_rate_stats *rate, *rate2;
struct minstrel_priv *mp = priv; struct minstrel_priv *mp = priv;
bool last = false; bool last;
int group; int group;
int i = 0; int i;
if (!msp->is_ht) if (!msp->is_ht)
return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb); return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb);
@ -419,13 +419,11 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
mi->sample_packets += info->status.ampdu_len; mi->sample_packets += info->status.ampdu_len;
last = !minstrel_ht_txstat_valid(&ar[0]);
for (i = 0; !last; i++) { for (i = 0; !last; i++) {
last = (i == IEEE80211_TX_MAX_RATES - 1) || last = (i == IEEE80211_TX_MAX_RATES - 1) ||
!minstrel_ht_txstat_valid(&ar[i + 1]); !minstrel_ht_txstat_valid(&ar[i + 1]);
if (!minstrel_ht_txstat_valid(&ar[i]))
break;
group = minstrel_ht_get_group_idx(&ar[i]); group = minstrel_ht_get_group_idx(&ar[i]);
rate = &mi->groups[group].rates[ar[i].idx % 8]; rate = &mi->groups[group].rates[ar[i].idx % 8];

View File

@ -49,7 +49,7 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
/* driver bug */ /* driver bug */
WARN_ON(1); WARN_ON(1);
dev_kfree_skb(skb); dev_kfree_skb(skb);
skb = NULL; return NULL;
} }
} }
@ -111,6 +111,11 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
len += 8; len += 8;
} }
if (status->flag & RX_FLAG_VHT) {
len = ALIGN(len, 2);
len += 12;
}
if (status->vendor_radiotap_len) { if (status->vendor_radiotap_len) {
if (WARN_ON_ONCE(status->vendor_radiotap_align == 0)) if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
status->vendor_radiotap_align = 1; status->vendor_radiotap_align = 1;
@ -297,6 +302,41 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*pos++ = 0; *pos++ = 0;
} }
if (status->flag & RX_FLAG_VHT) {
u16 known = local->hw.radiotap_vht_details;
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
/* known field - how to handle 80+80? */
if (status->flag & RX_FLAG_80P80MHZ)
known &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
put_unaligned_le16(known, pos);
pos += 2;
/* flags */
if (status->flag & RX_FLAG_SHORT_GI)
*pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
pos++;
/* bandwidth */
if (status->flag & RX_FLAG_80MHZ)
*pos++ = 4;
else if (status->flag & RX_FLAG_80P80MHZ)
*pos++ = 0; /* marked not known above */
else if (status->flag & RX_FLAG_160MHZ)
*pos++ = 11;
else if (status->flag & RX_FLAG_40MHZ)
*pos++ = 1;
else /* 20 MHz */
*pos++ = 0;
/* MCS/NSS */
*pos = (status->rate_idx << 4) | status->vht_nss;
pos += 4;
/* coding field */
pos++;
/* group ID */
pos++;
/* partial_aid */
pos += 2;
}
if (status->vendor_radiotap_len) { if (status->vendor_radiotap_len) {
/* ensure 2 byte alignment for the vendor field as required */ /* ensure 2 byte alignment for the vendor field as required */
if ((pos - (u8 *)rthdr) & 1) if ((pos - (u8 *)rthdr) & 1)

View File

@ -247,6 +247,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
local->hw_scan_req->n_channels = n_chans; local->hw_scan_req->n_channels = n_chans;
ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
local->hw_scan_ies_bufsize,
req->ie, req->ie_len, band, req->ie, req->ie_len, band,
req->rates[band], 0); req->rates[band], 0);
local->hw_scan_req->ie_len = ielen; local->hw_scan_req->ie_len = ielen;
@ -445,11 +446,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if (local->ops->hw_scan) { if (local->ops->hw_scan) {
u8 *ies; u8 *ies;
local->hw_scan_ies_bufsize = 2 + IEEE80211_MAX_SSID_LEN +
local->scan_ies_len +
req->ie_len;
local->hw_scan_req = kmalloc( local->hw_scan_req = kmalloc(
sizeof(*local->hw_scan_req) + sizeof(*local->hw_scan_req) +
req->n_channels * sizeof(req->channels[0]) + req->n_channels * sizeof(req->channels[0]) +
2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len + local->hw_scan_ies_bufsize, GFP_KERNEL);
req->ie_len, GFP_KERNEL);
if (!local->hw_scan_req) if (!local->hw_scan_req)
return -ENOMEM; return -ENOMEM;
@ -928,7 +931,10 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_sched_scan_ies sched_scan_ies = {}; struct ieee80211_sched_scan_ies sched_scan_ies = {};
int ret, i; int ret, i, iebufsz;
iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
local->scan_ies_len + req->ie_len;
mutex_lock(&local->mtx); mutex_lock(&local->mtx);
@ -946,10 +952,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
if (!local->hw.wiphy->bands[i]) if (!local->hw.wiphy->bands[i])
continue; continue;
sched_scan_ies.ie[i] = kzalloc(2 + IEEE80211_MAX_SSID_LEN + sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL);
local->scan_ies_len +
req->ie_len,
GFP_KERNEL);
if (!sched_scan_ies.ie[i]) { if (!sched_scan_ies.ie[i]) {
ret = -ENOMEM; ret = -ENOMEM;
goto out_free; goto out_free;
@ -957,8 +960,8 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
sched_scan_ies.len[i] = sched_scan_ies.len[i] =
ieee80211_build_preq_ies(local, sched_scan_ies.ie[i], ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
req->ie, req->ie_len, i, iebufsz, req->ie, req->ie_len,
(u32) -1, 0); i, (u32) -1, 0);
} }
ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);

View File

@ -250,6 +250,7 @@ struct sta_ampdu_mlme {
* @rx_dropped: number of dropped MPDUs from this STA * @rx_dropped: number of dropped MPDUs from this STA
* @last_signal: signal of last received frame from this STA * @last_signal: signal of last received frame from this STA
* @avg_signal: moving average of signal of received frames from this STA * @avg_signal: moving average of signal of received frames from this STA
* @last_ack_signal: signal of last received Ack frame from this STA
* @last_seq_ctrl: last received seq/frag number from this STA (per RX queue) * @last_seq_ctrl: last received seq/frag number from this STA (per RX queue)
* @tx_filtered_count: number of frames the hardware filtered for this STA * @tx_filtered_count: number of frames the hardware filtered for this STA
* @tx_retry_failed: number of frames that failed retry * @tx_retry_failed: number of frames that failed retry
@ -329,6 +330,7 @@ struct sta_info {
unsigned long rx_dropped; unsigned long rx_dropped;
int last_signal; int last_signal;
struct ewma avg_signal; struct ewma avg_signal;
int last_ack_signal;
/* Plus 1 for non-QoS frames */ /* Plus 1 for non-QoS frames */
__le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1]; __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
@ -552,6 +554,8 @@ int sta_info_flush(struct ieee80211_local *local,
void sta_set_rate_info_tx(struct sta_info *sta, void sta_set_rate_info_tx(struct sta_info *sta,
const struct ieee80211_tx_rate *rate, const struct ieee80211_tx_rate *rate,
struct rate_info *rinfo); struct rate_info *rinfo);
void sta_set_rate_info_rx(struct sta_info *sta,
struct rate_info *rinfo);
void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
unsigned long exp_time); unsigned long exp_time);

View File

@ -539,6 +539,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
sta->lost_packets = 0; sta->lost_packets = 0;
} }
} }
if (acked)
sta->last_ack_signal = info->status.ack_signal;
} }
rcu_read_unlock(); rcu_read_unlock();

View File

@ -2623,7 +2623,7 @@ EXPORT_SYMBOL(ieee80211_nullfunc_get);
struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
const u8 *ssid, size_t ssid_len, const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len) size_t tailroom)
{ {
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
struct ieee80211_local *local; struct ieee80211_local *local;
@ -2637,7 +2637,7 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
ie_ssid_len = 2 + ssid_len; ie_ssid_len = 2 + ssid_len;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) + skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) +
ie_ssid_len + ie_len); ie_ssid_len + tailroom);
if (!skb) if (!skb)
return NULL; return NULL;
@ -2658,11 +2658,6 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
memcpy(pos, ssid, ssid_len); memcpy(pos, ssid, ssid_len);
pos += ssid_len; pos += ssid_len;
if (ie) {
pos = skb_put(skb, ie_len);
memcpy(pos, ie, ie_len);
}
return skb; return skb;
} }
EXPORT_SYMBOL(ieee80211_probereq_get); EXPORT_SYMBOL(ieee80211_probereq_get);

View File

@ -1107,12 +1107,12 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
} }
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
const u8 *ie, size_t ie_len, size_t buffer_len, const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask, enum ieee80211_band band, u32 rate_mask,
u8 channel) u8 channel)
{ {
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
u8 *pos; u8 *pos = buffer, *end = buffer + buffer_len;
size_t offset = 0, noffset; size_t offset = 0, noffset;
int supp_rates_len, i; int supp_rates_len, i;
u8 rates[32]; u8 rates[32];
@ -1123,8 +1123,6 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
if (WARN_ON_ONCE(!sband)) if (WARN_ON_ONCE(!sband))
return 0; return 0;
pos = buffer;
num_rates = 0; num_rates = 0;
for (i = 0; i < sband->n_bitrates; i++) { for (i = 0; i < sband->n_bitrates; i++) {
if ((BIT(i) & rate_mask) == 0) if ((BIT(i) & rate_mask) == 0)
@ -1134,6 +1132,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
supp_rates_len = min_t(int, num_rates, 8); supp_rates_len = min_t(int, num_rates, 8);
if (end - pos < 2 + supp_rates_len)
goto out_err;
*pos++ = WLAN_EID_SUPP_RATES; *pos++ = WLAN_EID_SUPP_RATES;
*pos++ = supp_rates_len; *pos++ = supp_rates_len;
memcpy(pos, rates, supp_rates_len); memcpy(pos, rates, supp_rates_len);
@ -1150,6 +1150,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
before_extrates, before_extrates,
ARRAY_SIZE(before_extrates), ARRAY_SIZE(before_extrates),
offset); offset);
if (end - pos < noffset - offset)
goto out_err;
memcpy(pos, ie + offset, noffset - offset); memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset; pos += noffset - offset;
offset = noffset; offset = noffset;
@ -1157,6 +1159,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
ext_rates_len = num_rates - supp_rates_len; ext_rates_len = num_rates - supp_rates_len;
if (ext_rates_len > 0) { if (ext_rates_len > 0) {
if (end - pos < 2 + ext_rates_len)
goto out_err;
*pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = ext_rates_len; *pos++ = ext_rates_len;
memcpy(pos, rates + supp_rates_len, ext_rates_len); memcpy(pos, rates + supp_rates_len, ext_rates_len);
@ -1164,6 +1168,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
} }
if (channel && sband->band == IEEE80211_BAND_2GHZ) { if (channel && sband->band == IEEE80211_BAND_2GHZ) {
if (end - pos < 3)
goto out_err;
*pos++ = WLAN_EID_DS_PARAMS; *pos++ = WLAN_EID_DS_PARAMS;
*pos++ = 1; *pos++ = 1;
*pos++ = channel; *pos++ = channel;
@ -1182,14 +1188,19 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
noffset = ieee80211_ie_split(ie, ie_len, noffset = ieee80211_ie_split(ie, ie_len,
before_ht, ARRAY_SIZE(before_ht), before_ht, ARRAY_SIZE(before_ht),
offset); offset);
if (end - pos < noffset - offset)
goto out_err;
memcpy(pos, ie + offset, noffset - offset); memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset; pos += noffset - offset;
offset = noffset; offset = noffset;
} }
if (sband->ht_cap.ht_supported) if (sband->ht_cap.ht_supported) {
if (end - pos < 2 + sizeof(struct ieee80211_ht_cap))
goto out_err;
pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
sband->ht_cap.cap); sband->ht_cap.cap);
}
/* /*
* If adding more here, adjust code in main.c * If adding more here, adjust code in main.c
@ -1199,14 +1210,22 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
/* add any remaining custom IEs */ /* add any remaining custom IEs */
if (ie && ie_len) { if (ie && ie_len) {
noffset = ie_len; noffset = ie_len;
if (end - pos < noffset - offset)
goto out_err;
memcpy(pos, ie + offset, noffset - offset); memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset; pos += noffset - offset;
} }
if (sband->vht_cap.vht_supported) if (sband->vht_cap.vht_supported) {
if (end - pos < 2 + sizeof(struct ieee80211_vht_cap))
goto out_err;
pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
sband->vht_cap.cap); sband->vht_cap.cap);
}
return pos - buffer;
out_err:
WARN_ONCE(1, "not enough space for preq IEs\n");
return pos - buffer; return pos - buffer;
} }
@ -1220,14 +1239,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct sk_buff *skb; struct sk_buff *skb;
struct ieee80211_mgmt *mgmt; struct ieee80211_mgmt *mgmt;
size_t buf_len;
u8 *buf;
u8 chan_no; u8 chan_no;
int ies_len;
/* FIXME: come up with a proper value */
buf = kmalloc(200 + ie_len, GFP_KERNEL);
if (!buf)
return NULL;
/* /*
* Do not send DS Channel parameter for directed probe requests * Do not send DS Channel parameter for directed probe requests
@ -1239,14 +1252,16 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
else else
chan_no = ieee80211_frequency_to_channel(chan->center_freq); chan_no = ieee80211_frequency_to_channel(chan->center_freq);
buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, chan->band,
ratemask, chan_no);
skb = ieee80211_probereq_get(&local->hw, &sdata->vif, skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
ssid, ssid_len, ssid, ssid_len, 100 + ie_len);
buf, buf_len);
if (!skb) if (!skb)
goto out; return NULL;
ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
skb_tailroom(skb),
ie, ie_len, chan->band,
ratemask, chan_no);
skb_put(skb, ies_len);
if (dst) { if (dst) {
mgmt = (struct ieee80211_mgmt *) skb->data; mgmt = (struct ieee80211_mgmt *) skb->data;
@ -1256,9 +1271,6 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
out:
kfree(buf);
return skb; return skb;
} }
@ -1527,7 +1539,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
changed |= BSS_CHANGED_IBSS; changed |= BSS_CHANGED_IBSS;
/* fall through */ /* fall through */
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
changed |= BSS_CHANGED_SSID; changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS;
if (sdata->vif.type == NL80211_IFTYPE_AP) { if (sdata->vif.type == NL80211_IFTYPE_AP) {
changed |= BSS_CHANGED_AP_PROBE_RESP; changed |= BSS_CHANGED_AP_PROBE_RESP;

View File

@ -44,7 +44,7 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
} }
EXPORT_SYMBOL(cfg80211_chandef_create); EXPORT_SYMBOL(cfg80211_chandef_create);
bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef) bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
{ {
u32 control_freq; u32 control_freq;
@ -105,6 +105,7 @@ bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef)
return true; return true;
} }
EXPORT_SYMBOL(cfg80211_chandef_valid);
static void chandef_primary_freqs(const struct cfg80211_chan_def *c, static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
int *pri40, int *pri80) int *pri40, int *pri80)
@ -187,9 +188,9 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
} }
EXPORT_SYMBOL(cfg80211_chandef_compatible); EXPORT_SYMBOL(cfg80211_chandef_compatible);
bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
u32 center_freq, u32 bandwidth, u32 center_freq, u32 bandwidth,
u32 prohibited_flags) u32 prohibited_flags)
{ {
struct ieee80211_channel *c; struct ieee80211_channel *c;
u32 freq; u32 freq;
@ -205,55 +206,88 @@ bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
return true; return true;
} }
static bool cfg80211_check_beacon_chans(struct wiphy *wiphy, bool cfg80211_chandef_usable(struct wiphy *wiphy,
u32 center_freq, u32 bw) const struct cfg80211_chan_def *chandef,
u32 prohibited_flags)
{ {
return cfg80211_secondary_chans_ok(wiphy, center_freq, bw, struct ieee80211_sta_ht_cap *ht_cap;
IEEE80211_CHAN_DISABLED | struct ieee80211_sta_vht_cap *vht_cap;
IEEE80211_CHAN_PASSIVE_SCAN | u32 width, control_freq;
IEEE80211_CHAN_NO_IBSS |
IEEE80211_CHAN_RADAR);
}
bool cfg80211_reg_can_beacon(struct wiphy *wiphy, if (WARN_ON(!cfg80211_chandef_valid(chandef)))
struct cfg80211_chan_def *chandef)
{
u32 width;
bool res;
trace_cfg80211_reg_can_beacon(wiphy, chandef);
if (WARN_ON(!cfg80211_chan_def_valid(chandef))) {
trace_cfg80211_return_bool(false);
return false; return false;
}
ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
control_freq = chandef->chan->center_freq;
switch (chandef->width) { switch (chandef->width) {
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20:
if (!ht_cap->ht_supported)
return false;
case NL80211_CHAN_WIDTH_20_NOHT:
width = 20; width = 20;
break; break;
case NL80211_CHAN_WIDTH_40: case NL80211_CHAN_WIDTH_40:
width = 40; width = 40;
if (!ht_cap->ht_supported)
return false;
if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
return false;
if (chandef->center_freq1 < control_freq &&
chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
return false;
if (chandef->center_freq1 > control_freq &&
chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
return false;
break; break;
case NL80211_CHAN_WIDTH_80:
case NL80211_CHAN_WIDTH_80P80: case NL80211_CHAN_WIDTH_80P80:
if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
return false;
case NL80211_CHAN_WIDTH_80:
if (!vht_cap->vht_supported)
return false;
width = 80; width = 80;
break; break;
case NL80211_CHAN_WIDTH_160: case NL80211_CHAN_WIDTH_160:
if (!vht_cap->vht_supported)
return false;
if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
return false;
width = 160; width = 160;
break; break;
default: default:
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
trace_cfg80211_return_bool(false);
return false; return false;
} }
res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq1, width); /* TODO: missing regulatory check on 80/160 bandwidth */
if (res && chandef->center_freq2) if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1,
res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq2, width, prohibited_flags))
width); return false;
if (!chandef->center_freq2)
return true;
return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2,
width, prohibited_flags);
}
EXPORT_SYMBOL(cfg80211_chandef_usable);
bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef)
{
bool res;
trace_cfg80211_reg_can_beacon(wiphy, chandef);
res = cfg80211_chandef_usable(wiphy, chandef,
IEEE80211_CHAN_DISABLED |
IEEE80211_CHAN_PASSIVE_SCAN |
IEEE80211_CHAN_NO_IBSS |
IEEE80211_CHAN_RADAR);
trace_cfg80211_return_bool(res); trace_cfg80211_return_bool(res);
return res; return res;

View File

@ -138,8 +138,6 @@ struct cfg80211_internal_bss {
unsigned long ts; unsigned long ts;
struct kref ref; struct kref ref;
atomic_t hold; atomic_t hold;
bool beacon_ies_allocated;
bool proberesp_ies_allocated;
/* must be last because of priv member */ /* must be last because of priv member */
struct cfg80211_bss pub; struct cfg80211_bss pub;
@ -483,12 +481,6 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
enum nl80211_iftype iftype, int num); enum nl80211_iftype iftype, int num);
bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef);
bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
u32 center_freq, u32 bandwidth,
u32 prohibited_flags);
#define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10 #define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10
#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS

View File

@ -146,7 +146,8 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
if (!setup->chandef.chan) if (!setup->chandef.chan)
return -EINVAL; return -EINVAL;
setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT;; setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
setup->chandef.center_freq1 = setup->chandef.chan->center_freq;
} }
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef)) if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))

View File

@ -363,6 +363,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, }, [NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, },
[NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN }, [NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN },
[NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 }, [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
[NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 },
[NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
}; };
/* policy for the key attributes */ /* policy for the key attributes */
@ -1369,9 +1371,7 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
struct genl_info *info, struct genl_info *info,
struct cfg80211_chan_def *chandef) struct cfg80211_chan_def *chandef)
{ {
struct ieee80211_sta_ht_cap *ht_cap; u32 control_freq;
struct ieee80211_sta_vht_cap *vht_cap;
u32 control_freq, width;
if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
return -EINVAL; return -EINVAL;
@ -1417,66 +1417,12 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
info->attrs[NL80211_ATTR_CENTER_FREQ2]); info->attrs[NL80211_ATTR_CENTER_FREQ2]);
} }
ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap; if (!cfg80211_chandef_valid(chandef))
vht_cap = &rdev->wiphy.bands[chandef->chan->band]->vht_cap;
if (!cfg80211_chan_def_valid(chandef))
return -EINVAL; return -EINVAL;
switch (chandef->width) { if (!cfg80211_chandef_usable(&rdev->wiphy, chandef,
case NL80211_CHAN_WIDTH_20: IEEE80211_CHAN_DISABLED))
if (!ht_cap->ht_supported)
return -EINVAL;
case NL80211_CHAN_WIDTH_20_NOHT:
width = 20;
break;
case NL80211_CHAN_WIDTH_40:
width = 40;
/* quick early regulatory check */
if (chandef->center_freq1 < control_freq &&
chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
return -EINVAL;
if (chandef->center_freq1 > control_freq &&
chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
return -EINVAL;
if (!ht_cap->ht_supported)
return -EINVAL;
if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
return -EINVAL;
break;
case NL80211_CHAN_WIDTH_80:
width = 80;
if (!vht_cap->vht_supported)
return -EINVAL;
break;
case NL80211_CHAN_WIDTH_80P80:
width = 80;
if (!vht_cap->vht_supported)
return -EINVAL;
if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
return -EINVAL;
break;
case NL80211_CHAN_WIDTH_160:
width = 160;
if (!vht_cap->vht_supported)
return -EINVAL;
if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
return -EINVAL;
break;
default:
return -EINVAL; return -EINVAL;
}
if (!cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq1,
width, IEEE80211_CHAN_DISABLED))
return -EINVAL;
if (chandef->center_freq2 &&
!cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq2,
width, IEEE80211_CHAN_DISABLED))
return -EINVAL;
/* TODO: missing regulatory check on bandwidth */
return 0; return 0;
} }
@ -1841,7 +1787,7 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
static int nl80211_send_chandef(struct sk_buff *msg, static int nl80211_send_chandef(struct sk_buff *msg,
struct cfg80211_chan_def *chandef) struct cfg80211_chan_def *chandef)
{ {
WARN_ON(!cfg80211_chan_def_valid(chandef)); WARN_ON(!cfg80211_chandef_valid(chandef));
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
chandef->chan->center_freq)) chandef->chan->center_freq))
@ -2732,6 +2678,32 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]); info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
} }
if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EINVAL;
params.p2p_ctwindow =
nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
if (params.p2p_ctwindow > 127)
return -EINVAL;
if (params.p2p_ctwindow != 0 &&
!(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
return -EINVAL;
}
if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
u8 tmp;
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EINVAL;
tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
if (tmp > 1)
return -EINVAL;
params.p2p_opp_ps = tmp;
if (params.p2p_opp_ps != 0 &&
!(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
return -EINVAL;
}
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
err = nl80211_parse_chandef(rdev, info, &params.chandef); err = nl80211_parse_chandef(rdev, info, &params.chandef);
if (err) if (err)
@ -3698,6 +3670,8 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
params.use_short_slot_time = -1; params.use_short_slot_time = -1;
params.ap_isolate = -1; params.ap_isolate = -1;
params.ht_opmode = -1; params.ht_opmode = -1;
params.p2p_ctwindow = -1;
params.p2p_opp_ps = -1;
if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
params.use_cts_prot = params.use_cts_prot =
@ -3720,6 +3694,32 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
params.ht_opmode = params.ht_opmode =
nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]); nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EINVAL;
params.p2p_ctwindow =
nla_get_s8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
if (params.p2p_ctwindow < 0)
return -EINVAL;
if (params.p2p_ctwindow != 0 &&
!(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
return -EINVAL;
}
if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
u8 tmp;
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EINVAL;
tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
if (tmp > 1)
return -EINVAL;
params.p2p_opp_ps = tmp;
if (params.p2p_opp_ps &&
!(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
return -EINVAL;
}
if (!rdev->ops->change_bss) if (!rdev->ops->change_bss)
return -EOPNOTSUPP; return -EOPNOTSUPP;
@ -4808,6 +4808,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
struct cfg80211_internal_bss *intbss) struct cfg80211_internal_bss *intbss)
{ {
struct cfg80211_bss *res = &intbss->pub; struct cfg80211_bss *res = &intbss->pub;
const struct cfg80211_bss_ies *ies;
void *hdr; void *hdr;
struct nlattr *bss; struct nlattr *bss;
@ -4828,16 +4829,24 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
if (!bss) if (!bss)
goto nla_put_failure; goto nla_put_failure;
if ((!is_zero_ether_addr(res->bssid) && if ((!is_zero_ether_addr(res->bssid) &&
nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)) || nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)))
(res->information_elements && res->len_information_elements &&
nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
res->len_information_elements,
res->information_elements)) ||
(res->beacon_ies && res->len_beacon_ies &&
res->beacon_ies != res->information_elements &&
nla_put(msg, NL80211_BSS_BEACON_IES,
res->len_beacon_ies, res->beacon_ies)))
goto nla_put_failure; goto nla_put_failure;
rcu_read_lock();
ies = rcu_dereference(res->ies);
if (ies && ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
ies->len, ies->data)) {
rcu_read_unlock();
goto nla_put_failure;
}
ies = rcu_dereference(res->beacon_ies);
if (ies && ies->len && nla_put(msg, NL80211_BSS_BEACON_IES,
ies->len, ies->data)) {
rcu_read_unlock();
goto nla_put_failure;
}
rcu_read_unlock();
if (res->tsf && if (res->tsf &&
nla_put_u64(msg, NL80211_BSS_TSF, res->tsf)) nla_put_u64(msg, NL80211_BSS_TSF, res->tsf))
goto nla_put_failure; goto nla_put_failure;
@ -5502,6 +5511,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
return -EINVAL; return -EINVAL;
if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT && if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
return -EINVAL;
ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
@ -6529,14 +6539,13 @@ nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
}; };
static int nl80211_set_cqm_txe(struct genl_info *info, static int nl80211_set_cqm_txe(struct genl_info *info,
u32 rate, u32 pkts, u32 intvl) u32 rate, u32 pkts, u32 intvl)
{ {
struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct wireless_dev *wdev; struct wireless_dev *wdev;
struct net_device *dev = info->user_ptr[1]; struct net_device *dev = info->user_ptr[1];
if ((rate < 0 || rate > 100) || if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL)
(intvl < 0 || intvl > NL80211_CQM_TXE_MAX_INTVL))
return -EINVAL; return -EINVAL;
wdev = dev->ieee80211_ptr; wdev = dev->ieee80211_ptr;

View File

@ -1796,7 +1796,7 @@ EXPORT_SYMBOL(regulatory_hint);
*/ */
void regulatory_hint_11d(struct wiphy *wiphy, void regulatory_hint_11d(struct wiphy *wiphy,
enum ieee80211_band band, enum ieee80211_band band,
u8 *country_ie, const u8 *country_ie,
u8 country_ie_len) u8 country_ie_len)
{ {
char alpha2[2]; char alpha2[2];

View File

@ -81,7 +81,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
*/ */
void regulatory_hint_11d(struct wiphy *wiphy, void regulatory_hint_11d(struct wiphy *wiphy,
enum ieee80211_band band, enum ieee80211_band band,
u8 *country_ie, const u8 *country_ie,
u8 country_ie_len); u8 country_ie_len);
/** /**

View File

@ -23,18 +23,23 @@
static void bss_release(struct kref *ref) static void bss_release(struct kref *ref)
{ {
struct cfg80211_bss_ies *ies;
struct cfg80211_internal_bss *bss; struct cfg80211_internal_bss *bss;
bss = container_of(ref, struct cfg80211_internal_bss, ref); bss = container_of(ref, struct cfg80211_internal_bss, ref);
if (WARN_ON(atomic_read(&bss->hold)))
return;
if (bss->pub.free_priv) if (bss->pub.free_priv)
bss->pub.free_priv(&bss->pub); bss->pub.free_priv(&bss->pub);
if (bss->beacon_ies_allocated) ies = (void *)rcu_access_pointer(bss->pub.beacon_ies);
kfree(bss->pub.beacon_ies); if (ies)
if (bss->proberesp_ies_allocated) kfree_rcu(ies, rcu_head);
kfree(bss->pub.proberesp_ies); ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies);
if (ies)
BUG_ON(atomic_read(&bss->hold)); kfree_rcu(ies, rcu_head);
kfree(bss); kfree(bss);
} }
@ -236,9 +241,8 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *bss; struct cfg80211_internal_bss *bss;
unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
list_for_each_entry(bss, &dev->bss_list, list) { list_for_each_entry(bss, &dev->bss_list, list)
bss->ts -= age_jiffies; bss->ts -= age_jiffies;
}
} }
void cfg80211_bss_expire(struct cfg80211_registered_device *dev) void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
@ -287,7 +291,7 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type,
} }
EXPORT_SYMBOL(cfg80211_find_vendor_ie); EXPORT_SYMBOL(cfg80211_find_vendor_ie);
static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) static int cmp_ies(u8 num, const u8 *ies1, int len1, const u8 *ies2, int len2)
{ {
const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); const u8 *ie1 = cfg80211_find_ie(num, ies1, len1);
const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); const u8 *ie2 = cfg80211_find_ie(num, ies2, len2);
@ -307,10 +311,10 @@ static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
return memcmp(ie1 + 2, ie2 + 2, ie1[1]); return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
} }
static bool is_bss(struct cfg80211_bss *a, static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
const u8 *bssid,
const u8 *ssid, size_t ssid_len) const u8 *ssid, size_t ssid_len)
{ {
const struct cfg80211_bss_ies *ies;
const u8 *ssidie; const u8 *ssidie;
if (bssid && !ether_addr_equal(a->bssid, bssid)) if (bssid && !ether_addr_equal(a->bssid, bssid))
@ -319,9 +323,10 @@ static bool is_bss(struct cfg80211_bss *a,
if (!ssid) if (!ssid)
return true; return true;
ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies = rcu_access_pointer(a->ies);
a->information_elements, if (!ies)
a->len_information_elements); return false;
ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
if (!ssidie) if (!ssidie)
return false; return false;
if (ssidie[1] != ssid_len) if (ssidie[1] != ssid_len)
@ -331,20 +336,21 @@ static bool is_bss(struct cfg80211_bss *a,
static bool is_mesh_bss(struct cfg80211_bss *a) static bool is_mesh_bss(struct cfg80211_bss *a)
{ {
const struct cfg80211_bss_ies *ies;
const u8 *ie; const u8 *ie;
if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
return false; return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies = rcu_access_pointer(a->ies);
a->information_elements, if (!ies)
a->len_information_elements); return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len);
if (!ie) if (!ie)
return false; return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len);
a->information_elements,
a->len_information_elements);
if (!ie) if (!ie)
return false; return false;
@ -355,14 +361,17 @@ static bool is_mesh(struct cfg80211_bss *a,
const u8 *meshid, size_t meshidlen, const u8 *meshid, size_t meshidlen,
const u8 *meshcfg) const u8 *meshcfg)
{ {
const struct cfg80211_bss_ies *ies;
const u8 *ie; const u8 *ie;
if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
return false; return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies = rcu_access_pointer(a->ies);
a->information_elements, if (!ies)
a->len_information_elements); return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len);
if (!ie) if (!ie)
return false; return false;
if (ie[1] != meshidlen) if (ie[1] != meshidlen)
@ -370,9 +379,7 @@ static bool is_mesh(struct cfg80211_bss *a,
if (memcmp(ie + 2, meshid, meshidlen)) if (memcmp(ie + 2, meshid, meshidlen))
return false; return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len);
a->information_elements,
a->len_information_elements);
if (!ie) if (!ie)
return false; return false;
if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
@ -384,30 +391,33 @@ static bool is_mesh(struct cfg80211_bss *a,
* part in the same mesh. * part in the same mesh.
*/ */
return memcmp(ie + 2, meshcfg, return memcmp(ie + 2, meshcfg,
sizeof(struct ieee80211_meshconf_ie) - 2) == 0; sizeof(struct ieee80211_meshconf_ie) - 2) == 0;
} }
static int cmp_bss_core(struct cfg80211_bss *a, static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b)
struct cfg80211_bss *b)
{ {
const struct cfg80211_bss_ies *a_ies, *b_ies;
int r; int r;
if (a->channel != b->channel) if (a->channel != b->channel)
return b->channel->center_freq - a->channel->center_freq; return b->channel->center_freq - a->channel->center_freq;
if (is_mesh_bss(a) && is_mesh_bss(b)) { if (is_mesh_bss(a) && is_mesh_bss(b)) {
a_ies = rcu_access_pointer(a->ies);
if (!a_ies)
return -1;
b_ies = rcu_access_pointer(b->ies);
if (!b_ies)
return 1;
r = cmp_ies(WLAN_EID_MESH_ID, r = cmp_ies(WLAN_EID_MESH_ID,
a->information_elements, a_ies->data, a_ies->len,
a->len_information_elements, b_ies->data, b_ies->len);
b->information_elements,
b->len_information_elements);
if (r) if (r)
return r; return r;
return cmp_ies(WLAN_EID_MESH_CONFIG, return cmp_ies(WLAN_EID_MESH_CONFIG,
a->information_elements, a_ies->data, a_ies->len,
a->len_information_elements, b_ies->data, b_ies->len);
b->information_elements,
b->len_information_elements);
} }
/* /*
@ -420,22 +430,28 @@ static int cmp_bss_core(struct cfg80211_bss *a,
static int cmp_bss(struct cfg80211_bss *a, static int cmp_bss(struct cfg80211_bss *a,
struct cfg80211_bss *b) struct cfg80211_bss *b)
{ {
const struct cfg80211_bss_ies *a_ies, *b_ies;
int r; int r;
r = cmp_bss_core(a, b); r = cmp_bss_core(a, b);
if (r) if (r)
return r; return r;
a_ies = rcu_access_pointer(a->ies);
if (!a_ies)
return -1;
b_ies = rcu_access_pointer(b->ies);
if (!b_ies)
return 1;
return cmp_ies(WLAN_EID_SSID, return cmp_ies(WLAN_EID_SSID,
a->information_elements, a_ies->data, a_ies->len,
a->len_information_elements, b_ies->data, b_ies->len);
b->information_elements,
b->len_information_elements);
} }
static int cmp_hidden_bss(struct cfg80211_bss *a, static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b)
struct cfg80211_bss *b)
{ {
const struct cfg80211_bss_ies *a_ies, *b_ies;
const u8 *ie1; const u8 *ie1;
const u8 *ie2; const u8 *ie2;
int i; int i;
@ -445,17 +461,26 @@ static int cmp_hidden_bss(struct cfg80211_bss *a,
if (r) if (r)
return r; return r;
ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies = rcu_access_pointer(a->ies);
a->information_elements, if (!a_ies)
a->len_information_elements); return -1;
ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies = rcu_access_pointer(b->ies);
b->information_elements, if (!b_ies)
b->len_information_elements); return 1;
/* Key comparator must use same algorithm in any rb-tree ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len);
ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len);
/*
* Key comparator must use same algorithm in any rb-tree
* search function (order is important), otherwise ordering * search function (order is important), otherwise ordering
* of items in the tree is broken and search gives incorrect * of items in the tree is broken and search gives incorrect
* results. This code uses same order as cmp_ies() does. */ * results. This code uses same order as cmp_ies() does.
*
* Note that due to the differring behaviour with hidden SSIDs
* this function only works when "b" is the tree element and
* "a" is the key we're looking for.
*/
/* sort missing IE before (left of) present IE */ /* sort missing IE before (left of) present IE */
if (!ie1) if (!ie1)
@ -471,10 +496,14 @@ static int cmp_hidden_bss(struct cfg80211_bss *a,
if (ie1[1] != ie2[1]) if (ie1[1] != ie2[1])
return ie2[1] - ie1[1]; return ie2[1] - ie1[1];
/* zeroed SSID ie is another indication of a hidden bss */ /*
* zeroed SSID ie is another indication of a hidden bss;
* if it isn't zeroed just return the regular sort value
* to find the next candidate
*/
for (i = 0; i < ie2[1]; i++) for (i = 0; i < ie2[1]; i++)
if (ie2[i + 2]) if (ie2[i + 2])
return -1; return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
return 0; return 0;
} }
@ -600,7 +629,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
static struct cfg80211_internal_bss * static struct cfg80211_internal_bss *
rb_find_hidden_bss(struct cfg80211_registered_device *dev, rb_find_hidden_bss(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *res) struct cfg80211_internal_bss *res)
{ {
struct rb_node *n = dev->bss_tree.rb_node; struct rb_node *n = dev->bss_tree.rb_node;
struct cfg80211_internal_bss *bss; struct cfg80211_internal_bss *bss;
@ -623,127 +652,86 @@ rb_find_hidden_bss(struct cfg80211_registered_device *dev,
static void static void
copy_hidden_ies(struct cfg80211_internal_bss *res, copy_hidden_ies(struct cfg80211_internal_bss *res,
struct cfg80211_internal_bss *hidden) struct cfg80211_internal_bss *hidden)
{ {
if (unlikely(res->pub.beacon_ies)) const struct cfg80211_bss_ies *ies;
return;
if (WARN_ON(!hidden->pub.beacon_ies)) if (rcu_access_pointer(res->pub.beacon_ies))
return; return;
res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC); ies = rcu_access_pointer(hidden->pub.beacon_ies);
if (unlikely(!res->pub.beacon_ies)) if (WARN_ON(!ies))
return; return;
res->beacon_ies_allocated = true; ies = kmemdup(ies, sizeof(*ies) + ies->len, GFP_ATOMIC);
res->pub.len_beacon_ies = hidden->pub.len_beacon_ies; if (unlikely(!ies))
memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies, return;
res->pub.len_beacon_ies); rcu_assign_pointer(res->pub.beacon_ies, ies);
} }
static struct cfg80211_internal_bss * static struct cfg80211_internal_bss *
cfg80211_bss_update(struct cfg80211_registered_device *dev, cfg80211_bss_update(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *res) struct cfg80211_internal_bss *tmp)
{ {
struct cfg80211_internal_bss *found = NULL; struct cfg80211_internal_bss *found = NULL;
/* if (WARN_ON(!tmp->pub.channel))
* The reference to "res" is donated to this function.
*/
if (WARN_ON(!res->pub.channel)) {
kref_put(&res->ref, bss_release);
return NULL; return NULL;
}
res->ts = jiffies; tmp->ts = jiffies;
spin_lock_bh(&dev->bss_lock); spin_lock_bh(&dev->bss_lock);
found = rb_find_bss(dev, res); if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) {
spin_unlock_bh(&dev->bss_lock);
return NULL;
}
found = rb_find_bss(dev, tmp);
if (found) { if (found) {
found->pub.beacon_interval = res->pub.beacon_interval; found->pub.beacon_interval = tmp->pub.beacon_interval;
found->pub.tsf = res->pub.tsf; found->pub.tsf = tmp->pub.tsf;
found->pub.signal = res->pub.signal; found->pub.signal = tmp->pub.signal;
found->pub.capability = res->pub.capability; found->pub.capability = tmp->pub.capability;
found->ts = res->ts; found->ts = tmp->ts;
/* Update IEs */ /* Update IEs */
if (res->pub.proberesp_ies) { if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
size_t used = dev->wiphy.bss_priv_size + sizeof(*res); const struct cfg80211_bss_ies *old;
size_t ielen = res->pub.len_proberesp_ies;
if (found->pub.proberesp_ies && old = rcu_access_pointer(found->pub.proberesp_ies);
!found->proberesp_ies_allocated &&
ksize(found) >= used + ielen) {
memcpy(found->pub.proberesp_ies,
res->pub.proberesp_ies, ielen);
found->pub.len_proberesp_ies = ielen;
} else {
u8 *ies = found->pub.proberesp_ies;
if (found->proberesp_ies_allocated)
ies = krealloc(ies, ielen, GFP_ATOMIC);
else
ies = kmalloc(ielen, GFP_ATOMIC);
if (ies) {
memcpy(ies, res->pub.proberesp_ies,
ielen);
found->proberesp_ies_allocated = true;
found->pub.proberesp_ies = ies;
found->pub.len_proberesp_ies = ielen;
}
}
rcu_assign_pointer(found->pub.proberesp_ies,
tmp->pub.proberesp_ies);
/* Override possible earlier Beacon frame IEs */ /* Override possible earlier Beacon frame IEs */
found->pub.information_elements = rcu_assign_pointer(found->pub.ies,
found->pub.proberesp_ies; tmp->pub.proberesp_ies);
found->pub.len_information_elements = if (old)
found->pub.len_proberesp_ies; kfree_rcu((struct cfg80211_bss_ies *)old,
} rcu_head);
if (res->pub.beacon_ies) { } else if (rcu_access_pointer(tmp->pub.beacon_ies)) {
size_t used = dev->wiphy.bss_priv_size + sizeof(*res); const struct cfg80211_bss_ies *old, *ies;
size_t ielen = res->pub.len_beacon_ies;
bool information_elements_is_beacon_ies =
(found->pub.information_elements ==
found->pub.beacon_ies);
if (found->pub.beacon_ies && old = rcu_access_pointer(found->pub.beacon_ies);
!found->beacon_ies_allocated && ies = rcu_access_pointer(found->pub.ies);
ksize(found) >= used + ielen) {
memcpy(found->pub.beacon_ies,
res->pub.beacon_ies, ielen);
found->pub.len_beacon_ies = ielen;
} else {
u8 *ies = found->pub.beacon_ies;
if (found->beacon_ies_allocated) rcu_assign_pointer(found->pub.beacon_ies,
ies = krealloc(ies, ielen, GFP_ATOMIC); tmp->pub.beacon_ies);
else
ies = kmalloc(ielen, GFP_ATOMIC);
if (ies) {
memcpy(ies, res->pub.beacon_ies,
ielen);
found->beacon_ies_allocated = true;
found->pub.beacon_ies = ies;
found->pub.len_beacon_ies = ielen;
}
}
/* Override IEs if they were from a beacon before */ /* Override IEs if they were from a beacon before */
if (information_elements_is_beacon_ies) { if (old == ies)
found->pub.information_elements = rcu_assign_pointer(found->pub.ies,
found->pub.beacon_ies; tmp->pub.beacon_ies);
found->pub.len_information_elements =
found->pub.len_beacon_ies;
}
}
kref_put(&res->ref, bss_release); if (old)
kfree_rcu((struct cfg80211_bss_ies *)old,
rcu_head);
}
} else { } else {
struct cfg80211_internal_bss *new;
struct cfg80211_internal_bss *hidden; struct cfg80211_internal_bss *hidden;
struct cfg80211_bss_ies *ies;
/* First check if the beacon is a probe response from /* First check if the beacon is a probe response from
* a hidden bss. If so, copy beacon ies (with nullified * a hidden bss. If so, copy beacon ies (with nullified
@ -754,14 +742,32 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
/* TODO: The code is not trying to update existing probe /* TODO: The code is not trying to update existing probe
* response bss entries when beacon ies are * response bss entries when beacon ies are
* getting changed. */ * getting changed. */
hidden = rb_find_hidden_bss(dev, res); hidden = rb_find_hidden_bss(dev, tmp);
if (hidden) if (hidden)
copy_hidden_ies(res, hidden); copy_hidden_ies(tmp, hidden);
/* this "consumes" the reference */ /*
list_add_tail(&res->list, &dev->bss_list); * create a copy -- the "res" variable that is passed in
rb_insert_bss(dev, res); * is allocated on the stack since it's not needed in the
found = res; * more common case of an update
*/
new = kzalloc(sizeof(*new) + dev->wiphy.bss_priv_size,
GFP_ATOMIC);
if (!new) {
ies = (void *)rcu_dereference(tmp->pub.beacon_ies);
if (ies)
kfree_rcu(ies, rcu_head);
ies = (void *)rcu_dereference(tmp->pub.proberesp_ies);
if (ies)
kfree_rcu(ies, rcu_head);
spin_unlock_bh(&dev->bss_lock);
return NULL;
}
memcpy(new, tmp, sizeof(*new));
kref_init(&new->ref);
list_add_tail(&new->list, &dev->bss_list);
rb_insert_bss(dev, new);
found = new;
} }
dev->bss_generation++; dev->bss_generation++;
@ -810,14 +816,12 @@ cfg80211_inform_bss(struct wiphy *wiphy,
u16 beacon_interval, const u8 *ie, size_t ielen, u16 beacon_interval, const u8 *ie, size_t ielen,
s32 signal, gfp_t gfp) s32 signal, gfp_t gfp)
{ {
struct cfg80211_internal_bss *res; struct cfg80211_bss_ies *ies;
size_t privsz; struct cfg80211_internal_bss tmp = {}, *res;
if (WARN_ON(!wiphy)) if (WARN_ON(!wiphy))
return NULL; return NULL;
privsz = wiphy->bss_priv_size;
if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC && if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
(signal < 0 || signal > 100))) (signal < 0 || signal > 100)))
return NULL; return NULL;
@ -826,36 +830,33 @@ cfg80211_inform_bss(struct wiphy *wiphy,
if (!channel) if (!channel)
return NULL; return NULL;
res = kzalloc(sizeof(*res) + privsz + ielen, gfp); memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
if (!res) tmp.pub.channel = channel;
return NULL; tmp.pub.signal = signal;
tmp.pub.tsf = tsf;
memcpy(res->pub.bssid, bssid, ETH_ALEN); tmp.pub.beacon_interval = beacon_interval;
res->pub.channel = channel; tmp.pub.capability = capability;
res->pub.signal = signal;
res->pub.tsf = tsf;
res->pub.beacon_interval = beacon_interval;
res->pub.capability = capability;
/* /*
* Since we do not know here whether the IEs are from a Beacon or Probe * Since we do not know here whether the IEs are from a Beacon or Probe
* Response frame, we need to pick one of the options and only use it * Response frame, we need to pick one of the options and only use it
* with the driver that does not provide the full Beacon/Probe Response * with the driver that does not provide the full Beacon/Probe Response
* frame. Use Beacon frame pointer to avoid indicating that this should * frame. Use Beacon frame pointer to avoid indicating that this should
* override the information_elements pointer should we have received an * override the iies pointer should we have received an earlier
* earlier indication of Probe Response data. * indication of Probe Response data.
* *
* The initial buffer for the IEs is allocated with the BSS entry and * The initial buffer for the IEs is allocated with the BSS entry and
* is located after the private area. * is located after the private area.
*/ */
res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz; ies = kmalloc(sizeof(*ies) + ielen, gfp);
memcpy(res->pub.beacon_ies, ie, ielen); if (!ies)
res->pub.len_beacon_ies = ielen; return NULL;
res->pub.information_elements = res->pub.beacon_ies; ies->len = ielen;
res->pub.len_information_elements = res->pub.len_beacon_ies; memcpy(ies->data, ie, ielen);
kref_init(&res->ref); rcu_assign_pointer(tmp.pub.beacon_ies, ies);
rcu_assign_pointer(tmp.pub.ies, ies);
res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);
if (!res) if (!res)
return NULL; return NULL;
@ -874,10 +875,10 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_mgmt *mgmt, size_t len,
s32 signal, gfp_t gfp) s32 signal, gfp_t gfp)
{ {
struct cfg80211_internal_bss *res; struct cfg80211_internal_bss tmp = {}, *res;
struct cfg80211_bss_ies *ies;
size_t ielen = len - offsetof(struct ieee80211_mgmt, size_t ielen = len - offsetof(struct ieee80211_mgmt,
u.probe_resp.variable); u.probe_resp.variable);
size_t privsz;
BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) != BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
offsetof(struct ieee80211_mgmt, u.beacon.variable)); offsetof(struct ieee80211_mgmt, u.beacon.variable));
@ -897,45 +898,31 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable))) if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
return NULL; return NULL;
privsz = wiphy->bss_priv_size;
channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable, channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
ielen, channel); ielen, channel);
if (!channel) if (!channel)
return NULL; return NULL;
res = kzalloc(sizeof(*res) + privsz + ielen, gfp); ies = kmalloc(sizeof(*ies) + ielen, gfp);
if (!res) if (!ies)
return NULL; return NULL;
ies->len = ielen;
memcpy(ies->data, mgmt->u.probe_resp.variable, ielen);
memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN); if (ieee80211_is_probe_resp(mgmt->frame_control))
res->pub.channel = channel; rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
res->pub.signal = signal; else
res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); rcu_assign_pointer(tmp.pub.beacon_ies, ies);
res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); rcu_assign_pointer(tmp.pub.ies, ies);
res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
/* memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
* The initial buffer for the IEs is allocated with the BSS entry and tmp.pub.channel = channel;
* is located after the private area. tmp.pub.signal = signal;
*/ tmp.pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
if (ieee80211_is_probe_resp(mgmt->frame_control)) { tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz; tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable,
ielen);
res->pub.len_proberesp_ies = ielen;
res->pub.information_elements = res->pub.proberesp_ies;
res->pub.len_information_elements = res->pub.len_proberesp_ies;
} else {
res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz;
memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen);
res->pub.len_beacon_ies = ielen;
res->pub.information_elements = res->pub.beacon_ies;
res->pub.len_information_elements = res->pub.len_beacon_ies;
}
kref_init(&res->ref); res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);
res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
if (!res) if (!res)
return NULL; return NULL;
@ -1127,22 +1114,21 @@ int cfg80211_wext_siwscan(struct net_device *dev,
EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);
static void ieee80211_scan_add_ies(struct iw_request_info *info, static void ieee80211_scan_add_ies(struct iw_request_info *info,
struct cfg80211_bss *bss, const struct cfg80211_bss_ies *ies,
char **current_ev, char *end_buf) char **current_ev, char *end_buf)
{ {
u8 *pos, *end, *next; const u8 *pos, *end, *next;
struct iw_event iwe; struct iw_event iwe;
if (!bss->information_elements || if (!ies)
!bss->len_information_elements)
return; return;
/* /*
* If needed, fragment the IEs buffer (at IE boundaries) into short * If needed, fragment the IEs buffer (at IE boundaries) into short
* enough fragments to fit into IW_GENERIC_IE_MAX octet messages. * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
*/ */
pos = bss->information_elements; pos = ies->data;
end = pos + bss->len_information_elements; end = pos + ies->len;
while (end - pos > IW_GENERIC_IE_MAX) { while (end - pos > IW_GENERIC_IE_MAX) {
next = pos + 2 + pos[1]; next = pos + 2 + pos[1];
@ -1153,7 +1139,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
iwe.cmd = IWEVGENIE; iwe.cmd = IWEVGENIE;
iwe.u.data.length = next - pos; iwe.u.data.length = next - pos;
*current_ev = iwe_stream_add_point(info, *current_ev, *current_ev = iwe_stream_add_point(info, *current_ev,
end_buf, &iwe, pos); end_buf, &iwe,
(void *)pos);
pos = next; pos = next;
} }
@ -1163,7 +1150,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
iwe.cmd = IWEVGENIE; iwe.cmd = IWEVGENIE;
iwe.u.data.length = end - pos; iwe.u.data.length = end - pos;
*current_ev = iwe_stream_add_point(info, *current_ev, *current_ev = iwe_stream_add_point(info, *current_ev,
end_buf, &iwe, pos); end_buf, &iwe,
(void *)pos);
} }
} }
@ -1182,10 +1170,11 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
struct cfg80211_internal_bss *bss, char *current_ev, struct cfg80211_internal_bss *bss, char *current_ev,
char *end_buf) char *end_buf)
{ {
const struct cfg80211_bss_ies *ies;
struct iw_event iwe; struct iw_event iwe;
const u8 *ie;
u8 *buf, *cfg, *p; u8 *buf, *cfg, *p;
u8 *ie = bss->pub.information_elements; int rem, i, sig;
int rem = bss->pub.len_information_elements, i, sig;
bool ismesh = false; bool ismesh = false;
memset(&iwe, 0, sizeof(iwe)); memset(&iwe, 0, sizeof(iwe));
@ -1250,7 +1239,17 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
current_ev = iwe_stream_add_point(info, current_ev, end_buf, current_ev = iwe_stream_add_point(info, current_ev, end_buf,
&iwe, ""); &iwe, "");
while (rem >= 2) { rcu_read_lock();
ies = rcu_dereference(bss->pub.ies);
if (ies) {
rem = ies->len;
ie = ies->data;
} else {
rem = 0;
ie = NULL;
}
while (ies && rem >= 2) {
/* invalid data */ /* invalid data */
if (ie[1] > rem - 2) if (ie[1] > rem - 2)
break; break;
@ -1262,7 +1261,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
iwe.u.data.length = ie[1]; iwe.u.data.length = ie[1];
iwe.u.data.flags = 1; iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(info, current_ev, end_buf, current_ev = iwe_stream_add_point(info, current_ev, end_buf,
&iwe, ie + 2); &iwe, (u8 *)ie + 2);
break; break;
case WLAN_EID_MESH_ID: case WLAN_EID_MESH_ID:
memset(&iwe, 0, sizeof(iwe)); memset(&iwe, 0, sizeof(iwe));
@ -1270,7 +1269,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
iwe.u.data.length = ie[1]; iwe.u.data.length = ie[1];
iwe.u.data.flags = 1; iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(info, current_ev, end_buf, current_ev = iwe_stream_add_point(info, current_ev, end_buf,
&iwe, ie + 2); &iwe, (u8 *)ie + 2);
break; break;
case WLAN_EID_MESH_CONFIG: case WLAN_EID_MESH_CONFIG:
ismesh = true; ismesh = true;
@ -1279,7 +1278,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
buf = kmalloc(50, GFP_ATOMIC); buf = kmalloc(50, GFP_ATOMIC);
if (!buf) if (!buf)
break; break;
cfg = ie + 2; cfg = (u8 *)ie + 2;
memset(&iwe, 0, sizeof(iwe)); memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVCUSTOM; iwe.cmd = IWEVCUSTOM;
sprintf(buf, "Mesh Network Path Selection Protocol ID: " sprintf(buf, "Mesh Network Path Selection Protocol ID: "
@ -1377,7 +1376,8 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
kfree(buf); kfree(buf);
} }
ieee80211_scan_add_ies(info, &bss->pub, &current_ev, end_buf); ieee80211_scan_add_ies(info, ies, &current_ev, end_buf);
rcu_read_unlock();
return current_ev; return current_ev;
} }

View File

@ -417,7 +417,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
struct cfg80211_bss *bss) struct cfg80211_bss *bss)
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
u8 *country_ie; const u8 *country_ie;
#ifdef CONFIG_CFG80211_WEXT #ifdef CONFIG_CFG80211_WEXT
union iwreq_data wrqu; union iwreq_data wrqu;
#endif #endif
@ -501,7 +501,15 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
wdev->sme_state = CFG80211_SME_CONNECTED; wdev->sme_state = CFG80211_SME_CONNECTED;
cfg80211_upload_connect_keys(wdev); cfg80211_upload_connect_keys(wdev);
country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); rcu_read_lock();
country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
if (!country_ie) {
rcu_read_unlock();
return;
}
country_ie = kmemdup(country_ie, 2 + country_ie[1], GFP_ATOMIC);
rcu_read_unlock();
if (!country_ie) if (!country_ie)
return; return;
@ -515,6 +523,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
bss->channel->band, bss->channel->band,
country_ie + 2, country_ie + 2,
country_ie[1]); country_ie[1]);
kfree(country_ie);
} }
void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,

View File

@ -688,10 +688,13 @@ EXPORT_SYMBOL(cfg80211_classify8021d);
const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie)
{ {
if (bss->information_elements == NULL) const struct cfg80211_bss_ies *ies;
ies = rcu_dereference(bss->ies);
if (!ies)
return NULL; return NULL;
return cfg80211_find_ie(ie, bss->information_elements,
bss->len_information_elements); return cfg80211_find_ie(ie, ies->data, ies->len);
} }
EXPORT_SYMBOL(ieee80211_bss_get_ie); EXPORT_SYMBOL(ieee80211_bss_get_ie);

View File

@ -242,13 +242,17 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev,
wdev_lock(wdev); wdev_lock(wdev);
if (wdev->current_bss) { if (wdev->current_bss) {
const u8 *ie = ieee80211_bss_get_ie(&wdev->current_bss->pub, const u8 *ie;
WLAN_EID_SSID);
rcu_read_lock();
ie = ieee80211_bss_get_ie(&wdev->current_bss->pub,
WLAN_EID_SSID);
if (ie) { if (ie) {
data->flags = 1; data->flags = 1;
data->length = ie[1]; data->length = ie[1];
memcpy(ssid, ie + 2, data->length); memcpy(ssid, ie + 2, data->length);
} }
rcu_read_unlock();
} else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
data->flags = 1; data->flags = 1;
data->length = wdev->wext.connect.ssid_len; data->length = wdev->wext.connect.ssid_len;