From 3eb0928fc3e4b4702462164b399acddb7f0f09fd Mon Sep 17 00:00:00 2001 From: "Manoharan, Rajkumar" Date: Tue, 14 Feb 2017 12:27:16 -0800 Subject: [PATCH 01/48] mac80211: use DECLARE_EWMA for mesh_fail_avg As moving average is not considering fractional part, it will get stuck at the same level after certain state. For example, with current values, it can get stuck at 96. Fortunately the current threshold 95%, but if it were increased to 96 or more mesh paths would never be deactivated. Fix failure average movement by using EWMA helpers, which does take into account fractional parts. Signed-off-by: Rajkumar Manoharan [johannes: pick a larger EWMA factor for more precision with the limited range that we will feed into it, adjust to new API] Signed-off-by: Johannes Berg --- net/mac80211/mesh_hwmp.c | 21 +++++++++++++++------ net/mac80211/mesh_pathtbl.c | 3 +++ net/mac80211/sta_info.h | 5 ++++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index b747c9645e432..d07ee3ca07ee9 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -307,10 +307,11 @@ void ieee80211s_update_metric(struct ieee80211_local *local, failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK); - /* moving average, scaled to 100 */ - sta->mesh->fail_avg = - ((80 * sta->mesh->fail_avg + 5) / 100 + 20 * failed); - if (sta->mesh->fail_avg > 95) + /* moving average, scaled to 100. + * feed failure as 100 and success as 0 + */ + ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, failed * 100); + if (ewma_mesh_fail_avg_read(&sta->mesh->fail_avg) > 95) mesh_plink_broken(sta); } @@ -325,6 +326,8 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, int rate, err; u32 tx_time, estimated_retx; u64 result; + unsigned long fail_avg = + ewma_mesh_fail_avg_read(&sta->mesh->fail_avg); /* Try to get rate based on HW/SW RC algorithm. * Rate is returned in units of Kbps, correct this @@ -336,7 +339,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, if (rate) { err = 0; } else { - if (sta->mesh->fail_avg >= 100) + if (fail_avg >= 100) return MAX_METRIC; sta_set_rate_info_tx(sta, &sta->tx_stats.last_rate, &rinfo); @@ -344,7 +347,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, if (WARN_ON(!rate)) return MAX_METRIC; - err = (sta->mesh->fail_avg << ARITH_SHIFT) / 100; + err = (fail_avg << ARITH_SHIFT) / 100; } /* bitrate is in units of 100 Kbps, while we need rate in units of @@ -484,6 +487,9 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, ? mpath->exp_time : exp_time; mesh_path_activate(mpath); spin_unlock_bh(&mpath->state_lock); + ewma_mesh_fail_avg_init(&sta->mesh->fail_avg); + /* init it at a low value - 0 start is tricky */ + ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, 1); mesh_path_tx_pending(mpath); /* draft says preq_id should be saved to, but there does * not seem to be any use for it, skipping by now @@ -522,6 +528,9 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, ? mpath->exp_time : exp_time; mesh_path_activate(mpath); spin_unlock_bh(&mpath->state_lock); + ewma_mesh_fail_avg_init(&sta->mesh->fail_avg); + /* init it at a low value - 0 start is tricky */ + ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, 1); mesh_path_tx_pending(mpath); } else spin_unlock_bh(&mpath->state_lock); diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index f0e6175a9821f..98a3b1c0c3385 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -829,6 +829,9 @@ void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop) mpath->flags = MESH_PATH_FIXED | MESH_PATH_SN_VALID; mesh_path_activate(mpath); spin_unlock_bh(&mpath->state_lock); + ewma_mesh_fail_avg_init(&next_hop->mesh->fail_avg); + /* init it at a low value - 0 start is tricky */ + ewma_mesh_fail_avg_add(&next_hop->mesh->fail_avg, 1); mesh_path_tx_pending(mpath); } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index e65cda34d2bc0..cc413f52108e1 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -324,6 +324,9 @@ struct ieee80211_fast_rx { struct rcu_head rcu_head; }; +/* we use only values in the range 0-100, so pick a large precision */ +DECLARE_EWMA(mesh_fail_avg, 20, 8) + /** * struct mesh_sta - mesh STA information * @plink_lock: serialize access to plink fields @@ -369,7 +372,7 @@ struct mesh_sta { enum nl80211_mesh_power_mode nonpeer_pm; /* moving percentage of failed MSDUs */ - unsigned int fail_avg; + struct ewma_mesh_fail_avg fail_avg; }; DECLARE_EWMA(signal, 10, 8) From 4a4b8169501b18c3450ac735a7e277b24886a651 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Fri, 10 Feb 2017 10:02:31 +0100 Subject: [PATCH 02/48] cfg80211: Accept multiple RSSI thresholds for CQM Change the SET CQM command's RSSI threshold attribute to accept any number of thresholds as a sorted array. The API should be backwards compatible so that if one s32 threshold value is passed, the old mechanism is enabled. The netlink event generated is the same in both cases. cfg80211 handles an arbitrary number of RSSI thresholds but drivers have to provide a method (set_cqm_rssi_range_config) that configures a range set by a high and a low value. Drivers have to call back when the RSSI goes out of that range and there's no additional event for each time the range is reconfigured as there was with the current one-threshold API. This method doesn't have a hysteresis parameter because there's no benefit to the cfg80211 code from having the hysteresis be handled by hardware/driver in terms of the number of wakeups. At the same time it would likely be less consistent between drivers if offloaded or done in the drivers. Signed-off-by: Andrew Zaborowski Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 13 ++++ include/uapi/linux/nl80211.h | 9 ++- net/wireless/core.c | 9 +++ net/wireless/core.h | 9 +++ net/wireless/nl80211.c | 138 +++++++++++++++++++++++++++++++---- net/wireless/rdev-ops.h | 12 +++ net/wireless/trace.h | 22 ++++++ 7 files changed, 198 insertions(+), 14 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ead1aa6d003ef..ffc08687b31da 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2712,6 +2712,11 @@ struct cfg80211_nan_func { * the current level is above/below the configured threshold; this may * need some care when the configuration is changed (without first being * disabled.) + * @set_cqm_rssi_range_config: Configure two RSSI thresholds in the + * connection quality monitor. An event is to be sent only when the + * signal level is found to be outside the two values. The driver should + * set %NL80211_EXT_FEATURE_CQM_RSSI_LIST if this method is implemented. + * If it is provided then there's no point providing @set_cqm_rssi_config. * @set_cqm_txe_config: Configure connection quality monitor TX error * thresholds. * @sched_scan_start: Tell the driver to start a scheduled scan. @@ -3001,6 +3006,10 @@ struct cfg80211_ops { struct net_device *dev, s32 rssi_thold, u32 rssi_hyst); + int (*set_cqm_rssi_range_config)(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_low, s32 rssi_high); + int (*set_cqm_txe_config)(struct wiphy *wiphy, struct net_device *dev, u32 rate, u32 pkts, u32 intvl); @@ -3871,6 +3880,7 @@ void wiphy_free(struct wiphy *wiphy); struct cfg80211_conn; struct cfg80211_internal_bss; struct cfg80211_cached_keys; +struct cfg80211_cqm_config; /** * struct wireless_dev - wireless device state @@ -3934,6 +3944,7 @@ struct cfg80211_cached_keys; * @event_list: (private) list for internal event processing * @event_lock: (private) lock for event list * @owner_nlportid: (private) owner socket port ID + * @cqm_config: (private) nl80211 RSSI monitor state */ struct wireless_dev { struct wiphy *wiphy; @@ -4002,6 +4013,8 @@ struct wireless_dev { bool prev_bssid_valid; } wext; #endif + + struct cfg80211_cqm_config *cqm_config; }; static inline u8 *wdev_address(struct wireless_dev *wdev) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 5ed257c4cd4ea..9a499b15cfbc7 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3942,7 +3942,10 @@ enum nl80211_ps_state { * @__NL80211_ATTR_CQM_INVALID: invalid * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies * the threshold for the RSSI level at which an event will be sent. Zero - * to disable. + * to disable. Alternatively, if %NL80211_EXT_FEATURE_CQM_RSSI_LIST is + * set, multiple values can be supplied as a low-to-high sorted array of + * threshold values in dBm. Events will be sent when the RSSI value + * crosses any of the thresholds. * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies * the minimum amount the RSSI level must change after an event before a * new event may be issued (to reduce effects of RSSI oscillation). @@ -4753,6 +4756,9 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan * for reporting BSSs with better RSSI than the current connected BSS * (%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI). + * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the + * %NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more + * RSSI threshold values to monitor rather than exactly one threshold. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -4771,6 +4777,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA, NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED, NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI, + NL80211_EXT_FEATURE_CQM_RSSI_LIST, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/core.c b/net/wireless/core.c index e55e05bc48053..04143df20f7ff 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -954,6 +954,12 @@ void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) } EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); +void cfg80211_cqm_config_free(struct wireless_dev *wdev) +{ + kfree(wdev->cqm_config); + wdev->cqm_config = NULL; +} + void cfg80211_unregister_wdev(struct wireless_dev *wdev) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); @@ -980,6 +986,8 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev) WARN_ON_ONCE(1); break; } + + cfg80211_cqm_config_free(wdev); } EXPORT_SYMBOL(cfg80211_unregister_wdev); @@ -1234,6 +1242,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, kzfree(wdev->wext.keys); #endif flush_work(&wdev->disconnect_wk); + cfg80211_cqm_config_free(wdev); } /* * synchronise (so that we won't find this netdev diff --git a/net/wireless/core.h b/net/wireless/core.h index 58ca206982fea..efa690a7ef8d2 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -272,6 +272,13 @@ struct cfg80211_iface_destroy { u32 nlportid; }; +struct cfg80211_cqm_config { + u32 rssi_hyst; + s32 last_rssi_event_value; + int n_rssi_thresholds; + s32 rssi_thresholds[0]; +}; + void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev); /* free object */ @@ -512,4 +519,6 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, #define CFG80211_DEV_WARN_ON(cond) ({bool __r = (cond); __r; }) #endif +void cfg80211_cqm_config_free(struct wireless_dev *wdev); + #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d7f8be4e321a3..d516527fcb8ea 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -9473,7 +9473,7 @@ static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) static const struct nla_policy nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { - [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_BINARY }, [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 }, @@ -9502,28 +9502,123 @@ static int nl80211_set_cqm_txe(struct genl_info *info, return rdev_set_cqm_txe_config(rdev, dev, rate, pkts, intvl); } +static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + s32 last, low, high; + u32 hyst; + int i, n; + int err; + + /* RSSI reporting disabled? */ + if (!wdev->cqm_config) + return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0); + + /* + * Obtain current RSSI value if possible, if not and no RSSI threshold + * event has been received yet, we should receive an event after a + * connection is established and enough beacons received to calculate + * the average. + */ + if (!wdev->cqm_config->last_rssi_event_value && wdev->current_bss && + rdev->ops->get_station) { + struct station_info sinfo; + u8 *mac_addr; + + mac_addr = wdev->current_bss->pub.bssid; + + err = rdev_get_station(rdev, dev, mac_addr, &sinfo); + if (err) + return err; + + if (sinfo.filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG)) + wdev->cqm_config->last_rssi_event_value = + (s8) sinfo.rx_beacon_signal_avg; + } + + last = wdev->cqm_config->last_rssi_event_value; + hyst = wdev->cqm_config->rssi_hyst; + n = wdev->cqm_config->n_rssi_thresholds; + + for (i = 0; i < n; i++) + if (last < wdev->cqm_config->rssi_thresholds[i]) + break; + + low = i > 0 ? + (wdev->cqm_config->rssi_thresholds[i - 1] - hyst) : S32_MIN; + high = i < n ? + (wdev->cqm_config->rssi_thresholds[i] + hyst - 1) : S32_MAX; + + return rdev_set_cqm_rssi_range_config(rdev, dev, low, high); +} + static int nl80211_set_cqm_rssi(struct genl_info *info, - s32 threshold, u32 hysteresis) + const s32 *thresholds, int n_thresholds, + u32 hysteresis) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; + int i, err; + s32 prev = S32_MIN; - if (threshold > 0) - return -EINVAL; - - /* disabling - hysteresis should also be zero then */ - if (threshold == 0) - hysteresis = 0; + /* Check all values negative and sorted */ + for (i = 0; i < n_thresholds; i++) { + if (thresholds[i] > 0 || thresholds[i] <= prev) + return -EINVAL; - if (!rdev->ops->set_cqm_rssi_config) - return -EOPNOTSUPP; + prev = thresholds[i]; + } if (wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; - return rdev_set_cqm_rssi_config(rdev, dev, threshold, hysteresis); + wdev_lock(wdev); + cfg80211_cqm_config_free(wdev); + wdev_unlock(wdev); + + if (n_thresholds <= 1 && rdev->ops->set_cqm_rssi_config) { + if (n_thresholds == 0 || thresholds[0] == 0) /* Disabling */ + return rdev_set_cqm_rssi_config(rdev, dev, 0, 0); + + return rdev_set_cqm_rssi_config(rdev, dev, + thresholds[0], hysteresis); + } + + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_CQM_RSSI_LIST)) + return -EOPNOTSUPP; + + if (n_thresholds == 1 && thresholds[0] == 0) /* Disabling */ + n_thresholds = 0; + + wdev_lock(wdev); + if (n_thresholds) { + struct cfg80211_cqm_config *cqm_config; + + cqm_config = kzalloc(sizeof(struct cfg80211_cqm_config) + + n_thresholds * sizeof(s32), GFP_KERNEL); + if (!cqm_config) { + err = -ENOMEM; + goto unlock; + } + + cqm_config->rssi_hyst = hysteresis; + cqm_config->n_rssi_thresholds = n_thresholds; + memcpy(cqm_config->rssi_thresholds, thresholds, + n_thresholds * sizeof(s32)); + + wdev->cqm_config = cqm_config; + } + + err = cfg80211_cqm_rssi_update(rdev, dev); + +unlock: + wdev_unlock(wdev); + + return err; } static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) @@ -9543,10 +9638,16 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] && attrs[NL80211_ATTR_CQM_RSSI_HYST]) { - s32 threshold = nla_get_s32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); + const s32 *thresholds = + nla_data(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); + int len = nla_len(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); u32 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); - return nl80211_set_cqm_rssi(info, threshold, hysteresis); + if (len % 4) + return -EINVAL; + + return nl80211_set_cqm_rssi(info, thresholds, len / 4, + hysteresis); } if (attrs[NL80211_ATTR_CQM_TXE_RATE] && @@ -13983,6 +14084,8 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev, s32 rssi_level, gfp_t gfp) { struct sk_buff *msg; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); trace_cfg80211_cqm_rssi_notify(dev, rssi_event, rssi_level); @@ -13990,6 +14093,15 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev, rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH)) return; + if (wdev->cqm_config) { + wdev->cqm_config->last_rssi_event_value = rssi_level; + + cfg80211_cqm_rssi_update(rdev, dev); + + if (rssi_level == 0) + rssi_level = wdev->cqm_config->last_rssi_event_value; + } + msg = cfg80211_prepare_cqm(dev, NULL, gfp); if (!msg) return; diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 2f425075ada8e..f2baf59210911 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -749,6 +749,18 @@ rdev_set_cqm_rssi_config(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_set_cqm_rssi_range_config(struct cfg80211_registered_device *rdev, + struct net_device *dev, s32 low, s32 high) +{ + int ret; + trace_rdev_set_cqm_rssi_range_config(&rdev->wiphy, dev, low, high); + ret = rdev->ops->set_cqm_rssi_range_config(&rdev->wiphy, dev, + low, high); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline int rdev_set_cqm_txe_config(struct cfg80211_registered_device *rdev, struct net_device *dev, u32 rate, u32 pkts, u32 intvl) diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 776e80cef9b4e..fd55786f04620 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1322,6 +1322,28 @@ TRACE_EVENT(rdev_set_cqm_rssi_config, __entry->rssi_thold, __entry->rssi_hyst) ); +TRACE_EVENT(rdev_set_cqm_rssi_range_config, + TP_PROTO(struct wiphy *wiphy, + struct net_device *netdev, s32 low, s32 high), + TP_ARGS(wiphy, netdev, low, high), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(s32, rssi_low) + __field(s32, rssi_high) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->rssi_low = low; + __entry->rssi_high = high; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT + ", range: %d - %d ", + WIPHY_PR_ARG, NETDEV_PR_ARG, + __entry->rssi_low, __entry->rssi_high) +); + TRACE_EVENT(rdev_set_cqm_txe_config, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 rate, u32 pkts, u32 intvl), From 2c3c5f8c0cfa8e88a4c34d7651b5712c558ab9b7 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Fri, 10 Feb 2017 04:50:22 +0100 Subject: [PATCH 03/48] mac80211: Add set_cqm_rssi_range_config Support .set_cqm_rssi_range_config if the beacons are available for processing in mac80211. There's no reason that this couldn't be offloaded by mac80211-based drivers but there's no driver method for that added in this patch. Signed-off-by: Andrew Zaborowski Signed-off-by: Johannes Berg --- include/net/mac80211.h | 6 ++++++ net/mac80211/cfg.c | 28 ++++++++++++++++++++++++++++ net/mac80211/mlme.c | 24 ++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a3bab3c5ecfb3..1a26a375feb86 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -501,6 +501,10 @@ struct ieee80211_mu_group_data { * implies disabled. As with the cfg80211 callback, a change here should * cause an event to be sent indicating where the current value is in * relation to the newly configured threshold. + * @cqm_rssi_low: Connection quality monitor RSSI lower threshold, a zero value + * implies disabled. This is an alternative mechanism to the single + * threshold event and can't be enabled simultaneously with it. + * @cqm_rssi_high: Connection quality monitor RSSI upper threshold. * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis * @arp_addr_list: List of IPv4 addresses for hardware ARP filtering. The * may filter ARP queries targeted for other addresses than listed here. @@ -553,6 +557,8 @@ struct ieee80211_bss_conf { u16 ht_operation_mode; s32 cqm_rssi_thold; u32 cqm_rssi_hyst; + s32 cqm_rssi_low; + s32 cqm_rssi_high; struct cfg80211_chan_def chandef; struct ieee80211_mu_group_data mu_group; __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN]; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index ac879bb17870d..9c7490cb2243d 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2630,6 +2630,33 @@ static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy, bss_conf->cqm_rssi_thold = rssi_thold; bss_conf->cqm_rssi_hyst = rssi_hyst; + bss_conf->cqm_rssi_low = 0; + bss_conf->cqm_rssi_high = 0; + sdata->u.mgd.last_cqm_event_signal = 0; + + /* tell the driver upon association, unless already associated */ + if (sdata->u.mgd.associated && + sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI) + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM); + + return 0; +} + +static int ieee80211_set_cqm_rssi_range_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_low, s32 rssi_high) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_vif *vif = &sdata->vif; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + + if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER) + return -EOPNOTSUPP; + + bss_conf->cqm_rssi_low = rssi_low; + bss_conf->cqm_rssi_high = rssi_high; + bss_conf->cqm_rssi_thold = 0; + bss_conf->cqm_rssi_hyst = 0; sdata->u.mgd.last_cqm_event_signal = 0; /* tell the driver upon association, unless already associated */ @@ -3639,6 +3666,7 @@ const struct cfg80211_ops mac80211_config_ops = { .mgmt_tx = ieee80211_mgmt_tx, .mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait, .set_cqm_rssi_config = ieee80211_set_cqm_rssi_config, + .set_cqm_rssi_range_config = ieee80211_set_cqm_rssi_range_config, .mgmt_frame_register = ieee80211_mgmt_frame_register, .set_antenna = ieee80211_set_antenna, .get_antenna = ieee80211_get_antenna, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6e90301154d5a..23986934d7af2 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3430,6 +3430,30 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, } } + if (bss_conf->cqm_rssi_low && + ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) { + int sig = -ewma_beacon_signal_read(&ifmgd->ave_beacon_signal); + int last_event = ifmgd->last_cqm_event_signal; + int low = bss_conf->cqm_rssi_low; + int high = bss_conf->cqm_rssi_high; + + if (sig < low && + (last_event == 0 || last_event >= low)) { + ifmgd->last_cqm_event_signal = sig; + ieee80211_cqm_rssi_notify( + &sdata->vif, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + sig, GFP_KERNEL); + } else if (sig > high && + (last_event == 0 || last_event <= high)) { + ifmgd->last_cqm_event_signal = sig; + ieee80211_cqm_rssi_notify( + &sdata->vif, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + sig, GFP_KERNEL); + } + } + if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) { mlme_dbg_ratelimited(sdata, "cancelling AP probe due to a received beacon\n"); From ae44b502669d0cd1f167cdb48994292aa20fd3dd Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Fri, 10 Feb 2017 04:50:23 +0100 Subject: [PATCH 04/48] wireless: Set NL80211_EXT_FEATURE_CQM_RSSI_LIST in multiple drivers Set the NL80211_EXT_FEATURE_CQM_RSSI_LIST wiphy extended feature wholesale in all mac80211-based drivers that do not set the IEEE80211_VIF_BEACON_FILTER flags on their interfaces. mac80211 will be processing supplied RSSI values in ieee80211_rx_mgmt_beacon and will detect when the thresholds set by ieee80211_set_cqm_rssi_range_config are crossed. Remaining (few) drivers need code to enable the firmware to monitor the thresholds. This is mostly only compile-tested. Signed-off-by: Andrew Zaborowski Signed-off-by: Johannes Berg --- drivers/net/wireless/admtek/adm8211.c | 2 ++ drivers/net/wireless/ath/ar5523/ar5523.c | 2 ++ drivers/net/wireless/ath/ath10k/mac.c | 2 ++ drivers/net/wireless/ath/ath5k/base.c | 2 ++ drivers/net/wireless/ath/ath9k/htc_drv_init.c | 2 ++ drivers/net/wireless/ath/ath9k/init.c | 2 ++ drivers/net/wireless/ath/carl9170/main.c | 2 ++ drivers/net/wireless/ath/wcn36xx/main.c | 3 +++ drivers/net/wireless/atmel/at76c50x-usb.c | 2 ++ drivers/net/wireless/broadcom/b43/main.c | 2 ++ drivers/net/wireless/broadcom/b43legacy/main.c | 2 ++ drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c | 2 ++ drivers/net/wireless/intel/iwlegacy/3945-mac.c | 2 ++ drivers/net/wireless/intel/iwlegacy/4965-mac.c | 2 ++ drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c | 2 ++ drivers/net/wireless/mac80211_hwsim.c | 2 ++ drivers/net/wireless/marvell/libertas_tf/main.c | 2 ++ drivers/net/wireless/marvell/mwl8k.c | 2 ++ drivers/net/wireless/mediatek/mt7601u/init.c | 2 ++ drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 3 +++ drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c | 2 ++ drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c | 2 ++ drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 2 ++ drivers/net/wireless/rsi/rsi_91x_mac80211.c | 2 ++ drivers/net/wireless/zydas/zd1211rw/zd_mac.c | 2 ++ 25 files changed, 52 insertions(+) diff --git a/drivers/net/wireless/admtek/adm8211.c b/drivers/net/wireless/admtek/adm8211.c index 098c814e22c8b..ed626f568b580 100644 --- a/drivers/net/wireless/admtek/adm8211.c +++ b/drivers/net/wireless/admtek/adm8211.c @@ -1917,6 +1917,8 @@ static int adm8211_probe(struct pci_dev *pdev, dev->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band; + wiphy_ext_feature_set(dev->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + err = ieee80211_register_hw(dev); if (err) { printk(KERN_ERR "%s (adm8211): Cannot register device\n", diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 7a60d2e652dad..f2f4ccfdf8da0 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -1689,6 +1689,8 @@ static int ar5523_probe(struct usb_interface *intf, if (error) goto out_cancel_rx_cmd; + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + usb_set_intfdata(intf, hw); error = ieee80211_register_hw(hw); diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 3029f257a19a5..abc291de1d31a 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -8248,6 +8248,8 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->cipher_suites = cipher_suites; ar->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + ret = ieee80211_register_hw(ar->hw); if (ret) { ath10k_err(ar, "failed to register ieee80211: %d\n", ret); diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index d98fd421c7ec5..92ece64fd4559 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2564,6 +2564,8 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) hw->extra_tx_headroom = 2; + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + /* * Mark the device as detached to avoid processing * interrupts until setup is complete. diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index b65c1b661adeb..defacc6c9c995 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -780,6 +780,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, } SET_IEEE80211_PERM_ADDR(hw, common->macaddr); + + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); } static int ath9k_init_firmware_version(struct ath9k_htc_priv *priv) diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index fa4b3cc1ba22c..fd9a61834c17f 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -955,6 +955,8 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) ath9k_cmn_reload_chainmask(ah); SET_IEEE80211_PERM_ADDR(hw, common->macaddr); + + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); } int ath9k_init_device(u16 devid, struct ath_softc *sc, diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index ffb22a04beeb7..988c8857d78c9 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1874,6 +1874,8 @@ void *carl9170_alloc(size_t priv_size) for (i = 0; i < ARRAY_SIZE(ar->noise); i++) ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */ + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + return ar; err_nomem: diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 7a0c2e7da7f64..cee4f655bf36d 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -1112,6 +1112,9 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta); wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif); + wiphy_ext_feature_set(wcn->hw->wiphy, + NL80211_EXT_FEATURE_CQM_RSSI_LIST); + return ret; } diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c index 0e180677c7fc5..09defbcedd5e4 100644 --- a/drivers/net/wireless/atmel/at76c50x-usb.c +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -2377,6 +2377,8 @@ static int at76_init_new_device(struct at76_priv *priv, wiphy->hw_version = priv->board_type; + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + ret = ieee80211_register_hw(priv->hw); if (ret) { printk(KERN_ERR "cannot register mac80211 hw (status %d)!\n", diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index 52f3541ecbcfe..d23aac7503d33 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -5598,6 +5598,8 @@ static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev) hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + wl->hw_registred = false; hw->max_rates = 2; SET_IEEE80211_DEV(hw, dev->dev); diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c index cdafebb9c936b..f1e3dad576292 100644 --- a/drivers/net/wireless/broadcom/b43legacy/main.c +++ b/drivers/net/wireless/broadcom/b43legacy/main.c @@ -3850,6 +3850,8 @@ static int b43legacy_wireless_init(struct ssb_device *dev) else SET_IEEE80211_PERM_ADDR(hw, sprom->il0mac); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + /* Get and initialize struct b43legacy_wl */ wl = hw_to_b43legacy_wl(hw); memset(wl, 0, sizeof(*wl)); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c index 7c2a9a9bc372c..ddfdfe177e245 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c @@ -1082,6 +1082,8 @@ static int ieee_hw_init(struct ieee80211_hw *hw) * hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; */ + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + hw->rate_control_algorithm = "minstrel_ht"; hw->sta_data_size = 0; diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c index e8e65115feba4..38bf403bb1e1a 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c @@ -3592,6 +3592,8 @@ il3945_setup_mac(struct il_priv *il) il_leds_init(il); + wiphy_ext_feature_set(il->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + ret = ieee80211_register_hw(il->hw); if (ret) { IL_ERR("Failed to register hw (error %d)\n", ret); diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index 2781f5728d076..7eda525e3f4fe 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -5799,6 +5799,8 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length) il_leds_init(il); + wiphy_ext_feature_set(il->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + ret = ieee80211_register_hw(il->hw); if (ret) { IL_ERR("Failed to register hw (error %d)\n", ret); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index 2a04d0cd71aef..e3cab60ddf0fb 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -213,6 +213,8 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, iwl_leds_init(priv); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + ret = ieee80211_register_hw(priv->hw); if (ret) { IWL_ERR(priv, "Failed to register hw (error %d)\n", ret); diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 50c219fb1a52b..307a53a7da23e 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2645,6 +2645,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, if (param->no_vif) ieee80211_hw_set(hw, NO_AUTO_VIF); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + err = ieee80211_register_hw(hw); if (err < 0) { printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n", diff --git a/drivers/net/wireless/marvell/libertas_tf/main.c b/drivers/net/wireless/marvell/libertas_tf/main.c index 54e426c1e405f..d803331179892 100644 --- a/drivers/net/wireless/marvell/libertas_tf/main.c +++ b/drivers/net/wireless/marvell/libertas_tf/main.c @@ -641,6 +641,8 @@ struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev) BIT(NL80211_IFTYPE_ADHOC); skb_queue_head_init(&priv->bc_ps_buf); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + SET_IEEE80211_DEV(hw, dmdev); INIT_WORK(&priv->cmd_work, lbtf_cmd_work); diff --git a/drivers/net/wireless/marvell/mwl8k.c b/drivers/net/wireless/marvell/mwl8k.c index b1b400b59d864..c295a4c6e5cd5 100644 --- a/drivers/net/wireless/marvell/mwl8k.c +++ b/drivers/net/wireless/marvell/mwl8k.c @@ -6144,6 +6144,8 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv) if (priv->sta_macids_supported || priv->device_info->fw_image_sta) hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + rc = ieee80211_register_hw(hw); if (rc) { wiphy_err(hw->wiphy, "Cannot register device\n"); diff --git a/drivers/net/wireless/mediatek/mt7601u/init.c b/drivers/net/wireless/mediatek/mt7601u/init.c index a6e9017662269..d3b611aaf0618 100644 --- a/drivers/net/wireless/mediatek/mt7601u/init.c +++ b/drivers/net/wireless/mediatek/mt7601u/init.c @@ -615,6 +615,8 @@ int mt7601u_register_device(struct mt7601u_dev *dev) wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + ret = mt76_init_sband_2g(dev); if (ret) return ret; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index dd6678109b7e4..57e6af9a44a19 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -1384,6 +1384,9 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + wiphy_ext_feature_set(rt2x00dev->hw->wiphy, + NL80211_EXT_FEATURE_CQM_RSSI_LIST); + /* * Initialize ieee80211 structure. */ diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c index e895a84481da0..e387dec82d3d2 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c @@ -1877,6 +1877,8 @@ static int rtl8180_probe(struct pci_dev *pdev, else ieee80211_hw_set(dev, SIGNAL_UNSPEC); + wiphy_ext_feature_set(dev->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + rtl8180_eeprom_read(priv); switch (priv->rf_type) { diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c index 231f84db9ab06..274ad6d34d687 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c @@ -1609,6 +1609,8 @@ static int rtl8187_probe(struct usb_interface *intf, dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) ; + wiphy_ext_feature_set(dev->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b) printk(KERN_INFO "rtl8187: inconsistency between id with OEM" " info!\n"); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index e544dd1d618c3..9b4a9a00be641 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -6135,6 +6135,8 @@ static int rtl8xxxu_probe(struct usb_interface *interface, ieee80211_hw_set(hw, HAS_RATE_CONTROL); ieee80211_hw_set(hw, AMPDU_AGGREGATION); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + ret = ieee80211_register_hw(priv->hw); if (ret) { dev_err(&udev->dev, "%s: Failed to register: %i\n", diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index e3216473aecb7..021e5ac5f1073 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -1261,6 +1261,8 @@ int rsi_mac80211_attach(struct rsi_common *common) wiphy->reg_notifier = rsi_reg_notify; + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + status = ieee80211_register_hw(hw); if (status) return status; diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c index 3e37a045f7025..fe6517a621b08 100644 --- a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c @@ -1408,6 +1408,8 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf) BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + hw->max_signal = 100; hw->queues = 1; hw->extra_tx_headroom = sizeof(struct zd_ctrlset); From d4f29978675d7d07e7bb9da30ed05ecf588820a0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 13 Feb 2017 20:53:38 +0100 Subject: [PATCH 05/48] cfg80211: combine two nested ifs into a single condition Combine two instances of having two nested if statements into a single one with a combined condition to reduce the indentation. Signed-off-by: Johannes Berg --- net/wireless/core.c | 12 ++++++------ net/wireless/util.c | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index 04143df20f7ff..76e664144c8e4 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1216,12 +1216,12 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, */ if ((wdev->iftype == NL80211_IFTYPE_STATION || wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) && - rdev->ops->set_power_mgmt) - if (rdev_set_power_mgmt(rdev, dev, wdev->ps, - wdev->ps_timeout)) { - /* assume this means it's off */ - wdev->ps = false; - } + rdev->ops->set_power_mgmt && + rdev_set_power_mgmt(rdev, dev, wdev->ps, + wdev->ps_timeout)) { + /* assume this means it's off */ + wdev->ps = false; + } break; case NETDEV_UNREGISTER: /* diff --git a/net/wireless/util.c b/net/wireless/util.c index 68e5f2ecee1aa..daef500a7b64d 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -914,11 +914,11 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev) netdev_err(dev, "failed to set key %d\n", i); continue; } - if (wdev->connect_keys->def == i) - if (rdev_set_default_key(rdev, dev, i, true, true)) { - netdev_err(dev, "failed to set defkey %d\n", i); - continue; - } + if (wdev->connect_keys->def == i && + rdev_set_default_key(rdev, dev, i, true, true)) { + netdev_err(dev, "failed to set defkey %d\n", i); + continue; + } } kzfree(wdev->connect_keys); From f22775ede2eb58ed84b55e30768d041f607a2199 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Mon, 13 Feb 2017 14:37:41 +0200 Subject: [PATCH 06/48] ieee80211: add FT-PSK AKM suite selector Signed-off-by: Avraham Stern Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 0dd9498c694f9..6ea381c98aae5 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2347,6 +2347,7 @@ enum ieee80211_sa_query_action { /* AKM suite selectors */ #define WLAN_AKM_SUITE_8021X SUITE(0x000FAC, 1) #define WLAN_AKM_SUITE_PSK SUITE(0x000FAC, 2) +#define WLAN_AKM_SUITE_FT_PSK SUITE(0x000FAC, 4) #define WLAN_AKM_SUITE_8021X_SHA256 SUITE(0x000FAC, 5) #define WLAN_AKM_SUITE_PSK_SHA256 SUITE(0x000FAC, 6) #define WLAN_AKM_SUITE_TDLS SUITE(0x000FAC, 7) From 68506e9af132a6b5735c1dd4b11240da0cf5eeae Mon Sep 17 00:00:00 2001 From: Arkadiusz Miskiewicz Date: Wed, 15 Feb 2017 14:21:27 +0100 Subject: [PATCH 07/48] mac80211: Print text for disassociation reason MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When disassociation happens only numeric reason is printed in ieee80211_rx_mgmt_disassoc(). Add text variant, too. Signed-off-by: Arkadiusz Miƛkiewicz Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 23986934d7af2..1568a74757bcb 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2797,8 +2797,9 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); - sdata_info(sdata, "disassociated from %pM (Reason: %u)\n", - mgmt->sa, reason_code); + sdata_info(sdata, "disassociated from %pM (Reason: %u=%s)\n", + mgmt->sa, reason_code, + ieee80211_get_reason_code_string(reason_code)); ieee80211_set_disassoc(sdata, 0, 0, false, NULL); From 2fb51c35815dc08638a7d9b1a497a9d7cb4109b8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Feb 2017 15:02:06 +0100 Subject: [PATCH 08/48] ieee80211: rename CCFS1/CCFS2 to CCFS0/CCFS1 This matches the spec, and otherwise things are really confusing with the next patch adding CCFS2. Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/tdls.c | 2 +- include/linux/ieee80211.h | 4 ++-- net/mac80211/spectmgmt.c | 4 ++-- net/mac80211/util.c | 22 ++++++++++----------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c index df9704de07150..5fc8319ed3026 100644 --- a/drivers/net/wireless/marvell/mwifiex/tdls.c +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -349,7 +349,7 @@ static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv, chan_bw = IEEE80211_VHT_CHANWIDTH_USE_HT; break; } - vht_oper->center_freq_seg1_idx = + vht_oper->center_freq_seg0_idx = mwifiex_get_center_freq_index(priv, BAND_AAC, bss_desc->channel, chan_bw); diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 6ea381c98aae5..e167a262d3b09 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1525,14 +1525,14 @@ enum ieee80211_vht_chanwidth { * This structure is the "VHT operation element" as * described in 802.11ac D3.0 8.4.2.161 * @chan_width: Operating channel width + * @center_freq_seg0_idx: center freq segment 0 index * @center_freq_seg1_idx: center freq segment 1 index - * @center_freq_seg2_idx: center freq segment 2 index * @basic_mcs_set: VHT Basic MCS rate set */ struct ieee80211_vht_operation { u8 chan_width; + u8 center_freq_seg0_idx; u8 center_freq_seg1_idx; - u8 center_freq_seg2_idx; __le16 basic_mcs_set; } __packed; diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 97f4c9d6b54ce..0782e486fe893 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -132,9 +132,9 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, struct ieee80211_vht_operation vht_oper = { .chan_width = wide_bw_chansw_ie->new_channel_width, - .center_freq_seg1_idx = + .center_freq_seg0_idx = wide_bw_chansw_ie->new_center_freq_seg0, - .center_freq_seg2_idx = + .center_freq_seg1_idx = wide_bw_chansw_ie->new_center_freq_seg1, /* .basic_mcs_set doesn't matter */ }; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ac59fbd280dff..7a37ce78bb389 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2413,13 +2413,13 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, *pos++ = WLAN_EID_VHT_OPERATION; *pos++ = sizeof(struct ieee80211_vht_operation); vht_oper = (struct ieee80211_vht_operation *)pos; - vht_oper->center_freq_seg1_idx = ieee80211_frequency_to_channel( + vht_oper->center_freq_seg0_idx = ieee80211_frequency_to_channel( chandef->center_freq1); if (chandef->center_freq2) - vht_oper->center_freq_seg2_idx = + vht_oper->center_freq_seg1_idx = ieee80211_frequency_to_channel(chandef->center_freq2); else - vht_oper->center_freq_seg2_idx = 0x00; + vht_oper->center_freq_seg1_idx = 0x00; switch (chandef->width) { case NL80211_CHAN_WIDTH_160: @@ -2428,11 +2428,11 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, * workaround. */ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; - vht_oper->center_freq_seg2_idx = vht_oper->center_freq_seg1_idx; + vht_oper->center_freq_seg1_idx = vht_oper->center_freq_seg0_idx; if (chandef->chan->center_freq < chandef->center_freq1) - vht_oper->center_freq_seg1_idx -= 8; + vht_oper->center_freq_seg0_idx -= 8; else - vht_oper->center_freq_seg1_idx += 8; + vht_oper->center_freq_seg0_idx += 8; break; case NL80211_CHAN_WIDTH_80P80: /* @@ -2491,9 +2491,9 @@ bool ieee80211_chandef_vht_oper(const struct ieee80211_vht_operation *oper, if (!oper) return false; - cf1 = ieee80211_channel_to_frequency(oper->center_freq_seg1_idx, + cf1 = ieee80211_channel_to_frequency(oper->center_freq_seg0_idx, chandef->chan->band); - cf2 = ieee80211_channel_to_frequency(oper->center_freq_seg2_idx, + cf2 = ieee80211_channel_to_frequency(oper->center_freq_seg1_idx, chandef->chan->band); switch (oper->chan_width) { @@ -2503,11 +2503,11 @@ bool ieee80211_chandef_vht_oper(const struct ieee80211_vht_operation *oper, new.width = NL80211_CHAN_WIDTH_80; new.center_freq1 = cf1; /* If needed, adjust based on the newer interop workaround. */ - if (oper->center_freq_seg2_idx) { + if (oper->center_freq_seg1_idx) { unsigned int diff; - diff = abs(oper->center_freq_seg2_idx - - oper->center_freq_seg1_idx); + diff = abs(oper->center_freq_seg1_idx - + oper->center_freq_seg0_idx); if (diff == 8) { new.width = NL80211_CHAN_WIDTH_160; new.center_freq1 = cf2; From a858958b689211dcfe54cdd94c93160d2d659eba Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Feb 2017 15:02:07 +0100 Subject: [PATCH 09/48] mac80211: remove local pointer from rate_ctrl_ref This pointer really isn't needed, so remove it. Signed-off-by: Johannes Berg --- net/mac80211/rate.c | 10 +++++----- net/mac80211/rate.h | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 206698bc93f40..094c15645228e 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -208,7 +208,6 @@ static struct rate_control_ref *rate_control_alloc(const char *name, ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL); if (!ref) return NULL; - ref->local = local; ref->ops = ieee80211_rate_control_ops_get(name); if (!ref->ops) goto free; @@ -229,13 +228,14 @@ static struct rate_control_ref *rate_control_alloc(const char *name, return NULL; } -static void rate_control_free(struct rate_control_ref *ctrl_ref) +static void rate_control_free(struct ieee80211_local *local, + struct rate_control_ref *ctrl_ref) { ctrl_ref->ops->free(ctrl_ref->priv); #ifdef CONFIG_MAC80211_DEBUGFS - debugfs_remove_recursive(ctrl_ref->local->debugfs.rcdir); - ctrl_ref->local->debugfs.rcdir = NULL; + debugfs_remove_recursive(local->debugfs.rcdir); + local->debugfs.rcdir = NULL; #endif kfree(ctrl_ref); @@ -936,6 +936,6 @@ void rate_control_deinitialize(struct ieee80211_local *local) return; local->rate_ctrl = NULL; - rate_control_free(ref); + rate_control_free(local, ref); } diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index 8d3260785b940..d51a1cce4d4a8 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -20,7 +20,6 @@ #include "driver-ops.h" struct rate_control_ref { - struct ieee80211_local *local; const struct rate_control_ops *ops; void *priv; }; From 0c1eca4e2f96000077900e0108fff23994c73486 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Feb 2017 15:02:08 +0100 Subject: [PATCH 10/48] cfg80211: refactor cfg80211_calculate_bitrate() This function contains the HT calculations, which makes no sense - split that out into a separate function. As a side effect, this makes the 60G flag independent from HT_MCS so remove the MCS one from wil6210 (also deleting a duplicate assignment.) Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/wil6210/cfg80211.c | 3 +- net/wireless/util.c | 60 +++++++++++---------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 83155b5ddbfb1..79d107018eac3 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -178,9 +178,8 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, BIT(NL80211_STA_INFO_RX_DROP_MISC) | BIT(NL80211_STA_INFO_TX_FAILED); - sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; + sinfo->txrate.flags = RATE_INFO_FLAGS_60G; sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs); - sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; sinfo->rxrate.mcs = stats->last_mcs_rx; sinfo->rx_bytes = stats->rx_bytes; sinfo->rx_packets = stats->rx_packets; diff --git a/net/wireless/util.c b/net/wireless/util.c index daef500a7b64d..737c9c2c9cc9a 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1097,6 +1097,35 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, return err; } +static u32 cfg80211_calculate_bitrate_ht(struct rate_info *rate) +{ + int modulation, streams, bitrate; + + /* the formula below does only work for MCS values smaller than 32 */ + if (WARN_ON_ONCE(rate->mcs >= 32)) + return 0; + + modulation = rate->mcs & 7; + streams = (rate->mcs >> 3) + 1; + + bitrate = (rate->bw == RATE_INFO_BW_40) ? 13500000 : 6500000; + + if (modulation < 4) + bitrate *= (modulation + 1); + else if (modulation == 4) + bitrate *= (modulation + 2); + else + bitrate *= (modulation + 3); + + bitrate *= streams; + + if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) + bitrate = (bitrate / 9) * 10; + + /* do NOT round down here */ + return (bitrate + 50000) / 100000; +} + static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate) { static const u32 __mcs2bitrate[] = { @@ -1230,39 +1259,14 @@ static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate) u32 cfg80211_calculate_bitrate(struct rate_info *rate) { - int modulation, streams, bitrate; - - if (!(rate->flags & RATE_INFO_FLAGS_MCS) && - !(rate->flags & RATE_INFO_FLAGS_VHT_MCS)) - return rate->legacy; + if (rate->flags & RATE_INFO_FLAGS_MCS) + return cfg80211_calculate_bitrate_ht(rate); if (rate->flags & RATE_INFO_FLAGS_60G) return cfg80211_calculate_bitrate_60g(rate); if (rate->flags & RATE_INFO_FLAGS_VHT_MCS) return cfg80211_calculate_bitrate_vht(rate); - /* the formula below does only work for MCS values smaller than 32 */ - if (WARN_ON_ONCE(rate->mcs >= 32)) - return 0; - - modulation = rate->mcs & 7; - streams = (rate->mcs >> 3) + 1; - - bitrate = (rate->bw == RATE_INFO_BW_40) ? 13500000 : 6500000; - - if (modulation < 4) - bitrate *= (modulation + 1); - else if (modulation == 4) - bitrate *= (modulation + 2); - else - bitrate *= (modulation + 3); - - bitrate *= streams; - - if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) - bitrate = (bitrate / 9) * 10; - - /* do NOT round down here */ - return (bitrate + 50000) / 100000; + return rate->legacy; } EXPORT_SYMBOL(cfg80211_calculate_bitrate); From 7f406cd16a0f0965c761ea02bc1f03154b06bbfb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Feb 2017 15:02:09 +0100 Subject: [PATCH 11/48] mac80211: encode rate type (legacy, HT, VHT) with fewer bits We don't really need three different bits for each, since the types are mutually exclusive. Use just two bits for it. Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 11 ++++++++--- net/mac80211/sta_info.h | 13 +++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 3323a2fb289bd..81ec1f72518d0 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1960,14 +1960,17 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate, rinfo->bw = (rate & STA_STATS_RATE_BW_MASK) >> STA_STATS_RATE_BW_SHIFT; - if (rate & STA_STATS_RATE_VHT) { + switch (rate & STA_STATS_RATE_TYPE_MASK) { + case STA_STATS_RATE_TYPE_VHT: rinfo->flags = RATE_INFO_FLAGS_VHT_MCS; rinfo->mcs = rate & 0xf; rinfo->nss = (rate & 0xf0) >> 4; - } else if (rate & STA_STATS_RATE_HT) { + break; + case STA_STATS_RATE_TYPE_HT: rinfo->flags = RATE_INFO_FLAGS_MCS; rinfo->mcs = rate & 0xff; - } else if (rate & STA_STATS_RATE_LEGACY) { + break; + case STA_STATS_RATE_TYPE_LEGACY: { struct ieee80211_supported_band *sband; u16 brate; unsigned int shift; @@ -1982,6 +1985,8 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate, else shift = 0; rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift); + break; + } } if (rate & STA_STATS_RATE_SGI) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index cc413f52108e1..8949266d7bc37 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -728,9 +728,10 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); unsigned long ieee80211_sta_last_active(struct sta_info *sta); #define STA_STATS_RATE_INVALID 0 -#define STA_STATS_RATE_VHT 0x8000 -#define STA_STATS_RATE_HT 0x4000 -#define STA_STATS_RATE_LEGACY 0x2000 +#define STA_STATS_RATE_TYPE_MASK 0xC000 +#define STA_STATS_RATE_TYPE_LEGACY 0x4000 +#define STA_STATS_RATE_TYPE_HT 0x8000 +#define STA_STATS_RATE_TYPE_VHT 0xC000 #define STA_STATS_RATE_SGI 0x1000 #define STA_STATS_RATE_BW_SHIFT 9 #define STA_STATS_RATE_BW_MASK (0x7 << STA_STATS_RATE_BW_SHIFT) @@ -756,11 +757,11 @@ static inline u16 sta_stats_encode_rate(struct ieee80211_rx_status *s) r |= STA_STATS_RATE_SGI; if (s->flag & RX_FLAG_VHT) - r |= STA_STATS_RATE_VHT | (s->vht_nss << 4); + r |= STA_STATS_RATE_TYPE_VHT | (s->vht_nss << 4); else if (s->flag & RX_FLAG_HT) - r |= STA_STATS_RATE_HT; + r |= STA_STATS_RATE_TYPE_HT; else - r |= STA_STATS_RATE_LEGACY | (s->band << 4); + r |= STA_STATS_RATE_TYPE_LEGACY | (s->band << 4); return r; } From 75b99bc300463e65f87c90425704c2688489f963 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Feb 2017 15:02:10 +0100 Subject: [PATCH 12/48] ieee80211: define HT operation CCFS2 field The Channel Center Frequency Segment 2 field is used in 802.11-2016 for encoding the actual channel position of the 80+80/160 MHz channel, if the max NSS is restricted. This is used for backwards compatibility. Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index e167a262d3b09..22bf0676d9280 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1411,6 +1411,8 @@ struct ieee80211_ht_operation { #define IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED 3 #define IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT 0x0004 #define IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT 0x0010 +#define IEEE80211_HT_OP_MODE_CCFS2_SHIFT 5 +#define IEEE80211_HT_OP_MODE_CCFS2_MASK 0x1fe0 /* for stbc_param */ #define IEEE80211_HT_STBC_PARAM_DUAL_BEACON 0x0040 From fe56c9c17b09769691e8b91747b32aa2555bef35 Mon Sep 17 00:00:00 2001 From: "Manoharan, Rajkumar" Date: Wed, 15 Feb 2017 12:46:50 -0800 Subject: [PATCH 13/48] mac80211: fix mesh fail_avg check Mesh failure average never be more than 100. Only in case of fixed path, average will be more than threshold limit (95%). With recent EWMA changes it may go upto 99 as it is scaled to 100. It make sense to return maximum metric when average is greater than threshold limit. Signed-off-by: Rajkumar Manoharan Signed-off-by: Johannes Berg --- net/mac80211/mesh_hwmp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index d07ee3ca07ee9..4005edd71fe86 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -16,6 +16,7 @@ #define TEST_FRAME_LEN 8192 #define MAX_METRIC 0xffffffff #define ARITH_SHIFT 8 +#define LINK_FAIL_THRESH 95 #define MAX_PREQ_QUEUE_LEN 64 @@ -311,7 +312,8 @@ void ieee80211s_update_metric(struct ieee80211_local *local, * feed failure as 100 and success as 0 */ ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, failed * 100); - if (ewma_mesh_fail_avg_read(&sta->mesh->fail_avg) > 95) + if (ewma_mesh_fail_avg_read(&sta->mesh->fail_avg) > + LINK_FAIL_THRESH) mesh_plink_broken(sta); } @@ -339,7 +341,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, if (rate) { err = 0; } else { - if (fail_avg >= 100) + if (fail_avg > LINK_FAIL_THRESH) return MAX_METRIC; sta_set_rate_info_tx(sta, &sta->tx_stats.last_rate, &rinfo); From f7d3b4f5203832f57ece96300888323fd5a6c6b3 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Thu, 23 Feb 2017 13:02:10 +0100 Subject: [PATCH 14/48] mac80211_hwsim: Make sure NEW_RADIO contains final name ieee80211_alloc_hw_nm will validate the requested name (if any) before creating the new device and may use a name different from the one requested rather than fail. Make sure the HWSIM_CMD_NEW_RADIO event/response generated has the final name or userspace will receive the wrong name. Note that mac80211_hwsim_new_radio may now modify params. A check for duplicate radio name could be added separately. Signed-off-by: Andrew Zaborowski Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 307a53a7da23e..7b9662ae01432 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2438,6 +2438,9 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, goto failed; } + /* ieee80211_alloc_hw_nm may have used a default name */ + param->hwname = wiphy_name(hw->wiphy); + if (info) net = genl_info_net(info); else From 037651950d5800f236407e139572433a53efb798 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 2 Mar 2017 09:39:40 +0100 Subject: [PATCH 15/48] mac80211_hwsim: fix command documentation indentation Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h index 39f22467ca2a2..3f5eda591dba7 100644 --- a/drivers/net/wireless/mac80211_hwsim.h +++ b/drivers/net/wireless/mac80211_hwsim.h @@ -57,12 +57,12 @@ enum hwsim_tx_control_flags { * @HWSIM_CMD_REGISTER: request to register and received all broadcasted * frames by any mac80211_hwsim radio device. * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user - * space, uses: + * space, uses: * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER, * %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE, * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE, %HWSIM_ATTR_FREQ (optional) * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to - * kernel, uses: + * kernel, uses: * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS, * %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE * @HWSIM_CMD_NEW_RADIO: create a new radio with the given parameters, From 85bbd80373a7cc9d06d3ef103b1a93d934a8da43 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 6 Mar 2017 11:56:06 +0200 Subject: [PATCH 16/48] mac80211_hwsim: Add channel 169 (5845 MHz) This channel is defined in the IEEE 802.11 standard and available in number of countries, so extend the mac80211_hwsim channel list to cover channel 169 to enable additional testing. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 7b9662ae01432..8b823c70ab69c 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -350,6 +350,7 @@ static const struct ieee80211_channel hwsim_channels_5ghz[] = { CHAN5G(5785), /* Channel 157 */ CHAN5G(5805), /* Channel 161 */ CHAN5G(5825), /* Channel 165 */ + CHAN5G(5845), /* Channel 169 */ }; static const struct ieee80211_rate hwsim_rates[] = { From f8f118ceaa562d5b49252ecbfd7fe1f704f4e076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Lyson=C4=9Bk?= Date: Fri, 3 Mar 2017 13:45:35 +0100 Subject: [PATCH 17/48] mac80211: Use setup_timer instead of init_timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use setup_timer() and setup_deferrable_timer() to set the data and function timer fields. It makes the code cleaner and will allow for easier change of the timer struct internals. Signed-off-by: Ondƙej Lysoněk Signed-off-by: Jiri Slaby Cc: Johannes Berg Cc: "David S. Miller" Cc: Cc: Signed-off-by: Johannes Berg --- net/mac80211/agg-rx.c | 12 ++++++------ net/mac80211/agg-tx.c | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 4456559cb056d..1b7a4daf283c5 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -357,14 +357,14 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, spin_lock_init(&tid_agg_rx->reorder_lock); /* rx timer */ - tid_agg_rx->session_timer.function = sta_rx_agg_session_timer_expired; - tid_agg_rx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid]; - init_timer_deferrable(&tid_agg_rx->session_timer); + setup_deferrable_timer(&tid_agg_rx->session_timer, + sta_rx_agg_session_timer_expired, + (unsigned long)&sta->timer_to_tid[tid]); /* rx reorder timer */ - tid_agg_rx->reorder_timer.function = sta_rx_agg_reorder_timer_expired; - tid_agg_rx->reorder_timer.data = (unsigned long)&sta->timer_to_tid[tid]; - init_timer(&tid_agg_rx->reorder_timer); + setup_timer(&tid_agg_rx->reorder_timer, + sta_rx_agg_reorder_timer_expired, + (unsigned long)&sta->timer_to_tid[tid]); /* prepare reordering buffer */ tid_agg_rx->reorder_buf = diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 45319cc01121a..60e2a62f7bef2 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -670,14 +670,14 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, tid_tx->timeout = timeout; /* response timer */ - tid_tx->addba_resp_timer.function = sta_addba_resp_timer_expired; - tid_tx->addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[tid]; - init_timer(&tid_tx->addba_resp_timer); + setup_timer(&tid_tx->addba_resp_timer, + sta_addba_resp_timer_expired, + (unsigned long)&sta->timer_to_tid[tid]); /* tx timer */ - tid_tx->session_timer.function = sta_tx_agg_session_timer_expired; - tid_tx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid]; - init_timer_deferrable(&tid_tx->session_timer); + setup_deferrable_timer(&tid_tx->session_timer, + sta_tx_agg_session_timer_expired, + (unsigned long)&sta->timer_to_tid[tid]); /* assign a dialog token */ sta->ampdu_mlme.dialog_token_allocator++; From 7f813ce1bd103a28e2333bd97cc6782c8e2fc8a9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 6 Nov 2013 10:37:34 +0100 Subject: [PATCH 18/48] mac80211_hwsim: report survey data for scanned channels Currently, hwsim is reporting survey data (only a fake noise floor) for the current channel. This breaks when the multi-channel support is enabled since then there's no current channel. Make the dummy implementation closer to a real one and only report data while scanning, for all the scanned channels. At other times, no survey data might be available (in real hardware) due to power- save for example. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 73 ++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 8b823c70ab69c..67fc91dfcecda 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -526,6 +526,11 @@ struct mac80211_hwsim_data { struct ieee80211_vif *hw_scan_vif; int scan_chan_idx; u8 scan_addr[ETH_ALEN]; + struct { + struct ieee80211_channel *channel; + unsigned long next_start, start, end; + } survey_data[ARRAY_SIZE(hwsim_channels_2ghz) + + ARRAY_SIZE(hwsim_channels_5ghz)]; struct ieee80211_channel *channel; u64 beacon_int /* beacon interval in us */; @@ -1577,6 +1582,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) [IEEE80211_SMPS_STATIC] = "static", [IEEE80211_SMPS_DYNAMIC] = "dynamic", }; + int idx; if (conf->chandef.chan) wiphy_debug(hw->wiphy, @@ -1599,9 +1605,33 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) data->idle = !!(conf->flags & IEEE80211_CONF_IDLE); - data->channel = conf->chandef.chan; + WARN_ON(conf->chandef.chan && data->use_chanctx); + + mutex_lock(&data->mutex); + if (data->scanning && conf->chandef.chan) { + for (idx = 0; idx < ARRAY_SIZE(data->survey_data); idx++) { + if (data->survey_data[idx].channel == data->channel) { + data->survey_data[idx].start = + data->survey_data[idx].next_start; + data->survey_data[idx].end = jiffies; + break; + } + } - WARN_ON(data->channel && data->use_chanctx); + data->channel = conf->chandef.chan; + + for (idx = 0; idx < ARRAY_SIZE(data->survey_data); idx++) { + if (data->survey_data[idx].channel && + data->survey_data[idx].channel != data->channel) + continue; + data->survey_data[idx].channel = data->channel; + data->survey_data[idx].next_start = jiffies; + break; + } + } else { + data->channel = conf->chandef.chan; + } + mutex_unlock(&data->mutex); data->power_level = conf->power_level; if (!data->started || !data->beacon_int) @@ -1788,28 +1818,39 @@ static int mac80211_hwsim_conf_tx( return 0; } -static int mac80211_hwsim_get_survey( - struct ieee80211_hw *hw, int idx, - struct survey_info *survey) +static int mac80211_hwsim_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) { - struct ieee80211_conf *conf = &hw->conf; + struct mac80211_hwsim_data *hwsim = hw->priv; wiphy_debug(hw->wiphy, "%s (idx=%d)\n", __func__, idx); - if (idx != 0) + if (idx < 0 || idx >= ARRAY_SIZE(hwsim->survey_data)) return -ENOENT; - /* Current channel */ - survey->channel = conf->chandef.chan; + mutex_lock(&hwsim->mutex); + survey->channel = hwsim->survey_data[idx].channel; + if (!survey->channel) { + mutex_unlock(&hwsim->mutex); + return -ENOENT; + } /* - * Magically conjured noise level --- this is only ok for simulated hardware. + * Magically conjured dummy values --- this is only ok for simulated hardware. * - * A real driver which cannot determine the real channel noise MUST NOT - * report any noise, especially not a magically conjured one :-) + * A real driver which cannot determine real values noise MUST NOT + * report any, especially not a magically conjured ones :-) */ - survey->filled = SURVEY_INFO_NOISE_DBM; + survey->filled = SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY; survey->noise = -92; + survey->time = + jiffies_to_msecs(hwsim->survey_data[idx].end - + hwsim->survey_data[idx].start); + /* report 12.5% of channel time is used */ + survey->time_busy = survey->time/8; + mutex_unlock(&hwsim->mutex); return 0; } @@ -1987,6 +2028,10 @@ static void hw_scan_work(struct work_struct *work) } ieee80211_queue_delayed_work(hwsim->hw, &hwsim->hw_scan, msecs_to_jiffies(dwell)); + hwsim->survey_data[hwsim->scan_chan_idx].channel = hwsim->tmp_chan; + hwsim->survey_data[hwsim->scan_chan_idx].start = jiffies; + hwsim->survey_data[hwsim->scan_chan_idx].end = + jiffies + msecs_to_jiffies(dwell); hwsim->scan_chan_idx++; mutex_unlock(&hwsim->mutex); } @@ -2012,6 +2057,7 @@ static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw, hw_req->req.mac_addr_mask); else memcpy(hwsim->scan_addr, vif->addr, ETH_ALEN); + memset(hwsim->survey_data, 0, sizeof(hwsim->survey_data)); mutex_unlock(&hwsim->mutex); wiphy_debug(hw->wiphy, "hwsim hw_scan request\n"); @@ -2058,6 +2104,7 @@ static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw, memcpy(hwsim->scan_addr, mac_addr, ETH_ALEN); hwsim->scanning = true; + memset(hwsim->survey_data, 0, sizeof(hwsim->survey_data)); out: mutex_unlock(&hwsim->mutex); From b35a51c7dd25a823767969e3089542d7478777e9 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 27 Feb 2017 17:04:33 +0530 Subject: [PATCH 19/48] cfg80211: Make pre-CAC results valid only for ETSI domain DFS requirement for ETSI domain (section 4.7.1.4 in ETSI EN 301 893 V1.8.1) is the only one which explicitly states that once DFS channel is marked as available afer the CAC, this channel will remain in available state even moving to a different operating channel. But the same is not explicitly stated in FCC DFS requirement. Also, Pre-CAC requriements are not explicitly mentioned in FCC requirement. Current implementation in keeping DFS channel in available state is same as described in ETSI domain. For non-ETSI DFS domain, this patch gives a grace period of 2 seconds since the completion of successful CAC before moving the channel's DFS state to 'usable' from 'available' state. The same grace period is checked against the channel's dfs_state_entered timestamp while deciding if a DFS channel is available for operation. There is a new radar event, NL80211_RADAR_PRE_CAC_EXPIRED, reported when DFS channel is moved from available to usable state after the grace period. Also make sure the DFS channel state is reset to usable once the beaconing operation on that channel is brought down (like stop_ap, leave_ibss and leave_mesh) in non-ETSI domain. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 5 ++ net/wireless/ap.c | 5 ++ net/wireless/chan.c | 101 +++++++++++++++++++++++++++++++++++ net/wireless/core.h | 10 ++++ net/wireless/ibss.c | 1 + net/wireless/mesh.c | 1 + net/wireless/mlme.c | 40 ++++++++++---- net/wireless/reg.c | 28 ++++++++++ net/wireless/reg.h | 14 +++++ 9 files changed, 196 insertions(+), 9 deletions(-) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 9a499b15cfbc7..cd4dfef58fabb 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4913,12 +4913,17 @@ enum nl80211_smps_mode { * change to the channel status. * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is * over, channel becomes usable. + * @NL80211_RADAR_PRE_CAC_EXPIRED: Channel Availability Check done on this + * non-operating channel is expired and no longer valid. New CAC must + * be done on this channel before starting the operation. This is not + * applicable for ETSI dfs domain where pre-CAC is valid for ever. */ enum nl80211_radar_event { NL80211_RADAR_DETECTED, NL80211_RADAR_CAC_FINISHED, NL80211_RADAR_CAC_ABORTED, NL80211_RADAR_NOP_FINISHED, + NL80211_RADAR_PRE_CAC_EXPIRED, }; /** diff --git a/net/wireless/ap.c b/net/wireless/ap.c index bdad1f951561b..25666d3009be8 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -32,6 +32,11 @@ int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, rdev_set_qos_map(rdev, dev, NULL); if (notify) nl80211_send_ap_stopped(wdev); + + /* Should we apply the grace period during beaconing interface + * shutdown also? + */ + cfg80211_sched_dfs_chan_update(rdev); } return err; diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 5497d022fadab..099f13c0c39e2 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -456,6 +456,107 @@ bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy, return (r1 + r2 > 0); } +/* + * Checks if center frequency of chan falls with in the bandwidth + * range of chandef. + */ +bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef, + struct ieee80211_channel *chan) +{ + int width; + u32 cf_offset, freq; + + if (chandef->chan->center_freq == chan->center_freq) + return true; + + width = cfg80211_chandef_get_width(chandef); + if (width <= 20) + return false; + + cf_offset = width / 2 - 10; + + for (freq = chandef->center_freq1 - width / 2 + 10; + freq <= chandef->center_freq1 + width / 2 - 10; freq += 20) { + if (chan->center_freq == freq) + return true; + } + + if (!chandef->center_freq2) + return false; + + for (freq = chandef->center_freq2 - width / 2 + 10; + freq <= chandef->center_freq2 + width / 2 - 10; freq += 20) { + if (chan->center_freq == freq) + return true; + } + + return false; +} + +bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev) +{ + bool active = false; + + ASSERT_WDEV_LOCK(wdev); + + if (!wdev->chandef.chan) + return false; + + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + active = wdev->beacon_interval != 0; + break; + case NL80211_IFTYPE_ADHOC: + active = wdev->ssid_len != 0; + break; + case NL80211_IFTYPE_MESH_POINT: + active = wdev->mesh_id_len != 0; + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_OCB: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_P2P_DEVICE: + /* Can NAN type be considered as beaconing interface? */ + case NL80211_IFTYPE_NAN: + break; + case NL80211_IFTYPE_UNSPECIFIED: + case NUM_NL80211_IFTYPES: + WARN_ON(1); + } + + return active; +} + +bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, + struct ieee80211_channel *chan) +{ + struct wireless_dev *wdev; + + ASSERT_RTNL(); + + if (!(chan->flags & IEEE80211_CHAN_RADAR)) + return false; + + list_for_each_entry(wdev, &wiphy->wdev_list, list) { + wdev_lock(wdev); + if (!cfg80211_beaconing_iface_active(wdev)) { + wdev_unlock(wdev); + continue; + } + + if (cfg80211_is_sub_chan(&wdev->chandef, chan)) { + wdev_unlock(wdev); + return true; + } + wdev_unlock(wdev); + } + + return false; +} static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy, u32 center_freq, diff --git a/net/wireless/core.h b/net/wireless/core.h index efa690a7ef8d2..519a29ebde5b2 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -466,6 +466,16 @@ unsigned int cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef); +void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev); + +bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, + struct ieee80211_channel *chan); + +bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev); + +bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef, + struct ieee80211_channel *chan); + static inline unsigned int elapsed_jiffies_msecs(unsigned long start) { unsigned long end = jiffies; diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 364f900a3dc4d..10bf040a0982d 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -190,6 +190,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) if (!nowext) wdev->wext.ibss.ssid_len = 0; #endif + cfg80211_sched_dfs_chan_update(rdev); } void cfg80211_clear_ibss(struct net_device *dev, bool nowext) diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 2d8518a37eabc..ec0b1c20ac992 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -262,6 +262,7 @@ int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, wdev->beacon_interval = 0; memset(&wdev->chandef, 0, sizeof(wdev->chandef)); rdev_set_qos_map(rdev, dev, NULL); + cfg80211_sched_dfs_chan_update(rdev); } return err; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 22b3d99900655..cd29366a52068 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -745,6 +745,12 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, } EXPORT_SYMBOL(cfg80211_rx_mgmt); +void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev) +{ + cancel_delayed_work(&rdev->dfs_update_channels_wk); + queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, 0); +} + void cfg80211_dfs_channels_update_work(struct work_struct *work) { struct delayed_work *delayed_work = to_delayed_work(work); @@ -755,6 +761,8 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) struct wiphy *wiphy; bool check_again = false; unsigned long timeout, next_time = 0; + unsigned long time_dfs_update; + enum nl80211_radar_event radar_event; int bandid, i; rdev = container_of(delayed_work, struct cfg80211_registered_device, @@ -770,11 +778,27 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) for (i = 0; i < sband->n_channels; i++) { c = &sband->channels[i]; - if (c->dfs_state != NL80211_DFS_UNAVAILABLE) + if (!(c->flags & IEEE80211_CHAN_RADAR)) + continue; + + if (c->dfs_state != NL80211_DFS_UNAVAILABLE && + c->dfs_state != NL80211_DFS_AVAILABLE) continue; - timeout = c->dfs_state_entered + msecs_to_jiffies( - IEEE80211_DFS_MIN_NOP_TIME_MS); + if (c->dfs_state == NL80211_DFS_UNAVAILABLE) { + time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS; + radar_event = NL80211_RADAR_NOP_FINISHED; + } else { + if (regulatory_pre_cac_allowed(wiphy) || + cfg80211_any_wiphy_oper_chan(wiphy, c)) + continue; + + time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS; + radar_event = NL80211_RADAR_PRE_CAC_EXPIRED; + } + + timeout = c->dfs_state_entered + + msecs_to_jiffies(time_dfs_update); if (time_after_eq(jiffies, timeout)) { c->dfs_state = NL80211_DFS_USABLE; @@ -784,8 +808,8 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) NL80211_CHAN_NO_HT); nl80211_radar_notify(rdev, &chandef, - NL80211_RADAR_NOP_FINISHED, - NULL, GFP_ATOMIC); + radar_event, NULL, + GFP_ATOMIC); continue; } @@ -810,7 +834,6 @@ void cfg80211_radar_event(struct wiphy *wiphy, gfp_t gfp) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - unsigned long timeout; trace_cfg80211_radar_event(wiphy, chandef); @@ -820,9 +843,7 @@ void cfg80211_radar_event(struct wiphy *wiphy, */ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE); - timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS); - queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, - timeout); + cfg80211_sched_dfs_chan_update(rdev); nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp); } @@ -851,6 +872,7 @@ void cfg80211_cac_event(struct net_device *netdev, msecs_to_jiffies(wdev->cac_time_ms); WARN_ON(!time_after_eq(jiffies, timeout)); cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); + cfg80211_sched_dfs_chan_update(rdev); break; case NL80211_RADAR_CAC_ABORTED: break; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 753efcd51fa34..e59b192459e8c 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -3120,6 +3120,34 @@ bool regulatory_indoor_allowed(void) return reg_is_indoor; } +bool regulatory_pre_cac_allowed(struct wiphy *wiphy) +{ + const struct ieee80211_regdomain *regd = NULL; + const struct ieee80211_regdomain *wiphy_regd = NULL; + bool pre_cac_allowed = false; + + rcu_read_lock(); + + regd = rcu_dereference(cfg80211_regdomain); + wiphy_regd = rcu_dereference(wiphy->regd); + if (!wiphy_regd) { + if (regd->dfs_region == NL80211_DFS_ETSI) + pre_cac_allowed = true; + + rcu_read_unlock(); + + return pre_cac_allowed; + } + + if (regd->dfs_region == wiphy_regd->dfs_region && + wiphy_regd->dfs_region == NL80211_DFS_ETSI) + pre_cac_allowed = true; + + rcu_read_unlock(); + + return pre_cac_allowed; +} + int __init regulatory_init(void) { int err = 0; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index f6ced316b5a49..ff078f0939896 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -143,4 +143,18 @@ int cfg80211_get_unii(int freq); */ bool regulatory_indoor_allowed(void); +/* + * Grace period to timeout pre-CAC results on the dfs channels. This timeout + * value is used for Non-ETSI domain. + * TODO: May be make this timeout available through regdb? + */ +#define REG_PRE_CAC_EXPIRY_GRACE_MS 2000 + +/** + * regulatory_pre_cac_allowed - if pre-CAC allowed in the current dfs domain + * @wiphy: wiphy for which pre-CAC capability is checked. + + * Pre-CAC is allowed only in ETSI domain. + */ +bool regulatory_pre_cac_allowed(struct wiphy *wiphy); #endif /* __NET_WIRELESS_REG_H */ From 34373d12f3cbb74960a73431138ef619d857996f Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 27 Feb 2017 17:04:34 +0530 Subject: [PATCH 20/48] cfg80211: Disallow moving out of operating DFS channel in non-ETSI For non-ETSI regulatory domain, CAC result on DFS channel may not be valid once moving out of that channel (as done during remain-on-channel, scannning and off-channel tx). Running CAC on an operating DFS channel after every off-channel operation will only add complexity and disturb the current link. Better do not allow any off-channel switch from a DFS operating channel in non-ETSI domain. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 52 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d516527fcb8ea..b15903b9c0ab1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6556,6 +6556,19 @@ static int nl80211_parse_random_mac(struct nlattr **attrs, return 0; } +static bool cfg80211_off_channel_oper_allowed(struct wireless_dev *wdev) +{ + ASSERT_WDEV_LOCK(wdev); + + if (!cfg80211_beaconing_iface_active(wdev)) + return true; + + if (!(wdev->chandef.chan->flags & IEEE80211_CHAN_RADAR)) + return true; + + return regulatory_pre_cac_allowed(wdev->wiphy); +} + static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -6681,6 +6694,25 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) request->n_channels = i; + wdev_lock(wdev); + if (!cfg80211_off_channel_oper_allowed(wdev)) { + struct ieee80211_channel *chan; + + if (request->n_channels != 1) { + wdev_unlock(wdev); + err = -EBUSY; + goto out_free; + } + + chan = request->channels[0]; + if (chan->center_freq != wdev->chandef.chan->center_freq) { + wdev_unlock(wdev); + err = -EBUSY; + goto out_free; + } + } + wdev_unlock(wdev); + i = 0; if (n_ssids) { nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { @@ -9103,6 +9135,7 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; struct cfg80211_chan_def chandef; + const struct cfg80211_chan_def *compat_chandef; struct sk_buff *msg; void *hdr; u64 cookie; @@ -9131,6 +9164,18 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, if (err) return err; + wdev_lock(wdev); + if (!cfg80211_off_channel_oper_allowed(wdev) && + !cfg80211_chandef_identical(&wdev->chandef, &chandef)) { + compat_chandef = cfg80211_chandef_compatible(&wdev->chandef, + &chandef); + if (compat_chandef != &chandef) { + wdev_unlock(wdev); + return -EBUSY; + } + } + wdev_unlock(wdev); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -9306,6 +9351,13 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) if (!chandef.chan && params.offchan) return -EINVAL; + wdev_lock(wdev); + if (params.offchan && !cfg80211_off_channel_oper_allowed(wdev)) { + wdev_unlock(wdev); + return -EBUSY; + } + wdev_unlock(wdev); + params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]); From 8976672736d6089ae011fda3482e30e4380276f8 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 27 Feb 2017 17:04:35 +0530 Subject: [PATCH 21/48] cfg80211: Share Channel DFS state across wiphys of same DFS domain Sharing DFS channel state across multiple wiphys (radios) could be useful with multiple radios on the system. When one radio completes CAC and markes the channel available another radio can use this information and start beaconing without really doing CAC. Whenever there is a state change in dfs channel associated to a particular wiphy the the same state change is propagated to other wiphys having the same DFS reg domain configuration. Also when a new wiphy is created the dfs channel state of other existing wiphys of same DFS domain is copied. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Johannes Berg --- net/wireless/chan.c | 30 ++++++++--- net/wireless/core.c | 37 ++++++++++++++ net/wireless/core.h | 6 +++ net/wireless/mlme.c | 10 ++++ net/wireless/reg.c | 120 ++++++++++++++++++++++++++++++++++++++++++++ net/wireless/reg.h | 22 ++++++++ 6 files changed, 218 insertions(+), 7 deletions(-) diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 099f13c0c39e2..b8aa5a7d5c77a 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -531,16 +531,11 @@ bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev) return active; } -bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, - struct ieee80211_channel *chan) +static bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy, + struct ieee80211_channel *chan) { struct wireless_dev *wdev; - ASSERT_RTNL(); - - if (!(chan->flags & IEEE80211_CHAN_RADAR)) - return false; - list_for_each_entry(wdev, &wiphy->wdev_list, list) { wdev_lock(wdev); if (!cfg80211_beaconing_iface_active(wdev)) { @@ -558,6 +553,27 @@ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, return false; } +bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, + struct ieee80211_channel *chan) +{ + struct cfg80211_registered_device *rdev; + + ASSERT_RTNL(); + + if (!(chan->flags & IEEE80211_CHAN_RADAR)) + return false; + + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (!reg_dfs_domain_same(wiphy, &rdev->wiphy)) + continue; + + if (cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan)) + return true; + } + + return false; +} + static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy, u32 center_freq, u32 bandwidth) diff --git a/net/wireless/core.c b/net/wireless/core.c index 76e664144c8e4..b1a028d381efb 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -357,6 +357,38 @@ static void cfg80211_sched_scan_stop_wk(struct work_struct *work) rtnl_unlock(); } +static void cfg80211_propagate_radar_detect_wk(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(work, struct cfg80211_registered_device, + propagate_radar_detect_wk); + + rtnl_lock(); + + regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->radar_chandef, + NL80211_DFS_UNAVAILABLE, + NL80211_RADAR_DETECTED); + + rtnl_unlock(); +} + +static void cfg80211_propagate_cac_done_wk(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(work, struct cfg80211_registered_device, + propagate_cac_done_wk); + + rtnl_lock(); + + regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->cac_done_chandef, + NL80211_DFS_AVAILABLE, + NL80211_RADAR_CAC_FINISHED); + + rtnl_unlock(); +} + /* exported functions */ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, @@ -456,6 +488,9 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, spin_lock_init(&rdev->destroy_list_lock); INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk); INIT_WORK(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk); + INIT_WORK(&rdev->propagate_radar_detect_wk, + cfg80211_propagate_radar_detect_wk); + INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk); #ifdef CONFIG_CFG80211_DEFAULT_PS rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; @@ -915,6 +950,8 @@ void wiphy_unregister(struct wiphy *wiphy) flush_work(&rdev->destroy_work); flush_work(&rdev->sched_scan_stop_wk); flush_work(&rdev->mlme_unreg_wk); + flush_work(&rdev->propagate_radar_detect_wk); + flush_work(&rdev->propagate_cac_done_wk); #ifdef CONFIG_PM if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) diff --git a/net/wireless/core.h b/net/wireless/core.h index 519a29ebde5b2..a2fe8fc93283b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -97,6 +97,12 @@ struct cfg80211_registered_device { struct work_struct sched_scan_stop_wk; + struct cfg80211_chan_def radar_chandef; + struct work_struct propagate_radar_detect_wk; + + struct cfg80211_chan_def cac_done_chandef; + struct work_struct propagate_cac_done_wk; + /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ struct wiphy wiphy __aligned(NETDEV_ALIGN); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index cd29366a52068..01ce4a69e44dd 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -810,6 +810,10 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) nl80211_radar_notify(rdev, &chandef, radar_event, NULL, GFP_ATOMIC); + + regulatory_propagate_dfs_state(wiphy, &chandef, + c->dfs_state, + radar_event); continue; } @@ -846,6 +850,9 @@ void cfg80211_radar_event(struct wiphy *wiphy, cfg80211_sched_dfs_chan_update(rdev); nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp); + + memcpy(&rdev->radar_chandef, chandef, sizeof(struct cfg80211_chan_def)); + queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk); } EXPORT_SYMBOL(cfg80211_radar_event); @@ -872,6 +879,9 @@ void cfg80211_cac_event(struct net_device *netdev, msecs_to_jiffies(wdev->cac_time_ms); WARN_ON(!time_after_eq(jiffies, timeout)); cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); + memcpy(&rdev->cac_done_chandef, chandef, + sizeof(struct cfg80211_chan_def)); + queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk); cfg80211_sched_dfs_chan_update(rdev); break; case NL80211_RADAR_CAC_ABORTED: diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e59b192459e8c..a38f315819cd5 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2067,6 +2067,88 @@ reg_process_hint_country_ie(struct wiphy *wiphy, return REG_REQ_IGNORE; } +bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2) +{ + const struct ieee80211_regdomain *wiphy1_regd = NULL; + const struct ieee80211_regdomain *wiphy2_regd = NULL; + const struct ieee80211_regdomain *cfg80211_regd = NULL; + bool dfs_domain_same; + + rcu_read_lock(); + + cfg80211_regd = rcu_dereference(cfg80211_regdomain); + wiphy1_regd = rcu_dereference(wiphy1->regd); + if (!wiphy1_regd) + wiphy1_regd = cfg80211_regd; + + wiphy2_regd = rcu_dereference(wiphy2->regd); + if (!wiphy2_regd) + wiphy2_regd = cfg80211_regd; + + dfs_domain_same = wiphy1_regd->dfs_region == wiphy2_regd->dfs_region; + + rcu_read_unlock(); + + return dfs_domain_same; +} + +static void reg_copy_dfs_chan_state(struct ieee80211_channel *dst_chan, + struct ieee80211_channel *src_chan) +{ + if (!(dst_chan->flags & IEEE80211_CHAN_RADAR) || + !(src_chan->flags & IEEE80211_CHAN_RADAR)) + return; + + if (dst_chan->flags & IEEE80211_CHAN_DISABLED || + src_chan->flags & IEEE80211_CHAN_DISABLED) + return; + + if (src_chan->center_freq == dst_chan->center_freq && + dst_chan->dfs_state == NL80211_DFS_USABLE) { + dst_chan->dfs_state = src_chan->dfs_state; + dst_chan->dfs_state_entered = src_chan->dfs_state_entered; + } +} + +static void wiphy_share_dfs_chan_state(struct wiphy *dst_wiphy, + struct wiphy *src_wiphy) +{ + struct ieee80211_supported_band *src_sband, *dst_sband; + struct ieee80211_channel *src_chan, *dst_chan; + int i, j, band; + + if (!reg_dfs_domain_same(dst_wiphy, src_wiphy)) + return; + + for (band = 0; band < NUM_NL80211_BANDS; band++) { + dst_sband = dst_wiphy->bands[band]; + src_sband = src_wiphy->bands[band]; + if (!dst_sband || !src_sband) + continue; + + for (i = 0; i < dst_sband->n_channels; i++) { + dst_chan = &dst_sband->channels[i]; + for (j = 0; j < src_sband->n_channels; j++) { + src_chan = &src_sband->channels[j]; + reg_copy_dfs_chan_state(dst_chan, src_chan); + } + } + } +} + +static void wiphy_all_share_dfs_chan_state(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *rdev; + + ASSERT_RTNL(); + + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (wiphy == &rdev->wiphy) + continue; + wiphy_share_dfs_chan_state(wiphy, &rdev->wiphy); + } +} + /* This processes *all* regulatory hints */ static void reg_process_hint(struct regulatory_request *reg_request) { @@ -2110,6 +2192,7 @@ static void reg_process_hint(struct regulatory_request *reg_request) if (treatment == REG_REQ_ALREADY_SET && wiphy && wiphy->regulatory_flags & REGULATORY_STRICT_REG) { wiphy_update_regulatory(wiphy, reg_request->initiator); + wiphy_all_share_dfs_chan_state(wiphy); reg_check_channels(); } @@ -3061,6 +3144,7 @@ void wiphy_regulatory_register(struct wiphy *wiphy) lr = get_last_request(); wiphy_update_regulatory(wiphy, lr->initiator); + wiphy_all_share_dfs_chan_state(wiphy); } void wiphy_regulatory_deregister(struct wiphy *wiphy) @@ -3148,6 +3232,42 @@ bool regulatory_pre_cac_allowed(struct wiphy *wiphy) return pre_cac_allowed; } +void regulatory_propagate_dfs_state(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef, + enum nl80211_dfs_state dfs_state, + enum nl80211_radar_event event) +{ + struct cfg80211_registered_device *rdev; + + ASSERT_RTNL(); + + if (WARN_ON(!cfg80211_chandef_valid(chandef))) + return; + + if (WARN_ON(!(chandef->chan->flags & IEEE80211_CHAN_RADAR))) + return; + + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (wiphy == &rdev->wiphy) + continue; + + if (!reg_dfs_domain_same(wiphy, &rdev->wiphy)) + continue; + + if (!ieee80211_get_channel(&rdev->wiphy, + chandef->chan->center_freq)) + continue; + + cfg80211_set_dfs_state(&rdev->wiphy, chandef, dfs_state); + + if (event == NL80211_RADAR_DETECTED || + event == NL80211_RADAR_CAC_FINISHED) + cfg80211_sched_dfs_chan_update(rdev); + + nl80211_radar_notify(rdev, chandef, event, NULL, GFP_KERNEL); + } +} + int __init regulatory_init(void) { int err = 0; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index ff078f0939896..ca7fedf2e7a16 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -157,4 +157,26 @@ bool regulatory_indoor_allowed(void); * Pre-CAC is allowed only in ETSI domain. */ bool regulatory_pre_cac_allowed(struct wiphy *wiphy); + +/** + * regulatory_propagate_dfs_state - Propagate DFS channel state to other wiphys + * @wiphy - wiphy on which radar is detected and the event will be propagated + * to other available wiphys having the same DFS domain + * @chandef - Channel definition of radar detected channel + * @dfs_state - DFS channel state to be set + * @event - Type of radar event which triggered this DFS state change + * + * This function should be called with rtnl lock held. + */ +void regulatory_propagate_dfs_state(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef, + enum nl80211_dfs_state dfs_state, + enum nl80211_radar_event event); + +/** + * reg_dfs_domain_same - Checks if both wiphy have same DFS domain configured + * @wiphy1 - wiphy it's dfs_region to be checked against that of wiphy2 + * @wiphy2 - wiphy it's dfs_region to be checked against that of wiphy1 + */ +bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2); #endif /* __NET_WIRELESS_REG_H */ From a6289d3fcc7349402e198ea8fb22d63ed4cb09dd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Mar 2017 22:59:04 +0100 Subject: [PATCH 22/48] mac80211: ignore VHT membership selector when parsing rates There isn't really much harm in not ignoring, since it doesn't represent a valid rate, but since we already ignore the HT one also ignore VHT. Also simplify the code a bit. Fix a typo in the related comment (pointed out by Arend) while at it. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1568a74757bcb..4b4d29edec09d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2823,15 +2823,15 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband, *have_higher_than_11mbit = true; /* - * BSS_MEMBERSHIP_SELECTOR_HT_PHY is defined in 802.11n-2009 - * 7.3.2.2 as a magic value instead of a rate. Hence, skip it. + * Skip HT and VHT BSS membership selectors since they're not + * rates. * - * Note: Even through the membership selector and the basic + * Note: Even though the membership selector and the basic * rate flag share the same bit, they are not exactly * the same. */ - if (!!(supp_rates[i] & 0x80) && - (supp_rates[i] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY) + if (supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY) || + supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY)) continue; for (j = 0; j < sband->n_bitrates; j++) { From a3f9d596b1ea9cf7db482879a8aeb91f54a87e75 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Mar 2017 23:04:09 +0100 Subject: [PATCH 23/48] iwlegacy: remove usage of txrc->max_rate_idx Just calculate it like mac80211 does today, so we can get rid of the calculation in mac80211 for everyone else. Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlegacy/3945-rs.c | 2 +- drivers/net/wireless/intel/iwlegacy/4965-rs.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlegacy/3945-rs.c b/drivers/net/wireless/intel/iwlegacy/3945-rs.c index 03ad9b8b55f4e..b2f35dfbc01bd 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-rs.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-rs.c @@ -656,7 +656,7 @@ il3945_rs_get_rate(void *il_r, struct ieee80211_sta *sta, void *il_sta, rate_mask = sta->supp_rates[sband->band]; /* get user max rate if set */ - max_rate_idx = txrc->max_rate_idx; + max_rate_idx = fls(txrc->rate_idx_mask) - 1; if (sband->band == NL80211_BAND_5GHZ && max_rate_idx != -1) max_rate_idx += IL_FIRST_OFDM_RATE; if (max_rate_idx < 0 || max_rate_idx >= RATE_COUNT) diff --git a/drivers/net/wireless/intel/iwlegacy/4965-rs.c b/drivers/net/wireless/intel/iwlegacy/4965-rs.c index a867ae7f4095b..c055f6da11c6a 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-rs.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-rs.c @@ -2211,7 +2211,7 @@ il4965_rs_get_rate(void *il_r, struct ieee80211_sta *sta, void *il_sta, /* Get max rate if user set max rate */ if (lq_sta) { - lq_sta->max_rate_idx = txrc->max_rate_idx; + lq_sta->max_rate_idx = fls(txrc->rate_idx_mask) - 1; if (sband->band == NL80211_BAND_5GHZ && lq_sta->max_rate_idx != -1) lq_sta->max_rate_idx += IL_FIRST_OFDM_RATE; From ce1d834f9d213c9f76f693762710f7ecfbb69503 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Mar 2017 23:04:09 +0100 Subject: [PATCH 24/48] iwlwifi: dvm: remove usage of txrc->max_rate_idx Just calculate it like mac80211 does today, so we can get rid of the calculation in mac80211 for everyone else. Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/dvm/rs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rs.c b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c index ff44ebc5829d6..ddcd8c2d66cde 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c @@ -2720,7 +2720,7 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, /* Get max rate if user set max rate */ if (lq_sta) { - lq_sta->max_rate_idx = txrc->max_rate_idx; + lq_sta->max_rate_idx = fls(txrc->rate_idx_mask) - 1; if ((sband->band == NL80211_BAND_5GHZ) && (lq_sta->max_rate_idx != -1)) lq_sta->max_rate_idx += IWL_FIRST_OFDM_RATE; From b61fbda180b5c9f5f3ce7f2e63b0253c84ffdf09 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Mar 2017 22:59:59 +0100 Subject: [PATCH 25/48] mac80211: remove ieee80211_tx_rate_control.max_rate_idx As promised a little more than 7 years ago, remove it now since nothing uses it anymore. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 ---- net/mac80211/tx.c | 8 -------- 2 files changed, 12 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 1a26a375feb86..b1ac872dc88a3 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -5444,9 +5444,6 @@ void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif, * RTS threshold * @short_preamble: whether mac80211 will request short-preamble transmission * if the selected rate supports it - * @max_rate_idx: user-requested maximum (legacy) rate - * (deprecated; this will be removed once drivers get updated to use - * rate_idx_mask) * @rate_idx_mask: user-requested (legacy) rate mask * @rate_idx_mcs_mask: user-requested MCS rate mask (NULL if not in use) * @bss: whether this frame is sent out in AP or IBSS mode @@ -5458,7 +5455,6 @@ struct ieee80211_tx_rate_control { struct sk_buff *skb; struct ieee80211_tx_rate reported_rate; bool rts, short_preamble; - u8 max_rate_idx; u32 rate_idx_mask; u8 *rate_idx_mcs_mask; bool bss; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ba8d7db0a0716..f27719eeeed79 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -682,10 +682,6 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) txrc.skb = tx->skb; txrc.reported_rate.idx = -1; txrc.rate_idx_mask = tx->sdata->rc_rateidx_mask[info->band]; - if (txrc.rate_idx_mask == (1 << sband->n_bitrates) - 1) - txrc.max_rate_idx = -1; - else - txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; if (tx->sdata->rc_has_mcs_mask[info->band]) txrc.rate_idx_mcs_mask = @@ -4249,10 +4245,6 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, txrc.skb = skb; txrc.reported_rate.idx = -1; txrc.rate_idx_mask = sdata->rc_rateidx_mask[band]; - if (txrc.rate_idx_mask == (1 << txrc.sband->n_bitrates) - 1) - txrc.max_rate_idx = -1; - else - txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; txrc.bss = true; rate_control_get_rate(sdata, NULL, &txrc); From e8e4f5280ddd0a7b43a795f90a0758e3c99df6a6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 Mar 2017 11:12:10 +0100 Subject: [PATCH 26/48] mac80211: reject/clear user rate mask if not usable If the user rate mask results in no (basic) rates being usable, clear it. Also, if we're already operating when it's set, reject it instead. Technically, selecting basic rates as the criterion is a bit too restrictive, but calculating the usable rates over all stations (e.g. in AP mode) is harder, and all stations must support the basic rates. Similarly, in client mode, the basic rates will be used anyway for control frames. This fixes the "no supported rates (...) in rate_mask ..." warning that occurs on TX when you've selected a rate mask that's not compatible with the connection (e.g. an AP that enables only the rates 36, 48, 54 and you've selected only 6, 9, 12.) Reported-by: Kirtika Ruchandani Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 18 +++++++++++++++++- net/mac80211/mlme.c | 2 ++ net/mac80211/rate.c | 27 +++++++++++++++++++++++++++ net/mac80211/rate.h | 2 ++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 9c7490cb2243d..8bc3d36693482 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3,7 +3,7 @@ * * Copyright 2006-2010 Johannes Berg * Copyright 2013-2015 Intel Mobile Communications GmbH - * Copyright (C) 2015-2016 Intel Deutschland GmbH + * Copyright (C) 2015-2017 Intel Deutschland GmbH * * This file is GPLv2 as found in COPYING. */ @@ -2042,6 +2042,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy, params->basic_rates_len, &sdata->vif.bss_conf.basic_rates); changed |= BSS_CHANGED_BASIC_RATES; + ieee80211_check_rate_mask(sdata); } if (params->ap_isolate >= 0) { @@ -2685,6 +2686,21 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, return ret; } + /* + * If active validate the setting and reject it if it doesn't leave + * at least one basic rate usable, since we really have to be able + * to send something, and if we're an AP we have to be able to do + * so at a basic rate so that all clients can receive it. + */ + if (rcu_access_pointer(sdata->vif.chanctx_conf) && + sdata->vif.bss_conf.chandef.chan) { + u32 basic_rates = sdata->vif.bss_conf.basic_rates; + enum nl80211_band band = sdata->vif.bss_conf.chandef.chan->band; + + if (!(mask->control[band].legacy & basic_rates)) + return -EINVAL; + } + for (i = 0; i < NUM_NL80211_BANDS; i++) { struct ieee80211_supported_band *sband = wiphy->bands[i]; int j; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 4b4d29edec09d..24d69bcf71ad5 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1908,6 +1908,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, sdata->u.mgd.associated = cbss; memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN); + ieee80211_check_rate_mask(sdata); + sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; if (sdata->vif.p2p || diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 094c15645228e..3bddd9bbb76fc 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -2,6 +2,7 @@ * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2006 Jiri Benc + * Copyright 2017 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 @@ -241,6 +242,32 @@ static void rate_control_free(struct ieee80211_local *local, kfree(ctrl_ref); } +void ieee80211_check_rate_mask(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + u32 user_mask, basic_rates = sdata->vif.bss_conf.basic_rates; + enum nl80211_band band; + + if (WARN_ON(!sdata->vif.bss_conf.chandef.chan)) + return; + + if (WARN_ON_ONCE(!basic_rates)) + return; + + band = sdata->vif.bss_conf.chandef.chan->band; + user_mask = sdata->rc_rateidx_mask[band]; + sband = local->hw.wiphy->bands[band]; + + if (user_mask & basic_rates) + return; + + sdata_dbg(sdata, + "no overlap between basic rates (0x%x) and user mask (0x%x on band %d) - clearing the latter", + basic_rates, user_mask, band); + sdata->rc_rateidx_mask[band] = (1 << sband->n_bitrates) - 1; +} + static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc) { struct sk_buff *skb = txrc->skb; diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index d51a1cce4d4a8..f7825ef5f871b 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -110,6 +110,8 @@ static inline void rate_control_remove_sta_debugfs(struct sta_info *sta) #endif } +void ieee80211_check_rate_mask(struct ieee80211_sub_if_data *sdata); + /* Get a reference to the rate control algorithm. If `name' is NULL, get the * first available algorithm. */ int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local, From 5c19dfbe964f4bbb38c1868b851adf4855fc93ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Lyson=C4=9Bk?= Date: Thu, 9 Mar 2017 10:34:36 +0100 Subject: [PATCH 27/48] mac80211: Use setup_timer instead of init_timer for mesh path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use setup_timer() and setup_deferrable_timer() to set the data and function timer fields. It makes the code cleaner and will allow for easier change of the timer struct internals. Signed-off-by: Ondƙej Lysoněk Cc: Jiri Slaby Cc: Johannes Berg Cc: "David S. Miller" Cc: Cc: Signed-off-by: Johannes Berg --- net/mac80211/mesh_pathtbl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 98a3b1c0c3385..97269caafecd7 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -397,11 +397,10 @@ struct mesh_path *mesh_path_new(struct ieee80211_sub_if_data *sdata, new_mpath->sdata = sdata; new_mpath->flags = 0; skb_queue_head_init(&new_mpath->frame_queue); - new_mpath->timer.data = (unsigned long) new_mpath; - new_mpath->timer.function = mesh_path_timer; new_mpath->exp_time = jiffies; spin_lock_init(&new_mpath->state_lock); - init_timer(&new_mpath->timer); + setup_timer(&new_mpath->timer, mesh_path_timer, + (unsigned long) new_mpath); return new_mpath; } From 335d534938d327d5ba266564b7e6beaf5d456dd2 Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Thu, 16 Mar 2017 10:57:17 +0900 Subject: [PATCH 28/48] nl80211: Use signed function for a signed variable The rssi_threshold is defined as s32. Signed-off-by: Masashi Honma Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b15903b9c0ab1..bd5959fd29c53 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5714,7 +5714,7 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, cur_params.dot11MeshGateAnnouncementProtocol) || nla_put_u8(msg, NL80211_MESHCONF_FORWARDING, cur_params.dot11MeshForwarding) || - nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD, + nla_put_s32(msg, NL80211_MESHCONF_RSSI_THRESHOLD, cur_params.rssi_threshold) || nla_put_u32(msg, NL80211_MESHCONF_HT_OPMODE, cur_params.ht_opmode) || From b6ecfd469e82886308b44bf715d48fbe7b19ad4c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 17 Mar 2017 10:24:15 +0100 Subject: [PATCH 29/48] cfg80211: preserve wdev ID across netns changes When a wdev changes network namespace, its wdev ID will get reassigned since NETDEV_REGISTER is called again, in the new network namespace. Avoid that by checking if it was already assigned before, and document why we do that. Reported-and-tested-by: Arend Van Spriel Signed-off-by: Johannes Berg --- net/wireless/core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index b1a028d381efb..b0d6761f0cdd7 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1159,7 +1159,15 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, INIT_LIST_HEAD(&wdev->mgmt_registrations); spin_lock_init(&wdev->mgmt_registrations_lock); - wdev->identifier = ++rdev->wdev_id; + /* + * We get here also when the interface changes network namespaces, + * as it's registered into the new one, but we don't want it to + * change ID in that case. Checking if the ID is already assigned + * works, because 0 isn't considered a valid ID and the memory is + * 0-initialized. + */ + if (!wdev->identifier) + wdev->identifier = ++rdev->wdev_id; list_add_rcu(&wdev->list, &rdev->wiphy.wdev_list); rdev->devlist_generation++; /* can only change netns with wiphy */ From ed92a9b5d4aaea4a4346db3ab520e8a631f734fd Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Thu, 16 Mar 2017 10:57:18 +0900 Subject: [PATCH 30/48] mac80211: mesh: drop new node with weak power On some practical cases, it is useful to drop new node in the distance. Because mesh metric is calculated with hop count and without RSSI information, a node far from local peer and near to destination node could be used as best path. For example, the nodes are located in linear. Distance of 0 - 1 and 1 - 2 and 2 - 3 is 20meters. 0 to 3 signal is very weak. 0 --- 1 --- 2 --- 3 Though most robust path from 0 to 3 is 0 -> 1 -> 2 -> 3, unfortunately, node 0 could recognize node 3 as neighbor. Then node 3 could be next of node 0. This patch aims to avoid such a case. [Johannes:] Dropping the node entirely isn't ideal, but at least with encryption there will be a limit on # of keys the hardware can deal with, and there might also be a limit on the number of stations it supports. Signed-off-by: Masashi Honma Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 6e7b6a07b7d53..281d834c75486 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -1100,8 +1100,14 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) return; - if (mesh_matches_local(sdata, &elems)) - mesh_neighbour_update(sdata, mgmt->sa, &elems); + if (mesh_matches_local(sdata, &elems)) { + mpl_dbg(sdata, "rssi_threshold=%d,rx_status->signal=%d\n", + sdata->u.mesh.mshcfg.rssi_threshold, rx_status->signal); + if (!sdata->u.mesh.user_mpm || + sdata->u.mesh.mshcfg.rssi_threshold == 0 || + sdata->u.mesh.mshcfg.rssi_threshold < rx_status->signal) + mesh_neighbour_update(sdata, mgmt->sa, &elems); + } if (ifmsh->sync_ops) ifmsh->sync_ops->rx_bcn_presp(sdata, From 667a2e6bfeafa7ce202c22c702fea30ca7741c21 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Thu, 23 Mar 2017 16:26:16 -0700 Subject: [PATCH 31/48] mac80211-hwsim: remove dmesg spam about get-survey. This message just fills up dmesg and/or kernel logs and does not provide any useful information. Signed-off-by: Ben Greear Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 67fc91dfcecda..4e58513d24e89 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1823,8 +1823,6 @@ static int mac80211_hwsim_get_survey(struct ieee80211_hw *hw, int idx, { struct mac80211_hwsim_data *hwsim = hw->priv; - wiphy_debug(hw->wiphy, "%s (idx=%d)\n", __func__, idx); - if (idx < 0 || idx >= ARRAY_SIZE(hwsim->survey_data)) return -ENOENT; From 5349a0f7bfbdd7d81b8418c707dcd1439c714647 Mon Sep 17 00:00:00 2001 From: Vidyullatha Kanchanapally Date: Fri, 31 Mar 2017 00:22:33 +0300 Subject: [PATCH 32/48] cfg80211: Use a structure to pass connect response params Currently the connect event from driver takes all the connection response parameters as arguments. With support for new features these response parameters can grow. Use a structure to pass these parameters rather than passing them as function arguments. Signed-off-by: Vidyullatha Kanchanapally Signed-off-by: Jouni Malinen [add to documentation] Signed-off-by: Johannes Berg --- Documentation/driver-api/80211/cfg80211.rst | 6 + include/net/cfg80211.h | 91 ++++++++++-- net/wireless/core.h | 20 +-- net/wireless/mlme.c | 20 +-- net/wireless/nl80211.c | 30 ++-- net/wireless/nl80211.h | 7 +- net/wireless/sme.c | 149 +++++++++++--------- net/wireless/util.c | 12 +- 8 files changed, 205 insertions(+), 130 deletions(-) diff --git a/Documentation/driver-api/80211/cfg80211.rst b/Documentation/driver-api/80211/cfg80211.rst index eca534ab61725..b101bc0c195b7 100644 --- a/Documentation/driver-api/80211/cfg80211.rst +++ b/Documentation/driver-api/80211/cfg80211.rst @@ -179,6 +179,12 @@ Actions and configuration .. kernel-doc:: include/net/cfg80211.h :functions: cfg80211_ibss_joined +.. kernel-doc:: include/net/cfg80211.h + :functions: cfg80211_connect_resp_params + +.. kernel-doc:: include/net/cfg80211.h + :functions: cfg80211_connect_done + .. kernel-doc:: include/net/cfg80211.h :functions: cfg80211_connect_result diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ffc08687b31da..da12d5b86e1bb 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5135,6 +5135,60 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) #define CFG80211_TESTMODE_DUMP(cmd) #endif +/** + * struct cfg80211_connect_resp_params - Connection response params + * @status: Status code, %WLAN_STATUS_SUCCESS for successful connection, use + * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you + * the real status code for failures. If this call is used to report a + * failure due to a timeout (e.g., not receiving an Authentication frame + * from the AP) instead of an explicit rejection by the AP, -1 is used to + * indicate that this is a failure, but without a status code. + * @timeout_reason is used to report the reason for the timeout in that + * case. + * @bssid: The BSSID of the AP (may be %NULL) + * @bss: Entry of bss to which STA got connected to, can be obtained through + * cfg80211_get_bss() (may be %NULL). Only one parameter among @bssid and + * @bss needs to be specified. + * @req_ie: Association request IEs (may be %NULL) + * @req_ie_len: Association request IEs length + * @resp_ie: Association response IEs (may be %NULL) + * @resp_ie_len: Association response IEs length + * @timeout_reason: Reason for connection timeout. This is used when the + * connection fails due to a timeout instead of an explicit rejection from + * the AP. %NL80211_TIMEOUT_UNSPECIFIED is used when the timeout reason is + * not known. This value is used only if @status < 0 to indicate that the + * failure is due to a timeout and not due to explicit rejection by the AP. + * This value is ignored in other cases (@status >= 0). + */ +struct cfg80211_connect_resp_params { + int status; + const u8 *bssid; + struct cfg80211_bss *bss; + const u8 *req_ie; + size_t req_ie_len; + const u8 *resp_ie; + size_t resp_ie_len; + enum nl80211_timeout_reason timeout_reason; +}; + +/** + * cfg80211_connect_done - notify cfg80211 of connection result + * + * @dev: network device + * @params: connection response parameters + * @gfp: allocation flags + * + * It should be called by the underlying driver once execution of the connection + * request from connect() has been completed. This is similar to + * cfg80211_connect_bss(), but takes a structure pointer for connection response + * parameters. Only one of the functions among cfg80211_connect_bss(), + * cfg80211_connect_result(), cfg80211_connect_timeout(), + * and cfg80211_connect_done() should be called. + */ +void cfg80211_connect_done(struct net_device *dev, + struct cfg80211_connect_resp_params *params, + gfp_t gfp); + /** * cfg80211_connect_bss - notify cfg80211 of connection result * @@ -5165,13 +5219,31 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) * It should be called by the underlying driver once execution of the connection * request from connect() has been completed. This is similar to * cfg80211_connect_result(), but with the option of identifying the exact bss - * entry for the connection. Only one of these functions should be called. + * entry for the connection. Only one of the functions among + * cfg80211_connect_bss(), cfg80211_connect_result(), + * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called. */ -void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, - struct cfg80211_bss *bss, const u8 *req_ie, - size_t req_ie_len, const u8 *resp_ie, - size_t resp_ie_len, int status, gfp_t gfp, - enum nl80211_timeout_reason timeout_reason); +static inline void +cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, + struct cfg80211_bss *bss, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, int status, gfp_t gfp, + enum nl80211_timeout_reason timeout_reason) +{ + struct cfg80211_connect_resp_params params; + + memset(¶ms, 0, sizeof(params)); + params.status = status; + params.bssid = bssid; + params.bss = bss; + params.req_ie = req_ie; + params.req_ie_len = req_ie_len; + params.resp_ie = resp_ie; + params.resp_ie_len = resp_ie_len; + params.timeout_reason = timeout_reason; + + cfg80211_connect_done(dev, ¶ms, gfp); +} /** * cfg80211_connect_result - notify cfg80211 of connection result @@ -5190,7 +5262,8 @@ void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, * It should be called by the underlying driver once execution of the connection * request from connect() has been completed. This is similar to * cfg80211_connect_bss() which allows the exact bss entry to be specified. Only - * one of these functions should be called. + * one of the functions among cfg80211_connect_bss(), cfg80211_connect_result(), + * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called. */ static inline void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, @@ -5217,7 +5290,9 @@ cfg80211_connect_result(struct net_device *dev, const u8 *bssid, * in a sequence where no explicit authentication/association rejection was * received from the AP. This could happen, e.g., due to not being able to send * out the Authentication or Association Request frame or timing out while - * waiting for the response. + * waiting for the response. Only one of the functions among + * cfg80211_connect_bss(), cfg80211_connect_result(), + * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called. */ static inline void cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid, diff --git a/net/wireless/core.h b/net/wireless/core.h index a2fe8fc93283b..d614efb417261 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -226,16 +226,7 @@ struct cfg80211_event { enum cfg80211_event_type type; union { - struct { - u8 bssid[ETH_ALEN]; - const u8 *req_ie; - const u8 *resp_ie; - size_t req_ie_len; - size_t resp_ie_len; - struct cfg80211_bss *bss; - int status; /* -1 = failed; 0..65535 = status code */ - enum nl80211_timeout_reason timeout_reason; - } cr; + struct cfg80211_connect_resp_params cr; struct { const u8 *req_ie; const u8 *resp_ie; @@ -398,12 +389,9 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, struct cfg80211_connect_params *connect, struct cfg80211_cached_keys *connkeys, const u8 *prev_bssid); -void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - int status, bool wextev, - struct cfg80211_bss *bss, - enum nl80211_timeout_reason timeout_reason); +void __cfg80211_connect_result(struct net_device *dev, + struct cfg80211_connect_resp_params *params, + bool wextev); void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap); int cfg80211_disconnect(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 01ce4a69e44dd..d8df7a5180a04 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -26,9 +26,16 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; - u8 *ie = mgmt->u.assoc_resp.variable; - int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); - u16 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); + struct cfg80211_connect_resp_params cr; + + memset(&cr, 0, sizeof(cr)); + cr.status = (int)le16_to_cpu(mgmt->u.assoc_resp.status_code); + cr.bssid = mgmt->bssid; + cr.bss = bss; + cr.resp_ie = mgmt->u.assoc_resp.variable; + cr.resp_ie_len = + len - offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); + cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED; trace_cfg80211_send_rx_assoc(dev, bss); @@ -38,7 +45,7 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, * and got a reject -- we only try again with an assoc * frame instead of reassoc. */ - if (cfg80211_sme_rx_assoc_resp(wdev, status_code)) { + if (cfg80211_sme_rx_assoc_resp(wdev, cr.status)) { cfg80211_unhold_bss(bss_from_pub(bss)); cfg80211_put_bss(wiphy, bss); return; @@ -46,10 +53,7 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues); /* update current_bss etc., consumes the bss reference */ - __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, - status_code, - status_code == WLAN_STATUS_SUCCESS, bss, - NL80211_TIMEOUT_UNSPECIFIED); + __cfg80211_connect_result(dev, &cr, cr.status == WLAN_STATUS_SUCCESS); } EXPORT_SYMBOL(cfg80211_rx_assoc_resp); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index bd5959fd29c53..3d635c865281d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -13464,17 +13464,14 @@ void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, } void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - int status, - enum nl80211_timeout_reason timeout_reason, + struct net_device *netdev, + struct cfg80211_connect_resp_params *cr, gfp_t gfp) { struct sk_buff *msg; void *hdr; - msg = nlmsg_new(100 + req_ie_len + resp_ie_len, gfp); + msg = nlmsg_new(100 + cr->req_ie_len + cr->resp_ie_len, gfp); if (!msg) return; @@ -13486,17 +13483,20 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || - (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) || + (cr->bssid && + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, cr->bssid)) || nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, - status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE : - status) || - (status < 0 && + cr->status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE : + cr->status) || + (cr->status < 0 && (nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) || - nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON, timeout_reason))) || - (req_ie && - nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) || - (resp_ie && - nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie))) + nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON, + cr->timeout_reason))) || + (cr->req_ie && + nla_put(msg, NL80211_ATTR_REQ_IE, cr->req_ie_len, cr->req_ie)) || + (cr->resp_ie && + nla_put(msg, NL80211_ATTR_RESP_IE, cr->resp_ie_len, + cr->resp_ie))) goto nla_put_failure; genlmsg_end(msg, hdr); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index e488dca87423e..3cb17cd9577f5 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -53,11 +53,8 @@ void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, gfp_t gfp); void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - int status, - enum nl80211_timeout_reason timeout_reason, + struct net_device *netdev, + struct cfg80211_connect_resp_params *params, gfp_t gfp); void nl80211_send_roamed(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index b347e63d7aaa6..ebd7adc272462 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -253,10 +253,13 @@ void cfg80211_conn_work(struct work_struct *work) } treason = NL80211_TIMEOUT_UNSPECIFIED; if (cfg80211_conn_do_work(wdev, &treason)) { - __cfg80211_connect_result( - wdev->netdev, bssid, - NULL, 0, NULL, 0, -1, false, NULL, - treason); + struct cfg80211_connect_resp_params cr; + + memset(&cr, 0, sizeof(cr)); + cr.status = -1; + cr.bssid = bssid; + cr.timeout_reason = treason; + __cfg80211_connect_result(wdev->netdev, &cr, false); } wdev_unlock(wdev); } @@ -359,10 +362,13 @@ void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len) wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; schedule_work(&rdev->conn_work); } else if (status_code != WLAN_STATUS_SUCCESS) { - __cfg80211_connect_result(wdev->netdev, mgmt->bssid, - NULL, 0, NULL, 0, - status_code, false, NULL, - NL80211_TIMEOUT_UNSPECIFIED); + struct cfg80211_connect_resp_params cr; + + memset(&cr, 0, sizeof(cr)); + cr.status = status_code; + cr.bssid = mgmt->bssid; + cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED; + __cfg80211_connect_result(wdev->netdev, &cr, false); } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; schedule_work(&rdev->conn_work); @@ -669,12 +675,9 @@ static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); */ /* This method must consume bss one way or another */ -void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - int status, bool wextev, - struct cfg80211_bss *bss, - enum nl80211_timeout_reason timeout_reason) +void __cfg80211_connect_result(struct net_device *dev, + struct cfg80211_connect_resp_params *cr, + bool wextev) { struct wireless_dev *wdev = dev->ieee80211_ptr; const u8 *country_ie; @@ -686,48 +689,48 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) { - cfg80211_put_bss(wdev->wiphy, bss); + cfg80211_put_bss(wdev->wiphy, cr->bss); return; } - nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, - bssid, req_ie, req_ie_len, - resp_ie, resp_ie_len, - status, timeout_reason, GFP_KERNEL); + nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, cr, + GFP_KERNEL); #ifdef CONFIG_CFG80211_WEXT if (wextev) { - if (req_ie && status == WLAN_STATUS_SUCCESS) { + if (cr->req_ie && cr->status == WLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = req_ie_len; - wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie); + wrqu.data.length = cr->req_ie_len; + wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, + cr->req_ie); } - if (resp_ie && status == WLAN_STATUS_SUCCESS) { + if (cr->resp_ie && cr->status == WLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = resp_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + wrqu.data.length = cr->resp_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, + cr->resp_ie); } memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (bssid && status == WLAN_STATUS_SUCCESS) { - memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); - memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); + if (cr->bssid && cr->status == WLAN_STATUS_SUCCESS) { + memcpy(wrqu.ap_addr.sa_data, cr->bssid, ETH_ALEN); + memcpy(wdev->wext.prev_bssid, cr->bssid, ETH_ALEN); wdev->wext.prev_bssid_valid = true; } wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); } #endif - if (!bss && (status == WLAN_STATUS_SUCCESS)) { + if (!cr->bss && (cr->status == WLAN_STATUS_SUCCESS)) { WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect); - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, - wdev->ssid, wdev->ssid_len, - wdev->conn_bss_type, - IEEE80211_PRIVACY_ANY); - if (bss) - cfg80211_hold_bss(bss_from_pub(bss)); + cr->bss = cfg80211_get_bss(wdev->wiphy, NULL, cr->bssid, + wdev->ssid, wdev->ssid_len, + wdev->conn_bss_type, + IEEE80211_PRIVACY_ANY); + if (cr->bss) + cfg80211_hold_bss(bss_from_pub(cr->bss)); } if (wdev->current_bss) { @@ -736,29 +739,29 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, wdev->current_bss = NULL; } - if (status != WLAN_STATUS_SUCCESS) { + if (cr->status != WLAN_STATUS_SUCCESS) { kzfree(wdev->connect_keys); wdev->connect_keys = NULL; wdev->ssid_len = 0; wdev->conn_owner_nlportid = 0; - if (bss) { - cfg80211_unhold_bss(bss_from_pub(bss)); - cfg80211_put_bss(wdev->wiphy, bss); + if (cr->bss) { + cfg80211_unhold_bss(bss_from_pub(cr->bss)); + cfg80211_put_bss(wdev->wiphy, cr->bss); } cfg80211_sme_free(wdev); return; } - if (WARN_ON(!bss)) + if (WARN_ON(!cr->bss)) return; - wdev->current_bss = bss_from_pub(bss); + wdev->current_bss = bss_from_pub(cr->bss); if (!(wdev->wiphy->flags & WIPHY_FLAG_HAS_STATIC_WEP)) cfg80211_upload_connect_keys(wdev); rcu_read_lock(); - country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + country_ie = ieee80211_bss_get_ie(cr->bss, WLAN_EID_COUNTRY); if (!country_ie) { rcu_read_unlock(); return; @@ -775,64 +778,72 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, * - country_ie + 2, the start of the country ie data, and * - and country_ie[1] which is the IE length */ - regulatory_hint_country_ie(wdev->wiphy, bss->channel->band, + regulatory_hint_country_ie(wdev->wiphy, cr->bss->channel->band, country_ie + 2, country_ie[1]); kfree(country_ie); } /* Consumes bss object one way or another */ -void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, - struct cfg80211_bss *bss, const u8 *req_ie, - size_t req_ie_len, const u8 *resp_ie, - size_t resp_ie_len, int status, gfp_t gfp, - enum nl80211_timeout_reason timeout_reason) +void cfg80211_connect_done(struct net_device *dev, + struct cfg80211_connect_resp_params *params, + gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_event *ev; unsigned long flags; + u8 *next; - if (bss) { + if (params->bss) { /* Make sure the bss entry provided by the driver is valid. */ - struct cfg80211_internal_bss *ibss = bss_from_pub(bss); + struct cfg80211_internal_bss *ibss = bss_from_pub(params->bss); if (WARN_ON(list_empty(&ibss->list))) { - cfg80211_put_bss(wdev->wiphy, bss); + cfg80211_put_bss(wdev->wiphy, params->bss); return; } } - ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); + ev = kzalloc(sizeof(*ev) + (params->bssid ? ETH_ALEN : 0) + + params->req_ie_len + params->resp_ie_len, gfp); if (!ev) { - cfg80211_put_bss(wdev->wiphy, bss); + cfg80211_put_bss(wdev->wiphy, params->bss); return; } ev->type = EVENT_CONNECT_RESULT; - if (bssid) - memcpy(ev->cr.bssid, bssid, ETH_ALEN); - if (req_ie_len) { - ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); - ev->cr.req_ie_len = req_ie_len; - memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); + next = ((u8 *)ev) + sizeof(*ev); + if (params->bssid) { + ev->cr.bssid = next; + memcpy((void *)ev->cr.bssid, params->bssid, ETH_ALEN); + next += ETH_ALEN; } - if (resp_ie_len) { - ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; - ev->cr.resp_ie_len = resp_ie_len; - memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); + if (params->req_ie_len) { + ev->cr.req_ie = next; + ev->cr.req_ie_len = params->req_ie_len; + memcpy((void *)ev->cr.req_ie, params->req_ie, + params->req_ie_len); + next += params->req_ie_len; } - if (bss) - cfg80211_hold_bss(bss_from_pub(bss)); - ev->cr.bss = bss; - ev->cr.status = status; - ev->cr.timeout_reason = timeout_reason; + if (params->resp_ie_len) { + ev->cr.resp_ie = next; + ev->cr.resp_ie_len = params->resp_ie_len; + memcpy((void *)ev->cr.resp_ie, params->resp_ie, + params->resp_ie_len); + next += params->resp_ie_len; + } + if (params->bss) + cfg80211_hold_bss(bss_from_pub(params->bss)); + ev->cr.bss = params->bss; + ev->cr.status = params->status; + ev->cr.timeout_reason = params->timeout_reason; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); spin_unlock_irqrestore(&wdev->event_lock, flags); queue_work(cfg80211_wq, &rdev->event_work); } -EXPORT_SYMBOL(cfg80211_connect_bss); +EXPORT_SYMBOL(cfg80211_connect_done); /* Consumes bss object one way or another */ void __cfg80211_roamed(struct wireless_dev *wdev, diff --git a/net/wireless/util.c b/net/wireless/util.c index 737c9c2c9cc9a..8d6a0a7b1ca1c 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -929,7 +929,6 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) { struct cfg80211_event *ev; unsigned long flags; - const u8 *bssid = NULL; spin_lock_irqsave(&wdev->event_lock, flags); while (!list_empty(&wdev->event_list)) { @@ -941,15 +940,10 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) wdev_lock(wdev); switch (ev->type) { case EVENT_CONNECT_RESULT: - if (!is_zero_ether_addr(ev->cr.bssid)) - bssid = ev->cr.bssid; __cfg80211_connect_result( - wdev->netdev, bssid, - ev->cr.req_ie, ev->cr.req_ie_len, - ev->cr.resp_ie, ev->cr.resp_ie_len, - ev->cr.status, - ev->cr.status == WLAN_STATUS_SUCCESS, - ev->cr.bss, ev->cr.timeout_reason); + wdev->netdev, + &ev->cr, + ev->cr.status == WLAN_STATUS_SUCCESS); break; case EVENT_ROAMED: __cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie, From a3caf7440dedd2399f90f27ff11ac390bf03e6c4 Mon Sep 17 00:00:00 2001 From: Vidyullatha Kanchanapally Date: Fri, 31 Mar 2017 00:22:34 +0300 Subject: [PATCH 33/48] cfg80211: Add support for FILS shared key authentication offload Enhance nl80211 and cfg80211 connect request and response APIs to support FILS shared key authentication offload. The new nl80211 attributes can be used to provide additional information to the driver to establish a FILS connection. Also enhance the set/del PMKSA to allow support for adding and deleting PMKSA based on FILS cache identifier. Add a new feature flag that drivers can use to advertize support for FILS shared key authentication and association in station mode when using their own SME. Signed-off-by: Vidyullatha Kanchanapally Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 13 ++++++ include/net/cfg80211.h | 57 ++++++++++++++++++++++- include/uapi/linux/nl80211.h | 86 ++++++++++++++++++++++++++++++++-- net/wireless/nl80211.c | 90 +++++++++++++++++++++++++++++++++--- net/wireless/sme.c | 25 +++++++++- 5 files changed, 259 insertions(+), 12 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 22bf0676d9280..294fa6273a626 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1723,6 +1723,9 @@ enum ieee80211_statuscode { WLAN_STATUS_REJECT_DSE_BAND = 96, WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL = 99, WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT = 103, + /* 802.11ai */ + WLAN_STATUS_FILS_AUTHENTICATION_FAILURE = 108, + WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER = 109, }; @@ -2104,6 +2107,12 @@ enum ieee80211_key_len { #define FILS_NONCE_LEN 16 #define FILS_MAX_KEK_LEN 64 +#define FILS_ERP_MAX_USERNAME_LEN 16 +#define FILS_ERP_MAX_REALM_LEN 253 +#define FILS_ERP_MAX_RRK_LEN 64 + +#define PMK_MAX_LEN 48 + /* Public action codes */ enum ieee80211_pub_actioncode { WLAN_PUB_ACTION_EXT_CHANSW_ANN = 4, @@ -2355,6 +2364,10 @@ enum ieee80211_sa_query_action { #define WLAN_AKM_SUITE_TDLS SUITE(0x000FAC, 7) #define WLAN_AKM_SUITE_SAE SUITE(0x000FAC, 8) #define WLAN_AKM_SUITE_FT_OVER_SAE SUITE(0x000FAC, 9) +#define WLAN_AKM_SUITE_FILS_SHA256 SUITE(0x000FAC, 14) +#define WLAN_AKM_SUITE_FILS_SHA384 SUITE(0x000FAC, 15) +#define WLAN_AKM_SUITE_FT_FILS_SHA256 SUITE(0x000FAC, 16) +#define WLAN_AKM_SUITE_FT_FILS_SHA384 SUITE(0x000FAC, 17) #define WLAN_MAX_KEY_LEN 32 diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index da12d5b86e1bb..042137d7d2260 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2073,6 +2073,19 @@ struct cfg80211_bss_selection { * the BSSID of the current association, i.e., to the value that is * included in the Current AP address field of the Reassociation Request * frame. + * @fils_erp_username: EAP re-authentication protocol (ERP) username part of the + * NAI or %NULL if not specified. This is used to construct FILS wrapped + * data IE. + * @fils_erp_username_len: Length of @fils_erp_username in octets. + * @fils_erp_realm: EAP re-authentication protocol (ERP) realm part of NAI or + * %NULL if not specified. This specifies the domain name of ER server and + * is used to construct FILS wrapped data IE. + * @fils_erp_realm_len: Length of @fils_erp_realm in octets. + * @fils_erp_next_seq_num: The next sequence number to use in the FILS ERP + * messages. This is also used to construct FILS wrapped data IE. + * @fils_erp_rrk: ERP re-authentication Root Key (rRK) used to derive additional + * keys in FILS or %NULL if not specified. + * @fils_erp_rrk_len: Length of @fils_erp_rrk in octets. */ struct cfg80211_connect_params { struct ieee80211_channel *channel; @@ -2098,6 +2111,13 @@ struct cfg80211_connect_params { bool pbss; struct cfg80211_bss_selection bss_select; const u8 *prev_bssid; + const u8 *fils_erp_username; + size_t fils_erp_username_len; + const u8 *fils_erp_realm; + size_t fils_erp_realm_len; + u16 fils_erp_next_seq_num; + const u8 *fils_erp_rrk; + size_t fils_erp_rrk_len; }; /** @@ -2136,12 +2156,27 @@ enum wiphy_params_flags { * This structure is passed to the set/del_pmksa() method for PMKSA * caching. * - * @bssid: The AP's BSSID. - * @pmkid: The PMK material itself. + * @bssid: The AP's BSSID (may be %NULL). + * @pmkid: The identifier to refer a PMKSA. + * @pmk: The PMK for the PMKSA identified by @pmkid. This is used for key + * derivation by a FILS STA. Otherwise, %NULL. + * @pmk_len: Length of the @pmk. The length of @pmk can differ depending on + * the hash algorithm used to generate this. + * @ssid: SSID to specify the ESS within which a PMKSA is valid when using FILS + * cache identifier (may be %NULL). + * @ssid_len: Length of the @ssid in octets. + * @cache_id: 2-octet cache identifier advertized by a FILS AP identifying the + * scope of PMKSA. This is valid only if @ssid_len is non-zero (may be + * %NULL). */ struct cfg80211_pmksa { const u8 *bssid; const u8 *pmkid; + const u8 *pmk; + size_t pmk_len; + const u8 *ssid; + size_t ssid_len; + const u8 *cache_id; }; /** @@ -5153,6 +5188,17 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) * @req_ie_len: Association request IEs length * @resp_ie: Association response IEs (may be %NULL) * @resp_ie_len: Association response IEs length + * @fils_kek: KEK derived from a successful FILS connection (may be %NULL) + * @fils_kek_len: Length of @fils_kek in octets + * @update_erp_next_seq_num: Boolean value to specify whether the value in + * @fils_erp_next_seq_num is valid. + * @fils_erp_next_seq_num: The next sequence number to use in ERP message in + * FILS Authentication. This value should be specified irrespective of the + * status for a FILS connection. + * @pmk: A new PMK if derived from a successful FILS connection (may be %NULL). + * @pmk_len: Length of @pmk in octets + * @pmkid: A new PMKID if derived from a successful FILS connection or the PMKID + * used for this FILS connection (may be %NULL). * @timeout_reason: Reason for connection timeout. This is used when the * connection fails due to a timeout instead of an explicit rejection from * the AP. %NL80211_TIMEOUT_UNSPECIFIED is used when the timeout reason is @@ -5168,6 +5214,13 @@ struct cfg80211_connect_resp_params { size_t req_ie_len; const u8 *resp_ie; size_t resp_ie_len; + const u8 *fils_kek; + size_t fils_kek_len; + bool update_erp_next_seq_num; + u16 fils_erp_next_seq_num; + const u8 *pmk; + size_t pmk_len; + const u8 *pmkid; enum nl80211_timeout_reason timeout_reason; }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index cd4dfef58fabb..6095a6c4c4126 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -172,6 +172,42 @@ * Multiple such rules can be created. */ +/** + * DOC: FILS shared key authentication offload + * + * FILS shared key authentication offload can be advertized by drivers by + * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support + * FILS shared key authentication offload should be able to construct the + * authentication and association frames for FILS shared key authentication and + * eventually do a key derivation as per IEEE 802.11ai. The below additional + * parameters should be given to driver in %NL80211_CMD_CONNECT. + * %NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai + * %NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai + * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message + * %NL80211_ATTR_FILS_ERP_RRK - used to generate the rIK and rMSK + * rIK should be used to generate an authentication tag on the ERP message and + * rMSK should be used to derive a PMKSA. + * rIK, rMSK should be generated and keyname_nai, sequence number should be used + * as specified in IETF RFC 6696. + * + * When FILS shared key authentication is completed, driver needs to provide the + * below additional parameters to userspace. + * %NL80211_ATTR_FILS_KEK - used for key renewal + * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges + * %NL80211_ATTR_PMKID - used to identify the PMKSA used/generated + * %Nl80211_ATTR_PMK - used to update PMKSA cache in userspace + * The PMKSA can be maintained in userspace persistently so that it can be used + * later after reboots or wifi turn off/on also. + * + * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS + * capable AP supporting PMK caching. It specifies the scope within which the + * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and + * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based + * on FILS cache identifier. Additionally %NL80211_ATTR_PMK is used with + * %NL80211_SET_PMKSA to specify the PMK corresponding to a PMKSA for driver to + * use in a FILS shared key connection with PMKSA caching. + */ + /** * enum nl80211_commands - supported nl80211 commands * @@ -370,10 +406,18 @@ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) * - * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC - * (for the BSSID) and %NL80211_ATTR_PMKID. + * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry using %NL80211_ATTR_MAC + * (for the BSSID), %NL80211_ATTR_PMKID, and optionally %NL80211_ATTR_PMK + * (PMK is used for PTKSA derivation in case of FILS shared key offload) or + * using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID, + * %NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS + * authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier + * advertized by a FILS capable AP identifying the scope of PMKSA in an + * ESS. * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC - * (for the BSSID) and %NL80211_ATTR_PMKID. + * (for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID, + * %NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS + * authentication. * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries. * * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain @@ -2012,6 +2056,31 @@ enum nl80211_commands { * u32 attribute with an &enum nl80211_timeout_reason value. This is used, * e.g., with %NL80211_CMD_CONNECT event. * + * @NL80211_ATTR_FILS_ERP_USERNAME: EAP Re-authentication Protocol (ERP) + * username part of NAI used to refer keys rRK and rIK. This is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_REALM: EAP Re-authentication Protocol (ERP) realm part + * of NAI specifying the domain name of the ER server. This is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM: Unsigned 16-bit ERP next sequence number + * to use in ERP messages. This is used in generating the FILS wrapped data + * for FILS authentication and is used with %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_RRK: ERP re-authentication Root Key (rRK) for the + * NAI specified by %NL80211_ATTR_FILS_ERP_USERNAME and + * %NL80211_ATTR_FILS_ERP_REALM. This is used for generating rIK and rMSK + * from successful FILS authentication and is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP + * identifying the scope of PMKSAs. This is used with + * @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA. + * + * @NL80211_ATTR_PMK: PMK for the PMKSA identified by %NL80211_ATTR_PMKID. + * This is used with @NL80211_CMD_SET_PMKSA. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2423,6 +2492,14 @@ enum nl80211_attrs { NL80211_ATTR_TIMEOUT_REASON, + NL80211_ATTR_FILS_ERP_USERNAME, + NL80211_ATTR_FILS_ERP_REALM, + NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, + NL80211_ATTR_FILS_ERP_RRK, + NL80211_ATTR_FILS_CACHE_ID, + + NL80211_ATTR_PMK, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -4759,6 +4836,8 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the * %NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more * RSSI threshold values to monitor rather than exactly one threshold. + * @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key + * authentication with %NL80211_CMD_CONNECT. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -4778,6 +4857,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED, NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI, NL80211_EXT_FEATURE_CQM_RSSI_LIST, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 3d635c865281d..9910aae08f1a9 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -410,6 +410,15 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { .len = sizeof(struct nl80211_bss_select_rssi_adjust) }, [NL80211_ATTR_TIMEOUT_REASON] = { .type = NLA_U32 }, + [NL80211_ATTR_FILS_ERP_USERNAME] = { .type = NLA_BINARY, + .len = FILS_ERP_MAX_USERNAME_LEN }, + [NL80211_ATTR_FILS_ERP_REALM] = { .type = NLA_BINARY, + .len = FILS_ERP_MAX_REALM_LEN }, + [NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] = { .type = NLA_U16 }, + [NL80211_ATTR_FILS_ERP_RRK] = { .type = NLA_BINARY, + .len = FILS_ERP_MAX_RRK_LEN }, + [NL80211_ATTR_FILS_CACHE_ID] = { .len = 2 }, + [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN }, }; /* policy for the key attributes */ @@ -3832,6 +3841,19 @@ static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev, return false; return true; case NL80211_CMD_CONNECT: + /* SAE not supported yet */ + if (auth_type == NL80211_AUTHTYPE_SAE) + return false; + /* FILS with SK PFS or PK not supported yet */ + if (auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || + auth_type == NL80211_AUTHTYPE_FILS_PK) + return false; + if (!wiphy_ext_feature_isset( + &rdev->wiphy, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD) && + auth_type == NL80211_AUTHTYPE_FILS_SK) + return false; + return true; case NL80211_CMD_START_AP: /* SAE not supported yet */ if (auth_type == NL80211_AUTHTYPE_SAE) @@ -8906,6 +8928,35 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) } } + if (wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD) && + info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] && + info->attrs[NL80211_ATTR_FILS_ERP_REALM] && + info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] && + info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { + connect.fils_erp_username = + nla_data(info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); + connect.fils_erp_username_len = + nla_len(info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); + connect.fils_erp_realm = + nla_data(info->attrs[NL80211_ATTR_FILS_ERP_REALM]); + connect.fils_erp_realm_len = + nla_len(info->attrs[NL80211_ATTR_FILS_ERP_REALM]); + connect.fils_erp_next_seq_num = + nla_get_u16( + info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM]); + connect.fils_erp_rrk = + nla_data(info->attrs[NL80211_ATTR_FILS_ERP_RRK]); + connect.fils_erp_rrk_len = + nla_len(info->attrs[NL80211_ATTR_FILS_ERP_RRK]); + } else if (info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] || + info->attrs[NL80211_ATTR_FILS_ERP_REALM] || + info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] || + info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { + kzfree(connkeys); + return -EINVAL; + } + wdev_lock(dev->ieee80211_ptr); err = cfg80211_connect(rdev, dev, &connect, connkeys, @@ -9025,14 +9076,28 @@ static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info) memset(&pmksa, 0, sizeof(struct cfg80211_pmksa)); - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - if (!info->attrs[NL80211_ATTR_PMKID]) return -EINVAL; pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]); - pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_MAC]) { + pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + } else if (info->attrs[NL80211_ATTR_SSID] && + info->attrs[NL80211_ATTR_FILS_CACHE_ID] && + (info->genlhdr->cmd == NL80211_CMD_DEL_PMKSA || + info->attrs[NL80211_ATTR_PMK])) { + pmksa.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + pmksa.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + pmksa.cache_id = + nla_data(info->attrs[NL80211_ATTR_FILS_CACHE_ID]); + } else { + return -EINVAL; + } + if (info->attrs[NL80211_ATTR_PMK]) { + pmksa.pmk = nla_data(info->attrs[NL80211_ATTR_PMK]); + pmksa.pmk_len = nla_len(info->attrs[NL80211_ATTR_PMK]); + } if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) @@ -13471,7 +13536,9 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, struct sk_buff *msg; void *hdr; - msg = nlmsg_new(100 + cr->req_ie_len + cr->resp_ie_len, gfp); + msg = nlmsg_new(100 + cr->req_ie_len + cr->resp_ie_len + + cr->fils_kek_len + cr->pmk_len + + (cr->pmkid ? WLAN_PMKID_LEN : 0), gfp); if (!msg) return; @@ -13496,7 +13563,18 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, nla_put(msg, NL80211_ATTR_REQ_IE, cr->req_ie_len, cr->req_ie)) || (cr->resp_ie && nla_put(msg, NL80211_ATTR_RESP_IE, cr->resp_ie_len, - cr->resp_ie))) + cr->resp_ie)) || + (cr->update_erp_next_seq_num && + nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, + cr->fils_erp_next_seq_num)) || + (cr->status == WLAN_STATUS_SUCCESS && + ((cr->fils_kek && + nla_put(msg, NL80211_ATTR_FILS_KEK, cr->fils_kek_len, + cr->fils_kek)) || + (cr->pmk && + nla_put(msg, NL80211_ATTR_PMK, cr->pmk_len, cr->pmk)) || + (cr->pmkid && + nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->pmkid))))) goto nla_put_failure; genlmsg_end(msg, hdr); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index ebd7adc272462..6459bb7c21f79 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -805,7 +805,9 @@ void cfg80211_connect_done(struct net_device *dev, } ev = kzalloc(sizeof(*ev) + (params->bssid ? ETH_ALEN : 0) + - params->req_ie_len + params->resp_ie_len, gfp); + params->req_ie_len + params->resp_ie_len + + params->fils_kek_len + params->pmk_len + + (params->pmkid ? WLAN_PMKID_LEN : 0), gfp); if (!ev) { cfg80211_put_bss(wdev->wiphy, params->bss); return; @@ -832,6 +834,27 @@ void cfg80211_connect_done(struct net_device *dev, params->resp_ie_len); next += params->resp_ie_len; } + if (params->fils_kek_len) { + ev->cr.fils_kek = next; + ev->cr.fils_kek_len = params->fils_kek_len; + memcpy((void *)ev->cr.fils_kek, params->fils_kek, + params->fils_kek_len); + next += params->fils_kek_len; + } + if (params->pmk_len) { + ev->cr.pmk = next; + ev->cr.pmk_len = params->pmk_len; + memcpy((void *)ev->cr.pmk, params->pmk, params->pmk_len); + next += params->pmk_len; + } + if (params->pmkid) { + ev->cr.pmkid = next; + memcpy((void *)ev->cr.pmkid, params->pmkid, WLAN_PMKID_LEN); + next += WLAN_PMKID_LEN; + } + ev->cr.update_erp_next_seq_num = params->update_erp_next_seq_num; + if (params->update_erp_next_seq_num) + ev->cr.fils_erp_next_seq_num = params->fils_erp_next_seq_num; if (params->bss) cfg80211_hold_bss(bss_from_pub(params->bss)); ev->cr.bss = params->bss; From 2754867792edfc9a6f2f49294be278d9be533ce2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 31 Mar 2017 09:12:39 +0200 Subject: [PATCH 34/48] cfg80211: add documentation for cfg80211_get_bss() This was missing, but is referenced a lot in the documentation. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 042137d7d2260..273b1dca0861f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4699,12 +4699,22 @@ cfg80211_inform_bss(struct wiphy *wiphy, gfp); } +/** + * cfg80211_get_bss - get a BSS reference + * @wiphy: the wiphy this BSS struct belongs to + * @channel: the channel to search on (or %NULL) + * @bssid: the desired BSSID (or %NULL) + * @ssid: the desired SSID (or %NULL) + * @ssid_len: length of the SSID (or 0) + * @bss_type: type of BSS, see &enum ieee80211_bss_type + * @privacy: privacy filter, see &enum ieee80211_privacy + */ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, const u8 *ssid, size_t ssid_len, enum ieee80211_bss_type bss_type, - enum ieee80211_privacy); + enum ieee80211_privacy privacy); static inline struct cfg80211_bss * cfg80211_get_ibss(struct wiphy *wiphy, struct ieee80211_channel *channel, From a339e4c226c0f2ea0c138e9ffc419874d370f57b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 31 Mar 2017 09:13:13 +0200 Subject: [PATCH 35/48] cfg80211: add intro to documentation This introduction section should be used in the documentation, do that at the beginning of the cfg80211 chapter. Signed-off-by: Johannes Berg --- Documentation/driver-api/80211/cfg80211.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/driver-api/80211/cfg80211.rst b/Documentation/driver-api/80211/cfg80211.rst index b101bc0c195b7..8ffac57e1f5b7 100644 --- a/Documentation/driver-api/80211/cfg80211.rst +++ b/Documentation/driver-api/80211/cfg80211.rst @@ -2,6 +2,9 @@ cfg80211 subsystem ================== +.. kernel-doc:: include/net/cfg80211.h + :doc: Introduction + Device registration =================== From 93f56de259376d7e4fff2b2d104082e1fa66e237 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Thu, 6 Apr 2017 16:31:41 -0700 Subject: [PATCH 36/48] mac80211: Fix clang warning about constant operand in logical operation When clang detects a non-boolean constant in a logical operation it generates a 'constant-logical-operand' warning. In ieee80211_try_rate_control_ops_get() the result of strlen() is used in a logical operation, clang resolves the expression to an (integer) constant at compile time when clang's builtin strlen function is used. Change the condition to check for strlen() > 0 to make the constant operand boolean and thus avoid the warning. Signed-off-by: Matthias Kaehlcke Signed-off-by: Johannes Berg --- net/mac80211/rate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 3bddd9bbb76fc..9d7a1cd949fb5 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -174,9 +174,11 @@ ieee80211_rate_control_ops_get(const char *name) /* try default if specific alg requested but not found */ ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo); - /* try built-in one if specific alg requested but not found */ - if (!ops && strlen(CONFIG_MAC80211_RC_DEFAULT)) + /* Note: check for > 0 is intentional to avoid clang warning */ + if (!ops && (strlen(CONFIG_MAC80211_RC_DEFAULT) > 0)) + /* try built-in one if specific alg requested but not found */ ops = ieee80211_try_rate_control_ops_get(CONFIG_MAC80211_RC_DEFAULT); + kernel_param_unlock(THIS_MODULE); return ops; From 30841f5cdeccd24c4a68b9df681b3ef11b0dda53 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 11 Apr 2017 15:38:56 +0200 Subject: [PATCH 37/48] mac80211: drop frames too short for FCS earlier Instead of dropping such frames only when removing the monitor info, drop them earlier (keeping the warning) and simplify removing monitor info. While at it, make that function return void. Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index e48724a6725e3..e35c42ebb7a57 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -95,24 +95,13 @@ static u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, * This function cleans up the SKB, i.e. it removes all the stuff * only useful for monitoring. */ -static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, - struct sk_buff *skb, - unsigned int rtap_vendor_space) +static void remove_monitor_info(struct sk_buff *skb, + unsigned int present_fcs_len, + unsigned int rtap_vendor_space) { - if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) { - if (likely(skb->len > FCS_LEN)) - __pskb_trim(skb, skb->len - FCS_LEN); - else { - /* driver bug */ - WARN_ON(1); - dev_kfree_skb(skb); - return NULL; - } - } - + if (present_fcs_len) + __pskb_trim(skb, skb->len - present_fcs_len); __pskb_pull(skb, rtap_vendor_space); - - return skb; } static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len, @@ -534,8 +523,15 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, * the SKB because it has a bad FCS/PLCP checksum. */ - if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) + if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) { + if (unlikely(origskb->len <= FCS_LEN)) { + /* driver bug */ + WARN_ON(1); + dev_kfree_skb(origskb); + return NULL; + } present_fcs_len = FCS_LEN; + } /* ensure hdr->frame_control and vendor radiotap data are in skb head */ if (!pskb_may_pull(origskb, 2 + rtap_vendor_space)) { @@ -550,7 +546,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, return NULL; } - return remove_monitor_info(local, origskb, rtap_vendor_space); + remove_monitor_info(origskb, present_fcs_len, + rtap_vendor_space); + return origskb; } /* room for the radiotap header based on driver features */ @@ -580,9 +578,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, * and FCS from the original. */ skb = skb_copy_expand(origskb, needed_headroom, 0, GFP_ATOMIC); - - origskb = remove_monitor_info(local, origskb, - rtap_vendor_space); + remove_monitor_info(origskb, present_fcs_len, + rtap_vendor_space); if (!skb) return origskb; From b0265024b8b5fb35d1e1a1da6be65399e33e122e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 10:43:49 +0200 Subject: [PATCH 38/48] cfg80211: allow leaving MU-MIMO monitor configuration unchanged When changing monitor parameters, not setting the MU-MIMO attributes should mean that they're not changed - it's documented that to turn the feature off it's necessary to set all-zero group membership and an invalid follow-address. This isn't implemented. Fix this by making the parameters pointers, stop reusing the macaddr struct member, and documenting that NULL pointers mean unchanged. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 9 ++++++--- net/mac80211/cfg.c | 8 ++++---- net/wireless/nl80211.c | 8 +++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 273b1dca0861f..ba9348ee5327a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -370,13 +370,16 @@ static inline void wiphy_read_of_freq_limits(struct wiphy *wiphy) * 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. + * @vht_mumimo_groups: MU-MIMO groupID, used for monitoring MU-MIMO packets + * belonging to that MU-MIMO groupID; %NULL if not changed + * @vht_mumimo_follow_addr: MU-MIMO follow address, used for monitoring + * MU-MIMO packets going to the specified station; %NULL if not changed */ struct vif_params { int use_4addr; u8 macaddr[ETH_ALEN]; - u8 vht_mumimo_groups[VHT_MUMIMO_GROUPS_DATA_LEN]; + const u8 *vht_mumimo_groups; + const u8 *vht_mumimo_follow_addr; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8bc3d36693482..ef7de9eb94b18 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -80,8 +80,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy, u32 mu_mntr_cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; monitor_sdata = rtnl_dereference(local->monitor_sdata); - if (monitor_sdata && - wiphy_ext_feature_isset(wiphy, mu_mntr_cap_flag)) { + if (monitor_sdata && params->vht_mumimo_groups) { memcpy(monitor_sdata->vif.bss_conf.mu_group.membership, params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN); memcpy(monitor_sdata->vif.bss_conf.mu_group.position, @@ -90,10 +89,11 @@ static int ieee80211_change_iface(struct wiphy *wiphy, monitor_sdata->vif.mu_mimo_owner = true; ieee80211_bss_info_change_notify(monitor_sdata, BSS_CHANGED_MU_GROUPS); + } + if (monitor_sdata && params->vht_mumimo_follow_addr) ether_addr_copy(monitor_sdata->u.mntr.mu_follow_addr, - params->macaddr); - } + params->vht_mumimo_follow_addr); if (!flags) return 0; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9910aae08f1a9..5cdb0f9b01683 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2832,8 +2832,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) (mumimo_groups[VHT_MUMIMO_GROUPS_DATA_LEN - 1] & BIT(0))) return -EINVAL; - memcpy(params.vht_mumimo_groups, mumimo_groups, - VHT_MUMIMO_GROUPS_DATA_LEN); + params.vht_mumimo_groups = mumimo_groups; change = true; } @@ -2843,9 +2842,8 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) 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); + params.vht_mumimo_follow_addr = + nla_data(info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]); change = true; } From 8c5e68894450d3bb7471e426e2eec9a8472bb660 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 10:46:13 +0200 Subject: [PATCH 39/48] mac80211: correct MU-MIMO monitor follow functionality The MU-MIMO monitor follow functionality is broken because it doesn't clear the MU-MIMO owner even if both follow features are disabled. Fix that, and while at it move the code into a new helper function. Call this also when creating a new monitor interface to prepare for an upcoming cfg80211 change allowing that. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 78 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 20 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index ef7de9eb94b18..e276b8cb24a4f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -22,6 +22,49 @@ #include "mesh.h" #include "wme.h" +static int ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata, + struct vif_params *params) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *monitor_sdata; + bool mu_mimo_groups = false; + bool mu_mimo_follow = false; + + monitor_sdata = rtnl_dereference(local->monitor_sdata); + + if (!monitor_sdata) + return -EOPNOTSUPP; + + if (params->vht_mumimo_groups) { + u64 membership; + + BUILD_BUG_ON(sizeof(membership) != WLAN_MEMBERSHIP_LEN); + + memcpy(monitor_sdata->vif.bss_conf.mu_group.membership, + params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN); + memcpy(monitor_sdata->vif.bss_conf.mu_group.position, + params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN, + WLAN_USER_POSITION_LEN); + ieee80211_bss_info_change_notify(monitor_sdata, + BSS_CHANGED_MU_GROUPS); + /* don't care about endianness - just check for 0 */ + memcpy(&membership, params->vht_mumimo_groups, + WLAN_MEMBERSHIP_LEN); + mu_mimo_groups = membership != 0; + } + + if (params->vht_mumimo_follow_addr) { + mu_mimo_follow = + is_valid_ether_addr(params->vht_mumimo_follow_addr); + ether_addr_copy(monitor_sdata->u.mntr.mu_follow_addr, + params->vht_mumimo_follow_addr); + } + + monitor_sdata->vif.mu_mimo_owner = mu_mimo_groups || mu_mimo_follow; + + return 0; +} + static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, @@ -38,9 +81,17 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, if (err) return ERR_PTR(err); - if (type == NL80211_IFTYPE_MONITOR && flags) { - sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); - sdata->u.mntr.flags = *flags; + sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + + if (type == NL80211_IFTYPE_MONITOR) { + err = ieee80211_set_mu_mimo_follow(sdata, params); + if (err) { + ieee80211_if_remove(sdata); + return NULL; + } + + if (flags) + sdata->u.mntr.flags = *flags; } return wdev; @@ -76,24 +127,11 @@ static int ieee80211_change_iface(struct wiphy *wiphy, if (sdata->vif.type == NL80211_IFTYPE_MONITOR) { struct ieee80211_local *local = sdata->local; - struct ieee80211_sub_if_data *monitor_sdata; - u32 mu_mntr_cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; - - monitor_sdata = rtnl_dereference(local->monitor_sdata); - if (monitor_sdata && params->vht_mumimo_groups) { - memcpy(monitor_sdata->vif.bss_conf.mu_group.membership, - params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN); - memcpy(monitor_sdata->vif.bss_conf.mu_group.position, - params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN, - WLAN_USER_POSITION_LEN); - monitor_sdata->vif.mu_mimo_owner = true; - ieee80211_bss_info_change_notify(monitor_sdata, - BSS_CHANGED_MU_GROUPS); - } + int err; - if (monitor_sdata && params->vht_mumimo_follow_addr) - ether_addr_copy(monitor_sdata->u.mntr.mu_follow_addr, - params->vht_mumimo_follow_addr); + err = ieee80211_set_mu_mimo_follow(sdata, params); + if (err) + return err; if (!flags) return 0; From 818a986e4ebacea2020622e48c8bc04b7f500d89 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 11:23:28 +0200 Subject: [PATCH 40/48] cfg80211: move add/change interface monitor flags into params Instead passing both flags, which can be NULL, and vif_params, which are never NULL, move the flags into the vif_params and use BIT(0), which is invalid from userspace, to indicate that the flags were changed. While updating all drivers, fix a small bug in wil6210 where it was setting the flags to 0 instead of leaving them unchanged. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 3 +- drivers/net/wireless/ath/wil6210/cfg80211.c | 11 +++--- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 12 +++---- .../broadcom/brcm80211/brcmfmac/p2p.c | 3 +- .../broadcom/brcm80211/brcmfmac/p2p.h | 2 +- drivers/net/wireless/intersil/orinoco/cfg.c | 2 +- drivers/net/wireless/marvell/libertas/cfg.c | 2 +- .../net/wireless/marvell/mwifiex/cfg80211.c | 27 +++++++-------- drivers/net/wireless/marvell/mwifiex/main.c | 7 ++-- drivers/net/wireless/marvell/mwifiex/main.h | 1 - drivers/net/wireless/rndis_wlan.c | 4 +-- .../staging/wilc1000/wilc_wfi_cfgoperations.c | 3 +- drivers/staging/wlan-ng/cfg80211.c | 2 +- include/net/cfg80211.h | 8 +++-- net/mac80211/cfg.c | 15 ++++---- net/wireless/core.h | 2 +- net/wireless/nl80211.c | 34 +++++++++++-------- net/wireless/rdev-ops.h | 9 +++-- net/wireless/util.c | 4 +-- net/wireless/wext-compat.c | 2 +- 20 files changed, 73 insertions(+), 80 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 363b30a549c2b..48dc66aac67d0 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1505,7 +1505,6 @@ static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, - u32 *flags, struct vif_params *params) { struct ath6kl *ar = wiphy_priv(wiphy); @@ -1552,7 +1551,7 @@ static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy, static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct ath6kl_vif *vif = netdev_priv(ndev); diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 79d107018eac3..17fc152e33d96 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -255,7 +255,7 @@ static struct wireless_dev * wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, - u32 *flags, struct vif_params *params) + struct vif_params *params) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); struct net_device *ndev = wil_to_ndev(wil); @@ -306,7 +306,7 @@ static int wil_cfg80211_del_iface(struct wiphy *wiphy, static int wil_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); @@ -333,11 +333,8 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy, case NL80211_IFTYPE_P2P_GO: break; case NL80211_IFTYPE_MONITOR: - if (flags) - wil->monitor_flags = *flags; - else - wil->monitor_flags = 0; - + if (params->flags) + wil->monitor_flags = params->flags; break; default: return -EOPNOTSUPP; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 944b83cfc5197..bd245a6c70d94 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -575,12 +575,11 @@ static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp) * * @wiphy: wiphy device of new interface. * @name: name of the new interface. - * @flags: not used. * @params: contains mac address for AP device. */ static struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, - u32 *flags, struct vif_params *params) + struct vif_params *params) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); @@ -653,7 +652,6 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, - u32 *flags, struct vif_params *params) { struct wireless_dev *wdev; @@ -674,12 +672,12 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, case NL80211_IFTYPE_MESH_POINT: return ERR_PTR(-EOPNOTSUPP); case NL80211_IFTYPE_AP: - wdev = brcmf_ap_add_vif(wiphy, name, flags, params); + wdev = brcmf_ap_add_vif(wiphy, name, params); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: - wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params); + wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, params); break; case NL80211_IFTYPE_UNSPECIFIED: default: @@ -858,7 +856,7 @@ int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) static s32 brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); @@ -6549,7 +6547,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) if (err) goto default_conf_out; err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, - NULL, NULL); + NULL); if (err) goto default_conf_out; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index de19c7c92bc6c..208ae3535367b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -2141,12 +2141,11 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, * @name: name of the new interface. * @name_assign_type: origin of the interface name * @type: nl80211 interface type. - * @flags: not used. * @params: contains mac address for P2P device. */ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h index 8ce9447533ef8..0e8b34d2d85cb 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h @@ -150,7 +150,7 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced); void brcmf_p2p_detach(struct brcmf_p2p_info *p2p); struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params); int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev); int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg, diff --git a/drivers/net/wireless/intersil/orinoco/cfg.c b/drivers/net/wireless/intersil/orinoco/cfg.c index 7aa47069af0a7..b2d5ec8634b5d 100644 --- a/drivers/net/wireless/intersil/orinoco/cfg.c +++ b/drivers/net/wireless/intersil/orinoco/cfg.c @@ -97,7 +97,7 @@ int orinoco_wiphy_register(struct wiphy *wiphy) } static int orinoco_change_vif(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct orinoco_private *priv = wiphy_priv(wiphy); diff --git a/drivers/net/wireless/marvell/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c index 3f97acb57e66f..a0463fef79b01 100644 --- a/drivers/net/wireless/marvell/libertas/cfg.c +++ b/drivers/net/wireless/marvell/libertas/cfg.c @@ -1657,7 +1657,7 @@ static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, */ static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct lbs_private *priv = wiphy_priv(wiphy); diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 1e3bd435a6945..322adad0a65a6 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -916,7 +916,7 @@ mwifiex_init_new_priv_params(struct mwifiex_private *priv, static int mwifiex_change_vif_to_p2p(struct net_device *dev, enum nl80211_iftype curr_iftype, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct mwifiex_private *priv; @@ -988,7 +988,7 @@ mwifiex_change_vif_to_p2p(struct net_device *dev, static int mwifiex_change_vif_to_sta_adhoc(struct net_device *dev, enum nl80211_iftype curr_iftype, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct mwifiex_private *priv; @@ -1047,7 +1047,7 @@ mwifiex_change_vif_to_sta_adhoc(struct net_device *dev, static int mwifiex_change_vif_to_ap(struct net_device *dev, enum nl80211_iftype curr_iftype, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct mwifiex_private *priv; @@ -1103,7 +1103,7 @@ mwifiex_change_vif_to_ap(struct net_device *dev, static int mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); @@ -1124,10 +1124,10 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: return mwifiex_change_vif_to_p2p(dev, curr_iftype, - type, flags, params); + type, params); case NL80211_IFTYPE_AP: return mwifiex_change_vif_to_ap(dev, curr_iftype, type, - flags, params); + params); case NL80211_IFTYPE_UNSPECIFIED: mwifiex_dbg(priv->adapter, INFO, "%s: kept type as IBSS\n", dev->name); @@ -1154,10 +1154,10 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: return mwifiex_change_vif_to_p2p(dev, curr_iftype, - type, flags, params); + type, params); case NL80211_IFTYPE_AP: return mwifiex_change_vif_to_ap(dev, curr_iftype, type, - flags, params); + params); case NL80211_IFTYPE_UNSPECIFIED: mwifiex_dbg(priv->adapter, INFO, "%s: kept type as STA\n", dev->name); @@ -1175,13 +1175,12 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_STATION: return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype, - type, flags, - params); + type, params); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: return mwifiex_change_vif_to_p2p(dev, curr_iftype, - type, flags, params); + type, params); case NL80211_IFTYPE_UNSPECIFIED: mwifiex_dbg(priv->adapter, INFO, "%s: kept type as AP\n", dev->name); @@ -1214,14 +1213,13 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, if (mwifiex_cfg80211_deinit_p2p(priv)) return -EFAULT; return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype, - type, flags, - params); + type, params); break; case NL80211_IFTYPE_AP: if (mwifiex_cfg80211_deinit_p2p(priv)) return -EFAULT; return mwifiex_change_vif_to_ap(dev, curr_iftype, type, - flags, params); + params); case NL80211_IFTYPE_UNSPECIFIED: mwifiex_dbg(priv->adapter, INFO, "%s: kept type as P2P\n", dev->name); @@ -2822,7 +2820,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, - u32 *flags, struct vif_params *params) { struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 5ebca1d0cfc75..96b5fc5e53983 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -593,7 +593,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) rtnl_lock(); /* Create station interface by default */ wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM, - NL80211_IFTYPE_STATION, NULL, NULL); + NL80211_IFTYPE_STATION, NULL); if (IS_ERR(wdev)) { mwifiex_dbg(adapter, ERROR, "cannot create default STA interface\n"); @@ -603,7 +603,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) { wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM, - NL80211_IFTYPE_AP, NULL, NULL); + NL80211_IFTYPE_AP, NULL); if (IS_ERR(wdev)) { mwifiex_dbg(adapter, ERROR, "cannot create AP interface\n"); @@ -614,8 +614,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) { wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM, - NL80211_IFTYPE_P2P_CLIENT, NULL, - NULL); + NL80211_IFTYPE_P2P_CLIENT, NULL); if (IS_ERR(wdev)) { mwifiex_dbg(adapter, ERROR, "cannot create p2p client interface\n"); diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 5c8297207f339..bc19b0a3571fe 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1529,7 +1529,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, - u32 *flags, struct vif_params *params); int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev); diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 785334f7a5386..3495386482be7 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -479,7 +479,7 @@ struct rndis_wlan_private { */ static int rndis_change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params); static int rndis_scan(struct wiphy *wiphy, @@ -1857,7 +1857,7 @@ static struct ndis_80211_pmkid *update_pmkid(struct usbnet *usbdev, */ static int rndis_change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); diff --git a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c index 7961d1c568473..2b4536318ca62 100644 --- a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c +++ b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c @@ -1837,7 +1837,7 @@ static int set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, } static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, struct vif_params *params) + enum nl80211_iftype type, struct vif_params *params) { struct wilc_priv *priv; struct wilc_vif *vif; @@ -2099,7 +2099,6 @@ static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, - u32 *flags, struct vif_params *params) { struct wilc_vif *vif; diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c index 11870cb3f2548..cbb3388a97560 100644 --- a/drivers/staging/wlan-ng/cfg80211.c +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -100,7 +100,7 @@ static int prism2_domibset_pstr32(struct wlandevice *wlandev, /* The interface functions, called by the cfg80211 layer */ static int prism2_change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct wlandevice *wlandev = dev->ml_priv; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ba9348ee5327a..89fa4995ddca3 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -363,6 +363,8 @@ static inline void wiphy_read_of_freq_limits(struct wiphy *wiphy) /** * struct vif_params - describes virtual interface parameters + * @flags: monitor interface flags, unchanged if 0, otherwise + * %MONITOR_FLAG_CHANGED will be set * @use_4addr: use 4-address frames * @macaddr: address to use for this virtual interface. * If this parameter is set to zero address the driver may @@ -376,6 +378,7 @@ static inline void wiphy_read_of_freq_limits(struct wiphy *wiphy) * MU-MIMO packets going to the specified station; %NULL if not changed */ struct vif_params { + u32 flags; int use_4addr; u8 macaddr[ETH_ALEN]; const u8 *vht_mumimo_groups; @@ -1214,6 +1217,7 @@ static inline int cfg80211_get_station(struct net_device *dev, * Monitor interface configuration flags. Note that these must be the bits * according to the nl80211 flags. * + * @MONITOR_FLAG_CHANGED: set if the flags were changed * @MONITOR_FLAG_FCSFAIL: pass frames with bad FCS * @MONITOR_FLAG_PLCPFAIL: pass frames with bad PLCP * @MONITOR_FLAG_CONTROL: pass control frames @@ -1222,6 +1226,7 @@ static inline int cfg80211_get_station(struct net_device *dev, * @MONITOR_FLAG_ACTIVE: active monitor, ACKs frames on its MAC address */ enum monitor_flags { + MONITOR_FLAG_CHANGED = 1<<__NL80211_MNTR_FLAG_INVALID, MONITOR_FLAG_FCSFAIL = 1<u.mntr.flags = *flags; + sdata->u.mntr.flags = params->flags; } return wdev; @@ -106,7 +104,7 @@ static int ieee80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) static int ieee80211_change_iface(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -133,7 +131,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy, if (err) return err; - if (!flags) + if (!params->flags) return 0; if (ieee80211_sdata_running(sdata)) { @@ -149,11 +147,12 @@ static int ieee80211_change_iface(struct wiphy *wiphy, * cooked_mntrs, monitor and all fif_* counters * reconfigure hardware */ - if ((*flags & mask) != (sdata->u.mntr.flags & mask)) + if ((params->flags & mask) != + (sdata->u.mntr.flags & mask)) return -EBUSY; ieee80211_adjust_monitor_flags(sdata, -1); - sdata->u.mntr.flags = *flags; + sdata->u.mntr.flags = params->flags; ieee80211_adjust_monitor_flags(sdata, 1); ieee80211_configure_filter(local); @@ -163,7 +162,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy, * and ieee80211_do_open take care of "everything" * mentioned in the comment above. */ - sdata->u.mntr.flags = *flags; + sdata->u.mntr.flags = params->flags; } } diff --git a/net/wireless/core.h b/net/wireless/core.h index d614efb417261..5d27eca57d3b6 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -430,7 +430,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, void cfg80211_upload_connect_keys(struct wireless_dev *wdev); int cfg80211_change_iface(struct cfg80211_registered_device *rdev, struct net_device *dev, enum nl80211_iftype ntype, - u32 *flags, struct vif_params *params); + struct vif_params *params); void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); void cfg80211_process_wdev_events(struct wireless_dev *wdev); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5cdb0f9b01683..a07a55eda55be 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2726,6 +2726,8 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) if (flags[flag]) *mntrflags |= (1<user_ptr[1]; - u32 _flags, *flags = NULL; bool change = false; memset(¶ms, 0, sizeof(params)); @@ -2809,14 +2810,17 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) if (ntype != NL80211_IFTYPE_MONITOR) return -EINVAL; err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], - &_flags); + ¶ms.flags); if (err) return err; - flags = &_flags; change = true; } + if (params.flags & MONITOR_FLAG_ACTIVE && + !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) + return -EOPNOTSUPP; + if (info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]) { const u8 *mumimo_groups; u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; @@ -2847,12 +2851,8 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) change = true; } - if (flags && (*flags & MONITOR_FLAG_ACTIVE) && - !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) - return -EOPNOTSUPP; - if (change) - err = cfg80211_change_iface(rdev, dev, ntype, flags, ¶ms); + err = cfg80211_change_iface(rdev, dev, ntype, ¶ms); else err = 0; @@ -2870,7 +2870,6 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) struct sk_buff *msg; int err; enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; - u32 flags; /* to avoid failing a new interface creation due to pending removal */ cfg80211_destroy_ifaces(rdev); @@ -2906,11 +2905,17 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) return err; } - err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? - info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, - &flags); + if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { + if (type != NL80211_IFTYPE_MONITOR) + return -EINVAL; + + err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], + ¶ms.flags); + if (err) + return err; + } - if (!err && (flags & MONITOR_FLAG_ACTIVE) && + if (params.flags & MONITOR_FLAG_ACTIVE && !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) return -EOPNOTSUPP; @@ -2920,8 +2925,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) wdev = rdev_add_virtual_intf(rdev, nla_data(info->attrs[NL80211_ATTR_IFNAME]), - NET_NAME_USER, type, err ? NULL : &flags, - ¶ms); + NET_NAME_USER, type, ¶ms); if (WARN_ON(!wdev)) { nlmsg_free(msg); return -EPROTO; diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index f2baf59210911..e4a99989dd060 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -36,13 +36,13 @@ static inline void rdev_set_wakeup(struct cfg80211_registered_device *rdev, static inline struct wireless_dev *rdev_add_virtual_intf(struct cfg80211_registered_device *rdev, char *name, unsigned char name_assign_type, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct wireless_dev *ret; trace_rdev_add_virtual_intf(&rdev->wiphy, name, type); ret = rdev->ops->add_virtual_intf(&rdev->wiphy, name, name_assign_type, - type, flags, params); + type, params); trace_rdev_return_wdev(&rdev->wiphy, ret); return ret; } @@ -61,12 +61,11 @@ rdev_del_virtual_intf(struct cfg80211_registered_device *rdev, static inline int rdev_change_virtual_intf(struct cfg80211_registered_device *rdev, struct net_device *dev, enum nl80211_iftype type, - u32 *flags, struct vif_params *params) + struct vif_params *params) { int ret; trace_rdev_change_virtual_intf(&rdev->wiphy, dev, type); - ret = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, type, flags, - params); + ret = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, type, params); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } diff --git a/net/wireless/util.c b/net/wireless/util.c index 8d6a0a7b1ca1c..88f3a11dbcd33 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -985,7 +985,7 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev) int cfg80211_change_iface(struct cfg80211_registered_device *rdev, struct net_device *dev, enum nl80211_iftype ntype, - u32 *flags, struct vif_params *params) + struct vif_params *params) { int err; enum nl80211_iftype otype = dev->ieee80211_ptr->iftype; @@ -1043,7 +1043,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, cfg80211_process_rdev_events(rdev); } - err = rdev_change_virtual_intf(rdev, dev, ntype, flags, params); + err = rdev_change_virtual_intf(rdev, dev, ntype, params); WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype); diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index a220156cf2175..5d4a02c7979b0 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -62,7 +62,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, memset(&vifparams, 0, sizeof(vifparams)); - return cfg80211_change_iface(rdev, dev, type, NULL, &vifparams); + return cfg80211_change_iface(rdev, dev, type, &vifparams); } EXPORT_WEXT_HANDLER(cfg80211_wext_siwmode); From 1db77596e4c6b653f3cd001073a14dd09b8deb4a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 11:36:31 +0200 Subject: [PATCH 41/48] cfg80211: refactor nl80211 monitor option parsing Refactor the parsing of monitor flags and the MU-MIMO options. This will allow adding more things cleanly in the future and also allows setting the latter already when creating a monitor interface. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 126 +++++++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 56 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a07a55eda55be..671b635c0625c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2731,6 +2731,69 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) return 0; } +static int nl80211_parse_mon_options(struct cfg80211_registered_device *rdev, + enum nl80211_iftype type, + struct genl_info *info, + struct vif_params *params) +{ + bool change = false; + int err; + + if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { + if (type != NL80211_IFTYPE_MONITOR) + return -EINVAL; + + err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], + ¶ms->flags); + if (err) + return err; + + change = true; + } + + if (params->flags & MONITOR_FLAG_ACTIVE && + !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) + return -EOPNOTSUPP; + + if (info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]) { + const u8 *mumimo_groups; + u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; + + if (type != NL80211_IFTYPE_MONITOR) + return -EINVAL; + + 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; + + params->vht_mumimo_groups = mumimo_groups; + change = true; + } + + if (info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]) { + u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; + + if (type != NL80211_IFTYPE_MONITOR) + return -EINVAL; + + if (!wiphy_ext_feature_isset(&rdev->wiphy, cap_flag)) + return -EOPNOTSUPP; + + params->vht_mumimo_follow_addr = + nla_data(info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]); + change = true; + } + + return change ? 1 : 0; +} + static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev, struct net_device *netdev, u8 use_4addr, enum nl80211_iftype iftype) @@ -2806,50 +2869,11 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) params.use_4addr = -1; } - if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { - if (ntype != NL80211_IFTYPE_MONITOR) - return -EINVAL; - err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], - ¶ms.flags); - if (err) - return err; - - change = true; - } - - if (params.flags & MONITOR_FLAG_ACTIVE && - !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) - return -EOPNOTSUPP; - - 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; - - params.vht_mumimo_groups = mumimo_groups; - 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; - - params.vht_mumimo_follow_addr = - nla_data(info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]); + err = nl80211_parse_mon_options(rdev, ntype, info, ¶ms); + if (err < 0) + return err; + if (err > 0) change = true; - } if (change) err = cfg80211_change_iface(rdev, dev, ntype, ¶ms); @@ -2905,19 +2929,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) return err; } - if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { - if (type != NL80211_IFTYPE_MONITOR) - return -EINVAL; - - err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], - ¶ms.flags); - if (err) - return err; - } - - if (params.flags & MONITOR_FLAG_ACTIVE && - !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) - return -EOPNOTSUPP; + err = nl80211_parse_mon_options(rdev, type, info, ¶ms); + if (err < 0) + return err; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) From 65f1d6007e999f3a3dda1ba5f264447529247697 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 12:36:31 +0200 Subject: [PATCH 42/48] mac80211: use common code for monitor options in add/change Refactor the code to have common code for changing monitor options when adding and changing virtual interfaces. This will make it easier to add BPF filters to both paths. Note that this code carefully checks the error conditions first and only then applies the changes, to guarantee atomicity. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 122 ++++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 58 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 5c16d23e28ddf..d041f78ecee60 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -22,31 +22,23 @@ #include "mesh.h" #include "wme.h" -static int ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata, - struct vif_params *params) +static void ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata, + struct vif_params *params) { - struct ieee80211_local *local = sdata->local; - struct ieee80211_sub_if_data *monitor_sdata; bool mu_mimo_groups = false; bool mu_mimo_follow = false; - monitor_sdata = rtnl_dereference(local->monitor_sdata); - - if (!monitor_sdata) - return -EOPNOTSUPP; - if (params->vht_mumimo_groups) { u64 membership; BUILD_BUG_ON(sizeof(membership) != WLAN_MEMBERSHIP_LEN); - memcpy(monitor_sdata->vif.bss_conf.mu_group.membership, + memcpy(sdata->vif.bss_conf.mu_group.membership, params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN); - memcpy(monitor_sdata->vif.bss_conf.mu_group.position, + memcpy(sdata->vif.bss_conf.mu_group.position, params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN, WLAN_USER_POSITION_LEN); - ieee80211_bss_info_change_notify(monitor_sdata, - BSS_CHANGED_MU_GROUPS); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_MU_GROUPS); /* don't care about endianness - just check for 0 */ memcpy(&membership, params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN); @@ -56,11 +48,64 @@ static int ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata, if (params->vht_mumimo_follow_addr) { mu_mimo_follow = is_valid_ether_addr(params->vht_mumimo_follow_addr); - ether_addr_copy(monitor_sdata->u.mntr.mu_follow_addr, + ether_addr_copy(sdata->u.mntr.mu_follow_addr, params->vht_mumimo_follow_addr); } - monitor_sdata->vif.mu_mimo_owner = mu_mimo_groups || mu_mimo_follow; + sdata->vif.mu_mimo_owner = mu_mimo_groups || mu_mimo_follow; +} + +static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata, + struct vif_params *params) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *monitor_sdata; + + /* check flags first */ + if (params->flags && ieee80211_sdata_running(sdata)) { + u32 mask = MONITOR_FLAG_COOK_FRAMES | MONITOR_FLAG_ACTIVE; + + /* + * Prohibit MONITOR_FLAG_COOK_FRAMES and + * MONITOR_FLAG_ACTIVE to be changed while the + * interface is up. + * Else we would need to add a lot of cruft + * to update everything: + * cooked_mntrs, monitor and all fif_* counters + * reconfigure hardware + */ + if ((params->flags & mask) != (sdata->u.mntr.flags & mask)) + return -EBUSY; + } + + /* also validate MU-MIMO change */ + monitor_sdata = rtnl_dereference(local->monitor_sdata); + + if (!monitor_sdata && + (params->vht_mumimo_groups || params->vht_mumimo_follow_addr)) + return -EOPNOTSUPP; + + /* apply all changes now - no failures allowed */ + + if (monitor_sdata) + ieee80211_set_mu_mimo_follow(monitor_sdata, params); + + if (params->flags) { + if (ieee80211_sdata_running(sdata)) { + ieee80211_adjust_monitor_flags(sdata, -1); + sdata->u.mntr.flags = params->flags; + ieee80211_adjust_monitor_flags(sdata, 1); + + ieee80211_configure_filter(local); + } else { + /* + * Because the interface is down, ieee80211_do_stop + * and ieee80211_do_open take care of "everything" + * mentioned in the comment above. + */ + sdata->u.mntr.flags = params->flags; + } + } return 0; } @@ -83,13 +128,11 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); if (type == NL80211_IFTYPE_MONITOR) { - err = ieee80211_set_mu_mimo_follow(sdata, params); + err = ieee80211_set_mon_options(sdata, params); if (err) { ieee80211_if_remove(sdata); return NULL; } - - sdata->u.mntr.flags = params->flags; } return wdev; @@ -124,46 +167,9 @@ static int ieee80211_change_iface(struct wiphy *wiphy, } if (sdata->vif.type == NL80211_IFTYPE_MONITOR) { - struct ieee80211_local *local = sdata->local; - int err; - - err = ieee80211_set_mu_mimo_follow(sdata, params); - if (err) - return err; - - if (!params->flags) - return 0; - - if (ieee80211_sdata_running(sdata)) { - u32 mask = MONITOR_FLAG_COOK_FRAMES | - MONITOR_FLAG_ACTIVE; - - /* - * Prohibit MONITOR_FLAG_COOK_FRAMES and - * MONITOR_FLAG_ACTIVE to be changed while the - * interface is up. - * Else we would need to add a lot of cruft - * to update everything: - * cooked_mntrs, monitor and all fif_* counters - * reconfigure hardware - */ - if ((params->flags & mask) != - (sdata->u.mntr.flags & mask)) - return -EBUSY; - - ieee80211_adjust_monitor_flags(sdata, -1); - sdata->u.mntr.flags = params->flags; - ieee80211_adjust_monitor_flags(sdata, 1); - - ieee80211_configure_filter(local); - } else { - /* - * Because the interface is down, ieee80211_do_stop - * and ieee80211_do_open take care of "everything" - * mentioned in the comment above. - */ - sdata->u.mntr.flags = params->flags; - } + ret = ieee80211_set_mon_options(sdata, params); + if (ret) + return ret; } return 0; From 1d5e9f80ab021e3e1f9436627a4ad07a143ccb2c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Apr 2017 10:31:16 +0200 Subject: [PATCH 43/48] mac80211_hwsim: use per-interface power level When channel contexts are used, there's no global power level (the power_level is always 0). Use the per-interface TX power in mac80211_hwsim to have a proper setting for both cases. This fixes the bgscan_simple and bgscan_learn test cases when the number of channels advertised by hwsim is >1 by default. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 4e58513d24e89..7ac45af441442 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -558,8 +558,6 @@ struct mac80211_hwsim_data { /* wmediumd portid responsible for netgroup of this radio */ u32 wmediumd; - int power_level; - /* difference between this hw's clock and the real clock, in usecs */ s64 tsf_offset; s64 bcn_delta; @@ -1207,7 +1205,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, if (info->control.rates[0].flags & IEEE80211_TX_RC_SHORT_GI) rx_status.flag |= RX_FLAG_SHORT_GI; /* TODO: simulate real signal strength (and optional packet loss) */ - rx_status.signal = data->power_level - 50; + rx_status.signal = -50; + if (info->control.vif) + rx_status.signal += info->control.vif->bss_conf.txpower; if (data->ps != PS_DISABLED) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); @@ -1633,7 +1633,6 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) } mutex_unlock(&data->mutex); - data->power_level = conf->power_level; if (!data->started || !data->beacon_int) tasklet_hrtimer_cancel(&data->beacon_timer); else if (!hrtimer_is_queued(&data->beacon_timer.timer)) { @@ -2253,7 +2252,6 @@ static const char mac80211_hwsim_gstrings_stats[][ETH_GSTRING_LEN] = { "d_tx_failed", "d_ps_mode", "d_group", - "d_tx_power", }; #define MAC80211_HWSIM_SSTATS_LEN ARRAY_SIZE(mac80211_hwsim_gstrings_stats) @@ -2290,7 +2288,6 @@ static void mac80211_hwsim_get_et_stats(struct ieee80211_hw *hw, data[i++] = ar->tx_failed; data[i++] = ar->ps; data[i++] = ar->group; - data[i++] = ar->power_level; WARN_ON(i != MAC80211_HWSIM_SSTATS_LEN); } From 96b08fd6080efdfa8f6125cffc6742a2235d92f1 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Thu, 13 Apr 2017 13:06:27 +0100 Subject: [PATCH 44/48] nl80211: add request id in scheduled scan event messages For multi-scheduled scan support in subsequent patch a request id will be added. This patch add this request id to the scheduled scan event messages. For now the request id will always be zero. With multi-scheduled scan its value will inform user-space to which scan the event relates. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ net/wireless/nl80211.c | 23 +++++++++++------------ net/wireless/nl80211.h | 3 +-- net/wireless/scan.c | 5 ++--- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 89fa4995ddca3..2a200b964b7a9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1649,6 +1649,7 @@ struct cfg80211_bss_select_adjust { /** * struct cfg80211_sched_scan_request - scheduled scan request description * + * @reqid: identifies this request. * @ssids: SSIDs to scan for (passed in the probe_reqs in active scans) * @n_ssids: number of SSIDs * @n_channels: total number of channels to scan @@ -1693,6 +1694,7 @@ struct cfg80211_bss_select_adjust { * comparisions. */ struct cfg80211_sched_scan_request { + u64 reqid; struct cfg80211_ssid *ssids; int n_ssids; u32 n_channels; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 671b635c0625c..a047992e5df1b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7371,8 +7371,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, rcu_assign_pointer(rdev->sched_scan_req, sched_scan_req); - nl80211_send_sched_scan(rdev, dev, - NL80211_CMD_START_SCHED_SCAN); + nl80211_send_sched_scan(sched_scan_req, NL80211_CMD_START_SCHED_SCAN); return 0; out_free: @@ -13219,18 +13218,19 @@ static int nl80211_prep_scan_msg(struct sk_buff *msg, static int nl80211_prep_sched_scan_msg(struct sk_buff *msg, - struct cfg80211_registered_device *rdev, - struct net_device *netdev, - u32 portid, u32 seq, int flags, u32 cmd) + struct cfg80211_sched_scan_request *req, u32 cmd) { void *hdr; - hdr = nl80211hdr_put(msg, portid, seq, flags, cmd); + hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); if (!hdr) return -1; - if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex)) + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, + wiphy_to_rdev(req->wiphy)->wiphy_idx) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, req->dev->ifindex) || + nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->reqid, + NL80211_ATTR_PAD)) goto nla_put_failure; genlmsg_end(msg, hdr); @@ -13290,8 +13290,7 @@ void nl80211_send_scan_msg(struct cfg80211_registered_device *rdev, NL80211_MCGRP_SCAN, GFP_KERNEL); } -void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 cmd) +void nl80211_send_sched_scan(struct cfg80211_sched_scan_request *req, u32 cmd) { struct sk_buff *msg; @@ -13299,12 +13298,12 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, if (!msg) return; - if (nl80211_prep_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) { + if (nl80211_prep_sched_scan_msg(msg, req, cmd) < 0) { nlmsg_free(msg); return; } - genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, + genlmsg_multicast_netns(&nl80211_fam, wiphy_net(req->wiphy), msg, 0, NL80211_MCGRP_SCAN, GFP_KERNEL); } diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 3cb17cd9577f5..d5f6860e62ab8 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -16,8 +16,7 @@ struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, bool aborted); void nl80211_send_scan_msg(struct cfg80211_registered_device *rdev, struct sk_buff *msg); -void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 cmd); +void nl80211_send_sched_scan(struct cfg80211_sched_scan_request *req, u32 cmd); void nl80211_common_reg_change_event(enum nl80211_commands cmd_id, struct regulatory_request *request); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 21be56b3128ee..6f4996c0f4df1 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -321,8 +321,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk) spin_unlock_bh(&rdev->bss_lock); request->scan_start = jiffies; } - nl80211_send_sched_scan(rdev, request->dev, - NL80211_CMD_SCHED_SCAN_RESULTS); + nl80211_send_sched_scan(request, NL80211_CMD_SCHED_SCAN_RESULTS); } rtnl_unlock(); @@ -379,7 +378,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, return err; } - nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); + nl80211_send_sched_scan(sched_scan_req, NL80211_CMD_SCHED_SCAN_STOPPED); RCU_INIT_POINTER(rdev->sched_scan_req, NULL); kfree_rcu(sched_scan_req, rcu_head); From f64331d58045b05e5af581284884d5df9b26c031 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Apr 2017 13:28:18 +0200 Subject: [PATCH 45/48] mac80211: keep a separate list of monitor interfaces that are up In addition to keeping monitor interfaces on the regular list of interfaces, keep those that are up and not in cooked mode on a separate list. This saves having to iterate all interfaces when delivering to monitor interfaces. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 3 +++ net/mac80211/iface.c | 19 +++++++++++++++++-- net/mac80211/main.c | 1 + net/mac80211/rx.c | 11 +---------- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0e718437d080e..cf6d5abb65a3c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -839,6 +839,8 @@ struct txq_info { struct ieee80211_if_mntr { u32 flags; u8 mu_follow_addr[ETH_ALEN] __aligned(2); + + struct list_head list; }; /** @@ -1259,6 +1261,7 @@ struct ieee80211_local { /* see iface.c */ struct list_head interfaces; + struct list_head mon_list; /* only that are IFF_UP && !cooked */ struct mutex iflist_mtx; /* diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 40813dd3301c6..02d4d6a29b75c 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -676,7 +676,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) set_bit(SDATA_STATE_RUNNING, &sdata->state); - if (sdata->vif.type == NL80211_IFTYPE_WDS) { + switch (sdata->vif.type) { + case NL80211_IFTYPE_WDS: /* Create STA entry for the WDS peer */ sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr, GFP_KERNEL); @@ -697,8 +698,17 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) rate_control_rate_init(sta); netif_carrier_on(dev); - } else if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) { + break; + case NL80211_IFTYPE_P2P_DEVICE: rcu_assign_pointer(local->p2p_sdata, sdata); + break; + case NL80211_IFTYPE_MONITOR: + if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) + break; + list_add_tail_rcu(&sdata->u.mntr.list, &local->mon_list); + break; + default: + break; } /* @@ -816,6 +826,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP: cancel_work_sync(&sdata->u.ap.request_smps_work); break; + case NL80211_IFTYPE_MONITOR: + if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) + break; + list_del_rcu(&sdata->u.mntr.list); + break; default: break; } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 56fb47953b724..ae408a96c4078 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -603,6 +603,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, ARRAY_SIZE(local->ext_capa); INIT_LIST_HEAD(&local->interfaces); + INIT_LIST_HEAD(&local->mon_list); __hw_addr_init(&local->mc_list); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index e35c42ebb7a57..638dc63a51bf2 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -593,16 +593,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_802_2); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - if (sdata->vif.type != NL80211_IFTYPE_MONITOR) - continue; - - if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) - continue; - - if (!ieee80211_sdata_running(sdata)) - continue; - + list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) { if (prev_dev) { skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) { From aa1702dd162f420bf85ecef0c77686ef0dbc1496 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Thu, 13 Apr 2017 10:05:04 -0700 Subject: [PATCH 46/48] cfg80211: Fix array-bounds warning in fragment copy __ieee80211_amsdu_copy_frag intentionally initializes a pointer to array[-1] to increment it later to valid values. clang rightfully generates an array-bounds warning on the initialization statement. Initialize the pointer to array[0] and change the algorithm from increment before to increment after consume. Signed-off-by: Matthias Kaehlcke Signed-off-by: Johannes Berg --- net/wireless/util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/wireless/util.c b/net/wireless/util.c index 88f3a11dbcd33..a46bc42d0910e 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -659,7 +659,7 @@ __ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame, int offset, int len) { struct skb_shared_info *sh = skb_shinfo(skb); - const skb_frag_t *frag = &sh->frags[-1]; + const skb_frag_t *frag = &sh->frags[0]; struct page *frag_page; void *frag_ptr; int frag_len, frag_size; @@ -672,10 +672,10 @@ __ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame, while (offset >= frag_size) { offset -= frag_size; - frag++; frag_page = skb_frag_page(frag); frag_ptr = skb_frag_address(frag); frag_size = skb_frag_size(frag); + frag++; } frag_ptr += offset; @@ -687,12 +687,12 @@ __ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame, len -= cur_len; while (len > 0) { - frag++; frag_len = skb_frag_size(frag); cur_len = min(len, frag_len); __frame_add_frag(frame, skb_frag_page(frag), skb_frag_address(frag), cur_len, frag_len); len -= cur_len; + frag++; } } From a4ac6f2e53e568a77a2eb3710efd99ca08634c0a Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Mon, 17 Apr 2017 13:59:53 -0700 Subject: [PATCH 47/48] mac80211: ibss: Fix channel type enum in ieee80211_sta_join_ibss() cfg80211_chandef_create() expects an 'enum nl80211_channel_type' as channel type however in ieee80211_sta_join_ibss() NL80211_CHAN_WIDTH_20_NOHT is passed in two occasions, which is of the enum type 'nl80211_chan_width'. Change the value to NL80211_CHAN_NO_HT (20 MHz, non-HT channel) of the channel type enum. Signed-off-by: Matthias Kaehlcke Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 98999d3d52627..e957351976a27 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -425,7 +425,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, case NL80211_CHAN_WIDTH_5: case NL80211_CHAN_WIDTH_10: cfg80211_chandef_create(&chandef, cbss->channel, - NL80211_CHAN_WIDTH_20_NOHT); + NL80211_CHAN_NO_HT); chandef.width = sdata->u.ibss.chandef.width; break; case NL80211_CHAN_WIDTH_80: @@ -437,7 +437,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, default: /* fall back to 20 MHz for unsupported modes */ cfg80211_chandef_create(&chandef, cbss->channel, - NL80211_CHAN_WIDTH_20_NOHT); + NL80211_CHAN_NO_HT); break; } From bbf67e450a5dc2a595e1e7a67b4869f1a7f5a338 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Mon, 17 Apr 2017 15:59:52 -0700 Subject: [PATCH 48/48] nl80211: Fix enum type of variable in nl80211_put_sta_rate() rate_flg is of type 'enum nl80211_attrs', however it is assigned with 'enum nl80211_rate_info' values. Change the type of rate_flg accordingly. Signed-off-by: Matthias Kaehlcke Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a047992e5df1b..04c1c7896cc18 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4203,7 +4203,7 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, struct nlattr *rate; u32 bitrate; u16 bitrate_compat; - enum nl80211_attrs rate_flg; + enum nl80211_rate_info rate_flg; rate = nla_nest_start(msg, attr); if (!rate)