aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/nl80211.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-02-10 15:25:55 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-02-13 13:45:49 -0500
commit2a5193119269062608582418deba7af82844159a (patch)
tree1f2fe8cffbeb7530dce7fa708310f6fb29ab0dd8 /net/wireless/nl80211.c
parent849b7967818995a32c3017542e33eb3155944368 (diff)
cfg80211/nl80211: scanning (and mac80211 update to use it)
This patch adds basic scan capability to cfg80211/nl80211 and changes mac80211 to use it. The BSS list that cfg80211 maintains is made driver-accessible with a private area in each BSS struct, but mac80211 doesn't yet use it. That's another large project. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r--net/wireless/nl80211.c323
1 files changed, 323 insertions, 0 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index d452396006ee..298a4de59948 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -14,6 +14,7 @@
14#include <linux/nl80211.h> 14#include <linux/nl80211.h>
15#include <linux/rtnetlink.h> 15#include <linux/rtnetlink.h>
16#include <linux/netlink.h> 16#include <linux/netlink.h>
17#include <linux/etherdevice.h>
17#include <net/genetlink.h> 18#include <net/genetlink.h>
18#include <net/cfg80211.h> 19#include <net/cfg80211.h>
19#include "core.h" 20#include "core.h"
@@ -109,6 +110,8 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
109 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, 110 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
110 [NL80211_ATTR_IE] = { .type = NLA_BINARY, 111 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
111 .len = IEEE80211_MAX_DATA_LEN }, 112 .len = IEEE80211_MAX_DATA_LEN },
113 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
114 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
112}; 115};
113 116
114/* message building helper */ 117/* message building helper */
@@ -141,6 +144,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
141 144
142 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx); 145 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx);
143 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); 146 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
147 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
148 dev->wiphy.max_scan_ssids);
144 149
145 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); 150 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
146 if (!nl_modes) 151 if (!nl_modes)
@@ -2270,6 +2275,246 @@ static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb,
2270 return err; 2275 return err;
2271} 2276}
2272 2277
2278static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
2279{
2280 struct cfg80211_registered_device *drv;
2281 struct net_device *dev;
2282 struct cfg80211_scan_request *request;
2283 struct cfg80211_ssid *ssid;
2284 struct ieee80211_channel *channel;
2285 struct nlattr *attr;
2286 struct wiphy *wiphy;
2287 int err, tmp, n_ssids = 0, n_channels = 0, i;
2288 enum ieee80211_band band;
2289
2290 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2291 if (err)
2292 return err;
2293
2294 wiphy = &drv->wiphy;
2295
2296 if (!drv->ops->scan) {
2297 err = -EOPNOTSUPP;
2298 goto out;
2299 }
2300
2301 rtnl_lock();
2302
2303 if (drv->scan_req) {
2304 err = -EBUSY;
2305 goto out_unlock;
2306 }
2307
2308 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2309 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp)
2310 n_channels++;
2311 if (!n_channels) {
2312 err = -EINVAL;
2313 goto out_unlock;
2314 }
2315 } else {
2316 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
2317 if (wiphy->bands[band])
2318 n_channels += wiphy->bands[band]->n_channels;
2319 }
2320
2321 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
2322 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
2323 n_ssids++;
2324
2325 if (n_ssids > wiphy->max_scan_ssids) {
2326 err = -EINVAL;
2327 goto out_unlock;
2328 }
2329
2330 request = kzalloc(sizeof(*request)
2331 + sizeof(*ssid) * n_ssids
2332 + sizeof(channel) * n_channels, GFP_KERNEL);
2333 if (!request) {
2334 err = -ENOMEM;
2335 goto out_unlock;
2336 }
2337
2338 request->channels = (void *)((char *)request + sizeof(*request));
2339 request->n_channels = n_channels;
2340 if (n_ssids)
2341 request->ssids = (void *)(request->channels + n_channels);
2342 request->n_ssids = n_ssids;
2343
2344 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2345 /* user specified, bail out if channel not found */
2346 request->n_channels = n_channels;
2347 i = 0;
2348 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
2349 request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr));
2350 if (!request->channels[i]) {
2351 err = -EINVAL;
2352 goto out_free;
2353 }
2354 i++;
2355 }
2356 } else {
2357 /* all channels */
2358 i = 0;
2359 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
2360 int j;
2361 if (!wiphy->bands[band])
2362 continue;
2363 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
2364 request->channels[i] = &wiphy->bands[band]->channels[j];
2365 i++;
2366 }
2367 }
2368 }
2369
2370 i = 0;
2371 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
2372 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
2373 if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
2374 err = -EINVAL;
2375 goto out_free;
2376 }
2377 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
2378 request->ssids[i].ssid_len = nla_len(attr);
2379 i++;
2380 }
2381 }
2382
2383 request->ifidx = dev->ifindex;
2384 request->wiphy = &drv->wiphy;
2385
2386 drv->scan_req = request;
2387 err = drv->ops->scan(&drv->wiphy, dev, request);
2388
2389 out_free:
2390 if (err) {
2391 drv->scan_req = NULL;
2392 kfree(request);
2393 }
2394 out_unlock:
2395 rtnl_unlock();
2396 out:
2397 cfg80211_put_dev(drv);
2398 dev_put(dev);
2399 return err;
2400}
2401
2402static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
2403 struct cfg80211_registered_device *rdev,
2404 struct net_device *dev,
2405 struct cfg80211_bss *res)
2406{
2407 void *hdr;
2408 struct nlattr *bss;
2409
2410 hdr = nl80211hdr_put(msg, pid, seq, flags,
2411 NL80211_CMD_NEW_SCAN_RESULTS);
2412 if (!hdr)
2413 return -1;
2414
2415 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION,
2416 rdev->bss_generation);
2417 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2418
2419 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
2420 if (!bss)
2421 goto nla_put_failure;
2422 if (!is_zero_ether_addr(res->bssid))
2423 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
2424 if (res->information_elements && res->len_information_elements)
2425 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
2426 res->len_information_elements,
2427 res->information_elements);
2428 if (res->tsf)
2429 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
2430 if (res->beacon_interval)
2431 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
2432 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
2433 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
2434
2435 switch (res->signal_type) {
2436 case CFG80211_SIGNAL_TYPE_MBM:
2437 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
2438 break;
2439 case CFG80211_SIGNAL_TYPE_UNSPEC:
2440 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
2441 break;
2442 default:
2443 break;
2444 }
2445
2446 nla_nest_end(msg, bss);
2447
2448 return genlmsg_end(msg, hdr);
2449
2450 nla_put_failure:
2451 genlmsg_cancel(msg, hdr);
2452 return -EMSGSIZE;
2453}
2454
2455static int nl80211_dump_scan(struct sk_buff *skb,
2456 struct netlink_callback *cb)
2457{
2458 struct cfg80211_registered_device *dev;
2459 struct net_device *netdev;
2460 struct cfg80211_internal_bss *scan;
2461 int ifidx = cb->args[0];
2462 int start = cb->args[1], idx = 0;
2463 int err;
2464
2465 if (!ifidx) {
2466 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
2467 nl80211_fam.attrbuf, nl80211_fam.maxattr,
2468 nl80211_policy);
2469 if (err)
2470 return err;
2471
2472 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
2473 return -EINVAL;
2474
2475 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
2476 if (!ifidx)
2477 return -EINVAL;
2478 cb->args[0] = ifidx;
2479 }
2480
2481 netdev = dev_get_by_index(&init_net, ifidx);
2482 if (!netdev)
2483 return -ENODEV;
2484
2485 dev = cfg80211_get_dev_from_ifindex(ifidx);
2486 if (IS_ERR(dev)) {
2487 err = PTR_ERR(dev);
2488 goto out_put_netdev;
2489 }
2490
2491 spin_lock_bh(&dev->bss_lock);
2492 cfg80211_bss_expire(dev);
2493
2494 list_for_each_entry(scan, &dev->bss_list, list) {
2495 if (++idx <= start)
2496 continue;
2497 if (nl80211_send_bss(skb,
2498 NETLINK_CB(cb->skb).pid,
2499 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2500 dev, netdev, &scan->pub) < 0) {
2501 idx--;
2502 goto out;
2503 }
2504 }
2505
2506 out:
2507 spin_unlock_bh(&dev->bss_lock);
2508
2509 cb->args[1] = idx;
2510 err = skb->len;
2511 cfg80211_put_dev(dev);
2512 out_put_netdev:
2513 dev_put(netdev);
2514
2515 return err;
2516}
2517
2273static struct genl_ops nl80211_ops[] = { 2518static struct genl_ops nl80211_ops[] = {
2274 { 2519 {
2275 .cmd = NL80211_CMD_GET_WIPHY, 2520 .cmd = NL80211_CMD_GET_WIPHY,
@@ -2443,12 +2688,26 @@ static struct genl_ops nl80211_ops[] = {
2443 .policy = nl80211_policy, 2688 .policy = nl80211_policy,
2444 .flags = GENL_ADMIN_PERM, 2689 .flags = GENL_ADMIN_PERM,
2445 }, 2690 },
2691 {
2692 .cmd = NL80211_CMD_TRIGGER_SCAN,
2693 .doit = nl80211_trigger_scan,
2694 .policy = nl80211_policy,
2695 .flags = GENL_ADMIN_PERM,
2696 },
2697 {
2698 .cmd = NL80211_CMD_GET_SCAN,
2699 .policy = nl80211_policy,
2700 .dumpit = nl80211_dump_scan,
2701 },
2446}; 2702};
2447 2703
2448/* multicast groups */ 2704/* multicast groups */
2449static struct genl_multicast_group nl80211_config_mcgrp = { 2705static struct genl_multicast_group nl80211_config_mcgrp = {
2450 .name = "config", 2706 .name = "config",
2451}; 2707};
2708static struct genl_multicast_group nl80211_scan_mcgrp = {
2709 .name = "scan",
2710};
2452 2711
2453/* notification functions */ 2712/* notification functions */
2454 2713
@@ -2468,6 +2727,66 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
2468 genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL); 2727 genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
2469} 2728}
2470 2729
2730static int nl80211_send_scan_donemsg(struct sk_buff *msg,
2731 struct cfg80211_registered_device *rdev,
2732 struct net_device *netdev,
2733 u32 pid, u32 seq, int flags,
2734 u32 cmd)
2735{
2736 void *hdr;
2737
2738 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
2739 if (!hdr)
2740 return -1;
2741
2742 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->idx);
2743 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
2744
2745 /* XXX: we should probably bounce back the request? */
2746
2747 return genlmsg_end(msg, hdr);
2748
2749 nla_put_failure:
2750 genlmsg_cancel(msg, hdr);
2751 return -EMSGSIZE;
2752}
2753
2754void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
2755 struct net_device *netdev)
2756{
2757 struct sk_buff *msg;
2758
2759 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2760 if (!msg)
2761 return;
2762
2763 if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
2764 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
2765 nlmsg_free(msg);
2766 return;
2767 }
2768
2769 genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
2770}
2771
2772void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
2773 struct net_device *netdev)
2774{
2775 struct sk_buff *msg;
2776
2777 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2778 if (!msg)
2779 return;
2780
2781 if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
2782 NL80211_CMD_SCAN_ABORTED) < 0) {
2783 nlmsg_free(msg);
2784 return;
2785 }
2786
2787 genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
2788}
2789
2471/* initialisation/exit functions */ 2790/* initialisation/exit functions */
2472 2791
2473int nl80211_init(void) 2792int nl80211_init(void)
@@ -2488,6 +2807,10 @@ int nl80211_init(void)
2488 if (err) 2807 if (err)
2489 goto err_out; 2808 goto err_out;
2490 2809
2810 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
2811 if (err)
2812 goto err_out;
2813
2491 return 0; 2814 return 0;
2492 err_out: 2815 err_out:
2493 genl_unregister_family(&nl80211_fam); 2816 genl_unregister_family(&nl80211_fam);