History

nl80211: fix scheduled scan RSSI matchset attribute confusion

The scheduled scan matchsets were intended to be a list of filters, with the found BSS having to pass at least one of them to be passed to the host. When the RSSI attribute was added, however, this was broken and currently wpa_supplicant adds that attribute in its own matchset; however, it doesn't intend that to mean that anything that passes the RSSI filter should be passed to the host, instead it wants it to mean that everything needs to also have higher RSSI. This is semantically problematic because we have a list of filters like [ SSID1, SSID2, SSID3, RSSI ] with no real indication which one should be OR'ed and which one AND'ed. To fix this, move the RSSI filter attribute into each matchset. As we need to stay backward compatible, treat a matchset with only the RSSI attribute as a "default RSSI filter" for all other matchsets, but only if there are other matchsets (an RSSI-only matchset by itself is still desirable.) To make driver implementation easier, keep a global min_rssi_thold for the entire request as well. The only affected driver is ath6kl. I found this when I looked into the code after Raja Mani submitted a patch fixing the n_match_sets calculation to disregard the RSSI, but that patch didn't address the semantic issue.

Johannes Berg committed 12 years ago

Commit ea73cbc1 parent 9fa37a35 files · +92 −19Browse files

Unified · Split

5 files
  1. drivers/net/wireless/ath/ath6kl/cfg80211.c +14 −5
  2. drivers/net/wireless/iwlwifi/mvm/scan.c +3 −0
  3. include/net/cfg80211.h +6 −3
  4. include/uapi/linux/nl80211.h +8 −2
  5. net/wireless/nl80211.c +61 −9
drivers/net/wireless/ath/ath6kl/cfg80211.c+14−5
@@ -3256,6 +3256,15 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, · 3255 unmodified lines
3256 struct ath6kl_vif *vif = netdev_priv(dev);3256 struct ath6kl_vif *vif = netdev_priv(dev);
3257 u16 interval;3257 u16 interval;
3258 int ret, rssi_thold;3258 int ret, rssi_thold;
3259 int n_match_sets = request->n_match_sets;
3260
3261 /*
3262 * If there's a matchset w/o an SSID, then assume it's just for
3263 * the RSSI (nothing else is currently supported) and ignore it.
3264 * The device only supports a global RSSI filter that we set below.
3265 */
3266 if (n_match_sets == 1 && !request->match_sets[0].ssid.ssid_len)
3267 n_match_sets = 0;
3259 3268
3260 if (ar->state != ATH6KL_STATE_ON)3269 if (ar->state != ATH6KL_STATE_ON)
3261 return -EIO;3270 return -EIO;
@@ -3268,11 +3277,11 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, · 6 unmodified lines
3268 ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,3277 ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
3269 request->n_ssids,3278 request->n_ssids,
3270 request->match_sets,3279 request->match_sets,
3271 request->n_match_sets);3280 n_match_sets);
3272 if (ret < 0)3281 if (ret < 0)
3273 return ret;3282 return ret;
3274 3283
3275 if (!request->n_match_sets) {3284 if (!n_match_sets) {
3276 ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,3285 ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
3277 ALL_BSS_FILTER, 0);3286 ALL_BSS_FILTER, 0);
3278 if (ret < 0)3287 if (ret < 0)
@@ -3286,12 +3295,12 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, · 7 unmodified lines
3286 3295
3287 if (test_bit(ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD,3296 if (test_bit(ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD,
3288 ar->fw_capabilities)) {3297 ar->fw_capabilities)) {
3289 if (request->rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF)3298 if (request->min_rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF)
3290 rssi_thold = 0;3299 rssi_thold = 0;
3291 else if (request->rssi_thold < -127)3300 else if (request->min_rssi_thold < -127)
3292 rssi_thold = -127;3301 rssi_thold = -127;
3293 else3302 else
3294 rssi_thold = request->rssi_thold;3303 rssi_thold = request->min_rssi_thold;
3295 3304
3296 ret = ath6kl_wmi_set_rssi_filter_cmd(ar->wmi, vif->fw_vif_idx,3305 ret = ath6kl_wmi_set_rssi_filter_cmd(ar->wmi, vif->fw_vif_idx,
3297 rssi_thold);3306 rssi_thold);
drivers/net/wireless/iwlwifi/mvm/scan.c+3−0
@@ -595,6 +595,9 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req, · 594 unmodified lines
595 * config match list.595 * config match list.
596 */596 */
597 for (i = 0; i < req->n_match_sets && i < PROBE_OPTION_MAX; i++) {597 for (i = 0; i < req->n_match_sets && i < PROBE_OPTION_MAX; i++) {
598 /* skip empty SSID matchsets */
599 if (!req->match_sets[i].ssid.ssid_len)
600 continue;
598 scan->direct_scan[i].id = WLAN_EID_SSID;601 scan->direct_scan[i].id = WLAN_EID_SSID;
599 scan->direct_scan[i].len = req->match_sets[i].ssid.ssid_len;602 scan->direct_scan[i].len = req->match_sets[i].ssid.ssid_len;
600 memcpy(scan->direct_scan[i].ssid, req->match_sets[i].ssid.ssid,603 memcpy(scan->direct_scan[i].ssid, req->match_sets[i].ssid.ssid,
include/net/cfg80211.h+6−3
@@ -1394,10 +1394,12 @@ struct cfg80211_scan_request { · 1393 unmodified lines
1394/**1394/**
1395 * struct cfg80211_match_set - sets of attributes to match1395 * struct cfg80211_match_set - sets of attributes to match
1396 *1396 *
1397 * @ssid: SSID to be matched1397 * @ssid: SSID to be matched; may be zero-length for no match (RSSI only)
1398 * @rssi_thold: don't report scan results below this threshold (in s32 dBm)
1398 */1399 */
1399struct cfg80211_match_set {1400struct cfg80211_match_set {
1400 struct cfg80211_ssid ssid;1401 struct cfg80211_ssid ssid;
1402 s32 rssi_thold;
1401};1403};
1402 1404
1403/**1405/**
@@ -1420,7 +1422,8 @@ struct cfg80211_match_set { · 16 unmodified lines
1420 * @dev: the interface1422 * @dev: the interface
1421 * @scan_start: start time of the scheduled scan1423 * @scan_start: start time of the scheduled scan
1422 * @channels: channels to scan1424 * @channels: channels to scan
1423 * @rssi_thold: don't report scan results below this threshold (in s32 dBm)1425 * @min_rssi_thold: for drivers only supporting a single threshold, this
1426 * contains the minimum over all matchsets
1424 */1427 */
1425struct cfg80211_sched_scan_request {1428struct cfg80211_sched_scan_request {
1426 struct cfg80211_ssid *ssids;1429 struct cfg80211_ssid *ssids;
@@ -1433,7 +1436,7 @@ struct cfg80211_sched_scan_request { · 6 unmodified lines
1433 u32 flags;1436 u32 flags;
1434 struct cfg80211_match_set *match_sets;1437 struct cfg80211_match_set *match_sets;
1435 int n_match_sets;1438 int n_match_sets;
1436 s32 rssi_thold;1439 s32 min_rssi_thold;
1437 1440
1438 /* internal */1441 /* internal */
1439 struct wiphy *wiphy;1442 struct wiphy *wiphy;
include/uapi/linux/nl80211.h+8−2
@@ -2467,9 +2467,15 @@ enum nl80211_reg_rule_attr { · 2466 unmodified lines
2467 * enum nl80211_sched_scan_match_attr - scheduled scan match attributes2467 * enum nl80211_sched_scan_match_attr - scheduled scan match attributes
2468 * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved2468 * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
2469 * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,2469 * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
2470 * only report BSS with matching SSID.2470 * only report BSS with matching SSID.
2471 * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a2471 * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a
2472 * BSS in scan results. Filtering is turned off if not specified.2472 * BSS in scan results. Filtering is turned off if not specified. Note that
2473 * if this attribute is in a match set of its own, then it is treated as
2474 * the default value for all matchsets with an SSID, rather than being a
2475 * matchset of its own without an RSSI filter. This is due to problems with
2476 * how this API was implemented in the past. Also, due to the same problem,
2477 * the only way to create a matchset with only an RSSI filter (with this
2478 * attribute) is if there's only a single matchset with the RSSI attribute.
2473 * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter2479 * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
2474 * attribute number currently defined2480 * attribute number currently defined
2475 * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use2481 * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
net/wireless/nl80211.c+61−9
@@ -5467,6 +5467,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, · 5466 unmodified lines
5467 enum ieee80211_band band;5467 enum ieee80211_band band;
5468 size_t ie_len;5468 size_t ie_len;
5469 struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];5469 struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
5470 s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
5470 5471
5471 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||5472 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
5472 !rdev->ops->sched_scan_start)5473 !rdev->ops->sched_scan_start)
@@ -5501,11 +5502,40 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, · 28 unmodified lines
5501 if (n_ssids > wiphy->max_sched_scan_ssids)5502 if (n_ssids > wiphy->max_sched_scan_ssids)
5502 return -EINVAL;5503 return -EINVAL;
5503 5504
5504 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH])5505 /*
5506 * First, count the number of 'real' matchsets. Due to an issue with
5507 * the old implementation, matchsets containing only the RSSI attribute
5508 * (NL80211_SCHED_SCAN_MATCH_ATTR_RSSI) are considered as the 'default'
5509 * RSSI for all matchsets, rather than their own matchset for reporting
5510 * all APs with a strong RSSI. This is needed to be compatible with
5511 * older userspace that treated a matchset with only the RSSI as the
5512 * global RSSI for all other matchsets - if there are other matchsets.
5513 */
5514 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
5505 nla_for_each_nested(attr,5515 nla_for_each_nested(attr,
5506 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],5516 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
5507 tmp)5517 tmp) {
5508 n_match_sets++;5518 struct nlattr *rssi;
5519
5520 err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
5521 nla_data(attr), nla_len(attr),
5522 nl80211_match_policy);
5523 if (err)
5524 return err;
5525 /* add other standalone attributes here */
5526 if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) {
5527 n_match_sets++;
5528 continue;
5529 }
5530 rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
5531 if (rssi)
5532 default_match_rssi = nla_get_s32(rssi);
5533 }
5534 }
5535
5536 /* However, if there's no other matchset, add the RSSI one */
5537 if (!n_match_sets && default_match_rssi != NL80211_SCAN_RSSI_THOLD_OFF)
5538 n_match_sets = 1;
5509 5539
5510 if (n_match_sets > wiphy->max_match_sets)5540 if (n_match_sets > wiphy->max_match_sets)
5511 return -EINVAL;5541 return -EINVAL;
@@ -5633,6 +5663,15 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, · 121 unmodified lines
5633 goto out_free;5663 goto out_free;
5634 ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID];5664 ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID];
5635 if (ssid) {5665 if (ssid) {
5666 if (WARN_ON(i >= n_match_sets)) {
5667 /* this indicates a programming error,
5668 * the loop above should have verified
5669 * things properly
5670 */
5671 err = -EINVAL;
5672 goto out_free;
5673 }
5674
5636 if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {5675 if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
5637 err = -EINVAL;5676 err = -EINVAL;
5638 goto out_free;5677 goto out_free;
@@ -5641,15 +5680,28 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, · 2 unmodified lines
5641 nla_data(ssid), nla_len(ssid));5680 nla_data(ssid), nla_len(ssid));
5642 request->match_sets[i].ssid.ssid_len =5681 request->match_sets[i].ssid.ssid_len =
5643 nla_len(ssid);5682 nla_len(ssid);
5683 /* special attribute - old implemenation w/a */
5684 request->match_sets[i].rssi_thold =
5685 default_match_rssi;
5686 rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
5687 if (rssi)
5688 request->match_sets[i].rssi_thold =
5689 nla_get_s32(rssi);
5644 }5690 }
5645 rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
5646 if (rssi)
5647 request->rssi_thold = nla_get_u32(rssi);
5648 else
5649 request->rssi_thold =
5650 NL80211_SCAN_RSSI_THOLD_OFF;
5651 i++;5691 i++;
5652 }5692 }
5693
5694 /* there was no other matchset, so the RSSI one is alone */
5695 if (i == 0)
5696 request->match_sets[0].rssi_thold = default_match_rssi;
5697
5698 request->min_rssi_thold = INT_MAX;
5699 for (i = 0; i < n_match_sets; i++)
5700 request->min_rssi_thold =
5701 min(request->match_sets[i].rssi_thold,
5702 request->min_rssi_thold);
5703 } else {
5704 request->min_rssi_thold = NL80211_SCAN_RSSI_THOLD_OFF;
5653 }5705 }
5654 5706
5655 if (info->attrs[NL80211_ATTR_IE]) {5707 if (info->attrs[NL80211_ATTR_IE]) {