From f21e4d8ed16bb9cae27c7f1a07a2e0cd4daab736 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Sat, 14 May 2016 10:34:44 +0200 Subject: [PATCH 01/16] mac80211_hwsim: Allow wmediumd to attach to radios created in its netns Registering wmediumd is currently limited to the initial network namespace. This patch enables wmediumd to attach from non-initial network namespaces using a user namespace having CAP_NET_ADMIN. A registered wmediumd can forward frames on radios that have been created in the same network namespace, even if they have been moved to other network namespaces. The wmediumd Netlink portid is tracked per net namespace. Additionally, the portid is stored on all radios created in that net namespace to simplify the portid lookup in the data path. Signed-off-by: Martin Willi Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 92 ++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index a1e28a4fd658..382109bbc2c1 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -41,8 +41,6 @@ MODULE_AUTHOR("Jouni Malinen"); MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211"); MODULE_LICENSE("GPL"); -static u32 wmediumd_portid; - static int radios = 2; module_param(radios, int, 0444); MODULE_PARM_DESC(radios, "Number of simulated radios"); @@ -258,6 +256,7 @@ static int hwsim_netgroup; struct hwsim_net { int netgroup; + u32 wmediumd; }; static inline int hwsim_net_get_netgroup(struct net *net) @@ -274,6 +273,20 @@ static inline void hwsim_net_set_netgroup(struct net *net) hwsim_net->netgroup = hwsim_netgroup++; } +static inline u32 hwsim_net_get_wmediumd(struct net *net) +{ + struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id); + + return hwsim_net->wmediumd; +} + +static inline void hwsim_net_set_wmediumd(struct net *net, u32 portid) +{ + struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id); + + hwsim_net->wmediumd = portid; +} + static struct class *hwsim_class; static struct net_device *hwsim_mon; /* global monitor netdev */ @@ -552,6 +565,8 @@ struct mac80211_hwsim_data { /* group shared by radios created in the same netns */ int netgroup; + /* wmediumd portid responsible for netgroup of this radio */ + u32 wmediumd; int power_level; @@ -983,6 +998,29 @@ static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data, return true; } +static int hwsim_unicast_netgroup(struct mac80211_hwsim_data *data, + struct sk_buff *skb, int portid) +{ + struct net *net; + bool found = false; + int res = -ENOENT; + + rcu_read_lock(); + for_each_net_rcu(net) { + if (data->netgroup == hwsim_net_get_netgroup(net)) { + res = genlmsg_unicast(net, skb, portid); + found = true; + break; + } + } + rcu_read_unlock(); + + if (!found) + nlmsg_free(skb); + + return res; +} + static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, struct sk_buff *my_skb, int dst_portid) @@ -1062,7 +1100,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, goto nla_put_failure; genlmsg_end(skb, msg_head); - if (genlmsg_unicast(&init_net, skb, dst_portid)) + if (hwsim_unicast_netgroup(data, skb, dst_portid)) goto err_free_txskb; /* Enqueue the packet */ @@ -1355,7 +1393,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, mac80211_hwsim_monitor_rx(hw, skb, channel); /* wmediumd mode check */ - _portid = ACCESS_ONCE(wmediumd_portid); + _portid = ACCESS_ONCE(data->wmediumd); if (_portid) return mac80211_hwsim_tx_frame_nl(hw, skb, _portid); @@ -1451,7 +1489,8 @@ static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_channel *chan) { - u32 _pid = ACCESS_ONCE(wmediumd_portid); + struct mac80211_hwsim_data *data = hw->priv; + u32 _pid = ACCESS_ONCE(data->wmediumd); if (ieee80211_hw_check(hw, SUPPORTS_RC_TABLE)) { struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); @@ -2796,6 +2835,20 @@ static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(const u8 *addr) return data; } +static void hwsim_register_wmediumd(struct net *net, u32 portid) +{ + struct mac80211_hwsim_data *data; + + hwsim_net_set_wmediumd(net, portid); + + spin_lock_bh(&hwsim_radio_lock); + list_for_each_entry(data, &hwsim_radios, list) { + if (data->netgroup == hwsim_net_get_netgroup(net)) + data->wmediumd = portid; + } + spin_unlock_bh(&hwsim_radio_lock); +} + static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, struct genl_info *info) { @@ -2811,9 +2864,6 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, int i; bool found = false; - if (info->snd_portid != wmediumd_portid) - return -EINVAL; - if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] || !info->attrs[HWSIM_ATTR_FLAGS] || !info->attrs[HWSIM_ATTR_COOKIE] || @@ -2829,6 +2879,12 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, if (!data2) goto out; + if (hwsim_net_get_netgroup(genl_info_net(info)) != data2->netgroup) + goto out; + + if (info->snd_portid != data2->wmediumd) + goto out; + /* look for the skb matching the cookie passed back from user */ skb_queue_walk_safe(&data2->pending, skb, tmp) { u64 skb_cookie; @@ -2892,9 +2948,6 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, void *frame_data; struct sk_buff *skb = NULL; - if (info->snd_portid != wmediumd_portid) - return -EINVAL; - if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] || !info->attrs[HWSIM_ATTR_FRAME] || !info->attrs[HWSIM_ATTR_RX_RATE] || @@ -2920,6 +2973,12 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, if (!data2) goto out; + if (hwsim_net_get_netgroup(genl_info_net(info)) != data2->netgroup) + goto out; + + if (info->snd_portid != data2->wmediumd) + goto out; + /* check if radio is configured properly */ if (data2->idle || !data2->started) @@ -2966,6 +3025,7 @@ out: static int hwsim_register_received_nl(struct sk_buff *skb_2, struct genl_info *info) { + struct net *net = genl_info_net(info); struct mac80211_hwsim_data *data; int chans = 1; @@ -2982,10 +3042,10 @@ static int hwsim_register_received_nl(struct sk_buff *skb_2, if (chans > 1) return -EOPNOTSUPP; - if (wmediumd_portid) + if (hwsim_net_get_wmediumd(net)) return -EBUSY; - wmediumd_portid = info->snd_portid; + hwsim_register_wmediumd(net, info->snd_portid); printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, " "switching to wmediumd mode with pid %d\n", info->snd_portid); @@ -3152,7 +3212,7 @@ static const struct genl_ops hwsim_ops[] = { .cmd = HWSIM_CMD_REGISTER, .policy = hwsim_genl_policy, .doit = hwsim_register_received_nl, - .flags = GENL_ADMIN_PERM, + .flags = GENL_UNS_ADMIN_PERM, }, { .cmd = HWSIM_CMD_FRAME, @@ -3218,10 +3278,10 @@ static int mac80211_hwsim_netlink_notify(struct notifier_block *nb, remove_user_radios(notify->portid); - if (notify->portid == wmediumd_portid) { + if (notify->portid == hwsim_net_get_wmediumd(notify->net)) { printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink" " socket, switching to perfect channel medium\n"); - wmediumd_portid = 0; + hwsim_register_wmediumd(notify->net, 0); } return NOTIFY_DONE; From f151d9db4c1e7f7ac202ae75f4cbc62cfc784156 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 15 Jun 2016 22:29:41 +0200 Subject: [PATCH 02/16] nl80211: improve nl80211_parse_mesh_config type checking When building a kernel with W=1, the nl80211.c file causes a number of warnings, all about the same problem: net/wireless/nl80211.c: In function 'nl80211_parse_mesh_config': net/wireless/nl80211.c:5287:103: error: comparison is always false due to limited range of data type [-Werror=type-limits] net/wireless/nl80211.c:5290:96: error: comparison is always false due to limited range of data type [-Werror=type-limits] net/wireless/nl80211.c:5293:124: error: comparison is always false due to limited range of data type [-Werror=type-limits] net/wireless/nl80211.c:5295:148: error: comparison is always false due to limited range of data type [-Werror=type-limits] net/wireless/nl80211.c:5298:106: error: comparison is always false due to limited range of data type [-Werror=type-limits] net/wireless/nl80211.c:5305:116: error: comparison is always false due to limited range of data type [-Werror=type-limits] The problem is that gcc does not notice that the check is generate by a macro, so it complains about comparing an unsigned type against 0. I've tried to come up with a way to rephrase that code in a way that avoids the warnings and otherwise improves the code as well. This uses a set of new helper functions that perform the range checking, and should provide slightly better type safety than the older patch, at the expense of adding 44 lines to the code. Binary code size is basically unchanged though (20 bytes added to 126561 bytes .text). Signed-off-by: Arnd Bergmann Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 104 +++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 30 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c503e96bfd5a..244d552d5647 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5287,6 +5287,51 @@ static const struct nla_policy [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG }, }; +static int nl80211_check_bool(const struct nlattr *nla, u8 min, u8 max, bool *out) +{ + u8 val = nla_get_u8(nla); + if (val < min || val > max) + return -EINVAL; + *out = val; + return 0; +} + +static int nl80211_check_u8(const struct nlattr *nla, u8 min, u8 max, u8 *out) +{ + u8 val = nla_get_u8(nla); + if (val < min || val > max) + return -EINVAL; + *out = val; + return 0; +} + +static int nl80211_check_u16(const struct nlattr *nla, u16 min, u16 max, u16 *out) +{ + u16 val = nla_get_u16(nla); + if (val < min || val > max) + return -EINVAL; + *out = val; + return 0; +} + +static int nl80211_check_u32(const struct nlattr *nla, u32 min, u32 max, u32 *out) +{ + u32 val = nla_get_u32(nla); + if (val < min || val > max) + return -EINVAL; + *out = val; + return 0; +} + +static int nl80211_check_s32(const struct nlattr *nla, s32 min, s32 max, s32 *out) +{ + s32 val = nla_get_s32(nla); + if (val < min || val > max) + return -EINVAL; + *out = val; + return 0; +} + static int nl80211_parse_mesh_config(struct genl_info *info, struct mesh_config *cfg, u32 *mask_out) @@ -5297,9 +5342,8 @@ static int nl80211_parse_mesh_config(struct genl_info *info, #define FILL_IN_MESH_PARAM_IF_SET(tb, cfg, param, min, max, mask, attr, fn) \ do { \ if (tb[attr]) { \ - if (fn(tb[attr]) < min || fn(tb[attr]) > max) \ + if (fn(tb[attr], min, max, &cfg->param)) \ return -EINVAL; \ - cfg->param = fn(tb[attr]); \ mask |= (1 << (attr - 1)); \ } \ } while (0) @@ -5318,99 +5362,99 @@ do { \ /* Fill in the params struct */ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, 1, 255, mask, NL80211_MESHCONF_RETRY_TIMEOUT, - nla_get_u16); + nl80211_check_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, 1, 255, mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, - nla_get_u16); + nl80211_check_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, 1, 255, mask, NL80211_MESHCONF_HOLDING_TIMEOUT, - nla_get_u16); + nl80211_check_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, 0, 255, mask, NL80211_MESHCONF_MAX_PEER_LINKS, - nla_get_u16); + nl80211_check_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, 0, 16, mask, NL80211_MESHCONF_MAX_RETRIES, - nla_get_u8); + nl80211_check_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, 1, 255, - mask, NL80211_MESHCONF_TTL, nla_get_u8); + mask, NL80211_MESHCONF_TTL, nl80211_check_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, 1, 255, mask, NL80211_MESHCONF_ELEMENT_TTL, - nla_get_u8); + nl80211_check_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, 0, 1, mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, - nla_get_u8); + nl80211_check_bool); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, 1, 255, mask, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, - nla_get_u32); + nl80211_check_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, 0, 255, mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, - nla_get_u8); + nl80211_check_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, 1, 65535, mask, NL80211_MESHCONF_PATH_REFRESH_TIME, - nla_get_u32); + nl80211_check_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, 1, 65535, mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, - nla_get_u16); + nl80211_check_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, 1, 65535, mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, - nla_get_u32); + nl80211_check_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, 1, 65535, mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, - nla_get_u16); + nl80211_check_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval, 1, 65535, mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, - nla_get_u16); + nl80211_check_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPnetDiameterTraversalTime, 1, 65535, mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, - nla_get_u16); + nl80211_check_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, 0, 4, mask, NL80211_MESHCONF_HWMP_ROOTMODE, - nla_get_u8); + nl80211_check_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, 1, 65535, mask, NL80211_MESHCONF_HWMP_RANN_INTERVAL, - nla_get_u16); + nl80211_check_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshGateAnnouncementProtocol, 0, 1, mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, - nla_get_u8); + nl80211_check_bool); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, 0, 1, mask, NL80211_MESHCONF_FORWARDING, - nla_get_u8); + nl80211_check_bool); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, -255, 0, mask, NL80211_MESHCONF_RSSI_THRESHOLD, - nla_get_s32); + nl80211_check_s32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, 0, 16, mask, NL80211_MESHCONF_HT_OPMODE, - nla_get_u16); + nl80211_check_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout, 1, 65535, mask, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, - nla_get_u32); + nl80211_check_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, 1, 65535, mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, - nla_get_u16); + nl80211_check_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPconfirmationInterval, 1, 65535, mask, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, - nla_get_u16); + nl80211_check_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode, NL80211_MESH_POWER_ACTIVE, NL80211_MESH_POWER_MAX, mask, NL80211_MESHCONF_POWER_MODE, - nla_get_u32); + nl80211_check_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration, 0, 65535, mask, - NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16); + NL80211_MESHCONF_AWAKE_WINDOW, nl80211_check_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 0, 0xffffffff, mask, NL80211_MESHCONF_PLINK_TIMEOUT, - nla_get_u32); + nl80211_check_u32); if (mask_out) *mask_out = mask; From e98e915e11ad1efb11147122bd4932ec6b3425da Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Wed, 22 Jun 2016 20:23:03 +0900 Subject: [PATCH 03/16] wireless: Use macro instead of number Use IEEE80211_MIN_ACTION_SIZE macro for robust management frame check. Signed-off-by: Masashi Honma Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b118744d3382..1daebb307e6e 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2464,7 +2464,7 @@ static inline bool _ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr) */ static inline bool ieee80211_is_robust_mgmt_frame(struct sk_buff *skb) { - if (skb->len < 25) + if (skb->len < IEEE80211_MIN_ACTION_SIZE) return false; return _ieee80211_is_robust_mgmt_frame((void *)skb->data); } From 49708e3772ce648be425778702a266b207e89d4e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 27 Jun 2016 17:31:18 +0300 Subject: [PATCH 04/16] mac80211: silence an uninitialized variable warning We normally return an uninitialized value, but no one checks it so it doesn't matter. Anyway, let's silence the static checker warning. Signed-off-by: Dan Carpenter Signed-off-by: Johannes Berg --- net/mac80211/tdls.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 1c7d45a6d93e..b5d28f14b9cf 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -1747,6 +1747,7 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata, goto out; } + ret = 0; call_drv: drv_tdls_recv_channel_switch(sdata->local, sdata, ¶ms); From 46f6b06050b736dab4d41494dae27b883cddc365 Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Wed, 22 Jun 2016 19:55:20 +0900 Subject: [PATCH 05/16] mac80211: Encrypt "Group addressed privacy" action frames Previously, the action frames to group address was not encrypted. But [1] "Table 8-38 Category values" indicates "Mesh" and "Multihop" category action frames should be encrypted (Group addressed privacy == yes). And the encyption key should be MGTK ([1] 10.13 Group addressed robust management frame procedures). So this patch modifies the code to make it suitable for spec. [1] IEEE Std 802.11-2012 Signed-off-by: Masashi Honma Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 30 ++++++++++++++++++++++++++++++ net/mac80211/rx.c | 7 ++++++- net/mac80211/tx.c | 6 +++++- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 1daebb307e6e..a80516fd65c8 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -2486,6 +2487,35 @@ static inline bool ieee80211_is_public_action(struct ieee80211_hdr *hdr, return mgmt->u.action.category == WLAN_CATEGORY_PUBLIC; } +/** + * _ieee80211_is_group_privacy_action - check if frame is a group addressed + * privacy action frame + * @hdr: the frame + */ +static inline bool _ieee80211_is_group_privacy_action(struct ieee80211_hdr *hdr) +{ + struct ieee80211_mgmt *mgmt = (void *)hdr; + + if (!ieee80211_is_action(hdr->frame_control) || + !is_multicast_ether_addr(hdr->addr1)) + return false; + + return mgmt->u.action.category == WLAN_CATEGORY_MESH_ACTION || + mgmt->u.action.category == WLAN_CATEGORY_MULTIHOP_ACTION; +} + +/** + * ieee80211_is_group_privacy_action - check if frame is a group addressed + * privacy action frame + * @skb: the skb containing the frame, length will be checked + */ +static inline bool ieee80211_is_group_privacy_action(struct sk_buff *skb) +{ + if (skb->len < IEEE80211_MIN_ACTION_SIZE) + return false; + return _ieee80211_is_group_privacy_action((void *)skb->data); +} + /** * ieee80211_tu_to_usec - convert time units (TU) to microseconds * @tu: the TUs diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 9a1eb70cb120..2e8a9024625a 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1624,8 +1624,13 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) if (mmie_keyidx < NUM_DEFAULT_KEYS || mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) return RX_DROP_MONITOR; /* unexpected BIP keyidx */ - if (rx->sta) + if (rx->sta) { + if (ieee80211_is_group_privacy_action(skb) && + test_sta_flag(rx->sta, WLAN_STA_MFP)) + return RX_DROP_MONITOR; + rx->key = rcu_dereference(rx->sta->gtk[mmie_keyidx]); + } if (!rx->key) rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]); } else if (!ieee80211_has_protected(fc)) { diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 44ec605a5682..fa8d38eb9236 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -593,6 +593,9 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) else if (tx->sta && (key = rcu_dereference(tx->sta->ptk[tx->sta->ptk_idx]))) tx->key = key; + else if (ieee80211_is_group_privacy_action(tx->skb) && + (key = rcu_dereference(tx->sdata->default_multicast_key))) + tx->key = key; else if (ieee80211_is_mgmt(hdr->frame_control) && is_multicast_ether_addr(hdr->addr1) && ieee80211_is_robust_mgmt_frame(tx->skb) && @@ -625,7 +628,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) case WLAN_CIPHER_SUITE_GCMP_256: if (!ieee80211_is_data_present(hdr->frame_control) && !ieee80211_use_mfp(hdr->frame_control, tx->sta, - tx->skb)) + tx->skb) && + !ieee80211_is_group_privacy_action(tx->skb)) tx->key = NULL; else skip_hw = (tx->key->conf.flags & From efc401f49adf9c53a95f0430496c7a5433612e74 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Sat, 25 Jun 2016 19:14:16 -0400 Subject: [PATCH 06/16] mac80211: use common cleanup for user/!user_mpm We've accumulated a couple of different fixes now to mesh_sta_cleanup() due to the different paths that user_mpm and !user_mpm cases take -- one fix to flush nexthop paths and one to fix the counting. The only caller of mesh_plink_deactivate() is mesh_sta_cleanup(), so we can push the user_mpm checks down into there in order to share more code. In doing so, we can remove an extra call to mesh_path_flush_by_nexthop() and the (unnecessary) call to mesh_accept_plinks_update(). This will also ensure the powersaving state code gets called in the user_mpm case. The only cleanup tasks we need to avoid when MPM is in user-space are sending the peering frames and stopping the plink timer, so wrap those in the appropriate check. Signed-off-by: Bob Copeland Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 20 +------------------- net/mac80211/mesh_plink.c | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 6a1603bcdced..c66411df9863 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -148,25 +148,7 @@ u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata) void mesh_sta_cleanup(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; - u32 changed = 0; - - /* - * maybe userspace handles peer allocation and peering, but in either - * case the beacon is still generated by the kernel and we might need - * an update. - */ - if (sdata->u.mesh.user_mpm && - sta->mesh->plink_state == NL80211_PLINK_ESTAB) - changed |= mesh_plink_dec_estab_count(sdata); - changed |= mesh_accept_plinks_update(sdata); - if (!sdata->u.mesh.user_mpm) { - changed |= mesh_plink_deactivate(sta); - del_timer_sync(&sta->mesh->plink_timer); - } - - /* make sure no readers can access nexthop sta from here on */ - mesh_path_flush_by_nexthop(sta); - synchronize_net(); + u32 changed = mesh_plink_deactivate(sta); if (changed) ieee80211_mbss_info_change_notify(sdata, changed); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 79f2a0a13db8..7fcdcf622655 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -370,13 +370,21 @@ u32 mesh_plink_deactivate(struct sta_info *sta) spin_lock_bh(&sta->mesh->plink_lock); changed = __mesh_plink_deactivate(sta); - sta->mesh->reason = WLAN_REASON_MESH_PEER_CANCELED; - mesh_plink_frame_tx(sdata, sta, WLAN_SP_MESH_PEERING_CLOSE, - sta->sta.addr, sta->mesh->llid, sta->mesh->plid, - sta->mesh->reason); + + if (!sdata->u.mesh.user_mpm) { + sta->mesh->reason = WLAN_REASON_MESH_PEER_CANCELED; + mesh_plink_frame_tx(sdata, sta, WLAN_SP_MESH_PEERING_CLOSE, + sta->sta.addr, sta->mesh->llid, + sta->mesh->plid, sta->mesh->reason); + } spin_unlock_bh(&sta->mesh->plink_lock); + if (!sdata->u.mesh.user_mpm) + del_timer_sync(&sta->mesh->plink_timer); mesh_path_flush_by_nexthop(sta); + /* make sure no readers can access nexthop sta from here on */ + synchronize_net(); + return changed; } From 59a7c828d7e7d5a1be224a0d68a41ca2302843ea Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 29 Jun 2016 14:00:34 +0200 Subject: [PATCH 07/16] mac80211: fix fq lockdep warnings Some lockdep assertions were not fulfilled and resulted in a kernel warning/call trace if driver used intermediate software queues (e.g. ath10k). Existing code sequences should've guaranteed safety but it's always good to be extra careful. The call trace could look like this: [ 237.335805] ------------[ cut here ]------------ [ 237.335852] WARNING: CPU: 3 PID: 1921 at include/net/fq_impl.h:22 fq_flow_dequeue+0xed/0x140 [mac80211] [ 237.335855] Modules linked in: ath10k_pci(E-) ath10k_core(E) ath(E) mac80211(E) cfg80211(E) [ 237.335913] CPU: 3 PID: 1921 Comm: rmmod Tainted: G W E 4.7.0-rc4-wt-ath+ #1377 [ 237.335916] Hardware name: Hewlett-Packard HP ProBook 6540b/1722, BIOS 68CDD Ver. F.04 01/27/2010 [ 237.335918] 00200286 00200286 eff85dac c14151e2 f901574e 00000000 eff85de0 c1081075 [ 237.335928] c1ab91f0 00000003 00000781 f901574e 00000016 f8fbabad f8fbabad 00000016 [ 237.335938] eb24ff60 00000000 ef3886c0 eff85df4 c10810ba 00000009 00000000 00000000 [ 237.335948] Call Trace: [ 237.335953] [] dump_stack+0x76/0xb4 [ 237.335957] [] __warn+0xe5/0x100 [ 237.336002] [] ? fq_flow_dequeue+0xed/0x140 [mac80211] [ 237.336046] [] ? fq_flow_dequeue+0xed/0x140 [mac80211] [ 237.336053] [] warn_slowpath_null+0x2a/0x30 [ 237.336095] [] fq_flow_dequeue+0xed/0x140 [mac80211] [ 237.336137] [] fq_flow_reset.constprop.56+0x2a/0x90 [mac80211] [ 237.336180] [] fq_reset.constprop.59+0x2a/0x50 [mac80211] [ 237.336222] [] ieee80211_txq_teardown_flows+0x38/0x40 [mac80211] [ 237.336258] [] ieee80211_unregister_hw+0xe4/0x120 [mac80211] [ 237.336275] [] ath10k_mac_unregister+0x16/0x50 [ath10k_core] [ 237.336292] [] ath10k_core_unregister+0x3d/0x90 [ath10k_core] [ 237.336301] [] ath10k_pci_remove+0x36/0xa0 [ath10k_pci] [ 237.336307] [] pci_device_remove+0x38/0xb0 ... Fixes: 5caa328e3811 ("mac80211: implement codel on fair queuing flows") Fixes: fa962b92120b ("mac80211: implement fair queueing per txq") Tested-by: Kalle Valo Reported-by: Kalle Valo Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index fa8d38eb9236..91461c415525 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1449,7 +1449,9 @@ int ieee80211_txq_setup_flows(struct ieee80211_local *local) local->cvars = kcalloc(fq->flows_cnt, sizeof(local->cvars[0]), GFP_KERNEL); if (!local->cvars) { + spin_lock_bh(&fq->lock); fq_reset(fq, fq_skb_free_func); + spin_unlock_bh(&fq->lock); return -ENOMEM; } @@ -1469,7 +1471,9 @@ void ieee80211_txq_teardown_flows(struct ieee80211_local *local) kfree(local->cvars); local->cvars = NULL; + spin_lock_bh(&fq->lock); fq_reset(fq, fq_skb_free_func); + spin_unlock_bh(&fq->lock); } struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, From f89e07d4cf2660a2956bc350a201398dda85284e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 6 Jul 2016 14:44:14 +0200 Subject: [PATCH 08/16] mac80211: agg-rx: refuse ADDBA Request with timeout update The current implementation of handling ADDBA Request while a session is already active with the peer is wrong - in case the peer is using the existing session's dialog token this should be treated as update to the session, which can update the timeout value. We don't really have a good way of supporting that, so reject, but implement the required behaviour in the spec of "Even if the updated ADDBA Request frame is not accepted, the original Block ACK setup remains active." (802.11-2012 10.5.4) Signed-off-by: Johannes Berg --- net/mac80211/agg-rx.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 3a8f881b22f1..a9aff6079c42 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -306,6 +306,24 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, mutex_lock(&sta->ampdu_mlme.mtx); if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) { + tid_agg_rx = rcu_dereference_protected( + sta->ampdu_mlme.tid_rx[tid], + lockdep_is_held(&sta->ampdu_mlme.mtx)); + + if (tid_agg_rx->dialog_token == dialog_token) { + ht_dbg_ratelimited(sta->sdata, + "updated AddBA Req from %pM on tid %u\n", + sta->sta.addr, tid); + /* We have no API to update the timeout value in the + * driver so reject the timeout update. + */ + status = WLAN_STATUS_REQUEST_DECLINED; + ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, + tid, dialog_token, status, + 1, buf_size, timeout); + goto end; + } + ht_dbg_ratelimited(sta->sdata, "unexpected AddBA Req from %pM on tid %u\n", sta->sta.addr, tid); From c6e6a0c8be575c830a97b1942dabeab70f423fe0 Mon Sep 17 00:00:00 2001 From: Aviya Erenfeld Date: Tue, 5 Jul 2016 15:23:08 +0300 Subject: [PATCH 09/16] nl80211: Add API to support VHT MU-MIMO air sniffer add API to support VHT MU-MIMO air sniffer. in MU-MIMO there are parallel frames on the air while the HW has only one RX. add the capability to sniff one of the MU-MIMO parallel frames by giving the sniffer additional information so it'll know which of the parallel frames it shall follow. Add attribute - NL80211_ATTR_MU_MIMO_GROUP_DATA - for getting a MU-MIMO groupID in order to monitor packets from that group using VHT MU-MIMO. And add attribute -NL80211_ATTR_MU_MIMO_FOLLOW_ADDR - for passing MAC address to monitor mode. that option will be used by VHT MU-MIMO air sniffer to follow a station according to it's MAC address using VHT MU-MIMO. Signed-off-by: Aviya Erenfeld Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 10 ++++++++-- include/uapi/linux/nl80211.h | 29 +++++++++++++++++++++++++++++ net/wireless/nl80211.c | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7bbb00d8b2cd..fa4f0f793817 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -330,6 +330,9 @@ struct ieee80211_supported_band { * in a separate chapter. */ +#define VHT_MUMIMO_GROUPS_DATA_LEN (WLAN_MEMBERSHIP_LEN +\ + WLAN_USER_POSITION_LEN) + /** * struct vif_params - describes virtual interface parameters * @use_4addr: use 4-address frames @@ -339,10 +342,13 @@ struct ieee80211_supported_band { * This feature is only fully supported by drivers that enable the * %NL80211_FEATURE_MAC_ON_CREATE flag. Others may support creating ** only p2p devices with specified MAC. + * @vht_mumimo_groups: MU-MIMO groupID. used for monitoring only + * packets belonging to that MU-MIMO groupID. */ struct vif_params { - int use_4addr; - u8 macaddr[ETH_ALEN]; + int use_4addr; + u8 macaddr[ETH_ALEN]; + u8 vht_mumimo_groups[VHT_MUMIMO_GROUPS_DATA_LEN]; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 53c8278827a0..1d7da7888dcf 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1829,6 +1829,25 @@ enum nl80211_commands { * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities per * interface type. * + * @NL80211_ATTR_MU_MIMO_GROUP_DATA: array of 24 bytes that defines a MU-MIMO + * groupID for monitor mode. + * The first 8 bytes are a mask that defines the membership in each + * group (there are 64 groups, group 0 and 63 are reserved), + * each bit represents a group and set to 1 for being a member in + * that group and 0 for not being a member. + * The remaining 16 bytes define the position in each group: 2 bits for + * each group. + * (smaller group numbers represented on most significant bits and bigger + * group numbers on least significant bits.) + * This attribute is used only if all interfaces are in monitor mode. + * Set this attribute in order to monitor packets using the given MU-MIMO + * groupID data. + * to turn off that feature set all the bits of the groupID to zero. + * @NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR: mac address for the sniffer to follow + * when using MU-MIMO air sniffer. + * to turn that feature off set an invalid mac address + * (e.g. FF:FF:FF:FF:FF:FF) + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2213,6 +2232,9 @@ enum nl80211_attrs { NL80211_ATTR_IFTYPE_EXT_CAPA, + NL80211_ATTR_MU_MIMO_GROUP_DATA, + NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -4479,6 +4501,12 @@ enum nl80211_feature_flags { * %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set * the ASSOC_REQ_USE_RRM flag in the association request even if * NL80211_FEATURE_QUIET is not advertized. + * @NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER: This device supports MU-MIMO air + * sniffer which means that it can be configured to hear packets from + * certain groups which can be configured by the + * %NL80211_ATTR_MU_MIMO_GROUP_DATA attribute, + * or can be configured to follow a station by configuring the + * %NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR attribute. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -4486,6 +4514,7 @@ enum nl80211_feature_flags { enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_VHT_IBSS, NL80211_EXT_FEATURE_RRM, + NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 244d552d5647..447026f8cc76 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -405,6 +405,10 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_PBSS] = { .type = NLA_FLAG }, [NL80211_ATTR_BSS_SELECT] = { .type = NLA_NESTED }, [NL80211_ATTR_STA_SUPPORT_P2P_PS] = { .type = NLA_U8 }, + [NL80211_ATTR_MU_MIMO_GROUP_DATA] = { + .len = VHT_MUMIMO_GROUPS_DATA_LEN + }, + [NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR] = { .len = ETH_ALEN }, }; /* policy for the key attributes */ @@ -2695,6 +2699,38 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) change = true; } + if (info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]) { + const u8 *mumimo_groups; + u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; + + if (!wiphy_ext_feature_isset(&rdev->wiphy, cap_flag)) + return -EOPNOTSUPP; + + mumimo_groups = + nla_data(info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]); + + /* bits 0 and 63 are reserved and must be zero */ + if ((mumimo_groups[0] & BIT(7)) || + (mumimo_groups[VHT_MUMIMO_GROUPS_DATA_LEN - 1] & BIT(0))) + return -EINVAL; + + memcpy(params.vht_mumimo_groups, mumimo_groups, + VHT_MUMIMO_GROUPS_DATA_LEN); + change = true; + } + + if (info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]) { + u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; + + if (!wiphy_ext_feature_isset(&rdev->wiphy, cap_flag)) + return -EOPNOTSUPP; + + nla_memcpy(params.macaddr, + info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR], + ETH_ALEN); + change = true; + } + if (flags && (*flags & MONITOR_FLAG_ACTIVE) && !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) return -EOPNOTSUPP; From f7736f501f87b3f6ffbf07d173a6893dd419ac50 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 5 Jul 2016 15:23:09 +0300 Subject: [PATCH 10/16] mac80211_hwsim: Add radar bandwidths to the P2P Device combination Add radar_detect_widths to the interface combination that allows concurrent P2P Device dedicated interface and AP interfaces, to enable testing of radar detection when P2P Device interface is used. Clear the radar_detect_widths in case of multi channel contexts as this is not currently supported. As radar_detect_widths are now supported in all combinations, remove the hwsim_if_dfs_limits definition since it is no longer needed. Signed-off-by: Ilan Peer Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 382109bbc2c1..97196c4e2836 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -457,10 +457,6 @@ static const struct ieee80211_iface_limit hwsim_if_limits[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) } }; -static const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = { - { .max = 8, .types = BIT(NL80211_IFTYPE_AP) }, -}; - static const struct ieee80211_iface_combination hwsim_if_comb[] = { { .limits = hwsim_if_limits, @@ -468,18 +464,12 @@ static const struct ieee80211_iface_combination hwsim_if_comb[] = { .n_limits = ARRAY_SIZE(hwsim_if_limits) - 1, .max_interfaces = 2048, .num_different_channels = 1, - }, - { - .limits = hwsim_if_dfs_limits, - .n_limits = ARRAY_SIZE(hwsim_if_dfs_limits), - .max_interfaces = 8, - .num_different_channels = 1, .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | BIT(NL80211_CHAN_WIDTH_20) | BIT(NL80211_CHAN_WIDTH_40) | BIT(NL80211_CHAN_WIDTH_80) | BIT(NL80211_CHAN_WIDTH_160), - } + }, }; static const struct ieee80211_iface_combination hwsim_if_comb_p2p_dev[] = { @@ -488,18 +478,12 @@ static const struct ieee80211_iface_combination hwsim_if_comb_p2p_dev[] = { .n_limits = ARRAY_SIZE(hwsim_if_limits), .max_interfaces = 2048, .num_different_channels = 1, - }, - { - .limits = hwsim_if_dfs_limits, - .n_limits = ARRAY_SIZE(hwsim_if_dfs_limits), - .max_interfaces = 8, - .num_different_channels = 1, .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | BIT(NL80211_CHAN_WIDTH_20) | BIT(NL80211_CHAN_WIDTH_40) | BIT(NL80211_CHAN_WIDTH_80) | BIT(NL80211_CHAN_WIDTH_160), - } + }, }; static spinlock_t hwsim_radio_lock; @@ -2487,13 +2471,14 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, hw->wiphy->max_scan_ssids = 255; hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; hw->wiphy->max_remain_on_channel_duration = 1000; - /* For channels > 1 DFS is not allowed */ - hw->wiphy->n_iface_combinations = 1; hw->wiphy->iface_combinations = &data->if_combination; if (param->p2p_device) data->if_combination = hwsim_if_comb_p2p_dev[0]; else data->if_combination = hwsim_if_comb[0]; + hw->wiphy->n_iface_combinations = 1; + /* For channels > 1 DFS is not allowed */ + data->if_combination.radar_detect_widths = 0; data->if_combination.num_different_channels = data->channels; } else if (param->p2p_device) { hw->wiphy->iface_combinations = hwsim_if_comb_p2p_dev; From f1724b0258b412144964f2074b9b6edcf0dce87c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 6 Jul 2016 14:47:49 +0200 Subject: [PATCH 11/16] mac80211_hwsim: use signed net namespace ID The API expects a pointer to a signed int so we should not use an unsigned int for it. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 97196c4e2836..337794f9506e 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -250,7 +250,7 @@ static inline void hwsim_clear_chanctx_magic(struct ieee80211_chanctx_conf *c) cp->magic = 0; } -static unsigned int hwsim_net_id; +static int hwsim_net_id; static int hwsim_netgroup; From 1d76250bd34af86c6498fc51e50cab3bfbbeceaa Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Tue, 5 Jul 2016 17:10:13 +0300 Subject: [PATCH 12/16] nl80211: support beacon report scanning Beacon report radio measurement requires reporting observed BSSs on the channels specified in the beacon request. If the measurement mode is set to passive or active, it requires actually performing a scan (passive or active, accordingly), and reporting the time that the scan was started and the time each beacon/probe was received (both in terms of TSF of the BSS of the requesting AP). If the request mode is table, this information is optional. In addition, the radio measurement request specifies the channel dwell time for the measurement. In order to use scan for beacon report when the mode is active or passive, add a parameter to scan request that specifies the channel dwell time, and add scan start time and beacon received time to scan results information. Supporting beacon report is required for Multi Band Operation (MBO). Signed-off-by: Assaf Krauss Signed-off-by: David Spinadel Signed-off-by: Avraham Stern Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 17 ++++++-- drivers/net/wireless/ath/wil6210/cfg80211.c | 6 ++- drivers/net/wireless/ath/wil6210/main.c | 12 +++++- drivers/net/wireless/ath/wil6210/p2p.c | 6 ++- drivers/net/wireless/ath/wil6210/wmi.c | 8 ++-- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 6 ++- drivers/net/wireless/intersil/orinoco/scan.c | 12 +++++- drivers/net/wireless/marvell/libertas/cfg.c | 11 +++-- drivers/net/wireless/marvell/mwifiex/cmdevt.c | 12 +++++- drivers/net/wireless/marvell/mwifiex/main.c | 6 ++- drivers/net/wireless/marvell/mwifiex/scan.c | 12 +++++- drivers/net/wireless/rndis_wlan.c | 10 ++++- .../staging/rtl8723au/os_dep/ioctl_cfg80211.c | 11 +++-- .../staging/wilc1000/wilc_wfi_cfgoperations.c | 12 +++++- drivers/staging/wlan-ng/cfg80211.c | 5 ++- include/net/cfg80211.h | 40 +++++++++++++++--- include/uapi/linux/nl80211.h | 42 +++++++++++++++++++ net/mac80211/scan.c | 9 +++- net/wireless/core.c | 4 +- net/wireless/core.h | 12 ++++++ net/wireless/nl80211.c | 27 ++++++++++++ net/wireless/scan.c | 18 +++++--- net/wireless/trace.h | 31 ++++++++++---- 23 files changed, 278 insertions(+), 51 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 4e11ba06f089..ef5b40ef6d67 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -859,7 +859,11 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, struct ath6kl *ar = vif->ar; if (vif->scan_req) { - cfg80211_scan_done(vif->scan_req, true); + struct cfg80211_scan_info info = { + .aborted = true, + }; + + cfg80211_scan_done(vif->scan_req, &info); vif->scan_req = NULL; } @@ -1069,6 +1073,9 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted) { struct ath6kl *ar = vif->ar; + struct cfg80211_scan_info info = { + .aborted = aborted, + }; int i; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__, @@ -1089,7 +1096,7 @@ void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted) } out: - cfg80211_scan_done(vif->scan_req, aborted); + cfg80211_scan_done(vif->scan_req, &info); vif->scan_req = NULL; } @@ -3614,7 +3621,11 @@ void ath6kl_cfg80211_vif_stop(struct ath6kl_vif *vif, bool wmi_ready) } if (vif->scan_req) { - cfg80211_scan_done(vif->scan_req, true); + struct cfg80211_scan_info info = { + .aborted = true, + }; + + cfg80211_scan_done(vif->scan_req, &info); vif->scan_req = NULL; } diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 62bf9331bd7f..f0e1175fb76a 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -1369,7 +1369,11 @@ static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy, mutex_lock(&wil->mutex); started = wil_p2p_stop_discovery(wil); if (started && wil->scan_request) { - cfg80211_scan_done(wil->scan_request, 1); + struct cfg80211_scan_info info = { + .aborted = true, + }; + + cfg80211_scan_done(wil->scan_request, &info); wil->scan_request = NULL; wil->radio_wdev = wil->wdev; } diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 8e31d755bbee..4bc92e54984a 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -850,10 +850,14 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) mutex_unlock(&wil->wmi_mutex); if (wil->scan_request) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + wil_dbg_misc(wil, "Abort scan_request 0x%p\n", wil->scan_request); del_timer_sync(&wil->scan_timer); - cfg80211_scan_done(wil->scan_request, true); + cfg80211_scan_done(wil->scan_request, &info); wil->scan_request = NULL; } @@ -1049,10 +1053,14 @@ int __wil_down(struct wil6210_priv *wil) (void)wil_p2p_stop_discovery(wil); if (wil->scan_request) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + wil_dbg_misc(wil, "Abort scan_request 0x%p\n", wil->scan_request); del_timer_sync(&wil->scan_timer); - cfg80211_scan_done(wil->scan_request, true); + cfg80211_scan_done(wil->scan_request, &info); wil->scan_request = NULL; } diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c index 213b8259638c..e0f8aa0ebfac 100644 --- a/drivers/net/wireless/ath/wil6210/p2p.c +++ b/drivers/net/wireless/ath/wil6210/p2p.c @@ -252,8 +252,12 @@ void wil_p2p_search_expired(struct work_struct *work) mutex_unlock(&wil->mutex); if (started) { + struct cfg80211_scan_info info = { + .aborted = false, + }; + mutex_lock(&wil->p2p_wdev_mutex); - cfg80211_scan_done(wil->scan_request, 0); + cfg80211_scan_done(wil->scan_request, &info); wil->scan_request = NULL; wil->radio_wdev = wil->wdev; mutex_unlock(&wil->p2p_wdev_mutex); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index b80c5d850e1e..4d92541913c0 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -426,15 +426,17 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, { if (wil->scan_request) { struct wmi_scan_complete_event *data = d; - bool aborted = (data->status != WMI_SCAN_SUCCESS); + struct cfg80211_scan_info info = { + .aborted = (data->status != WMI_SCAN_SUCCESS), + }; wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); wil_dbg_misc(wil, "Complete scan_request 0x%p aborted %d\n", - wil->scan_request, aborted); + wil->scan_request, info.aborted); del_timer_sync(&wil->scan_timer); mutex_lock(&wil->p2p_wdev_mutex); - cfg80211_scan_done(wil->scan_request, aborted); + cfg80211_scan_done(wil->scan_request, &info); wil->radio_wdev = wil->wdev; mutex_unlock(&wil->p2p_wdev_mutex); wil->scan_request = NULL; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 264bd638a3d9..afe2b202040a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -775,9 +775,13 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, if (!aborted) cfg80211_sched_scan_results(cfg_to_wiphy(cfg)); } else if (scan_request) { + struct cfg80211_scan_info info = { + .aborted = aborted, + }; + brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n", aborted ? "Aborted" : "Done"); - cfg80211_scan_done(scan_request, aborted); + cfg80211_scan_done(scan_request, &info); } if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n"); diff --git a/drivers/net/wireless/intersil/orinoco/scan.c b/drivers/net/wireless/intersil/orinoco/scan.c index d0ceb06c72d0..6d1d084854fb 100644 --- a/drivers/net/wireless/intersil/orinoco/scan.c +++ b/drivers/net/wireless/intersil/orinoco/scan.c @@ -237,7 +237,11 @@ void orinoco_add_hostscan_results(struct orinoco_private *priv, scan_abort: if (priv->scan_request) { - cfg80211_scan_done(priv->scan_request, abort); + struct cfg80211_scan_info info = { + .aborted = abort, + }; + + cfg80211_scan_done(priv->scan_request, &info); priv->scan_request = NULL; } } @@ -245,7 +249,11 @@ void orinoco_add_hostscan_results(struct orinoco_private *priv, void orinoco_scan_done(struct orinoco_private *priv, bool abort) { if (priv->scan_request) { - cfg80211_scan_done(priv->scan_request, abort); + struct cfg80211_scan_info info = { + .aborted = abort, + }; + + cfg80211_scan_done(priv->scan_request, &info); priv->scan_request = NULL; } } diff --git a/drivers/net/wireless/marvell/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c index 776b44bfd93a..ea4802446618 100644 --- a/drivers/net/wireless/marvell/libertas/cfg.c +++ b/drivers/net/wireless/marvell/libertas/cfg.c @@ -796,10 +796,15 @@ void lbs_scan_done(struct lbs_private *priv) { WARN_ON(!priv->scan_req); - if (priv->internal_scan) + if (priv->internal_scan) { kfree(priv->scan_req); - else - cfg80211_scan_done(priv->scan_req, false); + } else { + struct cfg80211_scan_info info = { + .aborted = false, + }; + + cfg80211_scan_done(priv->scan_req, &info); + } priv->scan_req = NULL; } diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c index 6bc2011d8609..e7a21443647e 100644 --- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c +++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c @@ -1057,8 +1057,12 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) if (!priv) continue; if (priv->scan_request) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + mwifiex_dbg(adapter, WARN, "info: aborting scan\n"); - cfg80211_scan_done(priv->scan_request, 1); + cfg80211_scan_done(priv->scan_request, &info); priv->scan_request = NULL; } } @@ -1112,8 +1116,12 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) if (!priv) continue; if (priv->scan_request) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + mwifiex_dbg(adapter, WARN, "info: aborting scan\n"); - cfg80211_scan_done(priv->scan_request, 1); + cfg80211_scan_done(priv->scan_request, &info); priv->scan_request = NULL; } } diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 0e280f879b58..db4925db39aa 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -697,9 +697,13 @@ mwifiex_close(struct net_device *dev) struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); if (priv->scan_request) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + mwifiex_dbg(priv->adapter, INFO, "aborting scan on ndo_stop\n"); - cfg80211_scan_done(priv->scan_request, 1); + cfg80211_scan_done(priv->scan_request, &info); priv->scan_request = NULL; priv->scan_aborting = true; } diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index bc5e52cebce1..fdd749110fcb 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -1956,9 +1956,13 @@ static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) mwifiex_complete_scan(priv); if (priv->scan_request) { + struct cfg80211_scan_info info = { + .aborted = false, + }; + mwifiex_dbg(adapter, INFO, "info: notifying scan done\n"); - cfg80211_scan_done(priv->scan_request, 0); + cfg80211_scan_done(priv->scan_request, &info); priv->scan_request = NULL; } else { priv->scan_aborting = false; @@ -1977,9 +1981,13 @@ static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) if (!adapter->active_scan_triggered) { if (priv->scan_request) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + mwifiex_dbg(adapter, INFO, "info: aborting scan\n"); - cfg80211_scan_done(priv->scan_request, 1); + cfg80211_scan_done(priv->scan_request, &info); priv->scan_request = NULL; } else { priv->scan_aborting = false; diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 569918c485b4..603c90470225 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2134,6 +2134,7 @@ static void rndis_get_scan_results(struct work_struct *work) struct rndis_wlan_private *priv = container_of(work, struct rndis_wlan_private, scan_work.work); struct usbnet *usbdev = priv->usbdev; + struct cfg80211_scan_info info = {}; int ret; netdev_dbg(usbdev->net, "get_scan_results\n"); @@ -2143,7 +2144,8 @@ static void rndis_get_scan_results(struct work_struct *work) ret = rndis_check_bssid_list(usbdev, NULL, NULL); - cfg80211_scan_done(priv->scan_request, ret < 0); + info.aborted = ret < 0; + cfg80211_scan_done(priv->scan_request, &info); priv->scan_request = NULL; } @@ -3574,7 +3576,11 @@ static int rndis_wlan_stop(struct usbnet *usbdev) flush_workqueue(priv->workqueue); if (priv->scan_request) { - cfg80211_scan_done(priv->scan_request, true); + struct cfg80211_scan_info info = { + .aborted = true, + }; + + cfg80211_scan_done(priv->scan_request, &info); priv->scan_request = NULL; } diff --git a/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c index 0da559d929bc..d0ba3778990e 100644 --- a/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c @@ -1256,10 +1256,15 @@ void rtw_cfg80211_indicate_scan_done(struct rtw_wdev_priv *pwdev_priv, DBG_8723A("%s with scan req\n", __func__); if (pwdev_priv->scan_request->wiphy != - pwdev_priv->rtw_wdev->wiphy) + pwdev_priv->rtw_wdev->wiphy) { DBG_8723A("error wiphy compare\n"); - else - cfg80211_scan_done(pwdev_priv->scan_request, aborted); + } else { + struct cfg80211_scan_info info = { + .aborted = aborted, + }; + + cfg80211_scan_done(pwdev_priv->scan_request, &info); + } pwdev_priv->scan_request = NULL; } else { diff --git a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c index 51aff4ff7d7c..a0d8e22e575b 100644 --- a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c +++ b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c @@ -454,7 +454,11 @@ static void CfgScanResult(enum scan_event scan_event, mutex_lock(&priv->scan_req_lock); if (priv->pstrScanReq) { - cfg80211_scan_done(priv->pstrScanReq, false); + struct cfg80211_scan_info info = { + .aborted = false, + }; + + cfg80211_scan_done(priv->pstrScanReq, &info); priv->u32RcvdChCount = 0; priv->bCfgScanning = false; priv->pstrScanReq = NULL; @@ -464,10 +468,14 @@ static void CfgScanResult(enum scan_event scan_event, mutex_lock(&priv->scan_req_lock); if (priv->pstrScanReq) { + struct cfg80211_scan_info info = { + .aborted = false, + }; + update_scan_time(); refresh_scan(priv, 1, false); - cfg80211_scan_done(priv->pstrScanReq, false); + cfg80211_scan_done(priv->pstrScanReq, &info); priv->bCfgScanning = false; priv->pstrScanReq = NULL; } diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c index a6e6fb9f42e1..f46dfe6b24e8 100644 --- a/drivers/staging/wlan-ng/cfg80211.c +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -338,6 +338,8 @@ static int prism2_scan(struct wiphy *wiphy, struct p80211msg_dot11req_scan msg1; struct p80211msg_dot11req_scan_results msg2; struct cfg80211_bss *bss; + struct cfg80211_scan_info info = {}; + int result; int err = 0; int numbss = 0; @@ -440,7 +442,8 @@ static int prism2_scan(struct wiphy *wiphy, err = prism2_result2err(msg2.resultcode.data); exit: - cfg80211_scan_done(request, err ? 1 : 0); + info.aborted = !!(err); + cfg80211_scan_done(request, &info); priv->scan_request = NULL; return err; } diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index fa4f0f793817..e2658e392a1f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1423,6 +1423,21 @@ struct cfg80211_ssid { u8 ssid_len; }; +/** + * struct cfg80211_scan_info - information about completed scan + * @scan_start_tsf: scan start time in terms of the TSF of the BSS that the + * wireless device that requested the scan is connected to. If this + * information is not available, this field is left zero. + * @tsf_bssid: the BSSID according to which %scan_start_tsf is set. + * @aborted: set to true if the scan was aborted for any reason, + * userspace will be notified of that + */ +struct cfg80211_scan_info { + u64 scan_start_tsf; + u8 tsf_bssid[ETH_ALEN] __aligned(2); + bool aborted; +}; + /** * struct cfg80211_scan_request - scan request description * @@ -1433,12 +1448,17 @@ struct cfg80211_ssid { * @scan_width: channel width for scanning * @ie: optional information element(s) to add into Probe Request or %NULL * @ie_len: length of ie in octets + * @duration: how long to listen on each channel, in TUs. If + * %duration_mandatory is not set, this is the maximum dwell time and + * the actual dwell time may be shorter. + * @duration_mandatory: if set, the scan duration must be as specified by the + * %duration field. * @flags: bit field of flags controlling operation * @rates: bitmap of rates to advertise for each band * @wiphy: the wiphy this was for * @scan_start: time (in jiffies) when the scan started * @wdev: the wireless device to scan for - * @aborted: (internal) scan request was notified as aborted + * @info: (internal) information about completed scan * @notified: (internal) scan request was notified as done or aborted * @no_cck: used to send probe requests at non CCK rate in 2GHz band * @mac_addr: MAC address used with randomisation @@ -1454,6 +1474,8 @@ struct cfg80211_scan_request { enum nl80211_bss_scan_width scan_width; const u8 *ie; size_t ie_len; + u16 duration; + bool duration_mandatory; u32 flags; u32 rates[NUM_NL80211_BANDS]; @@ -1467,7 +1489,8 @@ struct cfg80211_scan_request { /* internal */ struct wiphy *wiphy; unsigned long scan_start; - bool aborted, notified; + struct cfg80211_scan_info info; + bool notified; bool no_cck; /* keep last */ @@ -1600,12 +1623,19 @@ enum cfg80211_signal_type { * buffered on the device) and be accurate to about 10ms. * If the frame isn't buffered, just passing the return value of * ktime_get_boot_ns() is likely appropriate. + * @parent_tsf: the time at the start of reception of the first octet of the + * timestamp field of the frame. The time is the TSF of the BSS specified + * by %parent_bssid. + * @parent_bssid: the BSS according to which %parent_tsf is set. This is set to + * the BSS that requested the scan in which the beacon/probe was received. */ struct cfg80211_inform_bss { struct ieee80211_channel *chan; enum nl80211_bss_scan_width scan_width; s32 signal; u64 boottime_ns; + u64 parent_tsf; + u8 parent_bssid[ETH_ALEN] __aligned(2); }; /** @@ -4067,10 +4097,10 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator); * cfg80211_scan_done - notify that scan finished * * @request: the corresponding scan request - * @aborted: set to true if the scan was aborted for any reason, - * userspace will be notified of that + * @info: information about the completed scan */ -void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted); +void cfg80211_scan_done(struct cfg80211_scan_request *request, + struct cfg80211_scan_info *info); /** * cfg80211_sched_scan_results - notify that new scan results are available diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 1d7da7888dcf..b39ccab45333 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1848,6 +1848,22 @@ enum nl80211_commands { * to turn that feature off set an invalid mac address * (e.g. FF:FF:FF:FF:FF:FF) * + * @NL80211_ATTR_SCAN_START_TIME_TSF: The time at which the scan was actually + * started (u64). The time is the TSF of the BSS the interface that + * requested the scan is connected to (if available, otherwise this + * attribute must not be included). + * @NL80211_ATTR_SCAN_START_TIME_TSF_BSSID: The BSS according to which + * %NL80211_ATTR_SCAN_START_TIME_TSF is set. + * @NL80211_ATTR_MEASUREMENT_DURATION: measurement duration in TUs (u16). If + * %NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY is not set, this is the + * maximum measurement duration allowed. This attribute is used with + * measurement requests. It can also be used with %NL80211_CMD_TRIGGER_SCAN + * if the scan is used for beacon report radio measurement. + * @NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY: flag attribute that indicates + * that the duration specified with %NL80211_ATTR_MEASUREMENT_DURATION is + * mandatory. If this flag is not set, the duration is the maximum duration + * and the actual measurement duration may be shorter. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2235,6 +2251,11 @@ enum nl80211_attrs { NL80211_ATTR_MU_MIMO_GROUP_DATA, NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR, + NL80211_ATTR_SCAN_START_TIME_TSF, + NL80211_ATTR_SCAN_START_TIME_TSF_BSSID, + NL80211_ATTR_MEASUREMENT_DURATION, + NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3496,6 +3517,12 @@ enum nl80211_bss_scan_width { * was last updated by a received frame. The value is expected to be * accurate to about 10ms. (u64, nanoseconds) * @NL80211_BSS_PAD: attribute used for padding for 64-bit alignment + * @NL80211_BSS_PARENT_TSF: the time at the start of reception of the first + * octet of the timestamp field of the last beacon/probe received for + * this BSS. The time is the TSF of the BSS specified by + * @NL80211_BSS_PARENT_BSSID. (u64). + * @NL80211_BSS_PARENT_BSSID: the BSS according to which @NL80211_BSS_PARENT_TSF + * is set. * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ @@ -3517,6 +3544,8 @@ enum nl80211_bss { NL80211_BSS_PRESP_DATA, NL80211_BSS_LAST_SEEN_BOOTTIME, NL80211_BSS_PAD, + NL80211_BSS_PARENT_TSF, + NL80211_BSS_PARENT_BSSID, /* keep last */ __NL80211_BSS_AFTER_LAST, @@ -4507,6 +4536,16 @@ enum nl80211_feature_flags { * %NL80211_ATTR_MU_MIMO_GROUP_DATA attribute, * or can be configured to follow a station by configuring the * %NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR attribute. + * @NL80211_EXT_FEATURE_SCAN_START_TIME: This driver includes the actual + * time the scan started in scan results event. The time is the TSF of + * the BSS that the interface that requested the scan is connected to + * (if available). + * @NL80211_EXT_FEATURE_BSS_PARENT_TSF: Per BSS, this driver reports the + * time the last beacon/probe was received. The time is the TSF of the + * BSS that the interface that requested the scan is connected to + * (if available). + * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of + * channel dwell time. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -4515,6 +4554,9 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_VHT_IBSS, NL80211_EXT_FEATURE_RRM, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER, + NL80211_EXT_FEATURE_SCAN_START_TIME, + NL80211_EXT_FEATURE_BSS_PARENT_TSF, + NL80211_EXT_FEATURE_SET_SCAN_DWELL, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index f9648ef9e31f..4ec1c52a1549 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -353,8 +353,13 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) scan_req = rcu_dereference_protected(local->scan_req, lockdep_is_held(&local->mtx)); - if (scan_req != local->int_scan_req) - cfg80211_scan_done(scan_req, aborted); + if (scan_req != local->int_scan_req) { + struct cfg80211_scan_info info = { + .aborted = aborted, + }; + + cfg80211_scan_done(scan_req, &info); + } RCU_INIT_POINTER(local->scan_req, NULL); scan_sdata = rcu_dereference_protected(local->scan_sdata, diff --git a/net/wireless/core.c b/net/wireless/core.c index 39d9abd309ea..7645e97362c0 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -220,7 +220,7 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, if (rdev->scan_req && rdev->scan_req->wdev == wdev) { if (WARN_ON(!rdev->scan_req->notified)) - rdev->scan_req->aborted = true; + rdev->scan_req->info.aborted = true; ___cfg80211_scan_done(rdev, false); } } @@ -1087,7 +1087,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, cfg80211_update_iface_num(rdev, wdev->iftype, -1); if (rdev->scan_req && rdev->scan_req->wdev == wdev) { if (WARN_ON(!rdev->scan_req->notified)) - rdev->scan_req->aborted = true; + rdev->scan_req->info.aborted = true; ___cfg80211_scan_done(rdev, false); } diff --git a/net/wireless/core.h b/net/wireless/core.h index a4d547f99f8d..eee91443924d 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -141,6 +141,18 @@ struct cfg80211_internal_bss { unsigned long refcount; atomic_t hold; + /* time at the start of the reception of the first octet of the + * timestamp field of the last beacon/probe received for this BSS. + * The time is the TSF of the BSS specified by %parent_bssid. + */ + u64 parent_tsf; + + /* the BSS according to which %parent_tsf is set. This is set to + * the BSS that the interface that requested the scan was connected to + * when the beacon/probe was received. + */ + u8 parent_bssid[ETH_ALEN] __aligned(2); + /* must be last because of priv member */ struct cfg80211_bss pub; }; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 447026f8cc76..c53b5462ed00 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6223,6 +6223,19 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) } } + if (info->attrs[NL80211_ATTR_MEASUREMENT_DURATION]) { + if (!wiphy_ext_feature_isset(wiphy, + NL80211_EXT_FEATURE_SET_SCAN_DWELL)) { + err = -EOPNOTSUPP; + goto out_free; + } + + request->duration = + nla_get_u16(info->attrs[NL80211_ATTR_MEASUREMENT_DURATION]); + request->duration_mandatory = + nla_get_flag(info->attrs[NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY]); + } + if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) { request->flags = nla_get_u32( info->attrs[NL80211_ATTR_SCAN_FLAGS]); @@ -7056,6 +7069,13 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, jiffies_to_msecs(jiffies - intbss->ts))) goto nla_put_failure; + if (intbss->parent_tsf && + (nla_put_u64_64bit(msg, NL80211_BSS_PARENT_TSF, + intbss->parent_tsf, NL80211_BSS_PAD) || + nla_put(msg, NL80211_BSS_PARENT_BSSID, ETH_ALEN, + intbss->parent_bssid))) + goto nla_put_failure; + if (intbss->ts_boottime && nla_put_u64_64bit(msg, NL80211_BSS_LAST_SEEN_BOOTTIME, intbss->ts_boottime, NL80211_BSS_PAD)) @@ -11829,6 +11849,13 @@ static int nl80211_add_scan_req(struct sk_buff *msg, nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, req->flags)) goto nla_put_failure; + if (req->info.scan_start_tsf && + (nla_put_u64_64bit(msg, NL80211_ATTR_SCAN_START_TIME_TSF, + req->info.scan_start_tsf, NL80211_BSS_PAD) || + nla_put(msg, NL80211_ATTR_SCAN_START_TIME_TSF_BSSID, ETH_ALEN, + req->info.tsf_bssid))) + goto nla_put_failure; + return 0; nla_put_failure: return -ENOBUFS; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index ef2955c89a00..0358e12be54b 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -3,6 +3,7 @@ * * Copyright 2008 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH + * Copyright 2016 Intel Deutschland GmbH */ #include #include @@ -194,7 +195,7 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, if (wdev->netdev) cfg80211_sme_scan_done(wdev->netdev); - if (!request->aborted && + if (!request->info.aborted && request->flags & NL80211_SCAN_FLAG_FLUSH) { /* flush entries from previous scans */ spin_lock_bh(&rdev->bss_lock); @@ -202,10 +203,10 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, spin_unlock_bh(&rdev->bss_lock); } - msg = nl80211_build_scan_msg(rdev, wdev, request->aborted); + msg = nl80211_build_scan_msg(rdev, wdev, request->info.aborted); #ifdef CONFIG_CFG80211_WEXT - if (wdev->netdev && !request->aborted) { + if (wdev->netdev && !request->info.aborted) { memset(&wrqu, 0, sizeof(wrqu)); wireless_send_event(wdev->netdev, SIOCGIWSCAN, &wrqu, NULL); @@ -236,12 +237,13 @@ void __cfg80211_scan_done(struct work_struct *wk) rtnl_unlock(); } -void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) +void cfg80211_scan_done(struct cfg80211_scan_request *request, + struct cfg80211_scan_info *info) { - trace_cfg80211_scan_done(request, aborted); + trace_cfg80211_scan_done(request, info); WARN_ON(request != wiphy_to_rdev(request->wiphy)->scan_req); - request->aborted = aborted; + request->info = *info; request->notified = true; queue_work(cfg80211_wq, &wiphy_to_rdev(request->wiphy)->scan_done_wk); } @@ -843,6 +845,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, found->pub.capability = tmp->pub.capability; found->ts = tmp->ts; found->ts_boottime = tmp->ts_boottime; + found->parent_tsf = tmp->parent_tsf; + ether_addr_copy(found->parent_bssid, tmp->parent_bssid); } else { struct cfg80211_internal_bss *new; struct cfg80211_internal_bss *hidden; @@ -1086,6 +1090,8 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy, 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); tmp.ts_boottime = data->boottime_ns; + tmp.parent_tsf = data->parent_tsf; + ether_addr_copy(tmp.parent_bssid, data->parent_bssid); signal_valid = abs(data->chan->center_freq - channel->center_freq) <= wiphy->max_adj_channel_rssi_comp; diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 3c1091ae6c36..72b5255cefe2 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2642,8 +2642,9 @@ TRACE_EVENT(cfg80211_tdls_oper_request, ); TRACE_EVENT(cfg80211_scan_done, - TP_PROTO(struct cfg80211_scan_request *request, bool aborted), - TP_ARGS(request, aborted), + TP_PROTO(struct cfg80211_scan_request *request, + struct cfg80211_scan_info *info), + TP_ARGS(request, info), TP_STRUCT__entry( __field(u32, n_channels) __dynamic_array(u8, ie, request ? request->ie_len : 0) @@ -2652,6 +2653,8 @@ TRACE_EVENT(cfg80211_scan_done, MAC_ENTRY(wiphy_mac) __field(bool, no_cck) __field(bool, aborted) + __field(u64, scan_start_tsf) + MAC_ENTRY(tsf_bssid) ), TP_fast_assign( if (request) { @@ -2666,9 +2669,16 @@ TRACE_EVENT(cfg80211_scan_done, request->wiphy->perm_addr); __entry->no_cck = request->no_cck; } - __entry->aborted = aborted; + if (info) { + __entry->aborted = info->aborted; + __entry->scan_start_tsf = info->scan_start_tsf; + MAC_ASSIGN(tsf_bssid, info->tsf_bssid); + } ), - TP_printk("aborted: %s", BOOL_TO_STR(__entry->aborted)) + TP_printk("aborted: %s, scan start (TSF): %llu, tsf_bssid: " MAC_PR_FMT, + BOOL_TO_STR(__entry->aborted), + (unsigned long long)__entry->scan_start_tsf, + MAC_PR_ARG(tsf_bssid)) ); DEFINE_EVENT(wiphy_only_evt, cfg80211_sched_scan_results, @@ -2721,6 +2731,8 @@ TRACE_EVENT(cfg80211_inform_bss_frame, __dynamic_array(u8, mgmt, len) __field(s32, signal) __field(u64, ts_boottime) + __field(u64, parent_tsf) + MAC_ENTRY(parent_bssid) ), TP_fast_assign( WIPHY_ASSIGN; @@ -2730,10 +2742,15 @@ TRACE_EVENT(cfg80211_inform_bss_frame, memcpy(__get_dynamic_array(mgmt), mgmt, len); __entry->signal = data->signal; __entry->ts_boottime = data->boottime_ns; + __entry->parent_tsf = data->parent_tsf; + MAC_ASSIGN(parent_bssid, data->parent_bssid); ), - TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "(scan_width: %d) signal: %d, tsb:%llu", - WIPHY_PR_ARG, CHAN_PR_ARG, __entry->scan_width, - __entry->signal, (unsigned long long)__entry->ts_boottime) + TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT + "(scan_width: %d) signal: %d, tsb:%llu, detect_tsf:%llu, tsf_bssid: " + MAC_PR_FMT, WIPHY_PR_ARG, CHAN_PR_ARG, __entry->scan_width, + __entry->signal, (unsigned long long)__entry->ts_boottime, + (unsigned long long)__entry->parent_tsf, + MAC_PR_ARG(parent_bssid)) ); DECLARE_EVENT_CLASS(cfg80211_bss_evt, From 7947d3e075cde1a18e538f2dafbc850aa356ff79 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Tue, 5 Jul 2016 15:23:12 +0300 Subject: [PATCH 13/16] mac80211: Add support for beacon report radio measurement Add the following to support beacon report radio measurement with the measurement mode field set to passive or active: 1. Propagate the required scan duration to the device 2. Report the scan start time (in terms of TSF) 3. Report each BSS's detection time (also in terms of TSF) TSF times refer to the BSS that the interface that requested the scan is connected to. Signed-off-by: Assaf Krauss Signed-off-by: Avraham Stern [changed ath9k/10k, at76c59x-usb, iwlegacy, wl1251 and wlcore to match the new API] Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath10k/mac.c | 14 ++++--- drivers/net/wireless/ath/ath9k/channel.c | 5 ++- drivers/net/wireless/atmel/at76c50x-usb.c | 5 ++- drivers/net/wireless/intel/iwlegacy/common.c | 6 ++- drivers/net/wireless/intel/iwlwifi/dvm/scan.c | 6 ++- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 37 ++++++++++++---- drivers/net/wireless/mac80211_hwsim.c | 11 ++++- drivers/net/wireless/st/cw1200/scan.c | 6 ++- drivers/net/wireless/ti/wl1251/event.c | 6 ++- drivers/net/wireless/ti/wl1251/main.c | 6 ++- drivers/net/wireless/ti/wlcore/main.c | 11 ++++- drivers/net/wireless/ti/wlcore/scan.c | 5 ++- include/net/mac80211.h | 5 ++- net/mac80211/ieee80211_i.h | 1 + net/mac80211/scan.c | 42 +++++++++++++++---- 15 files changed, 131 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index d4b7a168f7c0..ebc12c521fe0 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3858,12 +3858,16 @@ void __ath10k_scan_finish(struct ath10k *ar) break; case ATH10K_SCAN_RUNNING: case ATH10K_SCAN_ABORTING: - if (!ar->scan.is_roc) - ieee80211_scan_completed(ar->hw, - (ar->scan.state == - ATH10K_SCAN_ABORTING)); - else if (ar->scan.roc_notify) + if (!ar->scan.is_roc) { + struct cfg80211_scan_info info = { + .aborted = (ar->scan.state == + ATH10K_SCAN_ABORTING), + }; + + ieee80211_scan_completed(ar->hw, &info); + } else if (ar->scan.roc_notify) { ieee80211_remain_on_channel_expired(ar->hw); + } /* fall through */ case ATH10K_SCAN_STARTING: ar->scan.state = ATH10K_SCAN_IDLE; diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index e56bafcf5864..57e26a640477 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -960,6 +960,9 @@ void ath_roc_complete(struct ath_softc *sc, enum ath_roc_complete_reason reason) void ath_scan_complete(struct ath_softc *sc, bool abort) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct cfg80211_scan_info info = { + .aborted = abort, + }; if (abort) ath_dbg(common, CHAN_CTX, "HW scan aborted\n"); @@ -969,7 +972,7 @@ void ath_scan_complete(struct ath_softc *sc, bool abort) sc->offchannel.scan_req = NULL; sc->offchannel.scan_vif = NULL; sc->offchannel.state = ATH_OFFCHANNEL_IDLE; - ieee80211_scan_completed(sc->hw, abort); + ieee80211_scan_completed(sc->hw, &info); clear_bit(ATH_OP_SCANNING, &common->op_flags); spin_lock_bh(&sc->chan_lock); if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c index 7c108047fb46..0e180677c7fc 100644 --- a/drivers/net/wireless/atmel/at76c50x-usb.c +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -1922,6 +1922,9 @@ static void at76_dwork_hw_scan(struct work_struct *work) { struct at76_priv *priv = container_of(work, struct at76_priv, dwork_hw_scan.work); + struct cfg80211_scan_info info = { + .aborted = false, + }; int ret; if (priv->device_unplugged) @@ -1948,7 +1951,7 @@ static void at76_dwork_hw_scan(struct work_struct *work) mutex_unlock(&priv->mtx); - ieee80211_scan_completed(priv->hw, false); + ieee80211_scan_completed(priv->hw, &info); ieee80211_wake_queues(priv->hw); } diff --git a/drivers/net/wireless/intel/iwlegacy/common.c b/drivers/net/wireless/intel/iwlegacy/common.c index eb24b9241bb2..140b6ea8f7cc 100644 --- a/drivers/net/wireless/intel/iwlegacy/common.c +++ b/drivers/net/wireless/intel/iwlegacy/common.c @@ -1305,10 +1305,14 @@ il_send_scan_abort(struct il_priv *il) static void il_complete_scan(struct il_priv *il, bool aborted) { + struct cfg80211_scan_info info = { + .aborted = aborted, + }; + /* check if scan was requested from mac80211 */ if (il->scan_request) { D_SCAN("Complete scan in mac80211\n"); - ieee80211_scan_completed(il->hw, aborted); + ieee80211_scan_completed(il->hw, &info); } il->scan_vif = NULL; diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/scan.c b/drivers/net/wireless/intel/iwlwifi/dvm/scan.c index d01766f16175..17e6a32384d3 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/scan.c @@ -94,10 +94,14 @@ static int iwl_send_scan_abort(struct iwl_priv *priv) static void iwl_complete_scan(struct iwl_priv *priv, bool aborted) { + struct cfg80211_scan_info info = { + .aborted = aborted, + }; + /* check if scan was requested from mac80211 */ if (priv->scan_request) { IWL_DEBUG_SCAN(priv, "Complete scan in mac80211\n"); - ieee80211_scan_completed(priv->hw, aborted); + ieee80211_scan_completed(priv->hw, &info); } priv->scan_type = IWL_SCAN_NORMAL; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index e78fc567ff7d..1cac10c5d818 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -391,13 +391,16 @@ void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm, ieee80211_sched_scan_stopped(mvm->hw); mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED; } else if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) { + struct cfg80211_scan_info info = { + .aborted = aborted, + }; + IWL_DEBUG_SCAN(mvm, "Regular scan %s, EBS status %s (FW)\n", aborted ? "aborted" : "completed", iwl_mvm_ebs_status_str(scan_notif->ebs_status)); mvm->scan_status &= ~IWL_MVM_SCAN_REGULAR; - ieee80211_scan_completed(mvm->hw, - scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED); + ieee80211_scan_completed(mvm->hw, &info); iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); del_timer(&mvm->scan_timer); } else { @@ -1430,7 +1433,11 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, /* if the scan is already stopping, we don't need to notify mac80211 */ if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_REGULAR) { - ieee80211_scan_completed(mvm->hw, aborted); + struct cfg80211_scan_info info = { + .aborted = aborted, + }; + + ieee80211_scan_completed(mvm->hw, &info); iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); del_timer(&mvm->scan_timer); } else if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_SCHED) { @@ -1564,7 +1571,11 @@ void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm) uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_REGULAR); if (uid >= 0) { - ieee80211_scan_completed(mvm->hw, true); + struct cfg80211_scan_info info = { + .aborted = true, + }; + + ieee80211_scan_completed(mvm->hw, &info); mvm->scan_uid_status[uid] = 0; } uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_SCHED); @@ -1585,8 +1596,13 @@ void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm) mvm->scan_uid_status[i] = 0; } } else { - if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) - ieee80211_scan_completed(mvm->hw, true); + if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + ieee80211_scan_completed(mvm->hw, &info); + } /* Sched scan will be restarted by mac80211 in * restart_hw, so do not report if FW is about to be @@ -1629,8 +1645,13 @@ out: */ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); del_timer(&mvm->scan_timer); - if (notify) - ieee80211_scan_completed(mvm->hw, true); + if (notify) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + ieee80211_scan_completed(mvm->hw, &info); + } } else if (notify) { ieee80211_sched_scan_stopped(mvm->hw); mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 337794f9506e..8c35ac838fce 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1941,8 +1941,12 @@ static void hw_scan_work(struct work_struct *work) mutex_lock(&hwsim->mutex); if (hwsim->scan_chan_idx >= req->n_channels) { + struct cfg80211_scan_info info = { + .aborted = false, + }; + wiphy_debug(hwsim->hw->wiphy, "hw scan complete\n"); - ieee80211_scan_completed(hwsim->hw, false); + ieee80211_scan_completed(hwsim->hw, &info); hwsim->hw_scan_request = NULL; hwsim->hw_scan_vif = NULL; hwsim->tmp_chan = NULL; @@ -2027,13 +2031,16 @@ static void mac80211_hwsim_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct mac80211_hwsim_data *hwsim = hw->priv; + struct cfg80211_scan_info info = { + .aborted = true, + }; wiphy_debug(hw->wiphy, "hwsim cancel_hw_scan\n"); cancel_delayed_work_sync(&hwsim->hw_scan); mutex_lock(&hwsim->mutex); - ieee80211_scan_completed(hwsim->hw, true); + ieee80211_scan_completed(hwsim->hw, &info); hwsim->tmp_chan = NULL; hwsim->hw_scan_request = NULL; hwsim->hw_scan_vif = NULL; diff --git a/drivers/net/wireless/st/cw1200/scan.c b/drivers/net/wireless/st/cw1200/scan.c index 983788156bb0..0a0ff7e31f5b 100644 --- a/drivers/net/wireless/st/cw1200/scan.c +++ b/drivers/net/wireless/st/cw1200/scan.c @@ -167,6 +167,10 @@ void cw1200_scan_work(struct work_struct *work) } if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) { + struct cfg80211_scan_info info = { + .aborted = priv->scan.status ? 1 : 0, + }; + if (priv->scan.output_power != priv->output_power) wsm_set_output_power(priv, priv->output_power * 10); if (priv->join_status == CW1200_JOIN_STATUS_STA && @@ -188,7 +192,7 @@ void cw1200_scan_work(struct work_struct *work) cw1200_scan_restart_delayed(priv); wsm_unlock_tx(priv); mutex_unlock(&priv->conf_mutex); - ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0); + ieee80211_scan_completed(priv->hw, &info); up(&priv->scan.lock); return; } else { diff --git a/drivers/net/wireless/ti/wl1251/event.c b/drivers/net/wireless/ti/wl1251/event.c index c98630394a1a..d0593bc1f1a9 100644 --- a/drivers/net/wireless/ti/wl1251/event.c +++ b/drivers/net/wireless/ti/wl1251/event.c @@ -36,7 +36,11 @@ static int wl1251_event_scan_complete(struct wl1251 *wl, mbox->scheduled_scan_channels); if (wl->scanning) { - ieee80211_scan_completed(wl->hw, false); + struct cfg80211_scan_info info = { + .aborted = false, + }; + + ieee80211_scan_completed(wl->hw, &info); wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan completed"); wl->scanning = false; if (wl->hw->conf.flags & IEEE80211_CONF_IDLE) diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 56384a4e2a35..bbf7604889b7 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -448,7 +448,11 @@ static void wl1251_op_stop(struct ieee80211_hw *hw) WARN_ON(wl->state != WL1251_STATE_ON); if (wl->scanning) { - ieee80211_scan_completed(wl->hw, true); + struct cfg80211_scan_info info = { + .aborted = true, + }; + + ieee80211_scan_completed(wl->hw, &info); wl->scanning = false; } diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 10fd24c28ece..69267d592504 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2615,6 +2615,10 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, if (wl->scan.state != WL1271_SCAN_STATE_IDLE && wl->scan_wlvif == wlvif) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + /* * Rearm the tx watchdog just before idling scan. This * prevents just-finished scans from triggering the watchdog @@ -2625,7 +2629,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan_wlvif = NULL; wl->scan.req = NULL; - ieee80211_scan_completed(wl->hw, true); + ieee80211_scan_completed(wl->hw, &info); } if (wl->sched_vif == wlvif) @@ -3649,6 +3653,9 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw, { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + struct cfg80211_scan_info info = { + .aborted = true, + }; int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 cancel hw scan"); @@ -3681,7 +3688,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw, memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan_wlvif = NULL; wl->scan.req = NULL; - ieee80211_scan_completed(wl->hw, true); + ieee80211_scan_completed(wl->hw, &info); out_sleep: wl1271_ps_elp_sleep(wl); diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index 23343643207a..5612f5916b4e 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -36,6 +36,9 @@ void wl1271_scan_complete_work(struct work_struct *work) struct delayed_work *dwork; struct wl1271 *wl; struct wl12xx_vif *wlvif; + struct cfg80211_scan_info info = { + .aborted = false, + }; int ret; dwork = to_delayed_work(work); @@ -82,7 +85,7 @@ void wl1271_scan_complete_work(struct work_struct *work) wlcore_cmd_regdomain_config_locked(wl); - ieee80211_scan_completed(wl->hw, false); + ieee80211_scan_completed(wl->hw, &info); out: mutex_unlock(&wl->mutex); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a52009ffc19f..b4faadbb4e01 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4697,9 +4697,10 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw); * any context, including hardirq context. * * @hw: the hardware that finished the scan - * @aborted: set to true if scan was aborted + * @info: information about the completed scan */ -void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted); +void ieee80211_scan_completed(struct ieee80211_hw *hw, + struct cfg80211_scan_info *info); /** * ieee80211_sched_scan_results - got results from scheduled scan diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 54edfb6fc1d1..f56d342c31b8 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1250,6 +1250,7 @@ struct ieee80211_local { int scan_channel_idx; int scan_ies_len; int hw_scan_ies_bufsize; + struct cfg80211_scan_info scan_info; struct work_struct sched_scan_stopped_work; struct ieee80211_sub_if_data __rcu *sched_scan_sdata; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 4ec1c52a1549..8d4a9cd8a39a 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -7,6 +7,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007, Michael Wu * Copyright 2013-2015 Intel Mobile Communications GmbH + * Copyright 2016 Intel Deutschland GmbH * * 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 @@ -70,6 +71,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, .boottime_ns = rx_status->boottime_ns, }; bool signal_valid; + struct ieee80211_sub_if_data *scan_sdata; if (ieee80211_hw_check(&local->hw, SIGNAL_DBM)) bss_meta.signal = rx_status->signal * 100; @@ -83,6 +85,20 @@ ieee80211_bss_info_update(struct ieee80211_local *local, bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_10; bss_meta.chan = channel; + + rcu_read_lock(); + scan_sdata = rcu_dereference(local->scan_sdata); + if (scan_sdata && scan_sdata->vif.type == NL80211_IFTYPE_STATION && + scan_sdata->vif.bss_conf.assoc && + ieee80211_have_rx_timestamp(rx_status)) { + bss_meta.parent_tsf = + ieee80211_calculate_rx_timestamp(local, rx_status, + len + FCS_LEN, 24); + ether_addr_copy(bss_meta.parent_bssid, + scan_sdata->vif.bss_conf.bssid); + } + rcu_read_unlock(); + cbss = cfg80211_inform_bss_frame_data(local->hw.wiphy, &bss_meta, mgmt, len, GFP_ATOMIC); if (!cbss) @@ -345,6 +361,11 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (rc == 0) return; + + /* HW scan failed and is going to be reported as done, so clear + * old scan info. + */ + memset(&local->scan_info, 0, sizeof(local->scan_info)); } kfree(local->hw_scan_req); @@ -354,11 +375,8 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) lockdep_is_held(&local->mtx)); if (scan_req != local->int_scan_req) { - struct cfg80211_scan_info info = { - .aborted = aborted, - }; - - cfg80211_scan_done(scan_req, &info); + local->scan_info.aborted = aborted; + cfg80211_scan_done(scan_req, &local->scan_info); } RCU_INIT_POINTER(local->scan_req, NULL); @@ -396,15 +414,19 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) ieee80211_start_next_roc(local); } -void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) +void ieee80211_scan_completed(struct ieee80211_hw *hw, + struct cfg80211_scan_info *info) { struct ieee80211_local *local = hw_to_local(hw); - trace_api_scan_completed(local, aborted); + trace_api_scan_completed(local, info); set_bit(SCAN_COMPLETED, &local->scanning); - if (aborted) + if (info->aborted) set_bit(SCAN_ABORTED, &local->scanning); + + memcpy(&local->scan_info, info, sizeof(*info)); + ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0); } EXPORT_SYMBOL(ieee80211_scan_completed); @@ -571,6 +593,9 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, local->hw_scan_req->req.ie = ies; local->hw_scan_req->req.flags = req->flags; eth_broadcast_addr(local->hw_scan_req->req.bssid); + local->hw_scan_req->req.duration = req->duration; + local->hw_scan_req->req.duration_mandatory = + req->duration_mandatory; local->hw_scan_band = 0; @@ -1078,6 +1103,7 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) */ cancel_delayed_work(&local->scan_work); /* and clean up */ + memset(&local->scan_info, 0, sizeof(local->scan_info)); __ieee80211_scan_completed(&local->hw, true); out: mutex_unlock(&local->mtx); From 7d10f6b179bc82e6633a4521a4cd69ad6846723e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 5 Jul 2016 15:23:13 +0300 Subject: [PATCH 14/16] mac80211: report failure to start (partial) scan as scan abort Rather than reporting the scan as having completed, report it as being aborted. Signed-off-by: Johannes Berg --- net/mac80211/scan.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 8d4a9cd8a39a..070b40f15850 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -362,10 +362,11 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (rc == 0) return; - /* HW scan failed and is going to be reported as done, so clear - * old scan info. + /* HW scan failed and is going to be reported as aborted, + * so clear old scan info. */ memset(&local->scan_info, 0, sizeof(local->scan_info)); + aborted = true; } kfree(local->hw_scan_req); From 92b3a28a2b4b2fb777b64f0891a4d3388f617c15 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 5 Jul 2016 15:23:14 +0300 Subject: [PATCH 15/16] mac80211: parse wide bandwidth channel switch IE with workaround Continuing the workaround implemented in commit 23665aaf9170 ("mac80211: Interoperability workaround for 80+80 and 160 MHz channels") use the same code to parse the Wide Bandwidth Channel Switch element by converting to VHT Operation element since the spec also just refers to that for parsing semantics, particularly with the workaround. While at it, remove some dead code - the IEEE80211_STA_DISABLE_40MHZ flag can never be set at this point since it's checked earlier and the wide_bw_chansw_ie pointer is set to NULL if it's set. Signed-off-by: Johannes Berg --- net/mac80211/spectmgmt.c | 43 +++++++++++++++------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 2ddc661f0988..97f4c9d6b54c 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -129,42 +129,31 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, } if (wide_bw_chansw_ie) { - new_vht_chandef.chan = new_chan; - new_vht_chandef.center_freq1 = - ieee80211_channel_to_frequency( + struct ieee80211_vht_operation vht_oper = { + .chan_width = + wide_bw_chansw_ie->new_channel_width, + .center_freq_seg1_idx = wide_bw_chansw_ie->new_center_freq_seg0, - new_band); + .center_freq_seg2_idx = + wide_bw_chansw_ie->new_center_freq_seg1, + /* .basic_mcs_set doesn't matter */ + }; - switch (wide_bw_chansw_ie->new_channel_width) { - default: - /* hmmm, ignore VHT and use HT if present */ - case IEEE80211_VHT_CHANWIDTH_USE_HT: + /* default, for the case of IEEE80211_VHT_CHANWIDTH_USE_HT, + * to the previously parsed chandef + */ + new_vht_chandef = csa_ie->chandef; + + /* ignore if parsing fails */ + if (!ieee80211_chandef_vht_oper(&vht_oper, &new_vht_chandef)) new_vht_chandef.chan = NULL; - break; - case IEEE80211_VHT_CHANWIDTH_80MHZ: - new_vht_chandef.width = NL80211_CHAN_WIDTH_80; - break; - case IEEE80211_VHT_CHANWIDTH_160MHZ: - new_vht_chandef.width = NL80211_CHAN_WIDTH_160; - break; - case IEEE80211_VHT_CHANWIDTH_80P80MHZ: - /* field is otherwise reserved */ - new_vht_chandef.center_freq2 = - ieee80211_channel_to_frequency( - wide_bw_chansw_ie->new_center_freq_seg1, - new_band); - new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80; - break; - } + if (sta_flags & IEEE80211_STA_DISABLE_80P80MHZ && new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80) ieee80211_chandef_downgrade(&new_vht_chandef); if (sta_flags & IEEE80211_STA_DISABLE_160MHZ && new_vht_chandef.width == NL80211_CHAN_WIDTH_160) ieee80211_chandef_downgrade(&new_vht_chandef); - if (sta_flags & IEEE80211_STA_DISABLE_40MHZ && - new_vht_chandef.width > NL80211_CHAN_WIDTH_20) - ieee80211_chandef_downgrade(&new_vht_chandef); } /* if VHT data is there validate & use it */ From 7d27a0ba7adc8ef30c2aae7592fce4c162aee4df Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Fri, 1 Jul 2016 10:19:34 +0900 Subject: [PATCH 16/16] cfg80211: Add mesh peer AID setting API Previously, mesh power management functionality works only with kernel MPM. Because user space MPM did not report mesh peer AID to kernel, the kernel could not identify the bit in TIM element. So this patch adds mesh peer AID setting API. Signed-off-by: Masashi Honma Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 5 +++++ net/mac80211/cfg.c | 1 + net/wireless/nl80211.c | 6 ++++++ 4 files changed, 14 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e2658e392a1f..9c23f4d33e06 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -780,6 +780,7 @@ enum station_parameters_apply_mask { * (bitmask of BIT(NL80211_STA_FLAG_...)) * @listen_interval: listen interval or -1 for no change * @aid: AID or zero for no change + * @peer_aid: mesh peer AID or zero for no change * @plink_action: plink action to take * @plink_state: set the peer link state for a station * @ht_capa: HT capabilities of station @@ -811,6 +812,7 @@ struct station_parameters { u32 sta_modify_mask; int listen_interval; u16 aid; + u16 peer_aid; u8 supported_rates_len; u8 plink_action; u8 plink_state; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index b39ccab45333..220694151434 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1864,6 +1864,9 @@ enum nl80211_commands { * mandatory. If this flag is not set, the duration is the maximum duration * and the actual measurement duration may be shorter. * + * @NL80211_ATTR_MESH_PEER_AID: Association ID for the mesh peer (u16). This is + * used to pull the stored data for mesh peer in power save state. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2256,6 +2259,8 @@ enum nl80211_attrs { NL80211_ATTR_MEASUREMENT_DURATION, NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY, + NL80211_ATTR_MESH_PEER_AID, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 0c12e4001f19..47e99ab8d97a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -997,6 +997,7 @@ static void sta_apply_mesh_params(struct ieee80211_local *local, if (sta->mesh->plink_state != NL80211_PLINK_ESTAB) changed = mesh_plink_inc_estab_count(sdata); sta->mesh->plink_state = params->plink_state; + sta->mesh->aid = params->peer_aid; ieee80211_mps_sta_status_update(sta); changed |= ieee80211_mps_set_sta_local_pm(sta, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c53b5462ed00..5782f718d567 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4446,6 +4446,12 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); if (params.plink_state >= NUM_NL80211_PLINK_STATES) return -EINVAL; + if (info->attrs[NL80211_ATTR_MESH_PEER_AID]) { + params.peer_aid = nla_get_u16( + info->attrs[NL80211_ATTR_MESH_PEER_AID]); + if (params.peer_aid > IEEE80211_MAX_AID) + return -EINVAL; + } params.sta_modify_mask |= STATION_PARAM_APPLY_PLINK_STATE; }