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;
int n = 0;
rcu_read_lock();
rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_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;
n = 4;
}
rcu_read_unlock();
rate_tlv->header.len = cpu_to_le16(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);
/* add SSID TLV */
rcu_read_lock();
ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
if (ssid_eid)
pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]);
else
lbs_deb_assoc("no SSID\n");
rcu_read_unlock();
/* add DS param TLV */
if (bss->channel)
@ -1782,7 +1786,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv,
struct cfg80211_ibss_params *params,
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;
u8 preamble = RADIO_PREAMBLE_SHORT;
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
bss */
rcu_read_lock();
rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
if (!rates_eid) {
lbs_add_rates(cmd.bss.rates);
} 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 */
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,
req->ssids[i].ssid,
req->ssids[i].ssid_len,
req->ie, req->ie_len);
req->ie_len);
if (!probe)
continue;
if (req->ie_len)
memcpy(skb_put(probe, req->ie_len), req->ie,
req->ie_len);
local_bh_disable();
mac80211_hwsim_tx_frame(hwsim->hw, probe,
hwsim->tmp_chan);

View File

@ -160,11 +160,21 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
{
int ret;
u8 *beacon_ie;
size_t beacon_ie_len;
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) {
dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n");
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,
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 =
&priv->adapter->domain_reg;
country_ie = (u8 *)ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
if (!country_ie)
rcu_read_lock();
country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
if (!country_ie) {
rcu_read_unlock();
return 0;
}
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;
}
domain_info->country_code[0] = country_ie[2];
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,
&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,
HostCmd_ACT_GEN_SET, 0, NULL)) {
wiphy_err(priv->adapter->wiphy,

View File

@ -896,11 +896,13 @@ static int wl1251_op_hw_scan(struct ieee80211_hw *hw,
goto out;
skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len,
req->ie, req->ie_len);
req->ie_len);
if (!skb) {
ret = -ENOMEM;
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,
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;
skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
ie, ie_len);
ie_len);
if (!skb) {
ret = -ENOMEM;
goto out;
}
if (ie_len)
memcpy(skb_put(skb, ie_len), ie, ie_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;
} __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
*

View File

@ -58,6 +58,8 @@
* structures here describe these capabilities in detail.
*/
struct wiphy;
/*
* wireless hardware capability structures
*/
@ -387,6 +389,22 @@ const struct cfg80211_chan_def *
cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1,
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
*
@ -520,6 +538,8 @@ struct cfg80211_beacon_data {
* @privacy: the BSS uses privacy
* @auth_type: Authentication type (algorithm)
* @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_chan_def chandef;
@ -534,6 +554,8 @@ struct cfg80211_ap_settings {
bool privacy;
enum nl80211_auth_type auth_type;
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
* @ht_opmode: HT Operation mode
* (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 {
int use_cts_prot;
@ -904,6 +928,7 @@ struct bss_parameters {
u8 basic_rates_len;
int ap_isolate;
int ht_opmode;
s8 p2p_ctwindow, p2p_opp_ps;
};
/**
@ -1045,9 +1070,6 @@ struct ieee80211_txq_params {
u8 aifs;
};
/* from net/wireless.h */
struct wiphy;
/**
* DOC: Scanning and BSS list handling
*
@ -1183,6 +1205,18 @@ enum cfg80211_signal_type {
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
*
@ -1194,36 +1228,34 @@ enum cfg80211_signal_type {
* @tsf: timestamp of last received update
* @beacon_interval: the beacon interval as from the frame
* @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
* either the beacon_ies or proberesp_ies depending on whether Probe
* Response frame has been received
* @len_information_elements: total length of the information elements
* @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
* @len_proberesp_ies: total length of the proberesp_ies
* @signal: signal strength value (type depends on the wiphy's signal_type)
* @free_priv: function pointer to free private data
* @priv: private area for driver use, has at least wiphy->bss_priv_size bytes
*/
struct cfg80211_bss {
u64 tsf;
struct ieee80211_channel *channel;
u8 bssid[ETH_ALEN];
u64 tsf;
u16 beacon_interval;
u16 capability;
u8 *information_elements;
size_t len_information_elements;
u8 *beacon_ies;
size_t len_beacon_ies;
u8 *proberesp_ies;
size_t len_proberesp_ies;
const struct cfg80211_bss_ies __rcu *ies;
const struct cfg80211_bss_ies __rcu *beacon_ies;
const struct cfg80211_bss_ies __rcu *proberesp_ies;
void (*free_priv)(struct cfg80211_bss *bss);
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 *))));
};
@ -1231,6 +1263,9 @@ struct cfg80211_bss {
* ieee80211_bss_get_ie - find IE with given ID
* @bss: the bss to search
* @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.
*/
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
*
* 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 {
IEEE80211_RADIOTAP_TSFT = 0,
@ -209,6 +213,7 @@ enum ieee80211_radiotap_type {
IEEE80211_RADIOTAP_MCS = 19,
IEEE80211_RADIOTAP_AMPDU_STATUS = 20,
IEEE80211_RADIOTAP_VHT = 21,
/* valid in every it_present bitmap, even vendor namespaces */
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_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 */
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
* @rx_chains_dynamic: The number of RX chains that must be enabled
* 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
* 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
* 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
* from this HW. Note only HW checksum features are currently
* compatible with mac80211. Other feature bits will be rejected.
@ -1499,6 +1503,7 @@ struct ieee80211_hw {
u8 max_tx_aggregation_subframes;
u8 offchannel_tx_hw_queue;
u8 radiotap_mcs_details;
u16 radiotap_vht_details;
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.
* @ssid: SSID buffer
* @ssid_len: length of SSID
* @ie: buffer containing all IEs except SSID for the template
* @ie_len: length of the IE buffer
* @tailroom: tailroom to reserve at end of SKB for IEs
*
* Creates a Probe Request template which can, for example, be uploaded to
* 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 ieee80211_vif *vif,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len);
size_t tailroom);
/**
* 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_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_AFTER_LAST: internal use
*/
@ -1570,6 +1577,9 @@ enum nl80211_attrs {
NL80211_ATTR_CENTER_FREQ1,
NL80211_ATTR_CENTER_FREQ2,
NL80211_ATTR_P2P_CTWINDOW,
NL80211_ATTR_P2P_OPPPS,
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@ -3126,6 +3136,10 @@ enum nl80211_ap_sme_features {
* @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
* 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 {
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
@ -3139,6 +3153,8 @@ enum nl80211_feature_flags {
NL80211_FEATURE_AP_SCAN = 1 << 8,
NL80211_FEATURE_VIF_TXPOWER = 1 << 9,
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;
}
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)
{
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);
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;
sta_set_rate_info_rx(sta, &sinfo->rxrate);
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#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 |
BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_BEACON |
BSS_CHANGED_SSID;
BSS_CHANGED_SSID |
BSS_CHANGED_P2P_PS;
int err;
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 =
(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);
if (err < 0)
return err;
@ -1807,6 +1816,16 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
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);
return 0;

View File

@ -53,6 +53,7 @@ static const struct file_operations sta_ ##name## _ops = { \
STA_FILE(aid, sta.aid, D);
STA_FILE(dev, sdata->name, S);
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,
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);
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) \
debugfs_create_file(#name, 0400, \
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(last_signal);
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(tx_packets, tx_packets);

View File

@ -371,6 +371,8 @@ enum ieee80211_sta_flags {
IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9),
IEEE80211_STA_DISABLE_40MHZ = BIT(10),
IEEE80211_STA_DISABLE_VHT = BIT(11),
IEEE80211_STA_DISABLE_80P80MHZ = BIT(12),
IEEE80211_STA_DISABLE_160MHZ = BIT(13),
};
struct ieee80211_mgd_auth_data {
@ -1032,6 +1034,7 @@ struct ieee80211_local {
enum ieee80211_band hw_scan_band;
int scan_channel_idx;
int scan_ies_len;
int hw_scan_ies_bufsize;
struct work_struct sched_scan_stopped_work;
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,
bool send_frame, u8 *frame_buf);
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,
u8 channel);
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;
}
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)
{
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))
return -EBUSY;
ret = ieee80211_verify_mac(sdata->local, sa->sa_data);
if (ret)
return ret;
ret = eth_mac_addr(dev, sa);
if (ret == 0)

View File

@ -474,7 +474,8 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 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] = {
.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 |
IEEE80211_RADIOTAP_MCS_HAVE_GI |
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;
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;
}
rate_control_rate_init(sta);
if (insert)
rate_control_rate_init(sta);
spin_unlock_bh(&sta->lock);
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);
set_bit(MESH_WORK_DRIFT_ADJUST,
&ifmsh->wrkq_flags);
ifmsh->adjusting_tbtt = true;
} else {
msync_dbg(sdata,
"TBTT : max clockdrift=%lld; too small to adjust\n",
(long long)ifmsh->sync_offset_clockdrift_max);
ifmsh->sync_offset_clockdrift_max = 0;
ifmsh->adjusting_tbtt = false;
}
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 */
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 */
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
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;
}
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))
ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,
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");
ieee80211_queue_work(&sdata->local->hw,
&ifmgd->csa_connection_drop_work);
return;
}
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;
if (sdata->vif.p2p) {
u8 noa[2];
int ret;
const struct cfg80211_bss_ies *ies;
ret = cfg80211_get_p2p_attr(cbss->information_elements,
cbss->len_information_elements,
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_lock();
ies = rcu_dereference(cbss->ies);
if (ies) {
u8 noa[2];
int ret;
ret = cfg80211_get_p2p_attr(
ies->data, ies->len,
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 */
@ -1645,6 +1667,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
} else {
int ssid_len;
rcu_read_lock();
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
if (WARN_ON_ONCE(ssid == NULL))
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,
0, (u32) -1, true, false,
ifmgd->associated->channel, false);
rcu_read_unlock();
}
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
return NULL;
rcu_read_lock();
ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID);
if (WARN_ON_ONCE(ssid == NULL))
ssid_len = 0;
@ -1759,6 +1784,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
(u32) -1, cbss->channel,
ssid + 2, ssid_len,
NULL, 0, true);
rcu_read_unlock();
return skb;
}
@ -2844,9 +2870,12 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
auth_data->bss->bssid, auth_data->tries,
IEEE80211_AUTH_MAX_TRIES);
rcu_read_lock();
ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID);
if (!ssidie)
if (!ssidie) {
rcu_read_unlock();
return -EINVAL;
}
/*
* Direct probe is sent to broadcast address as some APs
* 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],
NULL, 0, (u32) -1, true, false,
auth_data->bss->channel, false);
rcu_read_unlock();
}
auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
@ -3183,106 +3213,313 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
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,
struct cfg80211_bss *cbss)
{
struct ieee80211_local *local = sdata->local;
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_vht_operation *vht_oper = NULL;
struct ieee80211_supported_band *sband;
struct cfg80211_chan_def chandef;
int ret;
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) {
ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
cbss->information_elements,
cbss->len_information_elements);
rcu_read_lock();
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
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))
ht_oper = (void *)(ht_oper_ie + 2);
}
if (ht_oper) {
ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
cbss->channel->band);
/* check that channel matches the right operating channel */
if (cbss->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.
*/
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
sband->vht_cap.vht_supported) {
const u8 *vht_oper_ie;
vht_oper_ie = ieee80211_bss_get_ie(cbss,
WLAN_EID_VHT_OPERATION);
if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper))
vht_oper = (void *)(vht_oper_ie + 2);
if (vht_oper && !ht_oper) {
vht_oper = NULL;
sdata_info(sdata,
"Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
cbss->channel->center_freq,
ht_cfreq, ht_oper->primary_chan,
cbss->channel->band);
ht_oper = NULL;
"AP advertised VHT without HT, disabling both\n");
sdata->flags |= IEEE80211_STA_DISABLE_HT;
sdata->flags |= IEEE80211_STA_DISABLE_VHT;
}
}
if (ht_oper) {
/*
* cfg80211 already verified that the channel itself can
* be used, but it didn't check that we can do the right
* 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;
ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
cbss->channel,
ht_oper, vht_oper,
&chandef);
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) {
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;
}
rcu_read_unlock();
/* will change later if needed */
sdata->smps_mode = IEEE80211_SMPS_OFF;
ieee80211_vif_release_channel(sdata);
cfg80211_chandef_create(&chandef, cbss->channel, channel_type);
return ieee80211_vif_use_channel(sdata, &chandef,
IEEE80211_CHANCTX_SHARED);
/*
* If this fails (possibly due to channel context sharing
* on incompatible channels, e.g. 80+80 and 160 sharing the
* 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,
@ -3510,14 +3747,21 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
const u8 *ssidie, *ht_ie;
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);
if (!assoc_data)
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);
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_len = bss->supp_rates_len;
rcu_read_lock();
ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION);
if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation))
assoc_data->ap_ht_param =
((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param;
else
ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
rcu_read_unlock();
if (bss->wmm_used && bss->uapsd_supported &&
(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;
}
memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]);
assoc_data->ssid_len = ssidie[1];
if (req->prev_bssid)
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 minstrel_rate_stats *rate, *rate2;
struct minstrel_priv *mp = priv;
bool last = false;
bool last;
int group;
int i = 0;
int i;
if (!msp->is_ht)
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)
mi->sample_packets += info->status.ampdu_len;
last = !minstrel_ht_txstat_valid(&ar[0]);
for (i = 0; !last; i++) {
last = (i == IEEE80211_TX_MAX_RATES - 1) ||
!minstrel_ht_txstat_valid(&ar[i + 1]);
if (!minstrel_ht_txstat_valid(&ar[i]))
break;
group = minstrel_ht_get_group_idx(&ar[i]);
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 */
WARN_ON(1);
dev_kfree_skb(skb);
skb = NULL;
return NULL;
}
}
@ -111,6 +111,11 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
len += 8;
}
if (status->flag & RX_FLAG_VHT) {
len = ALIGN(len, 2);
len += 12;
}
if (status->vendor_radiotap_len) {
if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
status->vendor_radiotap_align = 1;
@ -297,6 +302,41 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*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) {
/* ensure 2 byte alignment for the vendor field as required */
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;
ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
local->hw_scan_ies_bufsize,
req->ie, req->ie_len, band,
req->rates[band], 0);
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) {
u8 *ies;
local->hw_scan_ies_bufsize = 2 + IEEE80211_MAX_SSID_LEN +
local->scan_ies_len +
req->ie_len;
local->hw_scan_req = kmalloc(
sizeof(*local->hw_scan_req) +
req->n_channels * sizeof(req->channels[0]) +
2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len +
req->ie_len, GFP_KERNEL);
local->hw_scan_ies_bufsize, GFP_KERNEL);
if (!local->hw_scan_req)
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_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);
@ -946,10 +952,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
if (!local->hw.wiphy->bands[i])
continue;
sched_scan_ies.ie[i] = kzalloc(2 + IEEE80211_MAX_SSID_LEN +
local->scan_ies_len +
req->ie_len,
GFP_KERNEL);
sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL);
if (!sched_scan_ies.ie[i]) {
ret = -ENOMEM;
goto out_free;
@ -957,8 +960,8 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
sched_scan_ies.len[i] =
ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
req->ie, req->ie_len, i,
(u32) -1, 0);
iebufsz, req->ie, req->ie_len,
i, (u32) -1, 0);
}
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
* @last_signal: signal of last received frame 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)
* @tx_filtered_count: number of frames the hardware filtered for this STA
* @tx_retry_failed: number of frames that failed retry
@ -329,6 +330,7 @@ struct sta_info {
unsigned long rx_dropped;
int last_signal;
struct ewma avg_signal;
int last_ack_signal;
/* Plus 1 for non-QoS frames */
__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,
const struct ieee80211_tx_rate *rate,
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,
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;
}
}
if (acked)
sta->last_ack_signal = info->status.ack_signal;
}
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 ieee80211_vif *vif,
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_local *local;
@ -2637,7 +2637,7 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
ie_ssid_len = 2 + ssid_len;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) +
ie_ssid_len + ie_len);
ie_ssid_len + tailroom);
if (!skb)
return NULL;
@ -2658,11 +2658,6 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
memcpy(pos, ssid, ssid_len);
pos += ssid_len;
if (ie) {
pos = skb_put(skb, ie_len);
memcpy(pos, ie, ie_len);
}
return skb;
}
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,
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,
u8 channel)
{
struct ieee80211_supported_band *sband;
u8 *pos;
u8 *pos = buffer, *end = buffer + buffer_len;
size_t offset = 0, noffset;
int supp_rates_len, i;
u8 rates[32];
@ -1123,8 +1123,6 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
if (WARN_ON_ONCE(!sband))
return 0;
pos = buffer;
num_rates = 0;
for (i = 0; i < sband->n_bitrates; i++) {
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);
if (end - pos < 2 + supp_rates_len)
goto out_err;
*pos++ = WLAN_EID_SUPP_RATES;
*pos++ = 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,
ARRAY_SIZE(before_extrates),
offset);
if (end - pos < noffset - offset)
goto out_err;
memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset;
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;
if (ext_rates_len > 0) {
if (end - pos < 2 + ext_rates_len)
goto out_err;
*pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = 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 (end - pos < 3)
goto out_err;
*pos++ = WLAN_EID_DS_PARAMS;
*pos++ = 1;
*pos++ = channel;
@ -1182,14 +1188,19 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
noffset = ieee80211_ie_split(ie, ie_len,
before_ht, ARRAY_SIZE(before_ht),
offset);
if (end - pos < noffset - offset)
goto out_err;
memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset;
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,
sband->ht_cap.cap);
}
/*
* 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 */
if (ie && ie_len) {
noffset = ie_len;
if (end - pos < noffset - offset)
goto out_err;
memcpy(pos, ie + offset, 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,
sband->vht_cap.cap);
}
return pos - buffer;
out_err:
WARN_ONCE(1, "not enough space for preq IEs\n");
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 sk_buff *skb;
struct ieee80211_mgmt *mgmt;
size_t buf_len;
u8 *buf;
u8 chan_no;
/* FIXME: come up with a proper value */
buf = kmalloc(200 + ie_len, GFP_KERNEL);
if (!buf)
return NULL;
int ies_len;
/*
* 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
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,
ssid, ssid_len,
buf, buf_len);
ssid, ssid_len, 100 + ie_len);
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) {
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;
out:
kfree(buf);
return skb;
}
@ -1527,7 +1539,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
changed |= BSS_CHANGED_IBSS;
/* fall through */
case NL80211_IFTYPE_AP:
changed |= BSS_CHANGED_SSID;
changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS;
if (sdata->vif.type == NL80211_IFTYPE_AP) {
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);
bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef)
bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
{
u32 control_freq;
@ -105,6 +105,7 @@ bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef)
return true;
}
EXPORT_SYMBOL(cfg80211_chandef_valid);
static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
int *pri40, int *pri80)
@ -187,9 +188,9 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
}
EXPORT_SYMBOL(cfg80211_chandef_compatible);
bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
u32 center_freq, u32 bandwidth,
u32 prohibited_flags)
static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
u32 center_freq, u32 bandwidth,
u32 prohibited_flags)
{
struct ieee80211_channel *c;
u32 freq;
@ -205,55 +206,88 @@ bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
return true;
}
static bool cfg80211_check_beacon_chans(struct wiphy *wiphy,
u32 center_freq, u32 bw)
bool cfg80211_chandef_usable(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef,
u32 prohibited_flags)
{
return cfg80211_secondary_chans_ok(wiphy, center_freq, bw,
IEEE80211_CHAN_DISABLED |
IEEE80211_CHAN_PASSIVE_SCAN |
IEEE80211_CHAN_NO_IBSS |
IEEE80211_CHAN_RADAR);
}
struct ieee80211_sta_ht_cap *ht_cap;
struct ieee80211_sta_vht_cap *vht_cap;
u32 width, control_freq;
bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
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);
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
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) {
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_20:
if (!ht_cap->ht_supported)
return false;
case NL80211_CHAN_WIDTH_20_NOHT:
width = 20;
break;
case NL80211_CHAN_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;
case NL80211_CHAN_WIDTH_80:
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;
break;
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;
break;
default:
WARN_ON_ONCE(1);
trace_cfg80211_return_bool(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)
res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq2,
width);
if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1,
width, prohibited_flags))
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);
return res;

View File

@ -138,8 +138,6 @@ struct cfg80211_internal_bss {
unsigned long ts;
struct kref ref;
atomic_t hold;
bool beacon_ies_allocated;
bool proberesp_ies_allocated;
/* must be last because of priv member */
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,
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
#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS

View File

@ -146,7 +146,8 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
if (!setup->chandef.chan)
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))

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_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN },
[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 */
@ -1369,9 +1371,7 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
struct genl_info *info,
struct cfg80211_chan_def *chandef)
{
struct ieee80211_sta_ht_cap *ht_cap;
struct ieee80211_sta_vht_cap *vht_cap;
u32 control_freq, width;
u32 control_freq;
if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
return -EINVAL;
@ -1417,66 +1417,12 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
info->attrs[NL80211_ATTR_CENTER_FREQ2]);
}
ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap;
vht_cap = &rdev->wiphy.bands[chandef->chan->band]->vht_cap;
if (!cfg80211_chan_def_valid(chandef))
if (!cfg80211_chandef_valid(chandef))
return -EINVAL;
switch (chandef->width) {
case NL80211_CHAN_WIDTH_20:
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:
if (!cfg80211_chandef_usable(&rdev->wiphy, chandef,
IEEE80211_CHAN_DISABLED))
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;
}
@ -1841,7 +1787,7 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
static int nl80211_send_chandef(struct sk_buff *msg,
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,
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]);
}
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]) {
err = nl80211_parse_chandef(rdev, info, &params.chandef);
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.ap_isolate = -1;
params.ht_opmode = -1;
params.p2p_ctwindow = -1;
params.p2p_opp_ps = -1;
if (info->attrs[NL80211_ATTR_BSS_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 =
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)
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_bss *res = &intbss->pub;
const struct cfg80211_bss_ies *ies;
void *hdr;
struct nlattr *bss;
@ -4828,16 +4829,24 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
if (!bss)
goto nla_put_failure;
if ((!is_zero_ether_addr(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)))
nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)))
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 &&
nla_put_u64(msg, NL80211_BSS_TSF, res->tsf))
goto nla_put_failure;
@ -5502,6 +5511,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
return -EINVAL;
ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
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,
u32 rate, u32 pkts, u32 intvl)
u32 rate, u32 pkts, u32 intvl)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct wireless_dev *wdev;
struct net_device *dev = info->user_ptr[1];
if ((rate < 0 || rate > 100) ||
(intvl < 0 || intvl > NL80211_CQM_TXE_MAX_INTVL))
if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL)
return -EINVAL;
wdev = dev->ieee80211_ptr;

View File

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

View File

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

View File

@ -23,18 +23,23 @@
static void bss_release(struct kref *ref)
{
struct cfg80211_bss_ies *ies;
struct cfg80211_internal_bss *bss;
bss = container_of(ref, struct cfg80211_internal_bss, ref);
if (WARN_ON(atomic_read(&bss->hold)))
return;
if (bss->pub.free_priv)
bss->pub.free_priv(&bss->pub);
if (bss->beacon_ies_allocated)
kfree(bss->pub.beacon_ies);
if (bss->proberesp_ies_allocated)
kfree(bss->pub.proberesp_ies);
BUG_ON(atomic_read(&bss->hold));
ies = (void *)rcu_access_pointer(bss->pub.beacon_ies);
if (ies)
kfree_rcu(ies, rcu_head);
ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies);
if (ies)
kfree_rcu(ies, rcu_head);
kfree(bss);
}
@ -236,9 +241,8 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *bss;
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;
}
}
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);
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 *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]);
}
static bool is_bss(struct cfg80211_bss *a,
const u8 *bssid,
static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
const u8 *ssid, size_t ssid_len)
{
const struct cfg80211_bss_ies *ies;
const u8 *ssidie;
if (bssid && !ether_addr_equal(a->bssid, bssid))
@ -319,9 +323,10 @@ static bool is_bss(struct cfg80211_bss *a,
if (!ssid)
return true;
ssidie = cfg80211_find_ie(WLAN_EID_SSID,
a->information_elements,
a->len_information_elements);
ies = rcu_access_pointer(a->ies);
if (!ies)
return false;
ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
if (!ssidie)
return false;
if (ssidie[1] != ssid_len)
@ -331,20 +336,21 @@ static bool is_bss(struct cfg80211_bss *a,
static bool is_mesh_bss(struct cfg80211_bss *a)
{
const struct cfg80211_bss_ies *ies;
const u8 *ie;
if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
a->information_elements,
a->len_information_elements);
ies = rcu_access_pointer(a->ies);
if (!ies)
return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len);
if (!ie)
return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
a->information_elements,
a->len_information_elements);
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len);
if (!ie)
return false;
@ -355,14 +361,17 @@ static bool is_mesh(struct cfg80211_bss *a,
const u8 *meshid, size_t meshidlen,
const u8 *meshcfg)
{
const struct cfg80211_bss_ies *ies;
const u8 *ie;
if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
a->information_elements,
a->len_information_elements);
ies = rcu_access_pointer(a->ies);
if (!ies)
return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len);
if (!ie)
return false;
if (ie[1] != meshidlen)
@ -370,9 +379,7 @@ static bool is_mesh(struct cfg80211_bss *a,
if (memcmp(ie + 2, meshid, meshidlen))
return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
a->information_elements,
a->len_information_elements);
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len);
if (!ie)
return false;
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.
*/
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,
struct cfg80211_bss *b)
static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b)
{
const struct cfg80211_bss_ies *a_ies, *b_ies;
int r;
if (a->channel != b->channel)
return b->channel->center_freq - a->channel->center_freq;
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,
a->information_elements,
a->len_information_elements,
b->information_elements,
b->len_information_elements);
a_ies->data, a_ies->len,
b_ies->data, b_ies->len);
if (r)
return r;
return cmp_ies(WLAN_EID_MESH_CONFIG,
a->information_elements,
a->len_information_elements,
b->information_elements,
b->len_information_elements);
a_ies->data, a_ies->len,
b_ies->data, b_ies->len);
}
/*
@ -420,22 +430,28 @@ static int cmp_bss_core(struct cfg80211_bss *a,
static int cmp_bss(struct cfg80211_bss *a,
struct cfg80211_bss *b)
{
const struct cfg80211_bss_ies *a_ies, *b_ies;
int r;
r = cmp_bss_core(a, b);
if (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,
a->information_elements,
a->len_information_elements,
b->information_elements,
b->len_information_elements);
a_ies->data, a_ies->len,
b_ies->data, b_ies->len);
}
static int cmp_hidden_bss(struct cfg80211_bss *a,
struct cfg80211_bss *b)
static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b)
{
const struct cfg80211_bss_ies *a_ies, *b_ies;
const u8 *ie1;
const u8 *ie2;
int i;
@ -445,17 +461,26 @@ static int cmp_hidden_bss(struct cfg80211_bss *a,
if (r)
return r;
ie1 = cfg80211_find_ie(WLAN_EID_SSID,
a->information_elements,
a->len_information_elements);
ie2 = cfg80211_find_ie(WLAN_EID_SSID,
b->information_elements,
b->len_information_elements);
a_ies = rcu_access_pointer(a->ies);
if (!a_ies)
return -1;
b_ies = rcu_access_pointer(b->ies);
if (!b_ies)
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
* 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 */
if (!ie1)
@ -471,10 +496,14 @@ static int cmp_hidden_bss(struct cfg80211_bss *a,
if (ie1[1] != ie2[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++)
if (ie2[i + 2])
return -1;
return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
return 0;
}
@ -600,7 +629,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
static struct cfg80211_internal_bss *
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 cfg80211_internal_bss *bss;
@ -623,127 +652,86 @@ rb_find_hidden_bss(struct cfg80211_registered_device *dev,
static void
copy_hidden_ies(struct cfg80211_internal_bss *res,
struct cfg80211_internal_bss *hidden)
struct cfg80211_internal_bss *hidden)
{
if (unlikely(res->pub.beacon_ies))
return;
if (WARN_ON(!hidden->pub.beacon_ies))
const struct cfg80211_bss_ies *ies;
if (rcu_access_pointer(res->pub.beacon_ies))
return;
res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC);
if (unlikely(!res->pub.beacon_ies))
ies = rcu_access_pointer(hidden->pub.beacon_ies);
if (WARN_ON(!ies))
return;
res->beacon_ies_allocated = true;
res->pub.len_beacon_ies = hidden->pub.len_beacon_ies;
memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies,
res->pub.len_beacon_ies);
ies = kmemdup(ies, sizeof(*ies) + ies->len, GFP_ATOMIC);
if (unlikely(!ies))
return;
rcu_assign_pointer(res->pub.beacon_ies, ies);
}
static struct cfg80211_internal_bss *
cfg80211_bss_update(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *res)
struct cfg80211_internal_bss *tmp)
{
struct cfg80211_internal_bss *found = NULL;
/*
* The reference to "res" is donated to this function.
*/
if (WARN_ON(!res->pub.channel)) {
kref_put(&res->ref, bss_release);
if (WARN_ON(!tmp->pub.channel))
return NULL;
}
res->ts = jiffies;
tmp->ts = jiffies;
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) {
found->pub.beacon_interval = res->pub.beacon_interval;
found->pub.tsf = res->pub.tsf;
found->pub.signal = res->pub.signal;
found->pub.capability = res->pub.capability;
found->ts = res->ts;
found->pub.beacon_interval = tmp->pub.beacon_interval;
found->pub.tsf = tmp->pub.tsf;
found->pub.signal = tmp->pub.signal;
found->pub.capability = tmp->pub.capability;
found->ts = tmp->ts;
/* Update IEs */
if (res->pub.proberesp_ies) {
size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
size_t ielen = res->pub.len_proberesp_ies;
if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
const struct cfg80211_bss_ies *old;
if (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;
}
}
old = rcu_access_pointer(found->pub.proberesp_ies);
rcu_assign_pointer(found->pub.proberesp_ies,
tmp->pub.proberesp_ies);
/* Override possible earlier Beacon frame IEs */
found->pub.information_elements =
found->pub.proberesp_ies;
found->pub.len_information_elements =
found->pub.len_proberesp_ies;
}
if (res->pub.beacon_ies) {
size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
size_t ielen = res->pub.len_beacon_ies;
bool information_elements_is_beacon_ies =
(found->pub.information_elements ==
found->pub.beacon_ies);
rcu_assign_pointer(found->pub.ies,
tmp->pub.proberesp_ies);
if (old)
kfree_rcu((struct cfg80211_bss_ies *)old,
rcu_head);
} else if (rcu_access_pointer(tmp->pub.beacon_ies)) {
const struct cfg80211_bss_ies *old, *ies;
if (found->pub.beacon_ies &&
!found->beacon_ies_allocated &&
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;
old = rcu_access_pointer(found->pub.beacon_ies);
ies = rcu_access_pointer(found->pub.ies);
if (found->beacon_ies_allocated)
ies = krealloc(ies, ielen, GFP_ATOMIC);
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;
}
}
rcu_assign_pointer(found->pub.beacon_ies,
tmp->pub.beacon_ies);
/* Override IEs if they were from a beacon before */
if (information_elements_is_beacon_ies) {
found->pub.information_elements =
found->pub.beacon_ies;
found->pub.len_information_elements =
found->pub.len_beacon_ies;
}
}
if (old == ies)
rcu_assign_pointer(found->pub.ies,
tmp->pub.beacon_ies);
kref_put(&res->ref, bss_release);
if (old)
kfree_rcu((struct cfg80211_bss_ies *)old,
rcu_head);
}
} else {
struct cfg80211_internal_bss *new;
struct cfg80211_internal_bss *hidden;
struct cfg80211_bss_ies *ies;
/* First check if the beacon is a probe response from
* 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
* response bss entries when beacon ies are
* getting changed. */
hidden = rb_find_hidden_bss(dev, res);
hidden = rb_find_hidden_bss(dev, tmp);
if (hidden)
copy_hidden_ies(res, hidden);
copy_hidden_ies(tmp, hidden);
/* this "consumes" the reference */
list_add_tail(&res->list, &dev->bss_list);
rb_insert_bss(dev, res);
found = res;
/*
* create a copy -- the "res" variable that is passed in
* is allocated on the stack since it's not needed in the
* 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++;
@ -810,14 +816,12 @@ cfg80211_inform_bss(struct wiphy *wiphy,
u16 beacon_interval, const u8 *ie, size_t ielen,
s32 signal, gfp_t gfp)
{
struct cfg80211_internal_bss *res;
size_t privsz;
struct cfg80211_bss_ies *ies;
struct cfg80211_internal_bss tmp = {}, *res;
if (WARN_ON(!wiphy))
return NULL;
privsz = wiphy->bss_priv_size;
if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
(signal < 0 || signal > 100)))
return NULL;
@ -826,36 +830,33 @@ cfg80211_inform_bss(struct wiphy *wiphy,
if (!channel)
return NULL;
res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
if (!res)
return NULL;
memcpy(res->pub.bssid, bssid, ETH_ALEN);
res->pub.channel = channel;
res->pub.signal = signal;
res->pub.tsf = tsf;
res->pub.beacon_interval = beacon_interval;
res->pub.capability = capability;
memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
tmp.pub.channel = channel;
tmp.pub.signal = signal;
tmp.pub.tsf = tsf;
tmp.pub.beacon_interval = beacon_interval;
tmp.pub.capability = capability;
/*
* 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
* with the driver that does not provide the full Beacon/Probe Response
* frame. Use Beacon frame pointer to avoid indicating that this should
* override the information_elements pointer should we have received an
* earlier indication of Probe Response data.
* override the iies pointer should we have received an earlier
* indication of Probe Response data.
*
* The initial buffer for the IEs is allocated with the BSS entry and
* is located after the private area.
*/
res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz;
memcpy(res->pub.beacon_ies, ie, 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;
ies = kmalloc(sizeof(*ies) + ielen, gfp);
if (!ies)
return NULL;
ies->len = ielen;
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)
return NULL;
@ -874,10 +875,10 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
struct ieee80211_mgmt *mgmt, size_t len,
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,
u.probe_resp.variable);
size_t privsz;
BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.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)))
return NULL;
privsz = wiphy->bss_priv_size;
channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
ielen, channel);
if (!channel)
return NULL;
res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
if (!res)
ies = kmalloc(sizeof(*ies) + ielen, gfp);
if (!ies)
return NULL;
ies->len = ielen;
memcpy(ies->data, mgmt->u.probe_resp.variable, ielen);
memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN);
res->pub.channel = channel;
res->pub.signal = signal;
res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
/*
* The initial buffer for the IEs is allocated with the BSS entry and
* is located after the private area.
*/
if (ieee80211_is_probe_resp(mgmt->frame_control)) {
res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz;
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;
}
if (ieee80211_is_probe_resp(mgmt->frame_control))
rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
else
rcu_assign_pointer(tmp.pub.beacon_ies, ies);
rcu_assign_pointer(tmp.pub.ies, ies);
memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
tmp.pub.channel = channel;
tmp.pub.signal = signal;
tmp.pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
kref_init(&res->ref);
res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);
if (!res)
return NULL;
@ -1127,22 +1114,21 @@ int cfg80211_wext_siwscan(struct net_device *dev,
EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);
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)
{
u8 *pos, *end, *next;
const u8 *pos, *end, *next;
struct iw_event iwe;
if (!bss->information_elements ||
!bss->len_information_elements)
if (!ies)
return;
/*
* If needed, fragment the IEs buffer (at IE boundaries) into short
* enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
*/
pos = bss->information_elements;
end = pos + bss->len_information_elements;
pos = ies->data;
end = pos + ies->len;
while (end - pos > IW_GENERIC_IE_MAX) {
next = pos + 2 + pos[1];
@ -1153,7 +1139,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
iwe.cmd = IWEVGENIE;
iwe.u.data.length = next - pos;
*current_ev = iwe_stream_add_point(info, *current_ev,
end_buf, &iwe, pos);
end_buf, &iwe,
(void *)pos);
pos = next;
}
@ -1163,7 +1150,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
iwe.cmd = IWEVGENIE;
iwe.u.data.length = end - pos;
*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,
char *end_buf)
{
const struct cfg80211_bss_ies *ies;
struct iw_event iwe;
const u8 *ie;
u8 *buf, *cfg, *p;
u8 *ie = bss->pub.information_elements;
int rem = bss->pub.len_information_elements, i, sig;
int rem, i, sig;
bool ismesh = false;
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,
&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 */
if (ie[1] > rem - 2)
break;
@ -1262,7 +1261,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
iwe.u.data.length = ie[1];
iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
&iwe, ie + 2);
&iwe, (u8 *)ie + 2);
break;
case WLAN_EID_MESH_ID:
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.flags = 1;
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
&iwe, ie + 2);
&iwe, (u8 *)ie + 2);
break;
case WLAN_EID_MESH_CONFIG:
ismesh = true;
@ -1279,7 +1278,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
buf = kmalloc(50, GFP_ATOMIC);
if (!buf)
break;
cfg = ie + 2;
cfg = (u8 *)ie + 2;
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVCUSTOM;
sprintf(buf, "Mesh Network Path Selection Protocol ID: "
@ -1377,7 +1376,8 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
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;
}

View File

@ -417,7 +417,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
struct cfg80211_bss *bss)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
u8 *country_ie;
const u8 *country_ie;
#ifdef CONFIG_CFG80211_WEXT
union iwreq_data wrqu;
#endif
@ -501,7 +501,15 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
wdev->sme_state = CFG80211_SME_CONNECTED;
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)
return;
@ -515,6 +523,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
bss->channel->band,
country_ie + 2,
country_ie[1]);
kfree(country_ie);
}
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)
{
if (bss->information_elements == NULL)
const struct cfg80211_bss_ies *ies;
ies = rcu_dereference(bss->ies);
if (!ies)
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);

View File

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