This time we have various things - all across the board:

* MU-MIMO sniffer support in mac80211
  * a create_singlethread_workqueue() cleanup
  * interface dump filtering that was documented but not implemented
  * support for the new radiotap timestamp field
  * send delBA in two unexpected conditions (as required by the spec)
  * connect keys cleanups - allow only WEP with index 0-3
  * per-station aggregation limit to work around broken APs
  * debugfs improvement for the integrated codel algorithm
 and various other small improvements and cleanups.
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABCgAGBQJX2+umAAoJEGt7eEactAAdIMkP/jMmpbxkzD64L7nTkO4APGva
 r6RmMM1SmgVD/CtVkjlBLuvo5YOTWv/vWvy6KoUESOINAx/e6T3T7bmmCOXzbsOL
 e5/YYcS1AOqgn5SdhgIj1E5cpdYIhlUGRlNJ0qEjeLLrh4/TLUNbCcuPhOYybUMz
 fUrdPKgDeWb7x9EHLENhPsVtCXWwKnkDIS4qclPZCWgRj46XM4pNB4OlvCUzGY6k
 bOqGJfrtjYjgKFDmPFqfYA4JDA56980qqO41+eEKXeMvDKNs+pSiNco130Q+uU3E
 o7tk9DMnAnCy2GihpV1ZYVkLr6O+7o9xVuenj3NRlhyd1mn2gXxLcO4AkHcrZBkf
 Ei+2L+KgnWELyqiSOaGTJKlugsgS4DDoNnFEIVjSweQ9DIoBA/Gj/6+4uZeHXJ3M
 bEjtHnCLi5CuI067uBoevwXFoMi1poWra2KnZKOZzFS5OL3xHv4//x/Wmnn2/5Jz
 ffEwVyRmTY76sLWfnwXUDClrFWAYQrpNyTryc+k3cpYKzhnseiqt+z43cBuISm00
 uh5B9PpPB8RhtUnXrL/SHRyf8YEluaidTsI2lc1LvwXOc0+Zbp73mTCgP+rzLs9p
 K2qVRiozpIXanW6hKmmaDwjKlcAKKLP0xN2v90MqwQt4YdLIKlXnll1AH2BawzuP
 OWB3n8D0I6y0PWH+Yo8o
 =s1MY
 -----END PGP SIGNATURE-----

Merge tag 'mac80211-next-for-davem-2016-09-16' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

Johannes Berg says:

====================
This time we have various things - all across the board:
 * MU-MIMO sniffer support in mac80211
 * a create_singlethread_workqueue() cleanup
 * interface dump filtering that was documented but not implemented
 * support for the new radiotap timestamp field
 * send delBA in two unexpected conditions (as required by the spec)
 * connect keys cleanups - allow only WEP with index 0-3
 * per-station aggregation limit to work around broken APs
 * debugfs improvement for the integrated codel algorithm
and various other small improvements and cleanups.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2016-09-18 22:29:08 -04:00
commit c13ed534b8
34 changed files with 489 additions and 282 deletions

View file

@ -487,7 +487,7 @@ static const struct ieee80211_iface_combination hwsim_if_comb_p2p_dev[] = {
}; };
static spinlock_t hwsim_radio_lock; static spinlock_t hwsim_radio_lock;
static struct list_head hwsim_radios; static LIST_HEAD(hwsim_radios);
static int hwsim_radio_idx; static int hwsim_radio_idx;
static struct platform_driver mac80211_hwsim_driver = { static struct platform_driver mac80211_hwsim_driver = {
@ -3376,7 +3376,6 @@ static int __init init_mac80211_hwsim(void)
mac80211_hwsim_unassign_vif_chanctx; mac80211_hwsim_unassign_vif_chanctx;
spin_lock_init(&hwsim_radio_lock); spin_lock_init(&hwsim_radio_lock);
INIT_LIST_HEAD(&hwsim_radios);
err = register_pernet_device(&hwsim_net_ops); err = register_pernet_device(&hwsim_net_ops);
if (err) if (err)

View file

@ -2432,7 +2432,8 @@ struct cfg80211_qos_map {
* cases, the result of roaming is indicated with a call to * cases, the result of roaming is indicated with a call to
* cfg80211_roamed() or cfg80211_roamed_bss(). * cfg80211_roamed() or cfg80211_roamed_bss().
* (invoked with the wireless_dev mutex held) * (invoked with the wireless_dev mutex held)
* @disconnect: Disconnect from the BSS/ESS. * @disconnect: Disconnect from the BSS/ESS. Once done, call
* cfg80211_disconnected().
* (invoked with the wireless_dev mutex held) * (invoked with the wireless_dev mutex held)
* *
* @join_ibss: Join the specified IBSS (or create if necessary). Once done, call * @join_ibss: Join the specified IBSS (or create if necessary). Once done, call
@ -3954,6 +3955,34 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
unsigned int cfg80211_classify8021d(struct sk_buff *skb, unsigned int cfg80211_classify8021d(struct sk_buff *skb,
struct cfg80211_qos_map *qos_map); struct cfg80211_qos_map *qos_map);
/**
* cfg80211_find_ie_match - match information element and byte array in data
*
* @eid: element ID
* @ies: data consisting of IEs
* @len: length of data
* @match: byte array to match
* @match_len: number of bytes in the match array
* @match_offset: offset in the IE where the byte array should match.
* If match_len is zero, this must also be set to zero.
* Otherwise this must be set to 2 or more, because the first
* byte is the element id, which is already compared to eid, and
* the second byte is the IE length.
*
* Return: %NULL if the element ID could not be found or if
* the element is invalid (claims to be longer than the given
* data) or if the byte array doesn't match, or a pointer to the first
* byte of the requested element, that is the byte containing the
* element ID.
*
* Note: There are no checks on the element length other than
* having to fit into the given data and being large enough for the
* byte array to match.
*/
const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len,
const u8 *match, int match_len,
int match_offset);
/** /**
* cfg80211_find_ie - find information element in data * cfg80211_find_ie - find information element in data
* *
@ -3969,7 +3998,10 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,
* Note: There are no checks on the element length other than * Note: There are no checks on the element length other than
* having to fit into the given data. * having to fit into the given data.
*/ */
const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len); static inline const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
{
return cfg80211_find_ie_match(eid, ies, len, NULL, 0, 0);
}
/** /**
* cfg80211_find_vendor_ie - find vendor specific information element in data * cfg80211_find_vendor_ie - find vendor specific information element in data

View file

@ -190,6 +190,10 @@ struct ieee80211_radiotap_header {
* IEEE80211_RADIOTAP_VHT u16, u8, u8, u8[4], u8, u8, u16 * IEEE80211_RADIOTAP_VHT u16, u8, u8, u8[4], u8, u8, u16
* *
* Contains VHT information about this frame. * Contains VHT information about this frame.
*
* IEEE80211_RADIOTAP_TIMESTAMP u64, u16, u8, u8 variable
*
* Contains timestamp information for this frame.
*/ */
enum ieee80211_radiotap_type { enum ieee80211_radiotap_type {
IEEE80211_RADIOTAP_TSFT = 0, IEEE80211_RADIOTAP_TSFT = 0,
@ -214,6 +218,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, IEEE80211_RADIOTAP_VHT = 21,
IEEE80211_RADIOTAP_TIMESTAMP = 22,
/* 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,
@ -321,6 +326,22 @@ enum ieee80211_radiotap_type {
#define IEEE80211_RADIOTAP_CODING_LDPC_USER2 0x04 #define IEEE80211_RADIOTAP_CODING_LDPC_USER2 0x04
#define IEEE80211_RADIOTAP_CODING_LDPC_USER3 0x08 #define IEEE80211_RADIOTAP_CODING_LDPC_USER3 0x08
/* For IEEE80211_RADIOTAP_TIMESTAMP */
#define IEEE80211_RADIOTAP_TIMESTAMP_UNIT_MASK 0x000F
#define IEEE80211_RADIOTAP_TIMESTAMP_UNIT_MS 0x0000
#define IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US 0x0001
#define IEEE80211_RADIOTAP_TIMESTAMP_UNIT_NS 0x0003
#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_MASK 0x00F0
#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_BEGIN_MDPU 0x0000
#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_MPDU 0x0010
#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_PPDU 0x0020
#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_PLCP_SIG_ACQ 0x0030
#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_UNKNOWN 0x00F0
#define IEEE80211_RADIOTAP_TIMESTAMP_FLAG_64BIT 0x00
#define IEEE80211_RADIOTAP_TIMESTAMP_FLAG_32BIT 0x01
#define IEEE80211_RADIOTAP_TIMESTAMP_FLAG_ACCURACY 0x02
/* helpers */ /* helpers */
static inline int ieee80211_get_radiotap_len(unsigned char *data) static inline int ieee80211_get_radiotap_len(unsigned char *data)
{ {

View file

@ -1735,6 +1735,9 @@ struct ieee80211_sta_rates {
* @supp_rates: Bitmap of supported rates (per band) * @supp_rates: Bitmap of supported rates (per band)
* @ht_cap: HT capabilities of this STA; restricted to our own capabilities * @ht_cap: HT capabilities of this STA; restricted to our own capabilities
* @vht_cap: VHT capabilities of this STA; restricted to our own capabilities * @vht_cap: VHT capabilities of this STA; restricted to our own capabilities
* @max_rx_aggregation_subframes: maximal amount of frames in a single AMPDU
* that this station is allowed to transmit to us.
* Can be modified by driver.
* @wme: indicates whether the STA supports QoS/WME (if local devices does, * @wme: indicates whether the STA supports QoS/WME (if local devices does,
* otherwise always false) * otherwise always false)
* @drv_priv: data area for driver use, will always be aligned to * @drv_priv: data area for driver use, will always be aligned to
@ -1775,6 +1778,7 @@ struct ieee80211_sta {
u16 aid; u16 aid;
struct ieee80211_sta_ht_cap ht_cap; struct ieee80211_sta_ht_cap ht_cap;
struct ieee80211_sta_vht_cap vht_cap; struct ieee80211_sta_vht_cap vht_cap;
u8 max_rx_aggregation_subframes;
bool wme; bool wme;
u8 uapsd_queues; u8 uapsd_queues;
u8 max_sp; u8 max_sp;
@ -2014,6 +2018,11 @@ struct ieee80211_txq {
* @IEEE80211_HW_TX_FRAG_LIST: Hardware (or driver) supports sending frag_list * @IEEE80211_HW_TX_FRAG_LIST: Hardware (or driver) supports sending frag_list
* skbs, needed for zero-copy software A-MSDU. * skbs, needed for zero-copy software A-MSDU.
* *
* @IEEE80211_HW_REPORTS_LOW_ACK: The driver (or firmware) reports low ack event
* by ieee80211_report_low_ack() based on its own algorithm. For such
* drivers, mac80211 packet loss mechanism will not be triggered and driver
* is completely depending on firmware event for station kickout.
*
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/ */
enum ieee80211_hw_flags { enum ieee80211_hw_flags {
@ -2054,6 +2063,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_USES_RSS, IEEE80211_HW_USES_RSS,
IEEE80211_HW_TX_AMSDU, IEEE80211_HW_TX_AMSDU,
IEEE80211_HW_TX_FRAG_LIST, IEEE80211_HW_TX_FRAG_LIST,
IEEE80211_HW_REPORTS_LOW_ACK,
/* keep last, obviously */ /* keep last, obviously */
NUM_IEEE80211_HW_FLAGS NUM_IEEE80211_HW_FLAGS
@ -2141,6 +2151,14 @@ enum ieee80211_hw_flags {
* the default is _GI | _BANDWIDTH. * the default is _GI | _BANDWIDTH.
* Use the %IEEE80211_RADIOTAP_VHT_KNOWN_* values. * Use the %IEEE80211_RADIOTAP_VHT_KNOWN_* values.
* *
* @radiotap_timestamp: Information for the radiotap timestamp field; if the
* 'units_pos' member is set to a non-negative value it must be set to
* a combination of a IEEE80211_RADIOTAP_TIMESTAMP_UNIT_* and a
* IEEE80211_RADIOTAP_TIMESTAMP_SPOS_* value, and then the timestamp
* field will be added and populated from the &struct ieee80211_rx_status
* device_timestamp. If the 'accuracy' member is non-negative, it's put
* into the accuracy radiotap field and the accuracy known flag is set.
*
* @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 that not all features are usable with mac80211, * from this HW. Note that not all features are usable with mac80211,
* other features will be rejected during HW registration. * other features will be rejected during HW registration.
@ -2184,6 +2202,10 @@ struct ieee80211_hw {
u8 offchannel_tx_hw_queue; u8 offchannel_tx_hw_queue;
u8 radiotap_mcs_details; u8 radiotap_mcs_details;
u16 radiotap_vht_details; u16 radiotap_vht_details;
struct {
int units_pos;
s16 accuracy;
} radiotap_timestamp;
netdev_features_t netdev_features; netdev_features_t netdev_features;
u8 uapsd_queues; u8 uapsd_queues;
u8 uapsd_max_sp_len; u8 uapsd_max_sp_len;
@ -3085,11 +3107,8 @@ enum ieee80211_reconfig_type {
* *
* @sta_add_debugfs: Drivers can use this callback to add debugfs files * @sta_add_debugfs: Drivers can use this callback to add debugfs files
* when a station is added to mac80211's station list. This callback * when a station is added to mac80211's station list. This callback
* and @sta_remove_debugfs should be within a CONFIG_MAC80211_DEBUGFS * should be within a CONFIG_MAC80211_DEBUGFS conditional. This
* conditional. This callback can sleep. * callback can sleep.
*
* @sta_remove_debugfs: Remove the debugfs files which were added using
* @sta_add_debugfs. This callback can sleep.
* *
* @sta_notify: Notifies low level driver about power state transition of an * @sta_notify: Notifies low level driver about power state transition of an
* associated station, AP, IBSS/WDS/mesh peer etc. For a VIF operating * associated station, AP, IBSS/WDS/mesh peer etc. For a VIF operating
@ -3485,10 +3504,6 @@ struct ieee80211_ops {
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct ieee80211_sta *sta,
struct dentry *dir); struct dentry *dir);
void (*sta_remove_debugfs)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct dentry *dir);
#endif #endif
void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum sta_notify_cmd, struct ieee80211_sta *sta); enum sta_notify_cmd, struct ieee80211_sta *sta);

View file

@ -298,10 +298,13 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
buf_size = IEEE80211_MAX_AMPDU_BUF; buf_size = IEEE80211_MAX_AMPDU_BUF;
/* make sure the size doesn't exceed the maximum supported by the hw */ /* make sure the size doesn't exceed the maximum supported by the hw */
if (buf_size > local->hw.max_rx_aggregation_subframes) if (buf_size > sta->sta.max_rx_aggregation_subframes)
buf_size = local->hw.max_rx_aggregation_subframes; buf_size = sta->sta.max_rx_aggregation_subframes;
params.buf_size = buf_size; params.buf_size = buf_size;
ht_dbg(sta->sdata, "AddBA Req buf_size=%d for %pM\n",
buf_size, sta->sta.addr);
/* examine state machine */ /* examine state machine */
mutex_lock(&sta->ampdu_mlme.mtx); mutex_lock(&sta->ampdu_mlme.mtx);
@ -406,8 +409,10 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
} }
end: end:
if (status == WLAN_STATUS_SUCCESS) if (status == WLAN_STATUS_SUCCESS) {
__set_bit(tid, sta->ampdu_mlme.agg_session_valid); __set_bit(tid, sta->ampdu_mlme.agg_session_valid);
__clear_bit(tid, sta->ampdu_mlme.unexpected_agg);
}
mutex_unlock(&sta->ampdu_mlme.mtx); mutex_unlock(&sta->ampdu_mlme.mtx);
end_no_lock: end_no_lock:

View file

@ -39,7 +39,7 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
if (type == NL80211_IFTYPE_MONITOR && flags) { if (type == NL80211_IFTYPE_MONITOR && flags) {
sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
sdata->u.mntr_flags = *flags; sdata->u.mntr.flags = *flags;
} }
return wdev; return wdev;
@ -73,8 +73,29 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
sdata->u.mgd.use_4addr = params->use_4addr; sdata->u.mgd.use_4addr = params->use_4addr;
} }
if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags) { if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_sub_if_data *monitor_sdata;
u32 mu_mntr_cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER;
monitor_sdata = rtnl_dereference(local->monitor_sdata);
if (monitor_sdata &&
wiphy_ext_feature_isset(wiphy, mu_mntr_cap_flag)) {
memcpy(monitor_sdata->vif.bss_conf.mu_group.membership,
params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN);
memcpy(monitor_sdata->vif.bss_conf.mu_group.position,
params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN,
WLAN_USER_POSITION_LEN);
monitor_sdata->vif.mu_mimo_owner = true;
ieee80211_bss_info_change_notify(monitor_sdata,
BSS_CHANGED_MU_GROUPS);
ether_addr_copy(monitor_sdata->u.mntr.mu_follow_addr,
params->macaddr);
}
if (!flags)
return 0;
if (ieee80211_sdata_running(sdata)) { if (ieee80211_sdata_running(sdata)) {
u32 mask = MONITOR_FLAG_COOK_FRAMES | u32 mask = MONITOR_FLAG_COOK_FRAMES |
@ -89,11 +110,11 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
* cooked_mntrs, monitor and all fif_* counters * cooked_mntrs, monitor and all fif_* counters
* reconfigure hardware * reconfigure hardware
*/ */
if ((*flags & mask) != (sdata->u.mntr_flags & mask)) if ((*flags & mask) != (sdata->u.mntr.flags & mask))
return -EBUSY; return -EBUSY;
ieee80211_adjust_monitor_flags(sdata, -1); ieee80211_adjust_monitor_flags(sdata, -1);
sdata->u.mntr_flags = *flags; sdata->u.mntr.flags = *flags;
ieee80211_adjust_monitor_flags(sdata, 1); ieee80211_adjust_monitor_flags(sdata, 1);
ieee80211_configure_filter(local); ieee80211_configure_filter(local);
@ -103,7 +124,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
* and ieee80211_do_open take care of "everything" * and ieee80211_do_open take care of "everything"
* mentioned in the comment above. * mentioned in the comment above.
*/ */
sdata->u.mntr_flags = *flags; sdata->u.mntr.flags = *flags;
} }
} }
@ -2940,10 +2961,6 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
} }
chanctx = container_of(conf, struct ieee80211_chanctx, conf); chanctx = container_of(conf, struct ieee80211_chanctx, conf);
if (!chanctx) {
err = -EBUSY;
goto out;
}
ch_switch.timestamp = 0; ch_switch.timestamp = 0;
ch_switch.device_timestamp = 0; ch_switch.device_timestamp = 0;

View file

@ -71,138 +71,39 @@ DEBUGFS_READONLY_FILE(wep_iv, "%#08x",
DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s", DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s",
local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver"); local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver");
struct aqm_info {
struct ieee80211_local *local;
size_t size;
size_t len;
unsigned char buf[0];
};
#define AQM_HDR_LEN 200
#define AQM_HW_ENTRY_LEN 40
#define AQM_TXQ_ENTRY_LEN 110
static int aqm_open(struct inode *inode, struct file *file)
{
struct ieee80211_local *local = inode->i_private;
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
struct txq_info *txqi;
struct fq *fq = &local->fq;
struct aqm_info *info = NULL;
int len = 0;
int i;
if (!local->ops->wake_tx_queue)
return -EOPNOTSUPP;
len += AQM_HDR_LEN;
len += 6 * AQM_HW_ENTRY_LEN;
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list)
len += AQM_TXQ_ENTRY_LEN;
list_for_each_entry_rcu(sta, &local->sta_list, list)
len += AQM_TXQ_ENTRY_LEN * ARRAY_SIZE(sta->sta.txq);
rcu_read_unlock();
info = vmalloc(len);
if (!info)
return -ENOMEM;
spin_lock_bh(&local->fq.lock);
rcu_read_lock();
file->private_data = info;
info->local = local;
info->size = len;
len = 0;
len += scnprintf(info->buf + len, info->size - len,
"* hw\n"
"access name value\n"
"R fq_flows_cnt %u\n"
"R fq_backlog %u\n"
"R fq_overlimit %u\n"
"R fq_collisions %u\n"
"RW fq_limit %u\n"
"RW fq_quantum %u\n",
fq->flows_cnt,
fq->backlog,
fq->overlimit,
fq->collisions,
fq->limit,
fq->quantum);
len += scnprintf(info->buf + len,
info->size - len,
"* vif\n"
"ifname addr ac backlog-bytes backlog-packets flows overlimit collisions tx-bytes tx-packets\n");
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
txqi = to_txq_info(sdata->vif.txq);
len += scnprintf(info->buf + len, info->size - len,
"%s %pM %u %u %u %u %u %u %u %u\n",
sdata->name,
sdata->vif.addr,
txqi->txq.ac,
txqi->tin.backlog_bytes,
txqi->tin.backlog_packets,
txqi->tin.flows,
txqi->tin.overlimit,
txqi->tin.collisions,
txqi->tin.tx_bytes,
txqi->tin.tx_packets);
}
len += scnprintf(info->buf + len,
info->size - len,
"* sta\n"
"ifname addr tid ac backlog-bytes backlog-packets flows overlimit collisions tx-bytes tx-packets\n");
list_for_each_entry_rcu(sta, &local->sta_list, list) {
sdata = sta->sdata;
for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
txqi = to_txq_info(sta->sta.txq[i]);
len += scnprintf(info->buf + len, info->size - len,
"%s %pM %d %d %u %u %u %u %u %u %u\n",
sdata->name,
sta->sta.addr,
txqi->txq.tid,
txqi->txq.ac,
txqi->tin.backlog_bytes,
txqi->tin.backlog_packets,
txqi->tin.flows,
txqi->tin.overlimit,
txqi->tin.collisions,
txqi->tin.tx_bytes,
txqi->tin.tx_packets);
}
}
info->len = len;
rcu_read_unlock();
spin_unlock_bh(&local->fq.lock);
return 0;
}
static int aqm_release(struct inode *inode, struct file *file)
{
vfree(file->private_data);
return 0;
}
static ssize_t aqm_read(struct file *file, static ssize_t aqm_read(struct file *file,
char __user *user_buf, char __user *user_buf,
size_t count, size_t count,
loff_t *ppos) loff_t *ppos)
{ {
struct aqm_info *info = file->private_data; struct ieee80211_local *local = file->private_data;
struct fq *fq = &local->fq;
char buf[200];
int len = 0;
spin_lock_bh(&local->fq.lock);
rcu_read_lock();
len = scnprintf(buf, sizeof(buf),
"access name value\n"
"R fq_flows_cnt %u\n"
"R fq_backlog %u\n"
"R fq_overlimit %u\n"
"R fq_collisions %u\n"
"RW fq_limit %u\n"
"RW fq_quantum %u\n",
fq->flows_cnt,
fq->backlog,
fq->overlimit,
fq->collisions,
fq->limit,
fq->quantum);
rcu_read_unlock();
spin_unlock_bh(&local->fq.lock);
return simple_read_from_buffer(user_buf, count, ppos, return simple_read_from_buffer(user_buf, count, ppos,
info->buf, info->len); buf, len);
} }
static ssize_t aqm_write(struct file *file, static ssize_t aqm_write(struct file *file,
@ -210,8 +111,7 @@ static ssize_t aqm_write(struct file *file,
size_t count, size_t count,
loff_t *ppos) loff_t *ppos)
{ {
struct aqm_info *info = file->private_data; struct ieee80211_local *local = file->private_data;
struct ieee80211_local *local = info->local;
char buf[100]; char buf[100];
size_t len; size_t len;
@ -237,8 +137,7 @@ static ssize_t aqm_write(struct file *file,
static const struct file_operations aqm_ops = { static const struct file_operations aqm_ops = {
.write = aqm_write, .write = aqm_write,
.read = aqm_read, .read = aqm_read,
.open = aqm_open, .open = simple_open,
.release = aqm_release,
.llseek = default_llseek, .llseek = default_llseek,
}; };
@ -302,6 +201,7 @@ static const char *hw_flag_names[] = {
FLAG(USES_RSS), FLAG(USES_RSS),
FLAG(TX_AMSDU), FLAG(TX_AMSDU),
FLAG(TX_FRAG_LIST), FLAG(TX_FRAG_LIST),
FLAG(REPORTS_LOW_ACK),
#undef FLAG #undef FLAG
}; };
@ -428,7 +328,9 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_ADD(hwflags); DEBUGFS_ADD(hwflags);
DEBUGFS_ADD(user_power); DEBUGFS_ADD(user_power);
DEBUGFS_ADD(power); DEBUGFS_ADD(power);
DEBUGFS_ADD_MODE(aqm, 0600);
if (local->ops->wake_tx_queue)
DEBUGFS_ADD_MODE(aqm, 0600);
statsd = debugfs_create_dir("statistics", phyd); statsd = debugfs_create_dir("statistics", phyd);

View file

@ -30,7 +30,7 @@ static ssize_t ieee80211_if_read(
size_t count, loff_t *ppos, size_t count, loff_t *ppos,
ssize_t (*format)(const struct ieee80211_sub_if_data *, char *, int)) ssize_t (*format)(const struct ieee80211_sub_if_data *, char *, int))
{ {
char buf[70]; char buf[200];
ssize_t ret = -EINVAL; ssize_t ret = -EINVAL;
read_lock(&dev_base_lock); read_lock(&dev_base_lock);
@ -486,6 +486,38 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast(
} }
IEEE80211_IF_FILE_R(num_buffered_multicast); IEEE80211_IF_FILE_R(num_buffered_multicast);
static ssize_t ieee80211_if_fmt_aqm(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
{
struct ieee80211_local *local = sdata->local;
struct txq_info *txqi = to_txq_info(sdata->vif.txq);
int len;
spin_lock_bh(&local->fq.lock);
rcu_read_lock();
len = scnprintf(buf,
buflen,
"ac backlog-bytes backlog-packets new-flows drops marks overlimit collisions tx-bytes tx-packets\n"
"%u %u %u %u %u %u %u %u %u %u\n",
txqi->txq.ac,
txqi->tin.backlog_bytes,
txqi->tin.backlog_packets,
txqi->tin.flows,
txqi->cstats.drop_count,
txqi->cstats.ecn_mark,
txqi->tin.overlimit,
txqi->tin.collisions,
txqi->tin.tx_bytes,
txqi->tin.tx_packets);
rcu_read_unlock();
spin_unlock_bh(&local->fq.lock);
return len;
}
IEEE80211_IF_FILE_R(aqm);
/* IBSS attributes */ /* IBSS attributes */
static ssize_t ieee80211_if_fmt_tsf( static ssize_t ieee80211_if_fmt_tsf(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
@ -618,6 +650,9 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_2ghz); DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_5ghz); DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_5ghz);
DEBUGFS_ADD(hw_queues); DEBUGFS_ADD(hw_queues);
if (sdata->local->ops->wake_tx_queue)
DEBUGFS_ADD(aqm);
} }
static void add_sta_files(struct ieee80211_sub_if_data *sdata) static void add_sta_files(struct ieee80211_sub_if_data *sdata)

View file

@ -133,6 +133,55 @@ static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf,
} }
STA_OPS(last_seq_ctrl); STA_OPS(last_seq_ctrl);
#define AQM_TXQ_ENTRY_LEN 130
static ssize_t sta_aqm_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct sta_info *sta = file->private_data;
struct ieee80211_local *local = sta->local;
size_t bufsz = AQM_TXQ_ENTRY_LEN*(IEEE80211_NUM_TIDS+1);
char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
struct txq_info *txqi;
ssize_t rv;
int i;
if (!buf)
return -ENOMEM;
spin_lock_bh(&local->fq.lock);
rcu_read_lock();
p += scnprintf(p,
bufsz+buf-p,
"tid ac backlog-bytes backlog-packets new-flows drops marks overlimit collisions tx-bytes tx-packets\n");
for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
txqi = to_txq_info(sta->sta.txq[i]);
p += scnprintf(p, bufsz+buf-p,
"%d %d %u %u %u %u %u %u %u %u %u\n",
txqi->txq.tid,
txqi->txq.ac,
txqi->tin.backlog_bytes,
txqi->tin.backlog_packets,
txqi->tin.flows,
txqi->cstats.drop_count,
txqi->cstats.ecn_mark,
txqi->tin.overlimit,
txqi->tin.collisions,
txqi->tin.tx_bytes,
txqi->tin.tx_packets);
}
rcu_read_unlock();
spin_unlock_bh(&local->fq.lock);
rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
kfree(buf);
return rv;
}
STA_OPS(aqm);
static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
@ -478,6 +527,9 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD_COUNTER(rx_fragments, rx_stats.fragments); DEBUGFS_ADD_COUNTER(rx_fragments, rx_stats.fragments);
DEBUGFS_ADD_COUNTER(tx_filtered, status_stats.filtered); DEBUGFS_ADD_COUNTER(tx_filtered, status_stats.filtered);
if (local->ops->wake_tx_queue)
DEBUGFS_ADD(aqm);
if (sizeof(sta->driver_buffered_tids) == sizeof(u32)) if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
debugfs_create_x32("driver_buffered_tids", 0400, debugfs_create_x32("driver_buffered_tids", 0400,
sta->debugfs_dir, sta->debugfs_dir,
@ -492,10 +544,6 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
void ieee80211_sta_debugfs_remove(struct sta_info *sta) void ieee80211_sta_debugfs_remove(struct sta_info *sta)
{ {
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
drv_sta_remove_debugfs(local, sdata, &sta->sta, sta->debugfs_dir);
debugfs_remove_recursive(sta->debugfs_dir); debugfs_remove_recursive(sta->debugfs_dir);
sta->debugfs_dir = NULL; sta->debugfs_dir = NULL;
} }

View file

@ -62,7 +62,7 @@ int drv_add_interface(struct ieee80211_local *local,
if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
(sdata->vif.type == NL80211_IFTYPE_MONITOR && (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) && !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)))) !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))))
return -EINVAL; return -EINVAL;
trace_drv_add_interface(local, sdata); trace_drv_add_interface(local, sdata);

View file

@ -162,7 +162,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
return; return;
if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE || if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
sdata->vif.type == NL80211_IFTYPE_MONITOR)) (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
!sdata->vif.mu_mimo_owner)))
return; return;
if (!check_sdata_in_driver(sdata)) if (!check_sdata_in_driver(sdata))
@ -498,21 +499,6 @@ static inline void drv_sta_add_debugfs(struct ieee80211_local *local,
local->ops->sta_add_debugfs(&local->hw, &sdata->vif, local->ops->sta_add_debugfs(&local->hw, &sdata->vif,
sta, dir); sta, dir);
} }
static inline void drv_sta_remove_debugfs(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta,
struct dentry *dir)
{
might_sleep();
sdata = get_bss_sdata(sdata);
check_sdata_in_driver(sdata);
if (local->ops->sta_remove_debugfs)
local->ops->sta_remove_debugfs(&local->hw, &sdata->vif,
sta, dir);
}
#endif #endif
static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local, static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local,

View file

@ -3,7 +3,7 @@
* Copyright 2005, Devicescape Software, Inc. * Copyright 2005, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2013-2015 Intel Mobile Communications GmbH
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
@ -818,12 +818,18 @@ struct txq_info {
struct fq_tin tin; struct fq_tin tin;
struct fq_flow def_flow; struct fq_flow def_flow;
struct codel_vars def_cvars; struct codel_vars def_cvars;
struct codel_stats cstats;
unsigned long flags; unsigned long flags;
/* keep last! */ /* keep last! */
struct ieee80211_txq txq; struct ieee80211_txq txq;
}; };
struct ieee80211_if_mntr {
u32 flags;
u8 mu_follow_addr[ETH_ALEN] __aligned(2);
};
struct ieee80211_sub_if_data { struct ieee80211_sub_if_data {
struct list_head list; struct list_head list;
@ -922,7 +928,7 @@ struct ieee80211_sub_if_data {
struct ieee80211_if_ibss ibss; struct ieee80211_if_ibss ibss;
struct ieee80211_if_mesh mesh; struct ieee80211_if_mesh mesh;
struct ieee80211_if_ocb ocb; struct ieee80211_if_ocb ocb;
u32 mntr_flags; struct ieee80211_if_mntr mntr;
} u; } u;
#ifdef CONFIG_MAC80211_DEBUGFS #ifdef CONFIG_MAC80211_DEBUGFS
@ -1112,7 +1118,6 @@ struct ieee80211_local {
struct fq fq; struct fq fq;
struct codel_vars *cvars; struct codel_vars *cvars;
struct codel_params cparams; struct codel_params cparams;
struct codel_stats cstats;
const struct ieee80211_ops *ops; const struct ieee80211_ops *ops;

View file

@ -43,6 +43,8 @@
* by either the RTNL, the iflist_mtx or RCU. * by either the RTNL, the iflist_mtx or RCU.
*/ */
static void ieee80211_iface_work(struct work_struct *work);
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata) bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
{ {
struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx_conf *chanctx_conf;
@ -188,7 +190,7 @@ static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr,
continue; continue;
if (iter->vif.type == NL80211_IFTYPE_MONITOR && if (iter->vif.type == NL80211_IFTYPE_MONITOR &&
!(iter->u.mntr_flags & MONITOR_FLAG_ACTIVE)) !(iter->u.mntr.flags & MONITOR_FLAG_ACTIVE))
continue; continue;
m = iter->vif.addr; m = iter->vif.addr;
@ -217,7 +219,7 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr)
return -EBUSY; return -EBUSY;
if (sdata->vif.type == NL80211_IFTYPE_MONITOR && if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
check_dup = false; check_dup = false;
ret = ieee80211_verify_mac(sdata, sa->sa_data, check_dup); ret = ieee80211_verify_mac(sdata, sa->sa_data, check_dup);
@ -357,7 +359,7 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
const int offset) const int offset)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
u32 flags = sdata->u.mntr_flags; u32 flags = sdata->u.mntr.flags;
#define ADJUST(_f, _s) do { \ #define ADJUST(_f, _s) do { \
if (flags & MONITOR_FLAG_##_f) \ if (flags & MONITOR_FLAG_##_f) \
@ -448,6 +450,9 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
return ret; return ret;
} }
skb_queue_head_init(&sdata->skb_queue);
INIT_WORK(&sdata->work, ieee80211_iface_work);
return 0; return 0;
} }
@ -589,12 +594,12 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
} }
break; break;
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) { if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) {
local->cooked_mntrs++; local->cooked_mntrs++;
break; break;
} }
if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) { if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
res = drv_add_interface(local, sdata); res = drv_add_interface(local, sdata);
if (res) if (res)
goto err_stop; goto err_stop;
@ -926,7 +931,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
/* no need to tell driver */ /* no need to tell driver */
break; break;
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) { if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) {
local->cooked_mntrs--; local->cooked_mntrs--;
break; break;
} }
@ -1012,7 +1017,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_idle(local); ieee80211_recalc_idle(local);
mutex_unlock(&local->mtx); mutex_unlock(&local->mtx);
if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
break; break;
/* fall through */ /* fall through */
@ -1444,7 +1449,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
sdata->dev->type = ARPHRD_IEEE80211_RADIOTAP; sdata->dev->type = ARPHRD_IEEE80211_RADIOTAP;
sdata->dev->netdev_ops = &ieee80211_monitorif_ops; sdata->dev->netdev_ops = &ieee80211_monitorif_ops;
sdata->u.mntr_flags = MONITOR_FLAG_CONTROL | sdata->u.mntr.flags = MONITOR_FLAG_CONTROL |
MONITOR_FLAG_OTHER_BSS; MONITOR_FLAG_OTHER_BSS;
break; break;
case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_WDS:

View file

@ -660,6 +660,9 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
ieee80211_roc_setup(local); ieee80211_roc_setup(local);
local->hw.radiotap_timestamp.units_pos = -1;
local->hw.radiotap_timestamp.accuracy = -1;
return &local->hw; return &local->hw;
err_free: err_free:
wiphy_free(wiphy); wiphy_free(wiphy);

View file

@ -1672,11 +1672,15 @@ __ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
non_acm_ac++) non_acm_ac++)
if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac))) if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac)))
break; break;
/* The loop will result in using BK even if it requires /* Usually the loop will result in using BK even if it
* admission control, such configuration makes no sense * requires admission control, but such a configuration
* and we have to transmit somehow - the AC selection * makes no sense and we have to transmit somehow - the
* does the same thing. * AC selection does the same thing.
* If we started out trying to downgrade from BK, then
* the extra condition here might be needed.
*/ */
if (non_acm_ac >= IEEE80211_NUM_ACS)
non_acm_ac = IEEE80211_AC_BK;
if (drv_conf_tx(local, sdata, ac, if (drv_conf_tx(local, sdata, ac,
&sdata->tx_conf[non_acm_ac])) &sdata->tx_conf[non_acm_ac]))
sdata_err(sdata, sdata_err(sdata,

View file

@ -178,8 +178,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
WARN_ON(!list_empty(&local->chanctx_list)); WARN_ON(!list_empty(&local->chanctx_list));
/* stop hardware - this must stop RX */ /* stop hardware - this must stop RX */
if (local->open_count) ieee80211_stop_device(local);
ieee80211_stop_device(local);
suspend: suspend:
local->suspended = true; local->suspended = true;

View file

@ -180,6 +180,11 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
len += 12; len += 12;
} }
if (local->hw.radiotap_timestamp.units_pos >= 0) {
len = ALIGN(len, 8);
len += 12;
}
if (status->chains) { if (status->chains) {
/* antenna and antenna signal fields */ /* antenna and antenna signal fields */
len += 2 * hweight8(status->chains); len += 2 * hweight8(status->chains);
@ -447,6 +452,31 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
pos += 2; pos += 2;
} }
if (local->hw.radiotap_timestamp.units_pos >= 0) {
u16 accuracy = 0;
u8 flags = IEEE80211_RADIOTAP_TIMESTAMP_FLAG_32BIT;
rthdr->it_present |=
cpu_to_le32(1 << IEEE80211_RADIOTAP_TIMESTAMP);
/* ensure 8 byte alignment */
while ((pos - (u8 *)rthdr) & 7)
pos++;
put_unaligned_le64(status->device_timestamp, pos);
pos += sizeof(u64);
if (local->hw.radiotap_timestamp.accuracy >= 0) {
accuracy = local->hw.radiotap_timestamp.accuracy;
flags |= IEEE80211_RADIOTAP_TIMESTAMP_FLAG_ACCURACY;
}
put_unaligned_le16(accuracy, pos);
pos += sizeof(u16);
*pos++ = local->hw.radiotap_timestamp.units_pos;
*pos++ = flags;
}
for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) { for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
*pos++ = status->chain_signal[chain]; *pos++ = status->chain_signal[chain];
*pos++ = chain; *pos++ = chain;
@ -485,6 +515,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
struct net_device *prev_dev = NULL; struct net_device *prev_dev = NULL;
int present_fcs_len = 0; int present_fcs_len = 0;
unsigned int rtap_vendor_space = 0; unsigned int rtap_vendor_space = 0;
struct ieee80211_mgmt *mgmt;
struct ieee80211_sub_if_data *monitor_sdata =
rcu_dereference(local->monitor_sdata);
if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) { if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) {
struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data; struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data;
@ -567,7 +600,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
if (sdata->vif.type != NL80211_IFTYPE_MONITOR) if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
continue; continue;
if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES)
continue; continue;
if (!ieee80211_sdata_running(sdata)) if (!ieee80211_sdata_running(sdata))
@ -585,6 +618,23 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
ieee80211_rx_stats(sdata->dev, skb->len); ieee80211_rx_stats(sdata->dev, skb->len);
} }
mgmt = (void *)skb->data;
if (monitor_sdata &&
skb->len >= IEEE80211_MIN_ACTION_SIZE + 1 + VHT_MUMIMO_GROUPS_DATA_LEN &&
ieee80211_is_action(mgmt->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_VHT &&
mgmt->u.action.u.vht_group_notif.action_code == WLAN_VHT_ACTION_GROUPID_MGMT &&
is_valid_ether_addr(monitor_sdata->u.mntr.mu_follow_addr) &&
ether_addr_equal(mgmt->da, monitor_sdata->u.mntr.mu_follow_addr)) {
struct sk_buff *mu_skb = skb_copy(skb, GFP_ATOMIC);
if (mu_skb) {
mu_skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME;
skb_queue_tail(&monitor_sdata->skb_queue, mu_skb);
ieee80211_queue_work(&local->hw, &monitor_sdata->work);
}
}
if (prev_dev) { if (prev_dev) {
skb->dev = prev_dev; skb->dev = prev_dev;
netif_receive_skb(skb); netif_receive_skb(skb);
@ -1072,8 +1122,15 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
if (!tid_agg_rx) if (!tid_agg_rx) {
if (ack_policy == IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK &&
!test_bit(tid, rx->sta->ampdu_mlme.agg_session_valid) &&
!test_and_set_bit(tid, rx->sta->ampdu_mlme.unexpected_agg))
ieee80211_send_delba(rx->sdata, rx->sta->sta.addr, tid,
WLAN_BACK_RECIPIENT,
WLAN_REASON_QSTA_REQUIRE_SETUP);
goto dont_reorder; goto dont_reorder;
}
/* qos null data frames are excluded */ /* qos null data frames are excluded */
if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC))) if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
@ -2535,6 +2592,12 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
tid = le16_to_cpu(bar_data.control) >> 12; tid = le16_to_cpu(bar_data.control) >> 12;
if (!test_bit(tid, rx->sta->ampdu_mlme.agg_session_valid) &&
!test_and_set_bit(tid, rx->sta->ampdu_mlme.unexpected_agg))
ieee80211_send_delba(rx->sdata, rx->sta->sta.addr, tid,
WLAN_BACK_RECIPIENT,
WLAN_REASON_QSTA_REQUIRE_SETUP);
tid_agg_rx = rcu_dereference(rx->sta->ampdu_mlme.tid_rx[tid]); tid_agg_rx = rcu_dereference(rx->sta->ampdu_mlme.tid_rx[tid]);
if (!tid_agg_rx) if (!tid_agg_rx)
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
@ -3147,7 +3210,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
continue; continue;
if (sdata->vif.type != NL80211_IFTYPE_MONITOR || if (sdata->vif.type != NL80211_IFTYPE_MONITOR ||
!(sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)) !(sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES))
continue; continue;
if (prev_dev) { if (prev_dev) {

View file

@ -420,7 +420,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw,
{ {
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
trace_api_scan_completed(local, info); trace_api_scan_completed(local, info->aborted);
set_bit(SCAN_COMPLETED, &local->scanning); set_bit(SCAN_COMPLETED, &local->scanning);
if (info->aborted) if (info->aborted)

View file

@ -340,6 +340,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
memcpy(sta->addr, addr, ETH_ALEN); memcpy(sta->addr, addr, ETH_ALEN);
memcpy(sta->sta.addr, addr, ETH_ALEN); memcpy(sta->sta.addr, addr, ETH_ALEN);
sta->sta.max_rx_aggregation_subframes =
local->hw.max_rx_aggregation_subframes;
sta->local = local; sta->local = local;
sta->sdata = sdata; sta->sdata = sdata;
sta->rx_stats.last_rx = jiffies; sta->rx_stats.last_rx = jiffies;
@ -687,7 +690,7 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending)
} }
/* No need to do anything if the driver does all */ /* No need to do anything if the driver does all */
if (ieee80211_hw_check(&local->hw, AP_LINK_PS)) if (!local->ops->set_tim)
return; return;
if (sta->dead) if (sta->dead)

View file

@ -230,6 +230,8 @@ struct tid_ampdu_rx {
* @tid_rx_stop_requested: bitmap indicating which BA sessions per TID the * @tid_rx_stop_requested: bitmap indicating which BA sessions per TID the
* driver requested to close until the work for it runs * driver requested to close until the work for it runs
* @agg_session_valid: bitmap indicating which TID has a rx BA session open on * @agg_session_valid: bitmap indicating which TID has a rx BA session open on
* @unexpected_agg: bitmap indicating which TID already sent a delBA due to
* unexpected aggregation related frames outside a session
* @work: work struct for starting/stopping aggregation * @work: work struct for starting/stopping aggregation
* @tid_tx: aggregation info for Tx per TID * @tid_tx: aggregation info for Tx per TID
* @tid_start_tx: sessions where start was requested * @tid_start_tx: sessions where start was requested
@ -244,6 +246,7 @@ struct sta_ampdu_mlme {
unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
unsigned long agg_session_valid[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; unsigned long agg_session_valid[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
unsigned long unexpected_agg[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
/* tx */ /* tx */
struct work_struct work; struct work_struct work;
struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS]; struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS];

View file

@ -557,6 +557,12 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
static void ieee80211_lost_packet(struct sta_info *sta, static void ieee80211_lost_packet(struct sta_info *sta,
struct ieee80211_tx_info *info) struct ieee80211_tx_info *info)
{ {
/* If driver relies on its own algorithm for station kickout, skip
* mac80211 packet loss mechanism.
*/
if (ieee80211_hw_check(&sta->local->hw, REPORTS_LOW_ACK))
return;
/* This packet was aggregated but doesn't carry status info */ /* This packet was aggregated but doesn't carry status info */
if ((info->flags & IEEE80211_TX_CTL_AMPDU) && if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
!(info->flags & IEEE80211_TX_STAT_AMPDU)) !(info->flags & IEEE80211_TX_STAT_AMPDU))
@ -709,7 +715,7 @@ void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb,
if (!ieee80211_sdata_running(sdata)) if (!ieee80211_sdata_running(sdata))
continue; continue;
if ((sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) && if ((sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) &&
!send_to_cooked) !send_to_cooked)
continue; continue;

View file

@ -1343,7 +1343,7 @@ static struct sk_buff *fq_tin_dequeue_func(struct fq *fq,
local = container_of(fq, struct ieee80211_local, fq); local = container_of(fq, struct ieee80211_local, fq);
txqi = container_of(tin, struct txq_info, tin); txqi = container_of(tin, struct txq_info, tin);
cparams = &local->cparams; cparams = &local->cparams;
cstats = &local->cstats; cstats = &txqi->cstats;
if (flow == &txqi->def_flow) if (flow == &txqi->def_flow)
cvars = &txqi->def_cvars; cvars = &txqi->def_cvars;
@ -1403,6 +1403,7 @@ void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata,
fq_tin_init(&txqi->tin); fq_tin_init(&txqi->tin);
fq_flow_init(&txqi->def_flow); fq_flow_init(&txqi->def_flow);
codel_vars_init(&txqi->def_cvars); codel_vars_init(&txqi->def_cvars);
codel_stats_init(&txqi->cstats);
txqi->txq.vif = &sdata->vif; txqi->txq.vif = &sdata->vif;
@ -1441,7 +1442,6 @@ int ieee80211_txq_setup_flows(struct ieee80211_local *local)
return ret; return ret;
codel_params_init(&local->cparams); codel_params_init(&local->cparams);
codel_stats_init(&local->cstats);
local->cparams.interval = MS2TIME(100); local->cparams.interval = MS2TIME(100);
local->cparams.target = MS2TIME(20); local->cparams.target = MS2TIME(20);
local->cparams.ecn = true; local->cparams.ecn = true;
@ -1643,7 +1643,7 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
switch (sdata->vif.type) { switch (sdata->vif.type) {
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) { if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
vif = &sdata->vif; vif = &sdata->vif;
break; break;
} }
@ -2263,15 +2263,9 @@ static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) { if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
sta = sta_info_get(sdata, skb->data); sta = sta_info_get(sdata, skb->data);
if (sta) { if (sta && test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
bool tdls_peer, tdls_auth; if (test_sta_flag(sta,
WLAN_STA_TDLS_PEER_AUTH)) {
tdls_peer = test_sta_flag(sta,
WLAN_STA_TDLS_PEER);
tdls_auth = test_sta_flag(sta,
WLAN_STA_TDLS_PEER_AUTH);
if (tdls_peer && tdls_auth) {
*sta_out = sta; *sta_out = sta;
return 0; return 0;
} }
@ -2283,8 +2277,7 @@ static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
* after a TDLS sta is removed due to being * after a TDLS sta is removed due to being
* unreachable. * unreachable.
*/ */
if (tdls_peer && !tdls_auth && if (!ieee80211_is_tdls_setup(skb))
!ieee80211_is_tdls_setup(skb))
return -EINVAL; return -EINVAL;
} }

View file

@ -598,7 +598,7 @@ static void __iterate_interfaces(struct ieee80211_local *local,
list_for_each_entry_rcu(sdata, &local->interfaces, list) { list_for_each_entry_rcu(sdata, &local->interfaces, list) {
switch (sdata->vif.type) { switch (sdata->vif.type) {
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
continue; continue;
break; break;
case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP_VLAN:
@ -2555,7 +2555,6 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
if (need_basic && basic_rates & BIT(i)) if (need_basic && basic_rates & BIT(i))
basic = 0x80; basic = 0x80;
rate = sband->bitrates[i].bitrate;
rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
5 * (1 << shift)); 5 * (1 << shift));
*pos++ = basic | (u8) rate; *pos++ = basic | (u8) rate;

View file

@ -1252,7 +1252,7 @@ static int __init cfg80211_init(void)
if (err) if (err)
goto out_fail_reg; goto out_fail_reg;
cfg80211_wq = create_singlethread_workqueue("cfg80211"); cfg80211_wq = alloc_ordered_workqueue("cfg80211", WQ_MEM_RECLAIM);
if (!cfg80211_wq) { if (!cfg80211_wq) {
err = -ENOMEM; err = -ENOMEM;
goto out_fail_wq; goto out_fail_wq;

View file

@ -249,9 +249,9 @@ struct cfg80211_event {
}; };
struct cfg80211_cached_keys { struct cfg80211_cached_keys {
struct key_params params[6]; struct key_params params[4];
u8 data[6][WLAN_MAX_KEY_LEN]; u8 data[4][WLAN_KEY_LEN_WEP104];
int def, defmgmt; int def;
}; };
enum cfg80211_chan_mode { enum cfg80211_chan_mode {

View file

@ -114,6 +114,9 @@ static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
} }
} }
if (WARN_ON(connkeys && connkeys->def < 0))
return -EINVAL;
if (WARN_ON(wdev->connect_keys)) if (WARN_ON(wdev->connect_keys))
kzfree(wdev->connect_keys); kzfree(wdev->connect_keys);
wdev->connect_keys = connkeys; wdev->connect_keys = connkeys;
@ -284,18 +287,16 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
if (!netif_running(wdev->netdev)) if (!netif_running(wdev->netdev))
return 0; return 0;
if (wdev->wext.keys) { if (wdev->wext.keys)
wdev->wext.keys->def = wdev->wext.default_key; wdev->wext.keys->def = wdev->wext.default_key;
wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key;
}
wdev->wext.ibss.privacy = wdev->wext.default_key != -1; wdev->wext.ibss.privacy = wdev->wext.default_key != -1;
if (wdev->wext.keys) { if (wdev->wext.keys && wdev->wext.keys->def != -1) {
ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
if (!ck) if (!ck)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < 6; i++) for (i = 0; i < 4; i++)
ck->params[i].key = ck->data[i]; ck->params[i].key = ck->data[i];
} }
err = __cfg80211_join_ibss(rdev, wdev->netdev, err = __cfg80211_join_ibss(rdev, wdev->netdev,

View file

@ -222,7 +222,7 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
ASSERT_WDEV_LOCK(wdev); ASSERT_WDEV_LOCK(wdev);
if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
if (!key || !key_len || key_idx < 0 || key_idx > 4) if (!key || !key_len || key_idx < 0 || key_idx > 3)
return -EINVAL; return -EINVAL;
if (wdev->current_bss && if (wdev->current_bss &&

View file

@ -848,13 +848,21 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
struct nlattr *key; struct nlattr *key;
struct cfg80211_cached_keys *result; struct cfg80211_cached_keys *result;
int rem, err, def = 0; int rem, err, def = 0;
bool have_key = false;
nla_for_each_nested(key, keys, rem) {
have_key = true;
break;
}
if (!have_key)
return NULL;
result = kzalloc(sizeof(*result), GFP_KERNEL); result = kzalloc(sizeof(*result), GFP_KERNEL);
if (!result) if (!result)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
result->def = -1; result->def = -1;
result->defmgmt = -1;
nla_for_each_nested(key, keys, rem) { nla_for_each_nested(key, keys, rem) {
memset(&parse, 0, sizeof(parse)); memset(&parse, 0, sizeof(parse));
@ -866,7 +874,7 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
err = -EINVAL; err = -EINVAL;
if (!parse.p.key) if (!parse.p.key)
goto error; goto error;
if (parse.idx < 0 || parse.idx > 4) if (parse.idx < 0 || parse.idx > 3)
goto error; goto error;
if (parse.def) { if (parse.def) {
if (def) if (def)
@ -881,16 +889,24 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
parse.idx, false, NULL); parse.idx, false, NULL);
if (err) if (err)
goto error; goto error;
if (parse.p.cipher != WLAN_CIPHER_SUITE_WEP40 &&
parse.p.cipher != WLAN_CIPHER_SUITE_WEP104) {
err = -EINVAL;
goto error;
}
result->params[parse.idx].cipher = parse.p.cipher; result->params[parse.idx].cipher = parse.p.cipher;
result->params[parse.idx].key_len = parse.p.key_len; result->params[parse.idx].key_len = parse.p.key_len;
result->params[parse.idx].key = result->data[parse.idx]; result->params[parse.idx].key = result->data[parse.idx];
memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len); memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
if (parse.p.cipher == WLAN_CIPHER_SUITE_WEP40 || /* must be WEP key if we got here */
parse.p.cipher == WLAN_CIPHER_SUITE_WEP104) { if (no_ht)
if (no_ht) *no_ht = true;
*no_ht = true; }
}
if (result->def < 0) {
err = -EINVAL;
goto error;
} }
return result; return result;
@ -2525,10 +2541,35 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
int if_idx = 0; int if_idx = 0;
int wp_start = cb->args[0]; int wp_start = cb->args[0];
int if_start = cb->args[1]; int if_start = cb->args[1];
int filter_wiphy = -1;
struct cfg80211_registered_device *rdev; struct cfg80211_registered_device *rdev;
struct wireless_dev *wdev; struct wireless_dev *wdev;
rtnl_lock(); rtnl_lock();
if (!cb->args[2]) {
struct nl80211_dump_wiphy_state state = {
.filter_wiphy = -1,
};
int ret;
ret = nl80211_dump_wiphy_parse(skb, cb, &state);
if (ret)
return ret;
filter_wiphy = state.filter_wiphy;
/*
* if filtering, set cb->args[2] to +1 since 0 is the default
* value needed to determine that parsing is necessary.
*/
if (filter_wiphy >= 0)
cb->args[2] = filter_wiphy + 1;
else
cb->args[2] = -1;
} else if (cb->args[2] > 0) {
filter_wiphy = cb->args[2] - 1;
}
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk))) if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
continue; continue;
@ -2536,6 +2577,10 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
wp_idx++; wp_idx++;
continue; continue;
} }
if (filter_wiphy >= 0 && filter_wiphy != rdev->wiphy_idx)
continue;
if_idx = 0; if_idx = 0;
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
@ -7359,7 +7404,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
(key.p.cipher != WLAN_CIPHER_SUITE_WEP104 || (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
key.p.key_len != WLAN_KEY_LEN_WEP104)) key.p.key_len != WLAN_KEY_LEN_WEP104))
return -EINVAL; return -EINVAL;
if (key.idx > 4) if (key.idx > 3)
return -EINVAL; return -EINVAL;
} else { } else {
key.p.key_len = 0; key.p.key_len = 0;
@ -7977,6 +8022,8 @@ __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
} }
data = nla_nest_start(skb, attr); data = nla_nest_start(skb, attr);
if (!data)
goto nla_put_failure;
((void **)skb->cb)[0] = rdev; ((void **)skb->cb)[0] = rdev;
((void **)skb->cb)[1] = hdr; ((void **)skb->cb)[1] = hdr;
@ -9406,18 +9453,27 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
if (!freqs) if (!freqs)
return -ENOBUFS; return -ENOBUFS;
for (i = 0; i < req->n_channels; i++) for (i = 0; i < req->n_channels; i++) {
nla_put_u32(msg, i, req->channels[i]->center_freq); if (nla_put_u32(msg, i, req->channels[i]->center_freq))
return -ENOBUFS;
}
nla_nest_end(msg, freqs); nla_nest_end(msg, freqs);
if (req->n_match_sets) { if (req->n_match_sets) {
matches = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH); matches = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
if (!matches)
return -ENOBUFS;
for (i = 0; i < req->n_match_sets; i++) { for (i = 0; i < req->n_match_sets; i++) {
match = nla_nest_start(msg, i); match = nla_nest_start(msg, i);
nla_put(msg, NL80211_SCHED_SCAN_MATCH_ATTR_SSID, if (!match)
req->match_sets[i].ssid.ssid_len, return -ENOBUFS;
req->match_sets[i].ssid.ssid);
if (nla_put(msg, NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
req->match_sets[i].ssid.ssid_len,
req->match_sets[i].ssid.ssid))
return -ENOBUFS;
nla_nest_end(msg, match); nla_nest_end(msg, match);
} }
nla_nest_end(msg, matches); nla_nest_end(msg, matches);
@ -9429,6 +9485,9 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
for (i = 0; i < req->n_scan_plans; i++) { for (i = 0; i < req->n_scan_plans; i++) {
scan_plan = nla_nest_start(msg, i + 1); scan_plan = nla_nest_start(msg, i + 1);
if (!scan_plan)
return -ENOBUFS;
if (!scan_plan || if (!scan_plan ||
nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL, nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL,
req->scan_plans[i].interval) || req->scan_plans[i].interval) ||

View file

@ -352,52 +352,48 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *rdev)
__cfg80211_bss_expire(rdev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE); __cfg80211_bss_expire(rdev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);
} }
const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len,
const u8 *match, int match_len,
int match_offset)
{ {
while (len > 2 && ies[0] != eid) { /* match_offset can't be smaller than 2, unless match_len is
* zero, in which case match_offset must be zero as well.
*/
if (WARN_ON((match_len && match_offset < 2) ||
(!match_len && match_offset)))
return NULL;
while (len >= 2 && len >= ies[1] + 2) {
if ((ies[0] == eid) &&
(ies[1] + 2 >= match_offset + match_len) &&
!memcmp(ies + match_offset, match, match_len))
return ies;
len -= ies[1] + 2; len -= ies[1] + 2;
ies += ies[1] + 2; ies += ies[1] + 2;
} }
if (len < 2)
return NULL; return NULL;
if (len < 2 + ies[1])
return NULL;
return ies;
} }
EXPORT_SYMBOL(cfg80211_find_ie); EXPORT_SYMBOL(cfg80211_find_ie_match);
const u8 *cfg80211_find_vendor_ie(unsigned int oui, int oui_type, const u8 *cfg80211_find_vendor_ie(unsigned int oui, int oui_type,
const u8 *ies, int len) const u8 *ies, int len)
{ {
struct ieee80211_vendor_ie *ie; const u8 *ie;
const u8 *pos = ies, *end = ies + len; u8 match[] = { oui >> 16, oui >> 8, oui, oui_type };
int ie_oui; int match_len = (oui_type < 0) ? 3 : sizeof(match);
if (WARN_ON(oui_type > 0xff)) if (WARN_ON(oui_type > 0xff))
return NULL; return NULL;
while (pos < end) { ie = cfg80211_find_ie_match(WLAN_EID_VENDOR_SPECIFIC, ies, len,
pos = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, pos, match, match_len, 2);
end - pos);
if (!pos)
return NULL;
ie = (struct ieee80211_vendor_ie *)pos; if (ie && (ie[1] < 4))
return NULL;
/* make sure we can access ie->len */ return ie;
BUILD_BUG_ON(offsetof(struct ieee80211_vendor_ie, len) != 1);
if (ie->len < sizeof(*ie))
goto cont;
ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2];
if (ie_oui == oui &&
(oui_type < 0 || ie->oui_type == oui_type))
return pos;
cont:
pos += 2 + ie->len;
}
return NULL;
} }
EXPORT_SYMBOL(cfg80211_find_vendor_ie); EXPORT_SYMBOL(cfg80211_find_vendor_ie);

View file

@ -1043,6 +1043,9 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
connect->crypto.ciphers_pairwise[0] = cipher; connect->crypto.ciphers_pairwise[0] = cipher;
} }
} }
} else {
if (WARN_ON(connkeys))
return -EINVAL;
} }
wdev->connect_keys = connkeys; wdev->connect_keys = connkeys;

View file

@ -57,7 +57,7 @@ static ssize_t addresses_show(struct device *dev,
return sprintf(buf, "%pM\n", wiphy->perm_addr); return sprintf(buf, "%pM\n", wiphy->perm_addr);
for (i = 0; i < wiphy->n_addresses; i++) for (i = 0; i < wiphy->n_addresses; i++)
buf += sprintf(buf, "%pM\n", &wiphy->addresses[i].addr); buf += sprintf(buf, "%pM\n", wiphy->addresses[i].addr);
return buf - start; return buf - start;
} }

View file

@ -218,7 +218,7 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
struct key_params *params, int key_idx, struct key_params *params, int key_idx,
bool pairwise, const u8 *mac_addr) bool pairwise, const u8 *mac_addr)
{ {
if (key_idx > 5) if (key_idx < 0 || key_idx > 5)
return -EINVAL; return -EINVAL;
if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
@ -249,7 +249,13 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
/* Disallow BIP (group-only) cipher as pairwise cipher */ /* Disallow BIP (group-only) cipher as pairwise cipher */
if (pairwise) if (pairwise)
return -EINVAL; return -EINVAL;
if (key_idx < 4)
return -EINVAL;
break; break;
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
if (key_idx > 3)
return -EINVAL;
default: default:
break; break;
} }
@ -906,7 +912,7 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev)
if (!wdev->connect_keys) if (!wdev->connect_keys)
return; return;
for (i = 0; i < 6; i++) { for (i = 0; i < 4; i++) {
if (!wdev->connect_keys->params[i].cipher) if (!wdev->connect_keys->params[i].cipher)
continue; continue;
if (rdev_add_key(rdev, dev, i, false, NULL, if (rdev_add_key(rdev, dev, i, false, NULL,
@ -919,9 +925,6 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev)
netdev_err(dev, "failed to set defkey %d\n", i); netdev_err(dev, "failed to set defkey %d\n", i);
continue; continue;
} }
if (wdev->connect_keys->defmgmt == i)
if (rdev_set_default_mgmt_key(rdev, dev, i))
netdev_err(dev, "failed to set mgtdef %d\n", i);
} }
kzfree(wdev->connect_keys); kzfree(wdev->connect_keys);

View file

@ -408,10 +408,10 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
if (!wdev->wext.keys) { if (!wdev->wext.keys) {
wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys), wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys),
GFP_KERNEL); GFP_KERNEL);
if (!wdev->wext.keys) if (!wdev->wext.keys)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < 6; i++) for (i = 0; i < 4; i++)
wdev->wext.keys->params[i].key = wdev->wext.keys->params[i].key =
wdev->wext.keys->data[i]; wdev->wext.keys->data[i];
} }
@ -460,7 +460,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
if (err == -ENOENT) if (err == -ENOENT)
err = 0; err = 0;
if (!err) { if (!err) {
if (!addr) { if (!addr && idx < 4) {
memset(wdev->wext.keys->data[idx], 0, memset(wdev->wext.keys->data[idx], 0,
sizeof(wdev->wext.keys->data[idx])); sizeof(wdev->wext.keys->data[idx]));
wdev->wext.keys->params[idx].key_len = 0; wdev->wext.keys->params[idx].key_len = 0;
@ -487,6 +487,9 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
err = 0; err = 0;
if (wdev->current_bss) if (wdev->current_bss)
err = rdev_add_key(rdev, dev, idx, pairwise, addr, params); err = rdev_add_key(rdev, dev, idx, pairwise, addr, params);
else if (params->cipher != WLAN_CIPHER_SUITE_WEP40 &&
params->cipher != WLAN_CIPHER_SUITE_WEP104)
return -EINVAL;
if (err) if (err)
return err; return err;

View file

@ -35,7 +35,6 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
if (wdev->wext.keys) { if (wdev->wext.keys) {
wdev->wext.keys->def = wdev->wext.default_key; wdev->wext.keys->def = wdev->wext.default_key;
wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key;
if (wdev->wext.default_key != -1) if (wdev->wext.default_key != -1)
wdev->wext.connect.privacy = true; wdev->wext.connect.privacy = true;
} }
@ -43,11 +42,11 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
if (!wdev->wext.connect.ssid_len) if (!wdev->wext.connect.ssid_len)
return 0; return 0;
if (wdev->wext.keys) { if (wdev->wext.keys && wdev->wext.keys->def != -1) {
ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
if (!ck) if (!ck)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < 6; i++) for (i = 0; i < 4; i++)
ck->params[i].key = ck->data[i]; ck->params[i].key = ck->data[i];
} }