diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 95dd5832e719..4fac370284c0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -761,6 +761,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
761 | } | 761 | } |
762 | CMD(set_channel, SET_CHANNEL); | 762 | CMD(set_channel, SET_CHANNEL); |
763 | CMD(set_wds_peer, SET_WDS_PEER); | 763 | CMD(set_wds_peer, SET_WDS_PEER); |
764 | if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) | ||
765 | CMD(sched_scan_start, START_SCHED_SCAN); | ||
764 | 766 | ||
765 | #undef CMD | 767 | #undef CMD |
766 | 768 | ||
@@ -3357,6 +3359,179 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
3357 | return err; | 3359 | return err; |
3358 | } | 3360 | } |
3359 | 3361 | ||
3362 | static int nl80211_start_sched_scan(struct sk_buff *skb, | ||
3363 | struct genl_info *info) | ||
3364 | { | ||
3365 | struct cfg80211_sched_scan_request *request; | ||
3366 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
3367 | struct net_device *dev = info->user_ptr[1]; | ||
3368 | struct cfg80211_ssid *ssid; | ||
3369 | struct ieee80211_channel *channel; | ||
3370 | struct nlattr *attr; | ||
3371 | struct wiphy *wiphy; | ||
3372 | int err, tmp, n_ssids = 0, n_channels, i; | ||
3373 | enum ieee80211_band band; | ||
3374 | size_t ie_len; | ||
3375 | |||
3376 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || | ||
3377 | !rdev->ops->sched_scan_start) | ||
3378 | return -EOPNOTSUPP; | ||
3379 | |||
3380 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) | ||
3381 | return -EINVAL; | ||
3382 | |||
3383 | if (rdev->sched_scan_req) | ||
3384 | return -EINPROGRESS; | ||
3385 | |||
3386 | wiphy = &rdev->wiphy; | ||
3387 | |||
3388 | if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { | ||
3389 | n_channels = validate_scan_freqs( | ||
3390 | info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); | ||
3391 | if (!n_channels) | ||
3392 | return -EINVAL; | ||
3393 | } else { | ||
3394 | n_channels = 0; | ||
3395 | |||
3396 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) | ||
3397 | if (wiphy->bands[band]) | ||
3398 | n_channels += wiphy->bands[band]->n_channels; | ||
3399 | } | ||
3400 | |||
3401 | if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) | ||
3402 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], | ||
3403 | tmp) | ||
3404 | n_ssids++; | ||
3405 | |||
3406 | if (n_ssids > wiphy->max_scan_ssids) | ||
3407 | return -EINVAL; | ||
3408 | |||
3409 | if (info->attrs[NL80211_ATTR_IE]) | ||
3410 | ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); | ||
3411 | else | ||
3412 | ie_len = 0; | ||
3413 | |||
3414 | if (ie_len > wiphy->max_scan_ie_len) | ||
3415 | return -EINVAL; | ||
3416 | |||
3417 | request = kzalloc(sizeof(*request) | ||
3418 | + sizeof(*ssid) * n_ssids | ||
3419 | + sizeof(channel) * n_channels | ||
3420 | + ie_len, GFP_KERNEL); | ||
3421 | if (!request) | ||
3422 | return -ENOMEM; | ||
3423 | |||
3424 | if (n_ssids) | ||
3425 | request->ssids = (void *)&request->channels[n_channels]; | ||
3426 | request->n_ssids = n_ssids; | ||
3427 | if (ie_len) { | ||
3428 | if (request->ssids) | ||
3429 | request->ie = (void *)(request->ssids + n_ssids); | ||
3430 | else | ||
3431 | request->ie = (void *)(request->channels + n_channels); | ||
3432 | } | ||
3433 | |||
3434 | i = 0; | ||
3435 | if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { | ||
3436 | /* user specified, bail out if channel not found */ | ||
3437 | nla_for_each_nested(attr, | ||
3438 | info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], | ||
3439 | tmp) { | ||
3440 | struct ieee80211_channel *chan; | ||
3441 | |||
3442 | chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); | ||
3443 | |||
3444 | if (!chan) { | ||
3445 | err = -EINVAL; | ||
3446 | goto out_free; | ||
3447 | } | ||
3448 | |||
3449 | /* ignore disabled channels */ | ||
3450 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
3451 | continue; | ||
3452 | |||
3453 | request->channels[i] = chan; | ||
3454 | i++; | ||
3455 | } | ||
3456 | } else { | ||
3457 | /* all channels */ | ||
3458 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
3459 | int j; | ||
3460 | if (!wiphy->bands[band]) | ||
3461 | continue; | ||
3462 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { | ||
3463 | struct ieee80211_channel *chan; | ||
3464 | |||
3465 | chan = &wiphy->bands[band]->channels[j]; | ||
3466 | |||
3467 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
3468 | continue; | ||
3469 | |||
3470 | request->channels[i] = chan; | ||
3471 | i++; | ||
3472 | } | ||
3473 | } | ||
3474 | } | ||
3475 | |||
3476 | if (!i) { | ||
3477 | err = -EINVAL; | ||
3478 | goto out_free; | ||
3479 | } | ||
3480 | |||
3481 | request->n_channels = i; | ||
3482 | |||
3483 | i = 0; | ||
3484 | if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { | ||
3485 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], | ||
3486 | tmp) { | ||
3487 | if (request->ssids[i].ssid_len > | ||
3488 | IEEE80211_MAX_SSID_LEN) { | ||
3489 | err = -EINVAL; | ||
3490 | goto out_free; | ||
3491 | } | ||
3492 | memcpy(request->ssids[i].ssid, nla_data(attr), | ||
3493 | nla_len(attr)); | ||
3494 | request->ssids[i].ssid_len = nla_len(attr); | ||
3495 | i++; | ||
3496 | } | ||
3497 | } | ||
3498 | |||
3499 | if (info->attrs[NL80211_ATTR_IE]) { | ||
3500 | request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); | ||
3501 | memcpy((void *)request->ie, | ||
3502 | nla_data(info->attrs[NL80211_ATTR_IE]), | ||
3503 | request->ie_len); | ||
3504 | } | ||
3505 | |||
3506 | request->dev = dev; | ||
3507 | request->wiphy = &rdev->wiphy; | ||
3508 | |||
3509 | err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); | ||
3510 | if (!err) { | ||
3511 | rdev->sched_scan_req = request; | ||
3512 | nl80211_send_sched_scan(rdev, dev, | ||
3513 | NL80211_CMD_START_SCHED_SCAN); | ||
3514 | goto out; | ||
3515 | } | ||
3516 | |||
3517 | out_free: | ||
3518 | kfree(request); | ||
3519 | out: | ||
3520 | return err; | ||
3521 | } | ||
3522 | |||
3523 | static int nl80211_stop_sched_scan(struct sk_buff *skb, | ||
3524 | struct genl_info *info) | ||
3525 | { | ||
3526 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
3527 | |||
3528 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || | ||
3529 | !rdev->ops->sched_scan_stop) | ||
3530 | return -EOPNOTSUPP; | ||
3531 | |||
3532 | return __cfg80211_stop_sched_scan(rdev, false); | ||
3533 | } | ||
3534 | |||
3360 | static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, | 3535 | static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, |
3361 | struct cfg80211_registered_device *rdev, | 3536 | struct cfg80211_registered_device *rdev, |
3362 | struct wireless_dev *wdev, | 3537 | struct wireless_dev *wdev, |
@@ -5327,6 +5502,22 @@ static struct genl_ops nl80211_ops[] = { | |||
5327 | .dumpit = nl80211_dump_scan, | 5502 | .dumpit = nl80211_dump_scan, |
5328 | }, | 5503 | }, |
5329 | { | 5504 | { |
5505 | .cmd = NL80211_CMD_START_SCHED_SCAN, | ||
5506 | .doit = nl80211_start_sched_scan, | ||
5507 | .policy = nl80211_policy, | ||
5508 | .flags = GENL_ADMIN_PERM, | ||
5509 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | ||
5510 | NL80211_FLAG_NEED_RTNL, | ||
5511 | }, | ||
5512 | { | ||
5513 | .cmd = NL80211_CMD_STOP_SCHED_SCAN, | ||
5514 | .doit = nl80211_stop_sched_scan, | ||
5515 | .policy = nl80211_policy, | ||
5516 | .flags = GENL_ADMIN_PERM, | ||
5517 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | ||
5518 | NL80211_FLAG_NEED_RTNL, | ||
5519 | }, | ||
5520 | { | ||
5330 | .cmd = NL80211_CMD_AUTHENTICATE, | 5521 | .cmd = NL80211_CMD_AUTHENTICATE, |
5331 | .doit = nl80211_authenticate, | 5522 | .doit = nl80211_authenticate, |
5332 | .policy = nl80211_policy, | 5523 | .policy = nl80211_policy, |
@@ -5652,6 +5843,28 @@ static int nl80211_send_scan_msg(struct sk_buff *msg, | |||
5652 | return -EMSGSIZE; | 5843 | return -EMSGSIZE; |
5653 | } | 5844 | } |
5654 | 5845 | ||
5846 | static int | ||
5847 | nl80211_send_sched_scan_msg(struct sk_buff *msg, | ||
5848 | struct cfg80211_registered_device *rdev, | ||
5849 | struct net_device *netdev, | ||
5850 | u32 pid, u32 seq, int flags, u32 cmd) | ||
5851 | { | ||
5852 | void *hdr; | ||
5853 | |||
5854 | hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); | ||
5855 | if (!hdr) | ||
5856 | return -1; | ||
5857 | |||
5858 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5859 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5860 | |||
5861 | return genlmsg_end(msg, hdr); | ||
5862 | |||
5863 | nla_put_failure: | ||
5864 | genlmsg_cancel(msg, hdr); | ||
5865 | return -EMSGSIZE; | ||
5866 | } | ||
5867 | |||
5655 | void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, | 5868 | void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, |
5656 | struct net_device *netdev) | 5869 | struct net_device *netdev) |
5657 | { | 5870 | { |
@@ -5709,6 +5922,43 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, | |||
5709 | nl80211_scan_mcgrp.id, GFP_KERNEL); | 5922 | nl80211_scan_mcgrp.id, GFP_KERNEL); |
5710 | } | 5923 | } |
5711 | 5924 | ||
5925 | void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, | ||
5926 | struct net_device *netdev) | ||
5927 | { | ||
5928 | struct sk_buff *msg; | ||
5929 | |||
5930 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
5931 | if (!msg) | ||
5932 | return; | ||
5933 | |||
5934 | if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, | ||
5935 | NL80211_CMD_SCHED_SCAN_RESULTS) < 0) { | ||
5936 | nlmsg_free(msg); | ||
5937 | return; | ||
5938 | } | ||
5939 | |||
5940 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
5941 | nl80211_scan_mcgrp.id, GFP_KERNEL); | ||
5942 | } | ||
5943 | |||
5944 | void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, | ||
5945 | struct net_device *netdev, u32 cmd) | ||
5946 | { | ||
5947 | struct sk_buff *msg; | ||
5948 | |||
5949 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
5950 | if (!msg) | ||
5951 | return; | ||
5952 | |||
5953 | if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) { | ||
5954 | nlmsg_free(msg); | ||
5955 | return; | ||
5956 | } | ||
5957 | |||
5958 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
5959 | nl80211_scan_mcgrp.id, GFP_KERNEL); | ||
5960 | } | ||
5961 | |||
5712 | /* | 5962 | /* |
5713 | * This can happen on global regulatory changes or device specific settings | 5963 | * This can happen on global regulatory changes or device specific settings |
5714 | * based on custom world regulatory domains. | 5964 | * based on custom world regulatory domains. |