diff options
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/.gitignore | 1 | ||||
-rw-r--r-- | net/wireless/Kconfig | 13 | ||||
-rw-r--r-- | net/wireless/Makefile | 6 | ||||
-rw-r--r-- | net/wireless/chan.c | 41 | ||||
-rw-r--r-- | net/wireless/core.c | 59 | ||||
-rw-r--r-- | net/wireless/core.h | 20 | ||||
-rw-r--r-- | net/wireless/db.txt | 17 | ||||
-rw-r--r-- | net/wireless/genregdb.awk | 118 | ||||
-rw-r--r-- | net/wireless/lib80211_crypt_ccmp.c | 2 | ||||
-rw-r--r-- | net/wireless/lib80211_crypt_tkip.c | 23 | ||||
-rw-r--r-- | net/wireless/mlme.c | 214 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 866 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 23 | ||||
-rw-r--r-- | net/wireless/radiotap.c | 305 | ||||
-rw-r--r-- | net/wireless/reg.c | 687 | ||||
-rw-r--r-- | net/wireless/reg.h | 29 | ||||
-rw-r--r-- | net/wireless/regdb.h | 7 | ||||
-rw-r--r-- | net/wireless/scan.c | 158 | ||||
-rw-r--r-- | net/wireless/sme.c | 41 | ||||
-rw-r--r-- | net/wireless/sysfs.c | 20 | ||||
-rw-r--r-- | net/wireless/util.c | 137 | ||||
-rw-r--r-- | net/wireless/wext-compat.c | 49 | ||||
-rw-r--r-- | net/wireless/wext-proc.c | 4 |
23 files changed, 2380 insertions, 460 deletions
diff --git a/net/wireless/.gitignore b/net/wireless/.gitignore new file mode 100644 index 000000000000..c33451b896d9 --- /dev/null +++ b/net/wireless/.gitignore | |||
@@ -0,0 +1 @@ | |||
regdb.c | |||
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 90e93a5701aa..d0ee29063e5d 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig | |||
@@ -94,20 +94,21 @@ config CFG80211_DEBUGFS | |||
94 | 94 | ||
95 | If unsure, say N. | 95 | If unsure, say N. |
96 | 96 | ||
97 | config WIRELESS_OLD_REGULATORY | 97 | config CFG80211_INTERNAL_REGDB |
98 | bool "Old wireless static regulatory definitions" | 98 | bool "use statically compiled regulatory rules database" if EMBEDDED |
99 | default n | 99 | default n |
100 | depends on CFG80211 | 100 | depends on CFG80211 |
101 | ---help--- | 101 | ---help--- |
102 | This option enables the old static regulatory information | 102 | This option generates an internal data structure representing |
103 | and uses it within the new framework. This option is available | 103 | the wireless regulatory rules described in net/wireless/db.txt |
104 | for historical reasons and it is advised to leave it off. | 104 | and includes code to query that database. This is an alternative |
105 | to using CRDA for defining regulatory rules for the kernel. | ||
105 | 106 | ||
106 | For details see: | 107 | For details see: |
107 | 108 | ||
108 | http://wireless.kernel.org/en/developers/Regulatory | 109 | http://wireless.kernel.org/en/developers/Regulatory |
109 | 110 | ||
110 | Say N and if you say Y, please tell us why. The default is N. | 111 | Most distributions have a CRDA package. So if unsure, say N. |
111 | 112 | ||
112 | config CFG80211_WEXT | 113 | config CFG80211_WEXT |
113 | bool "cfg80211 wireless extensions compatibility" | 114 | bool "cfg80211 wireless extensions compatibility" |
diff --git a/net/wireless/Makefile b/net/wireless/Makefile index f07c8dc7aab2..e77e508126fa 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile | |||
@@ -13,5 +13,11 @@ cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o | |||
13 | cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o | 13 | cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o |
14 | cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o | 14 | cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o |
15 | cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o | 15 | cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o |
16 | cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o | ||
16 | 17 | ||
17 | ccflags-y += -D__CHECK_ENDIAN__ | 18 | ccflags-y += -D__CHECK_ENDIAN__ |
19 | |||
20 | $(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk | ||
21 | @$(AWK) -f $(srctree)/$(src)/genregdb.awk < $< > $@ | ||
22 | |||
23 | clean-files := regdb.c | ||
diff --git a/net/wireless/chan.c b/net/wireless/chan.c index a46ac6c9b365..bf1737fc9a7e 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c | |||
@@ -41,44 +41,57 @@ rdev_fixed_channel(struct cfg80211_registered_device *rdev, | |||
41 | return result; | 41 | return result; |
42 | } | 42 | } |
43 | 43 | ||
44 | int rdev_set_freq(struct cfg80211_registered_device *rdev, | 44 | struct ieee80211_channel * |
45 | struct wireless_dev *for_wdev, | 45 | rdev_freq_to_chan(struct cfg80211_registered_device *rdev, |
46 | int freq, enum nl80211_channel_type channel_type) | 46 | int freq, enum nl80211_channel_type channel_type) |
47 | { | 47 | { |
48 | struct ieee80211_channel *chan; | 48 | struct ieee80211_channel *chan; |
49 | struct ieee80211_sta_ht_cap *ht_cap; | 49 | struct ieee80211_sta_ht_cap *ht_cap; |
50 | int result; | ||
51 | |||
52 | if (rdev_fixed_channel(rdev, for_wdev)) | ||
53 | return -EBUSY; | ||
54 | |||
55 | if (!rdev->ops->set_channel) | ||
56 | return -EOPNOTSUPP; | ||
57 | 50 | ||
58 | chan = ieee80211_get_channel(&rdev->wiphy, freq); | 51 | chan = ieee80211_get_channel(&rdev->wiphy, freq); |
59 | 52 | ||
60 | /* Primary channel not allowed */ | 53 | /* Primary channel not allowed */ |
61 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) | 54 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) |
62 | return -EINVAL; | 55 | return NULL; |
63 | 56 | ||
64 | if (channel_type == NL80211_CHAN_HT40MINUS && | 57 | if (channel_type == NL80211_CHAN_HT40MINUS && |
65 | chan->flags & IEEE80211_CHAN_NO_HT40MINUS) | 58 | chan->flags & IEEE80211_CHAN_NO_HT40MINUS) |
66 | return -EINVAL; | 59 | return NULL; |
67 | else if (channel_type == NL80211_CHAN_HT40PLUS && | 60 | else if (channel_type == NL80211_CHAN_HT40PLUS && |
68 | chan->flags & IEEE80211_CHAN_NO_HT40PLUS) | 61 | chan->flags & IEEE80211_CHAN_NO_HT40PLUS) |
69 | return -EINVAL; | 62 | return NULL; |
70 | 63 | ||
71 | ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; | 64 | ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; |
72 | 65 | ||
73 | if (channel_type != NL80211_CHAN_NO_HT) { | 66 | if (channel_type != NL80211_CHAN_NO_HT) { |
74 | if (!ht_cap->ht_supported) | 67 | if (!ht_cap->ht_supported) |
75 | return -EINVAL; | 68 | return NULL; |
76 | 69 | ||
77 | if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || | 70 | if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || |
78 | ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) | 71 | ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) |
79 | return -EINVAL; | 72 | return NULL; |
80 | } | 73 | } |
81 | 74 | ||
75 | return chan; | ||
76 | } | ||
77 | |||
78 | int rdev_set_freq(struct cfg80211_registered_device *rdev, | ||
79 | struct wireless_dev *for_wdev, | ||
80 | int freq, enum nl80211_channel_type channel_type) | ||
81 | { | ||
82 | struct ieee80211_channel *chan; | ||
83 | int result; | ||
84 | |||
85 | if (rdev_fixed_channel(rdev, for_wdev)) | ||
86 | return -EBUSY; | ||
87 | |||
88 | if (!rdev->ops->set_channel) | ||
89 | return -EOPNOTSUPP; | ||
90 | |||
91 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
92 | if (!chan) | ||
93 | return -EINVAL; | ||
94 | |||
82 | result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type); | 95 | result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type); |
83 | if (result) | 96 | if (result) |
84 | return result; | 97 | return result; |
diff --git a/net/wireless/core.c b/net/wireless/core.c index 92b812442488..7fdb9409ad2a 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * This is the linux wireless configuration interface. | 2 | * This is the linux wireless configuration interface. |
3 | * | 3 | * |
4 | * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include <linux/if.h> | 7 | #include <linux/if.h> |
@@ -31,15 +31,10 @@ MODULE_AUTHOR("Johannes Berg"); | |||
31 | MODULE_LICENSE("GPL"); | 31 | MODULE_LICENSE("GPL"); |
32 | MODULE_DESCRIPTION("wireless configuration support"); | 32 | MODULE_DESCRIPTION("wireless configuration support"); |
33 | 33 | ||
34 | /* RCU might be appropriate here since we usually | 34 | /* RCU-protected (and cfg80211_mutex for writers) */ |
35 | * only read the list, and that can happen quite | ||
36 | * often because we need to do it for each command */ | ||
37 | LIST_HEAD(cfg80211_rdev_list); | 35 | LIST_HEAD(cfg80211_rdev_list); |
38 | int cfg80211_rdev_list_generation; | 36 | int cfg80211_rdev_list_generation; |
39 | 37 | ||
40 | /* | ||
41 | * This is used to protect the cfg80211_rdev_list | ||
42 | */ | ||
43 | DEFINE_MUTEX(cfg80211_mutex); | 38 | DEFINE_MUTEX(cfg80211_mutex); |
44 | 39 | ||
45 | /* for debugfs */ | 40 | /* for debugfs */ |
@@ -402,6 +397,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | |||
402 | rdev->wiphy.retry_long = 4; | 397 | rdev->wiphy.retry_long = 4; |
403 | rdev->wiphy.frag_threshold = (u32) -1; | 398 | rdev->wiphy.frag_threshold = (u32) -1; |
404 | rdev->wiphy.rts_threshold = (u32) -1; | 399 | rdev->wiphy.rts_threshold = (u32) -1; |
400 | rdev->wiphy.coverage_class = 0; | ||
405 | 401 | ||
406 | return &rdev->wiphy; | 402 | return &rdev->wiphy; |
407 | } | 403 | } |
@@ -417,6 +413,18 @@ int wiphy_register(struct wiphy *wiphy) | |||
417 | int i; | 413 | int i; |
418 | u16 ifmodes = wiphy->interface_modes; | 414 | u16 ifmodes = wiphy->interface_modes; |
419 | 415 | ||
416 | if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) | ||
417 | return -EINVAL; | ||
418 | |||
419 | if (WARN_ON(wiphy->addresses && | ||
420 | !is_zero_ether_addr(wiphy->perm_addr) && | ||
421 | memcmp(wiphy->perm_addr, wiphy->addresses[0].addr, | ||
422 | ETH_ALEN))) | ||
423 | return -EINVAL; | ||
424 | |||
425 | if (wiphy->addresses) | ||
426 | memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); | ||
427 | |||
420 | /* sanity check ifmodes */ | 428 | /* sanity check ifmodes */ |
421 | WARN_ON(!ifmodes); | 429 | WARN_ON(!ifmodes); |
422 | ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1; | 430 | ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1; |
@@ -476,7 +484,7 @@ int wiphy_register(struct wiphy *wiphy) | |||
476 | /* set up regulatory info */ | 484 | /* set up regulatory info */ |
477 | wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); | 485 | wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); |
478 | 486 | ||
479 | list_add(&rdev->list, &cfg80211_rdev_list); | 487 | list_add_rcu(&rdev->list, &cfg80211_rdev_list); |
480 | cfg80211_rdev_list_generation++; | 488 | cfg80211_rdev_list_generation++; |
481 | 489 | ||
482 | mutex_unlock(&cfg80211_mutex); | 490 | mutex_unlock(&cfg80211_mutex); |
@@ -553,7 +561,8 @@ void wiphy_unregister(struct wiphy *wiphy) | |||
553 | * it impossible to find from userspace. | 561 | * it impossible to find from userspace. |
554 | */ | 562 | */ |
555 | debugfs_remove_recursive(rdev->wiphy.debugfsdir); | 563 | debugfs_remove_recursive(rdev->wiphy.debugfsdir); |
556 | list_del(&rdev->list); | 564 | list_del_rcu(&rdev->list); |
565 | synchronize_rcu(); | ||
557 | 566 | ||
558 | /* | 567 | /* |
559 | * Try to grab rdev->mtx. If a command is still in progress, | 568 | * Try to grab rdev->mtx. If a command is still in progress, |
@@ -668,8 +677,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
668 | INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); | 677 | INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); |
669 | INIT_LIST_HEAD(&wdev->event_list); | 678 | INIT_LIST_HEAD(&wdev->event_list); |
670 | spin_lock_init(&wdev->event_lock); | 679 | spin_lock_init(&wdev->event_lock); |
680 | INIT_LIST_HEAD(&wdev->action_registrations); | ||
681 | spin_lock_init(&wdev->action_registrations_lock); | ||
682 | |||
671 | mutex_lock(&rdev->devlist_mtx); | 683 | mutex_lock(&rdev->devlist_mtx); |
672 | list_add(&wdev->list, &rdev->netdev_list); | 684 | list_add_rcu(&wdev->list, &rdev->netdev_list); |
673 | rdev->devlist_generation++; | 685 | rdev->devlist_generation++; |
674 | /* can only change netns with wiphy */ | 686 | /* can only change netns with wiphy */ |
675 | dev->features |= NETIF_F_NETNS_LOCAL; | 687 | dev->features |= NETIF_F_NETNS_LOCAL; |
@@ -686,19 +698,21 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
686 | wdev->wext.default_key = -1; | 698 | wdev->wext.default_key = -1; |
687 | wdev->wext.default_mgmt_key = -1; | 699 | wdev->wext.default_mgmt_key = -1; |
688 | wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; | 700 | wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; |
701 | #endif | ||
702 | |||
689 | if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) | 703 | if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) |
690 | wdev->wext.ps = true; | 704 | wdev->ps = true; |
691 | else | 705 | else |
692 | wdev->wext.ps = false; | 706 | wdev->ps = false; |
693 | wdev->wext.ps_timeout = 100; | 707 | wdev->ps_timeout = 100; |
694 | if (rdev->ops->set_power_mgmt) | 708 | if (rdev->ops->set_power_mgmt) |
695 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, | 709 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, |
696 | wdev->wext.ps, | 710 | wdev->ps, |
697 | wdev->wext.ps_timeout)) { | 711 | wdev->ps_timeout)) { |
698 | /* assume this means it's off */ | 712 | /* assume this means it's off */ |
699 | wdev->wext.ps = false; | 713 | wdev->ps = false; |
700 | } | 714 | } |
701 | #endif | 715 | |
702 | if (!dev->ethtool_ops) | 716 | if (!dev->ethtool_ops) |
703 | dev->ethtool_ops = &cfg80211_ethtool_ops; | 717 | dev->ethtool_ops = &cfg80211_ethtool_ops; |
704 | 718 | ||
@@ -781,13 +795,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
781 | */ | 795 | */ |
782 | if (!list_empty(&wdev->list)) { | 796 | if (!list_empty(&wdev->list)) { |
783 | sysfs_remove_link(&dev->dev.kobj, "phy80211"); | 797 | sysfs_remove_link(&dev->dev.kobj, "phy80211"); |
784 | list_del_init(&wdev->list); | 798 | list_del_rcu(&wdev->list); |
785 | rdev->devlist_generation++; | 799 | rdev->devlist_generation++; |
800 | cfg80211_mlme_purge_actions(wdev); | ||
786 | #ifdef CONFIG_CFG80211_WEXT | 801 | #ifdef CONFIG_CFG80211_WEXT |
787 | kfree(wdev->wext.keys); | 802 | kfree(wdev->wext.keys); |
788 | #endif | 803 | #endif |
789 | } | 804 | } |
790 | mutex_unlock(&rdev->devlist_mtx); | 805 | mutex_unlock(&rdev->devlist_mtx); |
806 | /* | ||
807 | * synchronise (so that we won't find this netdev | ||
808 | * from other code any more) and then clear the list | ||
809 | * head so that the above code can safely check for | ||
810 | * !list_empty() to avoid double-cleanup. | ||
811 | */ | ||
812 | synchronize_rcu(); | ||
813 | INIT_LIST_HEAD(&wdev->list); | ||
791 | break; | 814 | break; |
792 | case NETDEV_PRE_UP: | 815 | case NETDEV_PRE_UP: |
793 | if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) | 816 | if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) |
diff --git a/net/wireless/core.h b/net/wireless/core.h index 4ef3efc94106..d52da913145a 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Wireless configuration interface internals. | 2 | * Wireless configuration interface internals. |
3 | * | 3 | * |
4 | * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
5 | */ | 5 | */ |
6 | #ifndef __NET_WIRELESS_CORE_H | 6 | #ifndef __NET_WIRELESS_CORE_H |
7 | #define __NET_WIRELESS_CORE_H | 7 | #define __NET_WIRELESS_CORE_H |
@@ -48,6 +48,7 @@ struct cfg80211_registered_device { | |||
48 | 48 | ||
49 | /* associate netdev list */ | 49 | /* associate netdev list */ |
50 | struct mutex devlist_mtx; | 50 | struct mutex devlist_mtx; |
51 | /* protected by devlist_mtx or RCU */ | ||
51 | struct list_head netdev_list; | 52 | struct list_head netdev_list; |
52 | int devlist_generation; | 53 | int devlist_generation; |
53 | int opencount; /* also protected by devlist_mtx */ | 54 | int opencount; /* also protected by devlist_mtx */ |
@@ -111,7 +112,8 @@ struct cfg80211_internal_bss { | |||
111 | unsigned long ts; | 112 | unsigned long ts; |
112 | struct kref ref; | 113 | struct kref ref; |
113 | atomic_t hold; | 114 | atomic_t hold; |
114 | bool ies_allocated; | 115 | bool beacon_ies_allocated; |
116 | bool proberesp_ies_allocated; | ||
115 | 117 | ||
116 | /* must be last because of priv member */ | 118 | /* must be last because of priv member */ |
117 | struct cfg80211_bss pub; | 119 | struct cfg80211_bss pub; |
@@ -327,6 +329,15 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
327 | const u8 *resp_ie, size_t resp_ie_len, | 329 | const u8 *resp_ie, size_t resp_ie_len, |
328 | u16 status, bool wextev, | 330 | u16 status, bool wextev, |
329 | struct cfg80211_bss *bss); | 331 | struct cfg80211_bss *bss); |
332 | int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid, | ||
333 | const u8 *match_data, int match_len); | ||
334 | void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid); | ||
335 | void cfg80211_mlme_purge_actions(struct wireless_dev *wdev); | ||
336 | int cfg80211_mlme_action(struct cfg80211_registered_device *rdev, | ||
337 | struct net_device *dev, | ||
338 | struct ieee80211_channel *chan, | ||
339 | enum nl80211_channel_type channel_type, | ||
340 | const u8 *buf, size_t len, u64 *cookie); | ||
330 | 341 | ||
331 | /* SME */ | 342 | /* SME */ |
332 | int __cfg80211_connect(struct cfg80211_registered_device *rdev, | 343 | int __cfg80211_connect(struct cfg80211_registered_device *rdev, |
@@ -374,10 +385,15 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); | |||
374 | struct ieee80211_channel * | 385 | struct ieee80211_channel * |
375 | rdev_fixed_channel(struct cfg80211_registered_device *rdev, | 386 | rdev_fixed_channel(struct cfg80211_registered_device *rdev, |
376 | struct wireless_dev *for_wdev); | 387 | struct wireless_dev *for_wdev); |
388 | struct ieee80211_channel * | ||
389 | rdev_freq_to_chan(struct cfg80211_registered_device *rdev, | ||
390 | int freq, enum nl80211_channel_type channel_type); | ||
377 | int rdev_set_freq(struct cfg80211_registered_device *rdev, | 391 | int rdev_set_freq(struct cfg80211_registered_device *rdev, |
378 | struct wireless_dev *for_wdev, | 392 | struct wireless_dev *for_wdev, |
379 | int freq, enum nl80211_channel_type channel_type); | 393 | int freq, enum nl80211_channel_type channel_type); |
380 | 394 | ||
395 | u16 cfg80211_calculate_bitrate(struct rate_info *rate); | ||
396 | |||
381 | #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS | 397 | #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS |
382 | #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) | 398 | #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) |
383 | #else | 399 | #else |
diff --git a/net/wireless/db.txt b/net/wireless/db.txt new file mode 100644 index 000000000000..a2fc3a09ccdc --- /dev/null +++ b/net/wireless/db.txt | |||
@@ -0,0 +1,17 @@ | |||
1 | # | ||
2 | # This file is a placeholder to prevent accidental build breakage if someone | ||
3 | # enables CONFIG_CFG80211_INTERNAL_REGDB. Almost no one actually needs to | ||
4 | # enable that build option. | ||
5 | # | ||
6 | # You should be using CRDA instead. It is even better if you use the CRDA | ||
7 | # package provided by your distribution, since they will probably keep it | ||
8 | # up-to-date on your behalf. | ||
9 | # | ||
10 | # If you _really_ intend to use CONFIG_CFG80211_INTERNAL_REGDB then you will | ||
11 | # need to replace this file with one containing appropriately formatted | ||
12 | # regulatory rules that cover the regulatory domains you will be using. Your | ||
13 | # best option is to extract the db.txt file from the wireless-regdb git | ||
14 | # repository: | ||
15 | # | ||
16 | # git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git | ||
17 | # | ||
diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk new file mode 100644 index 000000000000..3cc9e69880a8 --- /dev/null +++ b/net/wireless/genregdb.awk | |||
@@ -0,0 +1,118 @@ | |||
1 | #!/usr/bin/awk -f | ||
2 | # | ||
3 | # genregdb.awk -- generate regdb.c from db.txt | ||
4 | # | ||
5 | # Actually, it reads from stdin (presumed to be db.txt) and writes | ||
6 | # to stdout (presumed to be regdb.c), but close enough... | ||
7 | # | ||
8 | # Copyright 2009 John W. Linville <linville@tuxdriver.com> | ||
9 | # | ||
10 | # This program is free software; you can redistribute it and/or modify | ||
11 | # it under the terms of the GNU General Public License version 2 as | ||
12 | # published by the Free Software Foundation. | ||
13 | # | ||
14 | |||
15 | BEGIN { | ||
16 | active = 0 | ||
17 | rules = 0; | ||
18 | print "/*" | ||
19 | print " * DO NOT EDIT -- file generated from data in db.txt" | ||
20 | print " */" | ||
21 | print "" | ||
22 | print "#include <linux/nl80211.h>" | ||
23 | print "#include <net/cfg80211.h>" | ||
24 | print "" | ||
25 | regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n" | ||
26 | } | ||
27 | |||
28 | /^[ \t]*#/ { | ||
29 | # Ignore | ||
30 | } | ||
31 | |||
32 | !active && /^[ \t]*$/ { | ||
33 | # Ignore | ||
34 | } | ||
35 | |||
36 | !active && /country/ { | ||
37 | country=$2 | ||
38 | sub(/:/, "", country) | ||
39 | printf "static const struct ieee80211_regdomain regdom_%s = {\n", country | ||
40 | printf "\t.alpha2 = \"%s\",\n", country | ||
41 | printf "\t.reg_rules = {\n" | ||
42 | active = 1 | ||
43 | regdb = regdb "\t®dom_" country ",\n" | ||
44 | } | ||
45 | |||
46 | active && /^[ \t]*\(/ { | ||
47 | start = $1 | ||
48 | sub(/\(/, "", start) | ||
49 | end = $3 | ||
50 | bw = $5 | ||
51 | sub(/\),/, "", bw) | ||
52 | gain = $6 | ||
53 | sub(/\(/, "", gain) | ||
54 | sub(/,/, "", gain) | ||
55 | power = $7 | ||
56 | sub(/\)/, "", power) | ||
57 | sub(/,/, "", power) | ||
58 | # power might be in mW... | ||
59 | units = $8 | ||
60 | sub(/\)/, "", units) | ||
61 | sub(/,/, "", units) | ||
62 | if (units == "mW") { | ||
63 | if (power == 100) { | ||
64 | power = 20 | ||
65 | } else if (power == 200) { | ||
66 | power = 23 | ||
67 | } else if (power == 500) { | ||
68 | power = 27 | ||
69 | } else if (power == 1000) { | ||
70 | power = 30 | ||
71 | } else { | ||
72 | print "Unknown power value in database!" | ||
73 | } | ||
74 | } | ||
75 | flagstr = "" | ||
76 | for (i=8; i<=NF; i++) | ||
77 | flagstr = flagstr $i | ||
78 | split(flagstr, flagarray, ",") | ||
79 | flags = "" | ||
80 | for (arg in flagarray) { | ||
81 | if (flagarray[arg] == "NO-OFDM") { | ||
82 | flags = flags "\n\t\t\tNL80211_RRF_NO_OFDM | " | ||
83 | } else if (flagarray[arg] == "NO-CCK") { | ||
84 | flags = flags "\n\t\t\tNL80211_RRF_NO_CCK | " | ||
85 | } else if (flagarray[arg] == "NO-INDOOR") { | ||
86 | flags = flags "\n\t\t\tNL80211_RRF_NO_INDOOR | " | ||
87 | } else if (flagarray[arg] == "NO-OUTDOOR") { | ||
88 | flags = flags "\n\t\t\tNL80211_RRF_NO_OUTDOOR | " | ||
89 | } else if (flagarray[arg] == "DFS") { | ||
90 | flags = flags "\n\t\t\tNL80211_RRF_DFS | " | ||
91 | } else if (flagarray[arg] == "PTP-ONLY") { | ||
92 | flags = flags "\n\t\t\tNL80211_RRF_PTP_ONLY | " | ||
93 | } else if (flagarray[arg] == "PTMP-ONLY") { | ||
94 | flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | " | ||
95 | } else if (flagarray[arg] == "PASSIVE-SCAN") { | ||
96 | flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | " | ||
97 | } else if (flagarray[arg] == "NO-IBSS") { | ||
98 | flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | " | ||
99 | } | ||
100 | } | ||
101 | flags = flags "0" | ||
102 | printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags | ||
103 | rules++ | ||
104 | } | ||
105 | |||
106 | active && /^[ \t]*$/ { | ||
107 | active = 0 | ||
108 | printf "\t},\n" | ||
109 | printf "\t.n_reg_rules = %d\n", rules | ||
110 | printf "};\n\n" | ||
111 | rules = 0; | ||
112 | } | ||
113 | |||
114 | END { | ||
115 | print regdb "};" | ||
116 | print "" | ||
117 | print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);" | ||
118 | } | ||
diff --git a/net/wireless/lib80211_crypt_ccmp.c b/net/wireless/lib80211_crypt_ccmp.c index 2301dc1edc4c..b7fa31d5fd13 100644 --- a/net/wireless/lib80211_crypt_ccmp.c +++ b/net/wireless/lib80211_crypt_ccmp.c | |||
@@ -237,7 +237,6 @@ static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) | |||
237 | return -1; | 237 | return -1; |
238 | 238 | ||
239 | pos = skb->data + hdr_len + CCMP_HDR_LEN; | 239 | pos = skb->data + hdr_len + CCMP_HDR_LEN; |
240 | mic = skb_put(skb, CCMP_MIC_LEN); | ||
241 | hdr = (struct ieee80211_hdr *)skb->data; | 240 | hdr = (struct ieee80211_hdr *)skb->data; |
242 | ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); | 241 | ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); |
243 | 242 | ||
@@ -257,6 +256,7 @@ static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) | |||
257 | pos += len; | 256 | pos += len; |
258 | } | 257 | } |
259 | 258 | ||
259 | mic = skb_put(skb, CCMP_MIC_LEN); | ||
260 | for (i = 0; i < CCMP_MIC_LEN; i++) | 260 | for (i = 0; i < CCMP_MIC_LEN; i++) |
261 | mic[i] = b[i] ^ s0[i]; | 261 | mic[i] = b[i] ^ s0[i]; |
262 | 262 | ||
diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c index c36287399d7e..8cbdb32ff316 100644 --- a/net/wireless/lib80211_crypt_tkip.c +++ b/net/wireless/lib80211_crypt_tkip.c | |||
@@ -36,6 +36,8 @@ MODULE_AUTHOR("Jouni Malinen"); | |||
36 | MODULE_DESCRIPTION("lib80211 crypt: TKIP"); | 36 | MODULE_DESCRIPTION("lib80211 crypt: TKIP"); |
37 | MODULE_LICENSE("GPL"); | 37 | MODULE_LICENSE("GPL"); |
38 | 38 | ||
39 | #define TKIP_HDR_LEN 8 | ||
40 | |||
39 | struct lib80211_tkip_data { | 41 | struct lib80211_tkip_data { |
40 | #define TKIP_KEY_LEN 32 | 42 | #define TKIP_KEY_LEN 32 |
41 | u8 key[TKIP_KEY_LEN]; | 43 | u8 key[TKIP_KEY_LEN]; |
@@ -314,13 +316,12 @@ static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len, | |||
314 | u8 * rc4key, int keylen, void *priv) | 316 | u8 * rc4key, int keylen, void *priv) |
315 | { | 317 | { |
316 | struct lib80211_tkip_data *tkey = priv; | 318 | struct lib80211_tkip_data *tkey = priv; |
317 | int len; | ||
318 | u8 *pos; | 319 | u8 *pos; |
319 | struct ieee80211_hdr *hdr; | 320 | struct ieee80211_hdr *hdr; |
320 | 321 | ||
321 | hdr = (struct ieee80211_hdr *)skb->data; | 322 | hdr = (struct ieee80211_hdr *)skb->data; |
322 | 323 | ||
323 | if (skb_headroom(skb) < 8 || skb->len < hdr_len) | 324 | if (skb_headroom(skb) < TKIP_HDR_LEN || skb->len < hdr_len) |
324 | return -1; | 325 | return -1; |
325 | 326 | ||
326 | if (rc4key == NULL || keylen < 16) | 327 | if (rc4key == NULL || keylen < 16) |
@@ -333,9 +334,8 @@ static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len, | |||
333 | } | 334 | } |
334 | tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16); | 335 | tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16); |
335 | 336 | ||
336 | len = skb->len - hdr_len; | 337 | pos = skb_push(skb, TKIP_HDR_LEN); |
337 | pos = skb_push(skb, 8); | 338 | memmove(pos, pos + TKIP_HDR_LEN, hdr_len); |
338 | memmove(pos, pos + 8, hdr_len); | ||
339 | pos += hdr_len; | 339 | pos += hdr_len; |
340 | 340 | ||
341 | *pos++ = *rc4key; | 341 | *pos++ = *rc4key; |
@@ -353,7 +353,7 @@ static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len, | |||
353 | tkey->tx_iv32++; | 353 | tkey->tx_iv32++; |
354 | } | 354 | } |
355 | 355 | ||
356 | return 8; | 356 | return TKIP_HDR_LEN; |
357 | } | 357 | } |
358 | 358 | ||
359 | static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) | 359 | static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) |
@@ -384,9 +384,8 @@ static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) | |||
384 | if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0) | 384 | if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0) |
385 | return -1; | 385 | return -1; |
386 | 386 | ||
387 | icv = skb_put(skb, 4); | ||
388 | |||
389 | crc = ~crc32_le(~0, pos, len); | 387 | crc = ~crc32_le(~0, pos, len); |
388 | icv = skb_put(skb, 4); | ||
390 | icv[0] = crc; | 389 | icv[0] = crc; |
391 | icv[1] = crc >> 8; | 390 | icv[1] = crc >> 8; |
392 | icv[2] = crc >> 16; | 391 | icv[2] = crc >> 16; |
@@ -434,7 +433,7 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) | |||
434 | return -1; | 433 | return -1; |
435 | } | 434 | } |
436 | 435 | ||
437 | if (skb->len < hdr_len + 8 + 4) | 436 | if (skb->len < hdr_len + TKIP_HDR_LEN + 4) |
438 | return -1; | 437 | return -1; |
439 | 438 | ||
440 | pos = skb->data + hdr_len; | 439 | pos = skb->data + hdr_len; |
@@ -462,7 +461,7 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) | |||
462 | } | 461 | } |
463 | iv16 = (pos[0] << 8) | pos[2]; | 462 | iv16 = (pos[0] << 8) | pos[2]; |
464 | iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); | 463 | iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); |
465 | pos += 8; | 464 | pos += TKIP_HDR_LEN; |
466 | 465 | ||
467 | if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) { | 466 | if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) { |
468 | #ifdef CONFIG_LIB80211_DEBUG | 467 | #ifdef CONFIG_LIB80211_DEBUG |
@@ -523,8 +522,8 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) | |||
523 | tkey->rx_iv16_new = iv16; | 522 | tkey->rx_iv16_new = iv16; |
524 | 523 | ||
525 | /* Remove IV and ICV */ | 524 | /* Remove IV and ICV */ |
526 | memmove(skb->data + 8, skb->data, hdr_len); | 525 | memmove(skb->data + TKIP_HDR_LEN, skb->data, hdr_len); |
527 | skb_pull(skb, 8); | 526 | skb_pull(skb, TKIP_HDR_LEN); |
528 | skb_trim(skb, skb->len - 4); | 527 | skb_trim(skb, skb->len - 4); |
529 | 528 | ||
530 | return keyidx; | 529 | return keyidx; |
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 82e6002c8d67..62bc8855e123 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c | |||
@@ -148,22 +148,23 @@ void __cfg80211_send_deauth(struct net_device *dev, | |||
148 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; | 148 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; |
149 | const u8 *bssid = mgmt->bssid; | 149 | const u8 *bssid = mgmt->bssid; |
150 | int i; | 150 | int i; |
151 | bool found = false; | ||
151 | 152 | ||
152 | ASSERT_WDEV_LOCK(wdev); | 153 | ASSERT_WDEV_LOCK(wdev); |
153 | 154 | ||
154 | nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); | ||
155 | |||
156 | if (wdev->current_bss && | 155 | if (wdev->current_bss && |
157 | memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { | 156 | memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { |
158 | cfg80211_unhold_bss(wdev->current_bss); | 157 | cfg80211_unhold_bss(wdev->current_bss); |
159 | cfg80211_put_bss(&wdev->current_bss->pub); | 158 | cfg80211_put_bss(&wdev->current_bss->pub); |
160 | wdev->current_bss = NULL; | 159 | wdev->current_bss = NULL; |
160 | found = true; | ||
161 | } else for (i = 0; i < MAX_AUTH_BSSES; i++) { | 161 | } else for (i = 0; i < MAX_AUTH_BSSES; i++) { |
162 | if (wdev->auth_bsses[i] && | 162 | if (wdev->auth_bsses[i] && |
163 | memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { | 163 | memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { |
164 | cfg80211_unhold_bss(wdev->auth_bsses[i]); | 164 | cfg80211_unhold_bss(wdev->auth_bsses[i]); |
165 | cfg80211_put_bss(&wdev->auth_bsses[i]->pub); | 165 | cfg80211_put_bss(&wdev->auth_bsses[i]->pub); |
166 | wdev->auth_bsses[i] = NULL; | 166 | wdev->auth_bsses[i] = NULL; |
167 | found = true; | ||
167 | break; | 168 | break; |
168 | } | 169 | } |
169 | if (wdev->authtry_bsses[i] && | 170 | if (wdev->authtry_bsses[i] && |
@@ -171,10 +172,16 @@ void __cfg80211_send_deauth(struct net_device *dev, | |||
171 | cfg80211_unhold_bss(wdev->authtry_bsses[i]); | 172 | cfg80211_unhold_bss(wdev->authtry_bsses[i]); |
172 | cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); | 173 | cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); |
173 | wdev->authtry_bsses[i] = NULL; | 174 | wdev->authtry_bsses[i] = NULL; |
175 | found = true; | ||
174 | break; | 176 | break; |
175 | } | 177 | } |
176 | } | 178 | } |
177 | 179 | ||
180 | if (!found) | ||
181 | return; | ||
182 | |||
183 | nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); | ||
184 | |||
178 | if (wdev->sme_state == CFG80211_SME_CONNECTED) { | 185 | if (wdev->sme_state == CFG80211_SME_CONNECTED) { |
179 | u16 reason_code; | 186 | u16 reason_code; |
180 | bool from_ap; | 187 | bool from_ap; |
@@ -684,3 +691,206 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, | |||
684 | } | 691 | } |
685 | } | 692 | } |
686 | } | 693 | } |
694 | |||
695 | void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie, | ||
696 | struct ieee80211_channel *chan, | ||
697 | enum nl80211_channel_type channel_type, | ||
698 | unsigned int duration, gfp_t gfp) | ||
699 | { | ||
700 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | ||
701 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
702 | |||
703 | nl80211_send_remain_on_channel(rdev, dev, cookie, chan, channel_type, | ||
704 | duration, gfp); | ||
705 | } | ||
706 | EXPORT_SYMBOL(cfg80211_ready_on_channel); | ||
707 | |||
708 | void cfg80211_remain_on_channel_expired(struct net_device *dev, | ||
709 | u64 cookie, | ||
710 | struct ieee80211_channel *chan, | ||
711 | enum nl80211_channel_type channel_type, | ||
712 | gfp_t gfp) | ||
713 | { | ||
714 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | ||
715 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
716 | |||
717 | nl80211_send_remain_on_channel_cancel(rdev, dev, cookie, chan, | ||
718 | channel_type, gfp); | ||
719 | } | ||
720 | EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); | ||
721 | |||
722 | void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, | ||
723 | struct station_info *sinfo, gfp_t gfp) | ||
724 | { | ||
725 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | ||
726 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
727 | |||
728 | nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp); | ||
729 | } | ||
730 | EXPORT_SYMBOL(cfg80211_new_sta); | ||
731 | |||
732 | struct cfg80211_action_registration { | ||
733 | struct list_head list; | ||
734 | |||
735 | u32 nlpid; | ||
736 | |||
737 | int match_len; | ||
738 | |||
739 | u8 match[]; | ||
740 | }; | ||
741 | |||
742 | int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid, | ||
743 | const u8 *match_data, int match_len) | ||
744 | { | ||
745 | struct cfg80211_action_registration *reg, *nreg; | ||
746 | int err = 0; | ||
747 | |||
748 | nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL); | ||
749 | if (!nreg) | ||
750 | return -ENOMEM; | ||
751 | |||
752 | spin_lock_bh(&wdev->action_registrations_lock); | ||
753 | |||
754 | list_for_each_entry(reg, &wdev->action_registrations, list) { | ||
755 | int mlen = min(match_len, reg->match_len); | ||
756 | |||
757 | if (memcmp(reg->match, match_data, mlen) == 0) { | ||
758 | err = -EALREADY; | ||
759 | break; | ||
760 | } | ||
761 | } | ||
762 | |||
763 | if (err) { | ||
764 | kfree(nreg); | ||
765 | goto out; | ||
766 | } | ||
767 | |||
768 | memcpy(nreg->match, match_data, match_len); | ||
769 | nreg->match_len = match_len; | ||
770 | nreg->nlpid = snd_pid; | ||
771 | list_add(&nreg->list, &wdev->action_registrations); | ||
772 | |||
773 | out: | ||
774 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
775 | return err; | ||
776 | } | ||
777 | |||
778 | void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid) | ||
779 | { | ||
780 | struct cfg80211_action_registration *reg, *tmp; | ||
781 | |||
782 | spin_lock_bh(&wdev->action_registrations_lock); | ||
783 | |||
784 | list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) { | ||
785 | if (reg->nlpid == nlpid) { | ||
786 | list_del(®->list); | ||
787 | kfree(reg); | ||
788 | } | ||
789 | } | ||
790 | |||
791 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
792 | } | ||
793 | |||
794 | void cfg80211_mlme_purge_actions(struct wireless_dev *wdev) | ||
795 | { | ||
796 | struct cfg80211_action_registration *reg, *tmp; | ||
797 | |||
798 | spin_lock_bh(&wdev->action_registrations_lock); | ||
799 | |||
800 | list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) { | ||
801 | list_del(®->list); | ||
802 | kfree(reg); | ||
803 | } | ||
804 | |||
805 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
806 | } | ||
807 | |||
808 | int cfg80211_mlme_action(struct cfg80211_registered_device *rdev, | ||
809 | struct net_device *dev, | ||
810 | struct ieee80211_channel *chan, | ||
811 | enum nl80211_channel_type channel_type, | ||
812 | const u8 *buf, size_t len, u64 *cookie) | ||
813 | { | ||
814 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
815 | const struct ieee80211_mgmt *mgmt; | ||
816 | |||
817 | if (rdev->ops->action == NULL) | ||
818 | return -EOPNOTSUPP; | ||
819 | if (len < 24 + 1) | ||
820 | return -EINVAL; | ||
821 | |||
822 | mgmt = (const struct ieee80211_mgmt *) buf; | ||
823 | if (!ieee80211_is_action(mgmt->frame_control)) | ||
824 | return -EINVAL; | ||
825 | if (mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) { | ||
826 | /* Verify that we are associated with the destination AP */ | ||
827 | if (!wdev->current_bss || | ||
828 | memcmp(wdev->current_bss->pub.bssid, mgmt->bssid, | ||
829 | ETH_ALEN) != 0 || | ||
830 | memcmp(wdev->current_bss->pub.bssid, mgmt->da, | ||
831 | ETH_ALEN) != 0) | ||
832 | return -ENOTCONN; | ||
833 | } | ||
834 | |||
835 | if (memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0) | ||
836 | return -EINVAL; | ||
837 | |||
838 | /* Transmit the Action frame as requested by user space */ | ||
839 | return rdev->ops->action(&rdev->wiphy, dev, chan, channel_type, | ||
840 | buf, len, cookie); | ||
841 | } | ||
842 | |||
843 | bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf, | ||
844 | size_t len, gfp_t gfp) | ||
845 | { | ||
846 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
847 | struct wiphy *wiphy = wdev->wiphy; | ||
848 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
849 | struct cfg80211_action_registration *reg; | ||
850 | const u8 *action_data; | ||
851 | int action_data_len; | ||
852 | bool result = false; | ||
853 | |||
854 | /* frame length - min size excluding category */ | ||
855 | action_data_len = len - (IEEE80211_MIN_ACTION_SIZE - 1); | ||
856 | |||
857 | /* action data starts with category */ | ||
858 | action_data = buf + IEEE80211_MIN_ACTION_SIZE - 1; | ||
859 | |||
860 | spin_lock_bh(&wdev->action_registrations_lock); | ||
861 | |||
862 | list_for_each_entry(reg, &wdev->action_registrations, list) { | ||
863 | if (reg->match_len > action_data_len) | ||
864 | continue; | ||
865 | |||
866 | if (memcmp(reg->match, action_data, reg->match_len)) | ||
867 | continue; | ||
868 | |||
869 | /* found match! */ | ||
870 | |||
871 | /* Indicate the received Action frame to user space */ | ||
872 | if (nl80211_send_action(rdev, dev, reg->nlpid, freq, | ||
873 | buf, len, gfp)) | ||
874 | continue; | ||
875 | |||
876 | result = true; | ||
877 | break; | ||
878 | } | ||
879 | |||
880 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
881 | |||
882 | return result; | ||
883 | } | ||
884 | EXPORT_SYMBOL(cfg80211_rx_action); | ||
885 | |||
886 | void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, | ||
887 | const u8 *buf, size_t len, bool ack, gfp_t gfp) | ||
888 | { | ||
889 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
890 | struct wiphy *wiphy = wdev->wiphy; | ||
891 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
892 | |||
893 | /* Indicate TX status of the Action frame to user space */ | ||
894 | nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp); | ||
895 | } | ||
896 | EXPORT_SYMBOL(cfg80211_action_tx_status); | ||
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a6028433e3a0..e447db04cf76 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * This is the new netlink-based wireless configuration interface. | 2 | * This is the new netlink-based wireless configuration interface. |
3 | * | 3 | * |
4 | * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include <linux/if.h> | 7 | #include <linux/if.h> |
@@ -58,7 +58,7 @@ static int get_rdev_dev_by_info_ifindex(struct genl_info *info, | |||
58 | } | 58 | } |
59 | 59 | ||
60 | /* policy for the attributes */ | 60 | /* policy for the attributes */ |
61 | static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | 61 | static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { |
62 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, | 62 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, |
63 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, | 63 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, |
64 | .len = 20-1 }, | 64 | .len = 20-1 }, |
@@ -69,6 +69,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
69 | [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, | 69 | [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, |
70 | [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, | 70 | [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, |
71 | [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, | 71 | [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, |
72 | [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 }, | ||
72 | 73 | ||
73 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, | 74 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, |
74 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, | 75 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, |
@@ -141,11 +142,17 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
141 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, | 142 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, |
142 | [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, | 143 | [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, |
143 | .len = WLAN_PMKID_LEN }, | 144 | .len = WLAN_PMKID_LEN }, |
145 | [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, | ||
146 | [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, | ||
147 | [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, | ||
148 | [NL80211_ATTR_FRAME] = { .type = NLA_BINARY, | ||
149 | .len = IEEE80211_MAX_DATA_LEN }, | ||
150 | [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, | ||
151 | [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, | ||
144 | }; | 152 | }; |
145 | 153 | ||
146 | /* policy for the attributes */ | 154 | /* policy for the attributes */ |
147 | static struct nla_policy | 155 | static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { |
148 | nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = { | ||
149 | [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, | 156 | [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, |
150 | [NL80211_KEY_IDX] = { .type = NLA_U8 }, | 157 | [NL80211_KEY_IDX] = { .type = NLA_U8 }, |
151 | [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, | 158 | [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, |
@@ -442,6 +449,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
442 | dev->wiphy.frag_threshold); | 449 | dev->wiphy.frag_threshold); |
443 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, | 450 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, |
444 | dev->wiphy.rts_threshold); | 451 | dev->wiphy.rts_threshold); |
452 | NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, | ||
453 | dev->wiphy.coverage_class); | ||
445 | 454 | ||
446 | NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, | 455 | NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, |
447 | dev->wiphy.max_scan_ssids); | 456 | dev->wiphy.max_scan_ssids); |
@@ -569,6 +578,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
569 | CMD(set_pmksa, SET_PMKSA); | 578 | CMD(set_pmksa, SET_PMKSA); |
570 | CMD(del_pmksa, DEL_PMKSA); | 579 | CMD(del_pmksa, DEL_PMKSA); |
571 | CMD(flush_pmksa, FLUSH_PMKSA); | 580 | CMD(flush_pmksa, FLUSH_PMKSA); |
581 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); | ||
582 | CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); | ||
583 | CMD(action, ACTION); | ||
572 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { | 584 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { |
573 | i++; | 585 | i++; |
574 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 586 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
@@ -681,6 +693,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
681 | u32 changed; | 693 | u32 changed; |
682 | u8 retry_short = 0, retry_long = 0; | 694 | u8 retry_short = 0, retry_long = 0; |
683 | u32 frag_threshold = 0, rts_threshold = 0; | 695 | u32 frag_threshold = 0, rts_threshold = 0; |
696 | u8 coverage_class = 0; | ||
684 | 697 | ||
685 | rtnl_lock(); | 698 | rtnl_lock(); |
686 | 699 | ||
@@ -803,9 +816,16 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
803 | changed |= WIPHY_PARAM_RTS_THRESHOLD; | 816 | changed |= WIPHY_PARAM_RTS_THRESHOLD; |
804 | } | 817 | } |
805 | 818 | ||
819 | if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) { | ||
820 | coverage_class = nla_get_u8( | ||
821 | info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]); | ||
822 | changed |= WIPHY_PARAM_COVERAGE_CLASS; | ||
823 | } | ||
824 | |||
806 | if (changed) { | 825 | if (changed) { |
807 | u8 old_retry_short, old_retry_long; | 826 | u8 old_retry_short, old_retry_long; |
808 | u32 old_frag_threshold, old_rts_threshold; | 827 | u32 old_frag_threshold, old_rts_threshold; |
828 | u8 old_coverage_class; | ||
809 | 829 | ||
810 | if (!rdev->ops->set_wiphy_params) { | 830 | if (!rdev->ops->set_wiphy_params) { |
811 | result = -EOPNOTSUPP; | 831 | result = -EOPNOTSUPP; |
@@ -816,6 +836,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
816 | old_retry_long = rdev->wiphy.retry_long; | 836 | old_retry_long = rdev->wiphy.retry_long; |
817 | old_frag_threshold = rdev->wiphy.frag_threshold; | 837 | old_frag_threshold = rdev->wiphy.frag_threshold; |
818 | old_rts_threshold = rdev->wiphy.rts_threshold; | 838 | old_rts_threshold = rdev->wiphy.rts_threshold; |
839 | old_coverage_class = rdev->wiphy.coverage_class; | ||
819 | 840 | ||
820 | if (changed & WIPHY_PARAM_RETRY_SHORT) | 841 | if (changed & WIPHY_PARAM_RETRY_SHORT) |
821 | rdev->wiphy.retry_short = retry_short; | 842 | rdev->wiphy.retry_short = retry_short; |
@@ -825,6 +846,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
825 | rdev->wiphy.frag_threshold = frag_threshold; | 846 | rdev->wiphy.frag_threshold = frag_threshold; |
826 | if (changed & WIPHY_PARAM_RTS_THRESHOLD) | 847 | if (changed & WIPHY_PARAM_RTS_THRESHOLD) |
827 | rdev->wiphy.rts_threshold = rts_threshold; | 848 | rdev->wiphy.rts_threshold = rts_threshold; |
849 | if (changed & WIPHY_PARAM_COVERAGE_CLASS) | ||
850 | rdev->wiphy.coverage_class = coverage_class; | ||
828 | 851 | ||
829 | result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); | 852 | result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); |
830 | if (result) { | 853 | if (result) { |
@@ -832,6 +855,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
832 | rdev->wiphy.retry_long = old_retry_long; | 855 | rdev->wiphy.retry_long = old_retry_long; |
833 | rdev->wiphy.frag_threshold = old_frag_threshold; | 856 | rdev->wiphy.frag_threshold = old_frag_threshold; |
834 | rdev->wiphy.rts_threshold = old_rts_threshold; | 857 | rdev->wiphy.rts_threshold = old_rts_threshold; |
858 | rdev->wiphy.coverage_class = old_coverage_class; | ||
835 | } | 859 | } |
836 | } | 860 | } |
837 | 861 | ||
@@ -1637,42 +1661,9 @@ static int parse_station_flags(struct genl_info *info, | |||
1637 | return 0; | 1661 | return 0; |
1638 | } | 1662 | } |
1639 | 1663 | ||
1640 | static u16 nl80211_calculate_bitrate(struct rate_info *rate) | ||
1641 | { | ||
1642 | int modulation, streams, bitrate; | ||
1643 | |||
1644 | if (!(rate->flags & RATE_INFO_FLAGS_MCS)) | ||
1645 | return rate->legacy; | ||
1646 | |||
1647 | /* the formula below does only work for MCS values smaller than 32 */ | ||
1648 | if (rate->mcs >= 32) | ||
1649 | return 0; | ||
1650 | |||
1651 | modulation = rate->mcs & 7; | ||
1652 | streams = (rate->mcs >> 3) + 1; | ||
1653 | |||
1654 | bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? | ||
1655 | 13500000 : 6500000; | ||
1656 | |||
1657 | if (modulation < 4) | ||
1658 | bitrate *= (modulation + 1); | ||
1659 | else if (modulation == 4) | ||
1660 | bitrate *= (modulation + 2); | ||
1661 | else | ||
1662 | bitrate *= (modulation + 3); | ||
1663 | |||
1664 | bitrate *= streams; | ||
1665 | |||
1666 | if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) | ||
1667 | bitrate = (bitrate / 9) * 10; | ||
1668 | |||
1669 | /* do NOT round down here */ | ||
1670 | return (bitrate + 50000) / 100000; | ||
1671 | } | ||
1672 | |||
1673 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | 1664 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, |
1674 | int flags, struct net_device *dev, | 1665 | int flags, struct net_device *dev, |
1675 | u8 *mac_addr, struct station_info *sinfo) | 1666 | const u8 *mac_addr, struct station_info *sinfo) |
1676 | { | 1667 | { |
1677 | void *hdr; | 1668 | void *hdr; |
1678 | struct nlattr *sinfoattr, *txrate; | 1669 | struct nlattr *sinfoattr, *txrate; |
@@ -1716,8 +1707,8 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | |||
1716 | if (!txrate) | 1707 | if (!txrate) |
1717 | goto nla_put_failure; | 1708 | goto nla_put_failure; |
1718 | 1709 | ||
1719 | /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */ | 1710 | /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ |
1720 | bitrate = nl80211_calculate_bitrate(&sinfo->txrate); | 1711 | bitrate = cfg80211_calculate_bitrate(&sinfo->txrate); |
1721 | if (bitrate > 0) | 1712 | if (bitrate > 0) |
1722 | NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); | 1713 | NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); |
1723 | 1714 | ||
@@ -2023,6 +2014,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
2023 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) | 2014 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) |
2024 | return -EINVAL; | 2015 | return -EINVAL; |
2025 | 2016 | ||
2017 | if (!info->attrs[NL80211_ATTR_STA_AID]) | ||
2018 | return -EINVAL; | ||
2019 | |||
2026 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | 2020 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); |
2027 | params.supported_rates = | 2021 | params.supported_rates = |
2028 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | 2022 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); |
@@ -2031,11 +2025,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
2031 | params.listen_interval = | 2025 | params.listen_interval = |
2032 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | 2026 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); |
2033 | 2027 | ||
2034 | if (info->attrs[NL80211_ATTR_STA_AID]) { | 2028 | params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); |
2035 | params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); | 2029 | if (!params.aid || params.aid > IEEE80211_MAX_AID) |
2036 | if (!params.aid || params.aid > IEEE80211_MAX_AID) | 2030 | return -EINVAL; |
2037 | return -EINVAL; | ||
2038 | } | ||
2039 | 2031 | ||
2040 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) | 2032 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) |
2041 | params.ht_capa = | 2033 | params.ht_capa = |
@@ -2050,6 +2042,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
2050 | if (err) | 2042 | if (err) |
2051 | goto out_rtnl; | 2043 | goto out_rtnl; |
2052 | 2044 | ||
2045 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | ||
2046 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { | ||
2047 | err = -EINVAL; | ||
2048 | goto out; | ||
2049 | } | ||
2050 | |||
2053 | err = get_vlan(info, rdev, ¶ms.vlan); | 2051 | err = get_vlan(info, rdev, ¶ms.vlan); |
2054 | if (err) | 2052 | if (err) |
2055 | goto out; | 2053 | goto out; |
@@ -2057,35 +2055,6 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
2057 | /* validate settings */ | 2055 | /* validate settings */ |
2058 | err = 0; | 2056 | err = 0; |
2059 | 2057 | ||
2060 | switch (dev->ieee80211_ptr->iftype) { | ||
2061 | case NL80211_IFTYPE_AP: | ||
2062 | case NL80211_IFTYPE_AP_VLAN: | ||
2063 | /* all ok but must have AID */ | ||
2064 | if (!params.aid) | ||
2065 | err = -EINVAL; | ||
2066 | break; | ||
2067 | case NL80211_IFTYPE_MESH_POINT: | ||
2068 | /* disallow things mesh doesn't support */ | ||
2069 | if (params.vlan) | ||
2070 | err = -EINVAL; | ||
2071 | if (params.aid) | ||
2072 | err = -EINVAL; | ||
2073 | if (params.ht_capa) | ||
2074 | err = -EINVAL; | ||
2075 | if (params.listen_interval >= 0) | ||
2076 | err = -EINVAL; | ||
2077 | if (params.supported_rates) | ||
2078 | err = -EINVAL; | ||
2079 | if (params.sta_flags_mask) | ||
2080 | err = -EINVAL; | ||
2081 | break; | ||
2082 | default: | ||
2083 | err = -EINVAL; | ||
2084 | } | ||
2085 | |||
2086 | if (err) | ||
2087 | goto out; | ||
2088 | |||
2089 | if (!rdev->ops->add_station) { | 2058 | if (!rdev->ops->add_station) { |
2090 | err = -EOPNOTSUPP; | 2059 | err = -EOPNOTSUPP; |
2091 | goto out; | 2060 | goto out; |
@@ -2126,8 +2095,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) | |||
2126 | goto out_rtnl; | 2095 | goto out_rtnl; |
2127 | 2096 | ||
2128 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 2097 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
2129 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && | 2098 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { |
2130 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { | ||
2131 | err = -EINVAL; | 2099 | err = -EINVAL; |
2132 | goto out; | 2100 | goto out; |
2133 | } | 2101 | } |
@@ -2514,8 +2482,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
2514 | return err; | 2482 | return err; |
2515 | } | 2483 | } |
2516 | 2484 | ||
2517 | static const struct nla_policy | 2485 | static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { |
2518 | reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { | ||
2519 | [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, | 2486 | [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, |
2520 | [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, | 2487 | [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, |
2521 | [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, | 2488 | [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, |
@@ -2583,12 +2550,6 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
2583 | 2550 | ||
2584 | data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); | 2551 | data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); |
2585 | 2552 | ||
2586 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | ||
2587 | /* We ignore world regdom requests with the old regdom setup */ | ||
2588 | if (is_world_regdom(data)) | ||
2589 | return -EINVAL; | ||
2590 | #endif | ||
2591 | |||
2592 | r = regulatory_hint_user(data); | 2553 | r = regulatory_hint_user(data); |
2593 | 2554 | ||
2594 | return r; | 2555 | return r; |
@@ -2690,8 +2651,7 @@ do {\ | |||
2690 | } \ | 2651 | } \ |
2691 | } while (0);\ | 2652 | } while (0);\ |
2692 | 2653 | ||
2693 | static struct nla_policy | 2654 | static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = { |
2694 | nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = { | ||
2695 | [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, | 2655 | [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, |
2696 | [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, | 2656 | [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, |
2697 | [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 }, | 2657 | [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 }, |
@@ -3182,6 +3142,10 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
3182 | NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, | 3142 | NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, |
3183 | res->len_information_elements, | 3143 | res->len_information_elements, |
3184 | res->information_elements); | 3144 | res->information_elements); |
3145 | if (res->beacon_ies && res->len_beacon_ies && | ||
3146 | res->beacon_ies != res->information_elements) | ||
3147 | NLA_PUT(msg, NL80211_BSS_BEACON_IES, | ||
3148 | res->len_beacon_ies, res->beacon_ies); | ||
3185 | if (res->tsf) | 3149 | if (res->tsf) |
3186 | NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); | 3150 | NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); |
3187 | if (res->beacon_interval) | 3151 | if (res->beacon_interval) |
@@ -3586,6 +3550,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
3586 | { | 3550 | { |
3587 | struct cfg80211_registered_device *rdev; | 3551 | struct cfg80211_registered_device *rdev; |
3588 | struct net_device *dev; | 3552 | struct net_device *dev; |
3553 | struct wireless_dev *wdev; | ||
3589 | struct cfg80211_crypto_settings crypto; | 3554 | struct cfg80211_crypto_settings crypto; |
3590 | struct ieee80211_channel *chan, *fixedchan; | 3555 | struct ieee80211_channel *chan, *fixedchan; |
3591 | const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; | 3556 | const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; |
@@ -3631,7 +3596,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
3631 | } | 3596 | } |
3632 | 3597 | ||
3633 | mutex_lock(&rdev->devlist_mtx); | 3598 | mutex_lock(&rdev->devlist_mtx); |
3634 | fixedchan = rdev_fixed_channel(rdev, NULL); | 3599 | wdev = dev->ieee80211_ptr; |
3600 | fixedchan = rdev_fixed_channel(rdev, wdev); | ||
3635 | if (fixedchan && chan != fixedchan) { | 3601 | if (fixedchan && chan != fixedchan) { |
3636 | err = -EBUSY; | 3602 | err = -EBUSY; |
3637 | mutex_unlock(&rdev->devlist_mtx); | 3603 | mutex_unlock(&rdev->devlist_mtx); |
@@ -4322,6 +4288,496 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) | |||
4322 | 4288 | ||
4323 | } | 4289 | } |
4324 | 4290 | ||
4291 | static int nl80211_remain_on_channel(struct sk_buff *skb, | ||
4292 | struct genl_info *info) | ||
4293 | { | ||
4294 | struct cfg80211_registered_device *rdev; | ||
4295 | struct net_device *dev; | ||
4296 | struct ieee80211_channel *chan; | ||
4297 | struct sk_buff *msg; | ||
4298 | void *hdr; | ||
4299 | u64 cookie; | ||
4300 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
4301 | u32 freq, duration; | ||
4302 | int err; | ||
4303 | |||
4304 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || | ||
4305 | !info->attrs[NL80211_ATTR_DURATION]) | ||
4306 | return -EINVAL; | ||
4307 | |||
4308 | duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); | ||
4309 | |||
4310 | /* | ||
4311 | * We should be on that channel for at least one jiffie, | ||
4312 | * and more than 5 seconds seems excessive. | ||
4313 | */ | ||
4314 | if (!duration || !msecs_to_jiffies(duration) || duration > 5000) | ||
4315 | return -EINVAL; | ||
4316 | |||
4317 | rtnl_lock(); | ||
4318 | |||
4319 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4320 | if (err) | ||
4321 | goto unlock_rtnl; | ||
4322 | |||
4323 | if (!rdev->ops->remain_on_channel) { | ||
4324 | err = -EOPNOTSUPP; | ||
4325 | goto out; | ||
4326 | } | ||
4327 | |||
4328 | if (!netif_running(dev)) { | ||
4329 | err = -ENETDOWN; | ||
4330 | goto out; | ||
4331 | } | ||
4332 | |||
4333 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
4334 | channel_type = nla_get_u32( | ||
4335 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
4336 | if (channel_type != NL80211_CHAN_NO_HT && | ||
4337 | channel_type != NL80211_CHAN_HT20 && | ||
4338 | channel_type != NL80211_CHAN_HT40PLUS && | ||
4339 | channel_type != NL80211_CHAN_HT40MINUS) | ||
4340 | err = -EINVAL; | ||
4341 | goto out; | ||
4342 | } | ||
4343 | |||
4344 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
4345 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
4346 | if (chan == NULL) { | ||
4347 | err = -EINVAL; | ||
4348 | goto out; | ||
4349 | } | ||
4350 | |||
4351 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
4352 | if (!msg) { | ||
4353 | err = -ENOMEM; | ||
4354 | goto out; | ||
4355 | } | ||
4356 | |||
4357 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
4358 | NL80211_CMD_REMAIN_ON_CHANNEL); | ||
4359 | |||
4360 | if (IS_ERR(hdr)) { | ||
4361 | err = PTR_ERR(hdr); | ||
4362 | goto free_msg; | ||
4363 | } | ||
4364 | |||
4365 | err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan, | ||
4366 | channel_type, duration, &cookie); | ||
4367 | |||
4368 | if (err) | ||
4369 | goto free_msg; | ||
4370 | |||
4371 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
4372 | |||
4373 | genlmsg_end(msg, hdr); | ||
4374 | err = genlmsg_reply(msg, info); | ||
4375 | goto out; | ||
4376 | |||
4377 | nla_put_failure: | ||
4378 | err = -ENOBUFS; | ||
4379 | free_msg: | ||
4380 | nlmsg_free(msg); | ||
4381 | out: | ||
4382 | cfg80211_unlock_rdev(rdev); | ||
4383 | dev_put(dev); | ||
4384 | unlock_rtnl: | ||
4385 | rtnl_unlock(); | ||
4386 | return err; | ||
4387 | } | ||
4388 | |||
4389 | static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, | ||
4390 | struct genl_info *info) | ||
4391 | { | ||
4392 | struct cfg80211_registered_device *rdev; | ||
4393 | struct net_device *dev; | ||
4394 | u64 cookie; | ||
4395 | int err; | ||
4396 | |||
4397 | if (!info->attrs[NL80211_ATTR_COOKIE]) | ||
4398 | return -EINVAL; | ||
4399 | |||
4400 | rtnl_lock(); | ||
4401 | |||
4402 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4403 | if (err) | ||
4404 | goto unlock_rtnl; | ||
4405 | |||
4406 | if (!rdev->ops->cancel_remain_on_channel) { | ||
4407 | err = -EOPNOTSUPP; | ||
4408 | goto out; | ||
4409 | } | ||
4410 | |||
4411 | if (!netif_running(dev)) { | ||
4412 | err = -ENETDOWN; | ||
4413 | goto out; | ||
4414 | } | ||
4415 | |||
4416 | cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); | ||
4417 | |||
4418 | err = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie); | ||
4419 | |||
4420 | out: | ||
4421 | cfg80211_unlock_rdev(rdev); | ||
4422 | dev_put(dev); | ||
4423 | unlock_rtnl: | ||
4424 | rtnl_unlock(); | ||
4425 | return err; | ||
4426 | } | ||
4427 | |||
4428 | static u32 rateset_to_mask(struct ieee80211_supported_band *sband, | ||
4429 | u8 *rates, u8 rates_len) | ||
4430 | { | ||
4431 | u8 i; | ||
4432 | u32 mask = 0; | ||
4433 | |||
4434 | for (i = 0; i < rates_len; i++) { | ||
4435 | int rate = (rates[i] & 0x7f) * 5; | ||
4436 | int ridx; | ||
4437 | for (ridx = 0; ridx < sband->n_bitrates; ridx++) { | ||
4438 | struct ieee80211_rate *srate = | ||
4439 | &sband->bitrates[ridx]; | ||
4440 | if (rate == srate->bitrate) { | ||
4441 | mask |= 1 << ridx; | ||
4442 | break; | ||
4443 | } | ||
4444 | } | ||
4445 | if (ridx == sband->n_bitrates) | ||
4446 | return 0; /* rate not found */ | ||
4447 | } | ||
4448 | |||
4449 | return mask; | ||
4450 | } | ||
4451 | |||
4452 | static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { | ||
4453 | [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, | ||
4454 | .len = NL80211_MAX_SUPP_RATES }, | ||
4455 | }; | ||
4456 | |||
4457 | static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, | ||
4458 | struct genl_info *info) | ||
4459 | { | ||
4460 | struct nlattr *tb[NL80211_TXRATE_MAX + 1]; | ||
4461 | struct cfg80211_registered_device *rdev; | ||
4462 | struct cfg80211_bitrate_mask mask; | ||
4463 | int err, rem, i; | ||
4464 | struct net_device *dev; | ||
4465 | struct nlattr *tx_rates; | ||
4466 | struct ieee80211_supported_band *sband; | ||
4467 | |||
4468 | if (info->attrs[NL80211_ATTR_TX_RATES] == NULL) | ||
4469 | return -EINVAL; | ||
4470 | |||
4471 | rtnl_lock(); | ||
4472 | |||
4473 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4474 | if (err) | ||
4475 | goto unlock_rtnl; | ||
4476 | |||
4477 | if (!rdev->ops->set_bitrate_mask) { | ||
4478 | err = -EOPNOTSUPP; | ||
4479 | goto unlock; | ||
4480 | } | ||
4481 | |||
4482 | memset(&mask, 0, sizeof(mask)); | ||
4483 | /* Default to all rates enabled */ | ||
4484 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) { | ||
4485 | sband = rdev->wiphy.bands[i]; | ||
4486 | mask.control[i].legacy = | ||
4487 | sband ? (1 << sband->n_bitrates) - 1 : 0; | ||
4488 | } | ||
4489 | |||
4490 | /* | ||
4491 | * The nested attribute uses enum nl80211_band as the index. This maps | ||
4492 | * directly to the enum ieee80211_band values used in cfg80211. | ||
4493 | */ | ||
4494 | nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) | ||
4495 | { | ||
4496 | enum ieee80211_band band = nla_type(tx_rates); | ||
4497 | if (band < 0 || band >= IEEE80211_NUM_BANDS) { | ||
4498 | err = -EINVAL; | ||
4499 | goto unlock; | ||
4500 | } | ||
4501 | sband = rdev->wiphy.bands[band]; | ||
4502 | if (sband == NULL) { | ||
4503 | err = -EINVAL; | ||
4504 | goto unlock; | ||
4505 | } | ||
4506 | nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates), | ||
4507 | nla_len(tx_rates), nl80211_txattr_policy); | ||
4508 | if (tb[NL80211_TXRATE_LEGACY]) { | ||
4509 | mask.control[band].legacy = rateset_to_mask( | ||
4510 | sband, | ||
4511 | nla_data(tb[NL80211_TXRATE_LEGACY]), | ||
4512 | nla_len(tb[NL80211_TXRATE_LEGACY])); | ||
4513 | if (mask.control[band].legacy == 0) { | ||
4514 | err = -EINVAL; | ||
4515 | goto unlock; | ||
4516 | } | ||
4517 | } | ||
4518 | } | ||
4519 | |||
4520 | err = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask); | ||
4521 | |||
4522 | unlock: | ||
4523 | dev_put(dev); | ||
4524 | cfg80211_unlock_rdev(rdev); | ||
4525 | unlock_rtnl: | ||
4526 | rtnl_unlock(); | ||
4527 | return err; | ||
4528 | } | ||
4529 | |||
4530 | static int nl80211_register_action(struct sk_buff *skb, struct genl_info *info) | ||
4531 | { | ||
4532 | struct cfg80211_registered_device *rdev; | ||
4533 | struct net_device *dev; | ||
4534 | int err; | ||
4535 | |||
4536 | if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) | ||
4537 | return -EINVAL; | ||
4538 | |||
4539 | if (nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]) < 1) | ||
4540 | return -EINVAL; | ||
4541 | |||
4542 | rtnl_lock(); | ||
4543 | |||
4544 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4545 | if (err) | ||
4546 | goto unlock_rtnl; | ||
4547 | |||
4548 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
4549 | err = -EOPNOTSUPP; | ||
4550 | goto out; | ||
4551 | } | ||
4552 | |||
4553 | /* not much point in registering if we can't reply */ | ||
4554 | if (!rdev->ops->action) { | ||
4555 | err = -EOPNOTSUPP; | ||
4556 | goto out; | ||
4557 | } | ||
4558 | |||
4559 | err = cfg80211_mlme_register_action(dev->ieee80211_ptr, info->snd_pid, | ||
4560 | nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), | ||
4561 | nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); | ||
4562 | out: | ||
4563 | cfg80211_unlock_rdev(rdev); | ||
4564 | dev_put(dev); | ||
4565 | unlock_rtnl: | ||
4566 | rtnl_unlock(); | ||
4567 | return err; | ||
4568 | } | ||
4569 | |||
4570 | static int nl80211_action(struct sk_buff *skb, struct genl_info *info) | ||
4571 | { | ||
4572 | struct cfg80211_registered_device *rdev; | ||
4573 | struct net_device *dev; | ||
4574 | struct ieee80211_channel *chan; | ||
4575 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
4576 | u32 freq; | ||
4577 | int err; | ||
4578 | void *hdr; | ||
4579 | u64 cookie; | ||
4580 | struct sk_buff *msg; | ||
4581 | |||
4582 | if (!info->attrs[NL80211_ATTR_FRAME] || | ||
4583 | !info->attrs[NL80211_ATTR_WIPHY_FREQ]) | ||
4584 | return -EINVAL; | ||
4585 | |||
4586 | rtnl_lock(); | ||
4587 | |||
4588 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4589 | if (err) | ||
4590 | goto unlock_rtnl; | ||
4591 | |||
4592 | if (!rdev->ops->action) { | ||
4593 | err = -EOPNOTSUPP; | ||
4594 | goto out; | ||
4595 | } | ||
4596 | |||
4597 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
4598 | err = -EOPNOTSUPP; | ||
4599 | goto out; | ||
4600 | } | ||
4601 | |||
4602 | if (!netif_running(dev)) { | ||
4603 | err = -ENETDOWN; | ||
4604 | goto out; | ||
4605 | } | ||
4606 | |||
4607 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
4608 | channel_type = nla_get_u32( | ||
4609 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
4610 | if (channel_type != NL80211_CHAN_NO_HT && | ||
4611 | channel_type != NL80211_CHAN_HT20 && | ||
4612 | channel_type != NL80211_CHAN_HT40PLUS && | ||
4613 | channel_type != NL80211_CHAN_HT40MINUS) | ||
4614 | err = -EINVAL; | ||
4615 | goto out; | ||
4616 | } | ||
4617 | |||
4618 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
4619 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
4620 | if (chan == NULL) { | ||
4621 | err = -EINVAL; | ||
4622 | goto out; | ||
4623 | } | ||
4624 | |||
4625 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
4626 | if (!msg) { | ||
4627 | err = -ENOMEM; | ||
4628 | goto out; | ||
4629 | } | ||
4630 | |||
4631 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
4632 | NL80211_CMD_ACTION); | ||
4633 | |||
4634 | if (IS_ERR(hdr)) { | ||
4635 | err = PTR_ERR(hdr); | ||
4636 | goto free_msg; | ||
4637 | } | ||
4638 | err = cfg80211_mlme_action(rdev, dev, chan, channel_type, | ||
4639 | nla_data(info->attrs[NL80211_ATTR_FRAME]), | ||
4640 | nla_len(info->attrs[NL80211_ATTR_FRAME]), | ||
4641 | &cookie); | ||
4642 | if (err) | ||
4643 | goto free_msg; | ||
4644 | |||
4645 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
4646 | |||
4647 | genlmsg_end(msg, hdr); | ||
4648 | err = genlmsg_reply(msg, info); | ||
4649 | goto out; | ||
4650 | |||
4651 | nla_put_failure: | ||
4652 | err = -ENOBUFS; | ||
4653 | free_msg: | ||
4654 | nlmsg_free(msg); | ||
4655 | out: | ||
4656 | cfg80211_unlock_rdev(rdev); | ||
4657 | dev_put(dev); | ||
4658 | unlock_rtnl: | ||
4659 | rtnl_unlock(); | ||
4660 | return err; | ||
4661 | } | ||
4662 | |||
4663 | static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) | ||
4664 | { | ||
4665 | struct cfg80211_registered_device *rdev; | ||
4666 | struct wireless_dev *wdev; | ||
4667 | struct net_device *dev; | ||
4668 | u8 ps_state; | ||
4669 | bool state; | ||
4670 | int err; | ||
4671 | |||
4672 | if (!info->attrs[NL80211_ATTR_PS_STATE]) { | ||
4673 | err = -EINVAL; | ||
4674 | goto out; | ||
4675 | } | ||
4676 | |||
4677 | ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]); | ||
4678 | |||
4679 | if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED) { | ||
4680 | err = -EINVAL; | ||
4681 | goto out; | ||
4682 | } | ||
4683 | |||
4684 | rtnl_lock(); | ||
4685 | |||
4686 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4687 | if (err) | ||
4688 | goto unlock_rdev; | ||
4689 | |||
4690 | wdev = dev->ieee80211_ptr; | ||
4691 | |||
4692 | if (!rdev->ops->set_power_mgmt) { | ||
4693 | err = -EOPNOTSUPP; | ||
4694 | goto unlock_rdev; | ||
4695 | } | ||
4696 | |||
4697 | state = (ps_state == NL80211_PS_ENABLED) ? true : false; | ||
4698 | |||
4699 | if (state == wdev->ps) | ||
4700 | goto unlock_rdev; | ||
4701 | |||
4702 | wdev->ps = state; | ||
4703 | |||
4704 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, wdev->ps, | ||
4705 | wdev->ps_timeout)) | ||
4706 | /* assume this means it's off */ | ||
4707 | wdev->ps = false; | ||
4708 | |||
4709 | unlock_rdev: | ||
4710 | cfg80211_unlock_rdev(rdev); | ||
4711 | dev_put(dev); | ||
4712 | rtnl_unlock(); | ||
4713 | |||
4714 | out: | ||
4715 | return err; | ||
4716 | } | ||
4717 | |||
4718 | static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) | ||
4719 | { | ||
4720 | struct cfg80211_registered_device *rdev; | ||
4721 | enum nl80211_ps_state ps_state; | ||
4722 | struct wireless_dev *wdev; | ||
4723 | struct net_device *dev; | ||
4724 | struct sk_buff *msg; | ||
4725 | void *hdr; | ||
4726 | int err; | ||
4727 | |||
4728 | rtnl_lock(); | ||
4729 | |||
4730 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4731 | if (err) | ||
4732 | goto unlock_rtnl; | ||
4733 | |||
4734 | wdev = dev->ieee80211_ptr; | ||
4735 | |||
4736 | if (!rdev->ops->set_power_mgmt) { | ||
4737 | err = -EOPNOTSUPP; | ||
4738 | goto out; | ||
4739 | } | ||
4740 | |||
4741 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
4742 | if (!msg) { | ||
4743 | err = -ENOMEM; | ||
4744 | goto out; | ||
4745 | } | ||
4746 | |||
4747 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
4748 | NL80211_CMD_GET_POWER_SAVE); | ||
4749 | if (!hdr) { | ||
4750 | err = -ENOMEM; | ||
4751 | goto free_msg; | ||
4752 | } | ||
4753 | |||
4754 | if (wdev->ps) | ||
4755 | ps_state = NL80211_PS_ENABLED; | ||
4756 | else | ||
4757 | ps_state = NL80211_PS_DISABLED; | ||
4758 | |||
4759 | NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state); | ||
4760 | |||
4761 | genlmsg_end(msg, hdr); | ||
4762 | err = genlmsg_reply(msg, info); | ||
4763 | goto out; | ||
4764 | |||
4765 | nla_put_failure: | ||
4766 | err = -ENOBUFS; | ||
4767 | |||
4768 | free_msg: | ||
4769 | nlmsg_free(msg); | ||
4770 | |||
4771 | out: | ||
4772 | cfg80211_unlock_rdev(rdev); | ||
4773 | dev_put(dev); | ||
4774 | |||
4775 | unlock_rtnl: | ||
4776 | rtnl_unlock(); | ||
4777 | |||
4778 | return err; | ||
4779 | } | ||
4780 | |||
4325 | static struct genl_ops nl80211_ops[] = { | 4781 | static struct genl_ops nl80211_ops[] = { |
4326 | { | 4782 | { |
4327 | .cmd = NL80211_CMD_GET_WIPHY, | 4783 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -4584,8 +5040,50 @@ static struct genl_ops nl80211_ops[] = { | |||
4584 | .policy = nl80211_policy, | 5040 | .policy = nl80211_policy, |
4585 | .flags = GENL_ADMIN_PERM, | 5041 | .flags = GENL_ADMIN_PERM, |
4586 | }, | 5042 | }, |
4587 | 5043 | { | |
5044 | .cmd = NL80211_CMD_REMAIN_ON_CHANNEL, | ||
5045 | .doit = nl80211_remain_on_channel, | ||
5046 | .policy = nl80211_policy, | ||
5047 | .flags = GENL_ADMIN_PERM, | ||
5048 | }, | ||
5049 | { | ||
5050 | .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
5051 | .doit = nl80211_cancel_remain_on_channel, | ||
5052 | .policy = nl80211_policy, | ||
5053 | .flags = GENL_ADMIN_PERM, | ||
5054 | }, | ||
5055 | { | ||
5056 | .cmd = NL80211_CMD_SET_TX_BITRATE_MASK, | ||
5057 | .doit = nl80211_set_tx_bitrate_mask, | ||
5058 | .policy = nl80211_policy, | ||
5059 | .flags = GENL_ADMIN_PERM, | ||
5060 | }, | ||
5061 | { | ||
5062 | .cmd = NL80211_CMD_REGISTER_ACTION, | ||
5063 | .doit = nl80211_register_action, | ||
5064 | .policy = nl80211_policy, | ||
5065 | .flags = GENL_ADMIN_PERM, | ||
5066 | }, | ||
5067 | { | ||
5068 | .cmd = NL80211_CMD_ACTION, | ||
5069 | .doit = nl80211_action, | ||
5070 | .policy = nl80211_policy, | ||
5071 | .flags = GENL_ADMIN_PERM, | ||
5072 | }, | ||
5073 | { | ||
5074 | .cmd = NL80211_CMD_SET_POWER_SAVE, | ||
5075 | .doit = nl80211_set_power_save, | ||
5076 | .policy = nl80211_policy, | ||
5077 | .flags = GENL_ADMIN_PERM, | ||
5078 | }, | ||
5079 | { | ||
5080 | .cmd = NL80211_CMD_GET_POWER_SAVE, | ||
5081 | .doit = nl80211_get_power_save, | ||
5082 | .policy = nl80211_policy, | ||
5083 | /* can be retrieved by unprivileged users */ | ||
5084 | }, | ||
4588 | }; | 5085 | }; |
5086 | |||
4589 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5087 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
4590 | .name = "mlme", | 5088 | .name = "mlme", |
4591 | }; | 5089 | }; |
@@ -5173,6 +5671,193 @@ nla_put_failure: | |||
5173 | nlmsg_free(msg); | 5671 | nlmsg_free(msg); |
5174 | } | 5672 | } |
5175 | 5673 | ||
5674 | static void nl80211_send_remain_on_chan_event( | ||
5675 | int cmd, struct cfg80211_registered_device *rdev, | ||
5676 | struct net_device *netdev, u64 cookie, | ||
5677 | struct ieee80211_channel *chan, | ||
5678 | enum nl80211_channel_type channel_type, | ||
5679 | unsigned int duration, gfp_t gfp) | ||
5680 | { | ||
5681 | struct sk_buff *msg; | ||
5682 | void *hdr; | ||
5683 | |||
5684 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
5685 | if (!msg) | ||
5686 | return; | ||
5687 | |||
5688 | hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); | ||
5689 | if (!hdr) { | ||
5690 | nlmsg_free(msg); | ||
5691 | return; | ||
5692 | } | ||
5693 | |||
5694 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5695 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5696 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq); | ||
5697 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type); | ||
5698 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
5699 | |||
5700 | if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL) | ||
5701 | NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); | ||
5702 | |||
5703 | if (genlmsg_end(msg, hdr) < 0) { | ||
5704 | nlmsg_free(msg); | ||
5705 | return; | ||
5706 | } | ||
5707 | |||
5708 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
5709 | nl80211_mlme_mcgrp.id, gfp); | ||
5710 | return; | ||
5711 | |||
5712 | nla_put_failure: | ||
5713 | genlmsg_cancel(msg, hdr); | ||
5714 | nlmsg_free(msg); | ||
5715 | } | ||
5716 | |||
5717 | void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, | ||
5718 | struct net_device *netdev, u64 cookie, | ||
5719 | struct ieee80211_channel *chan, | ||
5720 | enum nl80211_channel_type channel_type, | ||
5721 | unsigned int duration, gfp_t gfp) | ||
5722 | { | ||
5723 | nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, | ||
5724 | rdev, netdev, cookie, chan, | ||
5725 | channel_type, duration, gfp); | ||
5726 | } | ||
5727 | |||
5728 | void nl80211_send_remain_on_channel_cancel( | ||
5729 | struct cfg80211_registered_device *rdev, struct net_device *netdev, | ||
5730 | u64 cookie, struct ieee80211_channel *chan, | ||
5731 | enum nl80211_channel_type channel_type, gfp_t gfp) | ||
5732 | { | ||
5733 | nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
5734 | rdev, netdev, cookie, chan, | ||
5735 | channel_type, 0, gfp); | ||
5736 | } | ||
5737 | |||
5738 | void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, | ||
5739 | struct net_device *dev, const u8 *mac_addr, | ||
5740 | struct station_info *sinfo, gfp_t gfp) | ||
5741 | { | ||
5742 | struct sk_buff *msg; | ||
5743 | |||
5744 | msg = nlmsg_new(NLMSG_GOODSIZE, gfp); | ||
5745 | if (!msg) | ||
5746 | return; | ||
5747 | |||
5748 | if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) { | ||
5749 | nlmsg_free(msg); | ||
5750 | return; | ||
5751 | } | ||
5752 | |||
5753 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
5754 | nl80211_mlme_mcgrp.id, gfp); | ||
5755 | } | ||
5756 | |||
5757 | int nl80211_send_action(struct cfg80211_registered_device *rdev, | ||
5758 | struct net_device *netdev, u32 nlpid, | ||
5759 | int freq, const u8 *buf, size_t len, gfp_t gfp) | ||
5760 | { | ||
5761 | struct sk_buff *msg; | ||
5762 | void *hdr; | ||
5763 | int err; | ||
5764 | |||
5765 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
5766 | if (!msg) | ||
5767 | return -ENOMEM; | ||
5768 | |||
5769 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION); | ||
5770 | if (!hdr) { | ||
5771 | nlmsg_free(msg); | ||
5772 | return -ENOMEM; | ||
5773 | } | ||
5774 | |||
5775 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5776 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5777 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); | ||
5778 | NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); | ||
5779 | |||
5780 | err = genlmsg_end(msg, hdr); | ||
5781 | if (err < 0) { | ||
5782 | nlmsg_free(msg); | ||
5783 | return err; | ||
5784 | } | ||
5785 | |||
5786 | err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); | ||
5787 | if (err < 0) | ||
5788 | return err; | ||
5789 | return 0; | ||
5790 | |||
5791 | nla_put_failure: | ||
5792 | genlmsg_cancel(msg, hdr); | ||
5793 | nlmsg_free(msg); | ||
5794 | return -ENOBUFS; | ||
5795 | } | ||
5796 | |||
5797 | void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, | ||
5798 | struct net_device *netdev, u64 cookie, | ||
5799 | const u8 *buf, size_t len, bool ack, | ||
5800 | gfp_t gfp) | ||
5801 | { | ||
5802 | struct sk_buff *msg; | ||
5803 | void *hdr; | ||
5804 | |||
5805 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
5806 | if (!msg) | ||
5807 | return; | ||
5808 | |||
5809 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION_TX_STATUS); | ||
5810 | if (!hdr) { | ||
5811 | nlmsg_free(msg); | ||
5812 | return; | ||
5813 | } | ||
5814 | |||
5815 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5816 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5817 | NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); | ||
5818 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
5819 | if (ack) | ||
5820 | NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); | ||
5821 | |||
5822 | if (genlmsg_end(msg, hdr) < 0) { | ||
5823 | nlmsg_free(msg); | ||
5824 | return; | ||
5825 | } | ||
5826 | |||
5827 | genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); | ||
5828 | return; | ||
5829 | |||
5830 | nla_put_failure: | ||
5831 | genlmsg_cancel(msg, hdr); | ||
5832 | nlmsg_free(msg); | ||
5833 | } | ||
5834 | |||
5835 | static int nl80211_netlink_notify(struct notifier_block * nb, | ||
5836 | unsigned long state, | ||
5837 | void *_notify) | ||
5838 | { | ||
5839 | struct netlink_notify *notify = _notify; | ||
5840 | struct cfg80211_registered_device *rdev; | ||
5841 | struct wireless_dev *wdev; | ||
5842 | |||
5843 | if (state != NETLINK_URELEASE) | ||
5844 | return NOTIFY_DONE; | ||
5845 | |||
5846 | rcu_read_lock(); | ||
5847 | |||
5848 | list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) | ||
5849 | list_for_each_entry_rcu(wdev, &rdev->netdev_list, list) | ||
5850 | cfg80211_mlme_unregister_actions(wdev, notify->pid); | ||
5851 | |||
5852 | rcu_read_unlock(); | ||
5853 | |||
5854 | return NOTIFY_DONE; | ||
5855 | } | ||
5856 | |||
5857 | static struct notifier_block nl80211_netlink_notifier = { | ||
5858 | .notifier_call = nl80211_netlink_notify, | ||
5859 | }; | ||
5860 | |||
5176 | /* initialisation/exit functions */ | 5861 | /* initialisation/exit functions */ |
5177 | 5862 | ||
5178 | int nl80211_init(void) | 5863 | int nl80211_init(void) |
@@ -5206,6 +5891,10 @@ int nl80211_init(void) | |||
5206 | goto err_out; | 5891 | goto err_out; |
5207 | #endif | 5892 | #endif |
5208 | 5893 | ||
5894 | err = netlink_register_notifier(&nl80211_netlink_notifier); | ||
5895 | if (err) | ||
5896 | goto err_out; | ||
5897 | |||
5209 | return 0; | 5898 | return 0; |
5210 | err_out: | 5899 | err_out: |
5211 | genl_unregister_family(&nl80211_fam); | 5900 | genl_unregister_family(&nl80211_fam); |
@@ -5214,5 +5903,6 @@ int nl80211_init(void) | |||
5214 | 5903 | ||
5215 | void nl80211_exit(void) | 5904 | void nl80211_exit(void) |
5216 | { | 5905 | { |
5906 | netlink_unregister_notifier(&nl80211_netlink_notifier); | ||
5217 | genl_unregister_family(&nl80211_fam); | 5907 | genl_unregister_family(&nl80211_fam); |
5218 | } | 5908 | } |
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 44cc2a76a1b0..4ca511102c6c 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h | |||
@@ -59,4 +59,27 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, | |||
59 | struct net_device *netdev, const u8 *bssid, | 59 | struct net_device *netdev, const u8 *bssid, |
60 | gfp_t gfp); | 60 | gfp_t gfp); |
61 | 61 | ||
62 | void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, | ||
63 | struct net_device *netdev, | ||
64 | u64 cookie, | ||
65 | struct ieee80211_channel *chan, | ||
66 | enum nl80211_channel_type channel_type, | ||
67 | unsigned int duration, gfp_t gfp); | ||
68 | void nl80211_send_remain_on_channel_cancel( | ||
69 | struct cfg80211_registered_device *rdev, struct net_device *netdev, | ||
70 | u64 cookie, struct ieee80211_channel *chan, | ||
71 | enum nl80211_channel_type channel_type, gfp_t gfp); | ||
72 | |||
73 | void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, | ||
74 | struct net_device *dev, const u8 *mac_addr, | ||
75 | struct station_info *sinfo, gfp_t gfp); | ||
76 | |||
77 | int nl80211_send_action(struct cfg80211_registered_device *rdev, | ||
78 | struct net_device *netdev, u32 nlpid, int freq, | ||
79 | const u8 *buf, size_t len, gfp_t gfp); | ||
80 | void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, | ||
81 | struct net_device *netdev, u64 cookie, | ||
82 | const u8 *buf, size_t len, bool ack, | ||
83 | gfp_t gfp); | ||
84 | |||
62 | #endif /* __NET_WIRELESS_NL80211_H */ | 85 | #endif /* __NET_WIRELESS_NL80211_H */ |
diff --git a/net/wireless/radiotap.c b/net/wireless/radiotap.c index f591871a7b4f..1332c445d1c7 100644 --- a/net/wireless/radiotap.c +++ b/net/wireless/radiotap.c | |||
@@ -2,6 +2,16 @@ | |||
2 | * Radiotap parser | 2 | * Radiotap parser |
3 | * | 3 | * |
4 | * Copyright 2007 Andy Green <andy@warmcat.com> | 4 | * Copyright 2007 Andy Green <andy@warmcat.com> |
5 | * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * Alternatively, this software may be distributed under the terms of BSD | ||
12 | * license. | ||
13 | * | ||
14 | * See COPYING for more details. | ||
5 | */ | 15 | */ |
6 | 16 | ||
7 | #include <net/cfg80211.h> | 17 | #include <net/cfg80211.h> |
@@ -10,6 +20,35 @@ | |||
10 | 20 | ||
11 | /* function prototypes and related defs are in include/net/cfg80211.h */ | 21 | /* function prototypes and related defs are in include/net/cfg80211.h */ |
12 | 22 | ||
23 | static const struct radiotap_align_size rtap_namespace_sizes[] = { | ||
24 | [IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, }, | ||
25 | [IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, }, | ||
26 | [IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, }, | ||
27 | [IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, }, | ||
28 | [IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, }, | ||
29 | [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, }, | ||
30 | [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, }, | ||
31 | [IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, }, | ||
32 | [IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, }, | ||
33 | [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, }, | ||
34 | [IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, }, | ||
35 | [IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, }, | ||
36 | [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, }, | ||
37 | [IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, }, | ||
38 | [IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, }, | ||
39 | [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, }, | ||
40 | [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, }, | ||
41 | [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, }, | ||
42 | /* | ||
43 | * add more here as they are defined in radiotap.h | ||
44 | */ | ||
45 | }; | ||
46 | |||
47 | static const struct ieee80211_radiotap_namespace radiotap_ns = { | ||
48 | .n_bits = sizeof(rtap_namespace_sizes) / sizeof(rtap_namespace_sizes[0]), | ||
49 | .align_size = rtap_namespace_sizes, | ||
50 | }; | ||
51 | |||
13 | /** | 52 | /** |
14 | * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization | 53 | * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization |
15 | * @iterator: radiotap_iterator to initialize | 54 | * @iterator: radiotap_iterator to initialize |
@@ -50,9 +89,9 @@ | |||
50 | */ | 89 | */ |
51 | 90 | ||
52 | int ieee80211_radiotap_iterator_init( | 91 | int ieee80211_radiotap_iterator_init( |
53 | struct ieee80211_radiotap_iterator *iterator, | 92 | struct ieee80211_radiotap_iterator *iterator, |
54 | struct ieee80211_radiotap_header *radiotap_header, | 93 | struct ieee80211_radiotap_header *radiotap_header, |
55 | int max_length) | 94 | int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns) |
56 | { | 95 | { |
57 | /* Linux only supports version 0 radiotap format */ | 96 | /* Linux only supports version 0 radiotap format */ |
58 | if (radiotap_header->it_version) | 97 | if (radiotap_header->it_version) |
@@ -62,19 +101,24 @@ int ieee80211_radiotap_iterator_init( | |||
62 | if (max_length < get_unaligned_le16(&radiotap_header->it_len)) | 101 | if (max_length < get_unaligned_le16(&radiotap_header->it_len)) |
63 | return -EINVAL; | 102 | return -EINVAL; |
64 | 103 | ||
65 | iterator->rtheader = radiotap_header; | 104 | iterator->_rtheader = radiotap_header; |
66 | iterator->max_length = get_unaligned_le16(&radiotap_header->it_len); | 105 | iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len); |
67 | iterator->arg_index = 0; | 106 | iterator->_arg_index = 0; |
68 | iterator->bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present); | 107 | iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present); |
69 | iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header); | 108 | iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header); |
70 | iterator->this_arg = NULL; | 109 | iterator->_reset_on_ext = 0; |
110 | iterator->_next_bitmap = &radiotap_header->it_present; | ||
111 | iterator->_next_bitmap++; | ||
112 | iterator->_vns = vns; | ||
113 | iterator->current_namespace = &radiotap_ns; | ||
114 | iterator->is_radiotap_ns = 1; | ||
71 | 115 | ||
72 | /* find payload start allowing for extended bitmap(s) */ | 116 | /* find payload start allowing for extended bitmap(s) */ |
73 | 117 | ||
74 | if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) { | 118 | if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) { |
75 | while (get_unaligned_le32(iterator->arg) & | 119 | while (get_unaligned_le32(iterator->_arg) & |
76 | (1 << IEEE80211_RADIOTAP_EXT)) { | 120 | (1 << IEEE80211_RADIOTAP_EXT)) { |
77 | iterator->arg += sizeof(u32); | 121 | iterator->_arg += sizeof(uint32_t); |
78 | 122 | ||
79 | /* | 123 | /* |
80 | * check for insanity where the present bitmaps | 124 | * check for insanity where the present bitmaps |
@@ -82,12 +126,13 @@ int ieee80211_radiotap_iterator_init( | |||
82 | * stated radiotap header length | 126 | * stated radiotap header length |
83 | */ | 127 | */ |
84 | 128 | ||
85 | if (((ulong)iterator->arg - | 129 | if ((unsigned long)iterator->_arg - |
86 | (ulong)iterator->rtheader) > iterator->max_length) | 130 | (unsigned long)iterator->_rtheader > |
131 | (unsigned long)iterator->_max_length) | ||
87 | return -EINVAL; | 132 | return -EINVAL; |
88 | } | 133 | } |
89 | 134 | ||
90 | iterator->arg += sizeof(u32); | 135 | iterator->_arg += sizeof(uint32_t); |
91 | 136 | ||
92 | /* | 137 | /* |
93 | * no need to check again for blowing past stated radiotap | 138 | * no need to check again for blowing past stated radiotap |
@@ -96,12 +141,36 @@ int ieee80211_radiotap_iterator_init( | |||
96 | */ | 141 | */ |
97 | } | 142 | } |
98 | 143 | ||
144 | iterator->this_arg = iterator->_arg; | ||
145 | |||
99 | /* we are all initialized happily */ | 146 | /* we are all initialized happily */ |
100 | 147 | ||
101 | return 0; | 148 | return 0; |
102 | } | 149 | } |
103 | EXPORT_SYMBOL(ieee80211_radiotap_iterator_init); | 150 | EXPORT_SYMBOL(ieee80211_radiotap_iterator_init); |
104 | 151 | ||
152 | static void find_ns(struct ieee80211_radiotap_iterator *iterator, | ||
153 | uint32_t oui, uint8_t subns) | ||
154 | { | ||
155 | int i; | ||
156 | |||
157 | iterator->current_namespace = NULL; | ||
158 | |||
159 | if (!iterator->_vns) | ||
160 | return; | ||
161 | |||
162 | for (i = 0; i < iterator->_vns->n_ns; i++) { | ||
163 | if (iterator->_vns->ns[i].oui != oui) | ||
164 | continue; | ||
165 | if (iterator->_vns->ns[i].subns != subns) | ||
166 | continue; | ||
167 | |||
168 | iterator->current_namespace = &iterator->_vns->ns[i]; | ||
169 | break; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | |||
105 | 174 | ||
106 | /** | 175 | /** |
107 | * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg | 176 | * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg |
@@ -127,99 +196,80 @@ EXPORT_SYMBOL(ieee80211_radiotap_iterator_init); | |||
127 | */ | 196 | */ |
128 | 197 | ||
129 | int ieee80211_radiotap_iterator_next( | 198 | int ieee80211_radiotap_iterator_next( |
130 | struct ieee80211_radiotap_iterator *iterator) | 199 | struct ieee80211_radiotap_iterator *iterator) |
131 | { | 200 | { |
132 | 201 | while (1) { | |
133 | /* | ||
134 | * small length lookup table for all radiotap types we heard of | ||
135 | * starting from b0 in the bitmap, so we can walk the payload | ||
136 | * area of the radiotap header | ||
137 | * | ||
138 | * There is a requirement to pad args, so that args | ||
139 | * of a given length must begin at a boundary of that length | ||
140 | * -- but note that compound args are allowed (eg, 2 x u16 | ||
141 | * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not | ||
142 | * a reliable indicator of alignment requirement. | ||
143 | * | ||
144 | * upper nybble: content alignment for arg | ||
145 | * lower nybble: content length for arg | ||
146 | */ | ||
147 | |||
148 | static const u8 rt_sizes[] = { | ||
149 | [IEEE80211_RADIOTAP_TSFT] = 0x88, | ||
150 | [IEEE80211_RADIOTAP_FLAGS] = 0x11, | ||
151 | [IEEE80211_RADIOTAP_RATE] = 0x11, | ||
152 | [IEEE80211_RADIOTAP_CHANNEL] = 0x24, | ||
153 | [IEEE80211_RADIOTAP_FHSS] = 0x22, | ||
154 | [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11, | ||
155 | [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11, | ||
156 | [IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22, | ||
157 | [IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22, | ||
158 | [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22, | ||
159 | [IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11, | ||
160 | [IEEE80211_RADIOTAP_ANTENNA] = 0x11, | ||
161 | [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11, | ||
162 | [IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11, | ||
163 | [IEEE80211_RADIOTAP_RX_FLAGS] = 0x22, | ||
164 | [IEEE80211_RADIOTAP_TX_FLAGS] = 0x22, | ||
165 | [IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11, | ||
166 | [IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11, | ||
167 | /* | ||
168 | * add more here as they are defined in | ||
169 | * include/net/ieee80211_radiotap.h | ||
170 | */ | ||
171 | }; | ||
172 | |||
173 | /* | ||
174 | * for every radiotap entry we can at | ||
175 | * least skip (by knowing the length)... | ||
176 | */ | ||
177 | |||
178 | while (iterator->arg_index < sizeof(rt_sizes)) { | ||
179 | int hit = 0; | 202 | int hit = 0; |
180 | int pad; | 203 | int pad, align, size, subns, vnslen; |
204 | uint32_t oui; | ||
181 | 205 | ||
182 | if (!(iterator->bitmap_shifter & 1)) | 206 | /* if no more EXT bits, that's it */ |
207 | if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT && | ||
208 | !(iterator->_bitmap_shifter & 1)) | ||
209 | return -ENOENT; | ||
210 | |||
211 | if (!(iterator->_bitmap_shifter & 1)) | ||
183 | goto next_entry; /* arg not present */ | 212 | goto next_entry; /* arg not present */ |
184 | 213 | ||
214 | /* get alignment/size of data */ | ||
215 | switch (iterator->_arg_index % 32) { | ||
216 | case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: | ||
217 | case IEEE80211_RADIOTAP_EXT: | ||
218 | align = 1; | ||
219 | size = 0; | ||
220 | break; | ||
221 | case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: | ||
222 | align = 2; | ||
223 | size = 6; | ||
224 | break; | ||
225 | default: | ||
226 | if (!iterator->current_namespace || | ||
227 | iterator->_arg_index >= iterator->current_namespace->n_bits) { | ||
228 | if (iterator->current_namespace == &radiotap_ns) | ||
229 | return -ENOENT; | ||
230 | align = 0; | ||
231 | } else { | ||
232 | align = iterator->current_namespace->align_size[iterator->_arg_index].align; | ||
233 | size = iterator->current_namespace->align_size[iterator->_arg_index].size; | ||
234 | } | ||
235 | if (!align) { | ||
236 | /* skip all subsequent data */ | ||
237 | iterator->_arg = iterator->_next_ns_data; | ||
238 | /* give up on this namespace */ | ||
239 | iterator->current_namespace = NULL; | ||
240 | goto next_entry; | ||
241 | } | ||
242 | break; | ||
243 | } | ||
244 | |||
185 | /* | 245 | /* |
186 | * arg is present, account for alignment padding | 246 | * arg is present, account for alignment padding |
187 | * 8-bit args can be at any alignment | ||
188 | * 16-bit args must start on 16-bit boundary | ||
189 | * 32-bit args must start on 32-bit boundary | ||
190 | * 64-bit args must start on 64-bit boundary | ||
191 | * | 247 | * |
192 | * note that total arg size can differ from alignment of | 248 | * Note that these alignments are relative to the start |
193 | * elements inside arg, so we use upper nybble of length | 249 | * of the radiotap header. There is no guarantee |
194 | * table to base alignment on | ||
195 | * | ||
196 | * also note: these alignments are ** relative to the | ||
197 | * start of the radiotap header **. There is no guarantee | ||
198 | * that the radiotap header itself is aligned on any | 250 | * that the radiotap header itself is aligned on any |
199 | * kind of boundary. | 251 | * kind of boundary. |
200 | * | 252 | * |
201 | * the above is why get_unaligned() is used to dereference | 253 | * The above is why get_unaligned() is used to dereference |
202 | * multibyte elements from the radiotap area | 254 | * multibyte elements from the radiotap area. |
203 | */ | 255 | */ |
204 | 256 | ||
205 | pad = (((ulong)iterator->arg) - | 257 | pad = ((unsigned long)iterator->_arg - |
206 | ((ulong)iterator->rtheader)) & | 258 | (unsigned long)iterator->_rtheader) & (align - 1); |
207 | ((rt_sizes[iterator->arg_index] >> 4) - 1); | ||
208 | 259 | ||
209 | if (pad) | 260 | if (pad) |
210 | iterator->arg += | 261 | iterator->_arg += align - pad; |
211 | (rt_sizes[iterator->arg_index] >> 4) - pad; | ||
212 | 262 | ||
213 | /* | 263 | /* |
214 | * this is what we will return to user, but we need to | 264 | * this is what we will return to user, but we need to |
215 | * move on first so next call has something fresh to test | 265 | * move on first so next call has something fresh to test |
216 | */ | 266 | */ |
217 | iterator->this_arg_index = iterator->arg_index; | 267 | iterator->this_arg_index = iterator->_arg_index; |
218 | iterator->this_arg = iterator->arg; | 268 | iterator->this_arg = iterator->_arg; |
219 | hit = 1; | 269 | iterator->this_arg_size = size; |
220 | 270 | ||
221 | /* internally move on the size of this arg */ | 271 | /* internally move on the size of this arg */ |
222 | iterator->arg += rt_sizes[iterator->arg_index] & 0x0f; | 272 | iterator->_arg += size; |
223 | 273 | ||
224 | /* | 274 | /* |
225 | * check for insanity where we are given a bitmap that | 275 | * check for insanity where we are given a bitmap that |
@@ -228,32 +278,73 @@ int ieee80211_radiotap_iterator_next( | |||
228 | * max_length on the last arg, never exceeding it. | 278 | * max_length on the last arg, never exceeding it. |
229 | */ | 279 | */ |
230 | 280 | ||
231 | if (((ulong)iterator->arg - (ulong)iterator->rtheader) > | 281 | if ((unsigned long)iterator->_arg - |
232 | iterator->max_length) | 282 | (unsigned long)iterator->_rtheader > |
283 | (unsigned long)iterator->_max_length) | ||
233 | return -EINVAL; | 284 | return -EINVAL; |
234 | 285 | ||
235 | next_entry: | 286 | /* these special ones are valid in each bitmap word */ |
236 | iterator->arg_index++; | 287 | switch (iterator->_arg_index % 32) { |
237 | if (unlikely((iterator->arg_index & 31) == 0)) { | 288 | case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: |
238 | /* completed current u32 bitmap */ | 289 | iterator->_bitmap_shifter >>= 1; |
239 | if (iterator->bitmap_shifter & 1) { | 290 | iterator->_arg_index++; |
240 | /* b31 was set, there is more */ | 291 | |
241 | /* move to next u32 bitmap */ | 292 | iterator->_reset_on_ext = 1; |
242 | iterator->bitmap_shifter = | 293 | |
243 | get_unaligned_le32(iterator->next_bitmap); | 294 | vnslen = get_unaligned_le16(iterator->this_arg + 4); |
244 | iterator->next_bitmap++; | 295 | iterator->_next_ns_data = iterator->_arg + vnslen; |
245 | } else | 296 | oui = (*iterator->this_arg << 16) | |
246 | /* no more bitmaps: end */ | 297 | (*(iterator->this_arg + 1) << 8) | |
247 | iterator->arg_index = sizeof(rt_sizes); | 298 | *(iterator->this_arg + 2); |
248 | } else /* just try the next bit */ | 299 | subns = *(iterator->this_arg + 3); |
249 | iterator->bitmap_shifter >>= 1; | 300 | |
301 | find_ns(iterator, oui, subns); | ||
302 | |||
303 | iterator->is_radiotap_ns = 0; | ||
304 | /* allow parsers to show this information */ | ||
305 | iterator->this_arg_index = | ||
306 | IEEE80211_RADIOTAP_VENDOR_NAMESPACE; | ||
307 | iterator->this_arg_size += vnslen; | ||
308 | if ((unsigned long)iterator->this_arg + | ||
309 | iterator->this_arg_size - | ||
310 | (unsigned long)iterator->_rtheader > | ||
311 | (unsigned long)(unsigned long)iterator->_max_length) | ||
312 | return -EINVAL; | ||
313 | hit = 1; | ||
314 | break; | ||
315 | case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: | ||
316 | iterator->_bitmap_shifter >>= 1; | ||
317 | iterator->_arg_index++; | ||
318 | |||
319 | iterator->_reset_on_ext = 1; | ||
320 | iterator->current_namespace = &radiotap_ns; | ||
321 | iterator->is_radiotap_ns = 1; | ||
322 | break; | ||
323 | case IEEE80211_RADIOTAP_EXT: | ||
324 | /* | ||
325 | * bit 31 was set, there is more | ||
326 | * -- move to next u32 bitmap | ||
327 | */ | ||
328 | iterator->_bitmap_shifter = | ||
329 | get_unaligned_le32(iterator->_next_bitmap); | ||
330 | iterator->_next_bitmap++; | ||
331 | if (iterator->_reset_on_ext) | ||
332 | iterator->_arg_index = 0; | ||
333 | else | ||
334 | iterator->_arg_index++; | ||
335 | iterator->_reset_on_ext = 0; | ||
336 | break; | ||
337 | default: | ||
338 | /* we've got a hit! */ | ||
339 | hit = 1; | ||
340 | next_entry: | ||
341 | iterator->_bitmap_shifter >>= 1; | ||
342 | iterator->_arg_index++; | ||
343 | } | ||
250 | 344 | ||
251 | /* if we found a valid arg earlier, return it now */ | 345 | /* if we found a valid arg earlier, return it now */ |
252 | if (hit) | 346 | if (hit) |
253 | return 0; | 347 | return 0; |
254 | } | 348 | } |
255 | |||
256 | /* we don't know how to handle any more args, we're done */ | ||
257 | return -ENOENT; | ||
258 | } | 349 | } |
259 | EXPORT_SYMBOL(ieee80211_radiotap_iterator_next); | 350 | EXPORT_SYMBOL(ieee80211_radiotap_iterator_next); |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 7a0754c92df4..ed89c59bb431 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -40,8 +40,18 @@ | |||
40 | #include <net/cfg80211.h> | 40 | #include <net/cfg80211.h> |
41 | #include "core.h" | 41 | #include "core.h" |
42 | #include "reg.h" | 42 | #include "reg.h" |
43 | #include "regdb.h" | ||
43 | #include "nl80211.h" | 44 | #include "nl80211.h" |
44 | 45 | ||
46 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
47 | #define REG_DBG_PRINT(format, args...) \ | ||
48 | do { \ | ||
49 | printk(KERN_DEBUG format , ## args); \ | ||
50 | } while (0) | ||
51 | #else | ||
52 | #define REG_DBG_PRINT(args...) | ||
53 | #endif | ||
54 | |||
45 | /* Receipt of information from last regulatory request */ | 55 | /* Receipt of information from last regulatory request */ |
46 | static struct regulatory_request *last_request; | 56 | static struct regulatory_request *last_request; |
47 | 57 | ||
@@ -124,82 +134,11 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom = | |||
124 | &world_regdom; | 134 | &world_regdom; |
125 | 135 | ||
126 | static char *ieee80211_regdom = "00"; | 136 | static char *ieee80211_regdom = "00"; |
137 | static char user_alpha2[2]; | ||
127 | 138 | ||
128 | module_param(ieee80211_regdom, charp, 0444); | 139 | module_param(ieee80211_regdom, charp, 0444); |
129 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); | 140 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); |
130 | 141 | ||
131 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | ||
132 | /* | ||
133 | * We assume 40 MHz bandwidth for the old regulatory work. | ||
134 | * We make emphasis we are using the exact same frequencies | ||
135 | * as before | ||
136 | */ | ||
137 | |||
138 | static const struct ieee80211_regdomain us_regdom = { | ||
139 | .n_reg_rules = 6, | ||
140 | .alpha2 = "US", | ||
141 | .reg_rules = { | ||
142 | /* IEEE 802.11b/g, channels 1..11 */ | ||
143 | REG_RULE(2412-10, 2462+10, 40, 6, 27, 0), | ||
144 | /* IEEE 802.11a, channel 36..48 */ | ||
145 | REG_RULE(5180-10, 5240+10, 40, 6, 17, 0), | ||
146 | /* IEEE 802.11a, channels 48..64 */ | ||
147 | REG_RULE(5260-10, 5320+10, 40, 6, 20, NL80211_RRF_DFS), | ||
148 | /* IEEE 802.11a, channels 100..124 */ | ||
149 | REG_RULE(5500-10, 5590+10, 40, 6, 20, NL80211_RRF_DFS), | ||
150 | /* IEEE 802.11a, channels 132..144 */ | ||
151 | REG_RULE(5660-10, 5700+10, 40, 6, 20, NL80211_RRF_DFS), | ||
152 | /* IEEE 802.11a, channels 149..165, outdoor */ | ||
153 | REG_RULE(5745-10, 5825+10, 40, 6, 30, 0), | ||
154 | } | ||
155 | }; | ||
156 | |||
157 | static const struct ieee80211_regdomain jp_regdom = { | ||
158 | .n_reg_rules = 6, | ||
159 | .alpha2 = "JP", | ||
160 | .reg_rules = { | ||
161 | /* IEEE 802.11b/g, channels 1..11 */ | ||
162 | REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), | ||
163 | /* IEEE 802.11b/g, channels 12..13 */ | ||
164 | REG_RULE(2467-10, 2472+10, 20, 6, 20, 0), | ||
165 | /* IEEE 802.11b/g, channel 14 */ | ||
166 | REG_RULE(2484-10, 2484+10, 20, 6, 20, NL80211_RRF_NO_OFDM), | ||
167 | /* IEEE 802.11a, channels 36..48 */ | ||
168 | REG_RULE(5180-10, 5240+10, 40, 6, 20, 0), | ||
169 | /* IEEE 802.11a, channels 52..64 */ | ||
170 | REG_RULE(5260-10, 5320+10, 40, 6, 20, NL80211_RRF_DFS), | ||
171 | /* IEEE 802.11a, channels 100..144 */ | ||
172 | REG_RULE(5500-10, 5700+10, 40, 6, 23, NL80211_RRF_DFS), | ||
173 | } | ||
174 | }; | ||
175 | |||
176 | static const struct ieee80211_regdomain *static_regdom(char *alpha2) | ||
177 | { | ||
178 | if (alpha2[0] == 'U' && alpha2[1] == 'S') | ||
179 | return &us_regdom; | ||
180 | if (alpha2[0] == 'J' && alpha2[1] == 'P') | ||
181 | return &jp_regdom; | ||
182 | /* Use world roaming rules for "EU", since it was a pseudo | ||
183 | domain anyway... */ | ||
184 | if (alpha2[0] == 'E' && alpha2[1] == 'U') | ||
185 | return &world_regdom; | ||
186 | /* Default, world roaming rules */ | ||
187 | return &world_regdom; | ||
188 | } | ||
189 | |||
190 | static bool is_old_static_regdom(const struct ieee80211_regdomain *rd) | ||
191 | { | ||
192 | if (rd == &us_regdom || rd == &jp_regdom || rd == &world_regdom) | ||
193 | return true; | ||
194 | return false; | ||
195 | } | ||
196 | #else | ||
197 | static inline bool is_old_static_regdom(const struct ieee80211_regdomain *rd) | ||
198 | { | ||
199 | return false; | ||
200 | } | ||
201 | #endif | ||
202 | |||
203 | static void reset_regdomains(void) | 142 | static void reset_regdomains(void) |
204 | { | 143 | { |
205 | /* avoid freeing static information or freeing something twice */ | 144 | /* avoid freeing static information or freeing something twice */ |
@@ -209,8 +148,6 @@ static void reset_regdomains(void) | |||
209 | cfg80211_world_regdom = NULL; | 148 | cfg80211_world_regdom = NULL; |
210 | if (cfg80211_regdomain == &world_regdom) | 149 | if (cfg80211_regdomain == &world_regdom) |
211 | cfg80211_regdomain = NULL; | 150 | cfg80211_regdomain = NULL; |
212 | if (is_old_static_regdom(cfg80211_regdomain)) | ||
213 | cfg80211_regdomain = NULL; | ||
214 | 151 | ||
215 | kfree(cfg80211_regdomain); | 152 | kfree(cfg80211_regdomain); |
216 | kfree(cfg80211_world_regdom); | 153 | kfree(cfg80211_world_regdom); |
@@ -316,6 +253,27 @@ static bool regdom_changes(const char *alpha2) | |||
316 | return true; | 253 | return true; |
317 | } | 254 | } |
318 | 255 | ||
256 | /* | ||
257 | * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets | ||
258 | * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER | ||
259 | * has ever been issued. | ||
260 | */ | ||
261 | static bool is_user_regdom_saved(void) | ||
262 | { | ||
263 | if (user_alpha2[0] == '9' && user_alpha2[1] == '7') | ||
264 | return false; | ||
265 | |||
266 | /* This would indicate a mistake on the design */ | ||
267 | if (WARN((!is_world_regdom(user_alpha2) && | ||
268 | !is_an_alpha2(user_alpha2)), | ||
269 | "Unexpected user alpha2: %c%c\n", | ||
270 | user_alpha2[0], | ||
271 | user_alpha2[1])) | ||
272 | return false; | ||
273 | |||
274 | return true; | ||
275 | } | ||
276 | |||
319 | /** | 277 | /** |
320 | * country_ie_integrity_changes - tells us if the country IE has changed | 278 | * country_ie_integrity_changes - tells us if the country IE has changed |
321 | * @checksum: checksum of country IE of fields we are interested in | 279 | * @checksum: checksum of country IE of fields we are interested in |
@@ -335,6 +293,98 @@ static bool country_ie_integrity_changes(u32 checksum) | |||
335 | return false; | 293 | return false; |
336 | } | 294 | } |
337 | 295 | ||
296 | static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, | ||
297 | const struct ieee80211_regdomain *src_regd) | ||
298 | { | ||
299 | struct ieee80211_regdomain *regd; | ||
300 | int size_of_regd = 0; | ||
301 | unsigned int i; | ||
302 | |||
303 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
304 | ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
305 | |||
306 | regd = kzalloc(size_of_regd, GFP_KERNEL); | ||
307 | if (!regd) | ||
308 | return -ENOMEM; | ||
309 | |||
310 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); | ||
311 | |||
312 | for (i = 0; i < src_regd->n_reg_rules; i++) | ||
313 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], | ||
314 | sizeof(struct ieee80211_reg_rule)); | ||
315 | |||
316 | *dst_regd = regd; | ||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | #ifdef CONFIG_CFG80211_INTERNAL_REGDB | ||
321 | struct reg_regdb_search_request { | ||
322 | char alpha2[2]; | ||
323 | struct list_head list; | ||
324 | }; | ||
325 | |||
326 | static LIST_HEAD(reg_regdb_search_list); | ||
327 | static DEFINE_SPINLOCK(reg_regdb_search_lock); | ||
328 | |||
329 | static void reg_regdb_search(struct work_struct *work) | ||
330 | { | ||
331 | struct reg_regdb_search_request *request; | ||
332 | const struct ieee80211_regdomain *curdom, *regdom; | ||
333 | int i, r; | ||
334 | |||
335 | spin_lock(®_regdb_search_lock); | ||
336 | while (!list_empty(®_regdb_search_list)) { | ||
337 | request = list_first_entry(®_regdb_search_list, | ||
338 | struct reg_regdb_search_request, | ||
339 | list); | ||
340 | list_del(&request->list); | ||
341 | |||
342 | for (i=0; i<reg_regdb_size; i++) { | ||
343 | curdom = reg_regdb[i]; | ||
344 | |||
345 | if (!memcmp(request->alpha2, curdom->alpha2, 2)) { | ||
346 | r = reg_copy_regd(®dom, curdom); | ||
347 | if (r) | ||
348 | break; | ||
349 | spin_unlock(®_regdb_search_lock); | ||
350 | mutex_lock(&cfg80211_mutex); | ||
351 | set_regdom(regdom); | ||
352 | mutex_unlock(&cfg80211_mutex); | ||
353 | spin_lock(®_regdb_search_lock); | ||
354 | break; | ||
355 | } | ||
356 | } | ||
357 | |||
358 | kfree(request); | ||
359 | } | ||
360 | spin_unlock(®_regdb_search_lock); | ||
361 | } | ||
362 | |||
363 | static DECLARE_WORK(reg_regdb_work, reg_regdb_search); | ||
364 | |||
365 | static void reg_regdb_query(const char *alpha2) | ||
366 | { | ||
367 | struct reg_regdb_search_request *request; | ||
368 | |||
369 | if (!alpha2) | ||
370 | return; | ||
371 | |||
372 | request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL); | ||
373 | if (!request) | ||
374 | return; | ||
375 | |||
376 | memcpy(request->alpha2, alpha2, 2); | ||
377 | |||
378 | spin_lock(®_regdb_search_lock); | ||
379 | list_add_tail(&request->list, ®_regdb_search_list); | ||
380 | spin_unlock(®_regdb_search_lock); | ||
381 | |||
382 | schedule_work(®_regdb_work); | ||
383 | } | ||
384 | #else | ||
385 | static inline void reg_regdb_query(const char *alpha2) {} | ||
386 | #endif /* CONFIG_CFG80211_INTERNAL_REGDB */ | ||
387 | |||
338 | /* | 388 | /* |
339 | * This lets us keep regulatory code which is updated on a regulatory | 389 | * This lets us keep regulatory code which is updated on a regulatory |
340 | * basis in userspace. | 390 | * basis in userspace. |
@@ -354,6 +404,9 @@ static int call_crda(const char *alpha2) | |||
354 | printk(KERN_INFO "cfg80211: Calling CRDA to update world " | 404 | printk(KERN_INFO "cfg80211: Calling CRDA to update world " |
355 | "regulatory domain\n"); | 405 | "regulatory domain\n"); |
356 | 406 | ||
407 | /* query internal regulatory database (if it exists) */ | ||
408 | reg_regdb_query(alpha2); | ||
409 | |||
357 | country_env[8] = alpha2[0]; | 410 | country_env[8] = alpha2[0]; |
358 | country_env[9] = alpha2[1]; | 411 | country_env[9] = alpha2[1]; |
359 | 412 | ||
@@ -454,12 +507,212 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, | |||
454 | } | 507 | } |
455 | 508 | ||
456 | /* | 509 | /* |
510 | * This is a work around for sanity checking ieee80211_channel_to_frequency()'s | ||
511 | * work. ieee80211_channel_to_frequency() can for example currently provide a | ||
512 | * 2 GHz channel when in fact a 5 GHz channel was desired. An example would be | ||
513 | * an AP providing channel 8 on a country IE triplet when it sent this on the | ||
514 | * 5 GHz band, that channel is designed to be channel 8 on 5 GHz, not a 2 GHz | ||
515 | * channel. | ||
516 | * | ||
517 | * This can be removed once ieee80211_channel_to_frequency() takes in a band. | ||
518 | */ | ||
519 | static bool chan_in_band(int chan, enum ieee80211_band band) | ||
520 | { | ||
521 | int center_freq = ieee80211_channel_to_frequency(chan); | ||
522 | |||
523 | switch (band) { | ||
524 | case IEEE80211_BAND_2GHZ: | ||
525 | if (center_freq <= 2484) | ||
526 | return true; | ||
527 | return false; | ||
528 | case IEEE80211_BAND_5GHZ: | ||
529 | if (center_freq >= 5005) | ||
530 | return true; | ||
531 | return false; | ||
532 | default: | ||
533 | return false; | ||
534 | } | ||
535 | } | ||
536 | |||
537 | /* | ||
538 | * Some APs may send a country IE triplet for each channel they | ||
539 | * support and while this is completely overkill and silly we still | ||
540 | * need to support it. We avoid making a single rule for each channel | ||
541 | * though and to help us with this we use this helper to find the | ||
542 | * actual subband end channel. These type of country IE triplet | ||
543 | * scenerios are handled then, all yielding two regulaotry rules from | ||
544 | * parsing a country IE: | ||
545 | * | ||
546 | * [1] | ||
547 | * [2] | ||
548 | * [36] | ||
549 | * [40] | ||
550 | * | ||
551 | * [1] | ||
552 | * [2-4] | ||
553 | * [5-12] | ||
554 | * [36] | ||
555 | * [40-44] | ||
556 | * | ||
557 | * [1-4] | ||
558 | * [5-7] | ||
559 | * [36-44] | ||
560 | * [48-64] | ||
561 | * | ||
562 | * [36-36] | ||
563 | * [40-40] | ||
564 | * [44-44] | ||
565 | * [48-48] | ||
566 | * [52-52] | ||
567 | * [56-56] | ||
568 | * [60-60] | ||
569 | * [64-64] | ||
570 | * [100-100] | ||
571 | * [104-104] | ||
572 | * [108-108] | ||
573 | * [112-112] | ||
574 | * [116-116] | ||
575 | * [120-120] | ||
576 | * [124-124] | ||
577 | * [128-128] | ||
578 | * [132-132] | ||
579 | * [136-136] | ||
580 | * [140-140] | ||
581 | * | ||
582 | * Returns 0 if the IE has been found to be invalid in the middle | ||
583 | * somewhere. | ||
584 | */ | ||
585 | static int max_subband_chan(enum ieee80211_band band, | ||
586 | int orig_cur_chan, | ||
587 | int orig_end_channel, | ||
588 | s8 orig_max_power, | ||
589 | u8 **country_ie, | ||
590 | u8 *country_ie_len) | ||
591 | { | ||
592 | u8 *triplets_start = *country_ie; | ||
593 | u8 len_at_triplet = *country_ie_len; | ||
594 | int end_subband_chan = orig_end_channel; | ||
595 | |||
596 | /* | ||
597 | * We'll deal with padding for the caller unless | ||
598 | * its not immediate and we don't process any channels | ||
599 | */ | ||
600 | if (*country_ie_len == 1) { | ||
601 | *country_ie += 1; | ||
602 | *country_ie_len -= 1; | ||
603 | return orig_end_channel; | ||
604 | } | ||
605 | |||
606 | /* Move to the next triplet and then start search */ | ||
607 | *country_ie += 3; | ||
608 | *country_ie_len -= 3; | ||
609 | |||
610 | if (!chan_in_band(orig_cur_chan, band)) | ||
611 | return 0; | ||
612 | |||
613 | while (*country_ie_len >= 3) { | ||
614 | int end_channel = 0; | ||
615 | struct ieee80211_country_ie_triplet *triplet = | ||
616 | (struct ieee80211_country_ie_triplet *) *country_ie; | ||
617 | int cur_channel = 0, next_expected_chan; | ||
618 | |||
619 | /* means last triplet is completely unrelated to this one */ | ||
620 | if (triplet->ext.reg_extension_id >= | ||
621 | IEEE80211_COUNTRY_EXTENSION_ID) { | ||
622 | *country_ie -= 3; | ||
623 | *country_ie_len += 3; | ||
624 | break; | ||
625 | } | ||
626 | |||
627 | if (triplet->chans.first_channel == 0) { | ||
628 | *country_ie += 1; | ||
629 | *country_ie_len -= 1; | ||
630 | if (*country_ie_len != 0) | ||
631 | return 0; | ||
632 | break; | ||
633 | } | ||
634 | |||
635 | if (triplet->chans.num_channels == 0) | ||
636 | return 0; | ||
637 | |||
638 | /* Monitonically increasing channel order */ | ||
639 | if (triplet->chans.first_channel <= end_subband_chan) | ||
640 | return 0; | ||
641 | |||
642 | if (!chan_in_band(triplet->chans.first_channel, band)) | ||
643 | return 0; | ||
644 | |||
645 | /* 2 GHz */ | ||
646 | if (triplet->chans.first_channel <= 14) { | ||
647 | end_channel = triplet->chans.first_channel + | ||
648 | triplet->chans.num_channels - 1; | ||
649 | } | ||
650 | else { | ||
651 | end_channel = triplet->chans.first_channel + | ||
652 | (4 * (triplet->chans.num_channels - 1)); | ||
653 | } | ||
654 | |||
655 | if (!chan_in_band(end_channel, band)) | ||
656 | return 0; | ||
657 | |||
658 | if (orig_max_power != triplet->chans.max_power) { | ||
659 | *country_ie -= 3; | ||
660 | *country_ie_len += 3; | ||
661 | break; | ||
662 | } | ||
663 | |||
664 | cur_channel = triplet->chans.first_channel; | ||
665 | |||
666 | /* The key is finding the right next expected channel */ | ||
667 | if (band == IEEE80211_BAND_2GHZ) | ||
668 | next_expected_chan = end_subband_chan + 1; | ||
669 | else | ||
670 | next_expected_chan = end_subband_chan + 4; | ||
671 | |||
672 | if (cur_channel != next_expected_chan) { | ||
673 | *country_ie -= 3; | ||
674 | *country_ie_len += 3; | ||
675 | break; | ||
676 | } | ||
677 | |||
678 | end_subband_chan = end_channel; | ||
679 | |||
680 | /* Move to the next one */ | ||
681 | *country_ie += 3; | ||
682 | *country_ie_len -= 3; | ||
683 | |||
684 | /* | ||
685 | * Padding needs to be dealt with if we processed | ||
686 | * some channels. | ||
687 | */ | ||
688 | if (*country_ie_len == 1) { | ||
689 | *country_ie += 1; | ||
690 | *country_ie_len -= 1; | ||
691 | break; | ||
692 | } | ||
693 | |||
694 | /* If seen, the IE is invalid */ | ||
695 | if (*country_ie_len == 2) | ||
696 | return 0; | ||
697 | } | ||
698 | |||
699 | if (end_subband_chan == orig_end_channel) { | ||
700 | *country_ie = triplets_start; | ||
701 | *country_ie_len = len_at_triplet; | ||
702 | return orig_end_channel; | ||
703 | } | ||
704 | |||
705 | return end_subband_chan; | ||
706 | } | ||
707 | |||
708 | /* | ||
457 | * Converts a country IE to a regulatory domain. A regulatory domain | 709 | * Converts a country IE to a regulatory domain. A regulatory domain |
458 | * structure has a lot of information which the IE doesn't yet have, | 710 | * structure has a lot of information which the IE doesn't yet have, |
459 | * so for the other values we use upper max values as we will intersect | 711 | * so for the other values we use upper max values as we will intersect |
460 | * with our userspace regulatory agent to get lower bounds. | 712 | * with our userspace regulatory agent to get lower bounds. |
461 | */ | 713 | */ |
462 | static struct ieee80211_regdomain *country_ie_2_rd( | 714 | static struct ieee80211_regdomain *country_ie_2_rd( |
715 | enum ieee80211_band band, | ||
463 | u8 *country_ie, | 716 | u8 *country_ie, |
464 | u8 country_ie_len, | 717 | u8 country_ie_len, |
465 | u32 *checksum) | 718 | u32 *checksum) |
@@ -521,10 +774,29 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
521 | continue; | 774 | continue; |
522 | } | 775 | } |
523 | 776 | ||
777 | /* | ||
778 | * APs can add padding to make length divisible | ||
779 | * by two, required by the spec. | ||
780 | */ | ||
781 | if (triplet->chans.first_channel == 0) { | ||
782 | country_ie++; | ||
783 | country_ie_len--; | ||
784 | /* This is expected to be at the very end only */ | ||
785 | if (country_ie_len != 0) | ||
786 | return NULL; | ||
787 | break; | ||
788 | } | ||
789 | |||
790 | if (triplet->chans.num_channels == 0) | ||
791 | return NULL; | ||
792 | |||
793 | if (!chan_in_band(triplet->chans.first_channel, band)) | ||
794 | return NULL; | ||
795 | |||
524 | /* 2 GHz */ | 796 | /* 2 GHz */ |
525 | if (triplet->chans.first_channel <= 14) | 797 | if (band == IEEE80211_BAND_2GHZ) |
526 | end_channel = triplet->chans.first_channel + | 798 | end_channel = triplet->chans.first_channel + |
527 | triplet->chans.num_channels; | 799 | triplet->chans.num_channels - 1; |
528 | else | 800 | else |
529 | /* | 801 | /* |
530 | * 5 GHz -- For example in country IEs if the first | 802 | * 5 GHz -- For example in country IEs if the first |
@@ -539,6 +811,24 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
539 | (4 * (triplet->chans.num_channels - 1)); | 811 | (4 * (triplet->chans.num_channels - 1)); |
540 | 812 | ||
541 | cur_channel = triplet->chans.first_channel; | 813 | cur_channel = triplet->chans.first_channel; |
814 | |||
815 | /* | ||
816 | * Enhancement for APs that send a triplet for every channel | ||
817 | * or for whatever reason sends triplets with multiple channels | ||
818 | * separated when in fact they should be together. | ||
819 | */ | ||
820 | end_channel = max_subband_chan(band, | ||
821 | cur_channel, | ||
822 | end_channel, | ||
823 | triplet->chans.max_power, | ||
824 | &country_ie, | ||
825 | &country_ie_len); | ||
826 | if (!end_channel) | ||
827 | return NULL; | ||
828 | |||
829 | if (!chan_in_band(end_channel, band)) | ||
830 | return NULL; | ||
831 | |||
542 | cur_sub_max_channel = end_channel; | 832 | cur_sub_max_channel = end_channel; |
543 | 833 | ||
544 | /* Basic sanity check */ | 834 | /* Basic sanity check */ |
@@ -569,10 +859,13 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
569 | 859 | ||
570 | last_sub_max_channel = cur_sub_max_channel; | 860 | last_sub_max_channel = cur_sub_max_channel; |
571 | 861 | ||
572 | country_ie += 3; | ||
573 | country_ie_len -= 3; | ||
574 | num_rules++; | 862 | num_rules++; |
575 | 863 | ||
864 | if (country_ie_len >= 3) { | ||
865 | country_ie += 3; | ||
866 | country_ie_len -= 3; | ||
867 | } | ||
868 | |||
576 | /* | 869 | /* |
577 | * Note: this is not a IEEE requirement but | 870 | * Note: this is not a IEEE requirement but |
578 | * simply a memory requirement | 871 | * simply a memory requirement |
@@ -615,6 +908,12 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
615 | continue; | 908 | continue; |
616 | } | 909 | } |
617 | 910 | ||
911 | if (triplet->chans.first_channel == 0) { | ||
912 | country_ie++; | ||
913 | country_ie_len--; | ||
914 | break; | ||
915 | } | ||
916 | |||
618 | reg_rule = &rd->reg_rules[i]; | 917 | reg_rule = &rd->reg_rules[i]; |
619 | freq_range = ®_rule->freq_range; | 918 | freq_range = ®_rule->freq_range; |
620 | power_rule = ®_rule->power_rule; | 919 | power_rule = ®_rule->power_rule; |
@@ -622,13 +921,20 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
622 | reg_rule->flags = flags; | 921 | reg_rule->flags = flags; |
623 | 922 | ||
624 | /* 2 GHz */ | 923 | /* 2 GHz */ |
625 | if (triplet->chans.first_channel <= 14) | 924 | if (band == IEEE80211_BAND_2GHZ) |
626 | end_channel = triplet->chans.first_channel + | 925 | end_channel = triplet->chans.first_channel + |
627 | triplet->chans.num_channels; | 926 | triplet->chans.num_channels -1; |
628 | else | 927 | else |
629 | end_channel = triplet->chans.first_channel + | 928 | end_channel = triplet->chans.first_channel + |
630 | (4 * (triplet->chans.num_channels - 1)); | 929 | (4 * (triplet->chans.num_channels - 1)); |
631 | 930 | ||
931 | end_channel = max_subband_chan(band, | ||
932 | triplet->chans.first_channel, | ||
933 | end_channel, | ||
934 | triplet->chans.max_power, | ||
935 | &country_ie, | ||
936 | &country_ie_len); | ||
937 | |||
632 | /* | 938 | /* |
633 | * The +10 is since the regulatory domain expects | 939 | * The +10 is since the regulatory domain expects |
634 | * the actual band edge, not the center of freq for | 940 | * the actual band edge, not the center of freq for |
@@ -649,12 +955,15 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
649 | */ | 955 | */ |
650 | freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40); | 956 | freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40); |
651 | power_rule->max_antenna_gain = DBI_TO_MBI(100); | 957 | power_rule->max_antenna_gain = DBI_TO_MBI(100); |
652 | power_rule->max_eirp = DBM_TO_MBM(100); | 958 | power_rule->max_eirp = DBM_TO_MBM(triplet->chans.max_power); |
653 | 959 | ||
654 | country_ie += 3; | ||
655 | country_ie_len -= 3; | ||
656 | i++; | 960 | i++; |
657 | 961 | ||
962 | if (country_ie_len >= 3) { | ||
963 | country_ie += 3; | ||
964 | country_ie_len -= 3; | ||
965 | } | ||
966 | |||
658 | BUG_ON(i > NL80211_MAX_SUPP_REG_RULES); | 967 | BUG_ON(i > NL80211_MAX_SUPP_REG_RULES); |
659 | } | 968 | } |
660 | 969 | ||
@@ -950,25 +1259,21 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
950 | if (r == -ERANGE && | 1259 | if (r == -ERANGE && |
951 | last_request->initiator == | 1260 | last_request->initiator == |
952 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { | 1261 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
953 | #ifdef CONFIG_CFG80211_REG_DEBUG | 1262 | REG_DBG_PRINT("cfg80211: Leaving channel %d MHz " |
954 | printk(KERN_DEBUG "cfg80211: Leaving channel %d MHz " | ||
955 | "intact on %s - no rule found in band on " | 1263 | "intact on %s - no rule found in band on " |
956 | "Country IE\n", | 1264 | "Country IE\n", |
957 | chan->center_freq, wiphy_name(wiphy)); | 1265 | chan->center_freq, wiphy_name(wiphy)); |
958 | #endif | ||
959 | } else { | 1266 | } else { |
960 | /* | 1267 | /* |
961 | * In this case we know the country IE has at least one reg rule | 1268 | * In this case we know the country IE has at least one reg rule |
962 | * for the band so we respect its band definitions | 1269 | * for the band so we respect its band definitions |
963 | */ | 1270 | */ |
964 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
965 | if (last_request->initiator == | 1271 | if (last_request->initiator == |
966 | NL80211_REGDOM_SET_BY_COUNTRY_IE) | 1272 | NL80211_REGDOM_SET_BY_COUNTRY_IE) |
967 | printk(KERN_DEBUG "cfg80211: Disabling " | 1273 | REG_DBG_PRINT("cfg80211: Disabling " |
968 | "channel %d MHz on %s due to " | 1274 | "channel %d MHz on %s due to " |
969 | "Country IE\n", | 1275 | "Country IE\n", |
970 | chan->center_freq, wiphy_name(wiphy)); | 1276 | chan->center_freq, wiphy_name(wiphy)); |
971 | #endif | ||
972 | flags |= IEEE80211_CHAN_DISABLED; | 1277 | flags |= IEEE80211_CHAN_DISABLED; |
973 | chan->flags = flags; | 1278 | chan->flags = flags; |
974 | } | 1279 | } |
@@ -1342,30 +1647,6 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, | |||
1342 | } | 1647 | } |
1343 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); | 1648 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); |
1344 | 1649 | ||
1345 | static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, | ||
1346 | const struct ieee80211_regdomain *src_regd) | ||
1347 | { | ||
1348 | struct ieee80211_regdomain *regd; | ||
1349 | int size_of_regd = 0; | ||
1350 | unsigned int i; | ||
1351 | |||
1352 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
1353 | ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
1354 | |||
1355 | regd = kzalloc(size_of_regd, GFP_KERNEL); | ||
1356 | if (!regd) | ||
1357 | return -ENOMEM; | ||
1358 | |||
1359 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); | ||
1360 | |||
1361 | for (i = 0; i < src_regd->n_reg_rules; i++) | ||
1362 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], | ||
1363 | sizeof(struct ieee80211_reg_rule)); | ||
1364 | |||
1365 | *dst_regd = regd; | ||
1366 | return 0; | ||
1367 | } | ||
1368 | |||
1369 | /* | 1650 | /* |
1370 | * Return value which can be used by ignore_request() to indicate | 1651 | * Return value which can be used by ignore_request() to indicate |
1371 | * it has been determined we should intersect two regulatory domains | 1652 | * it has been determined we should intersect two regulatory domains |
@@ -1387,7 +1668,7 @@ static int ignore_request(struct wiphy *wiphy, | |||
1387 | 1668 | ||
1388 | switch (pending_request->initiator) { | 1669 | switch (pending_request->initiator) { |
1389 | case NL80211_REGDOM_SET_BY_CORE: | 1670 | case NL80211_REGDOM_SET_BY_CORE: |
1390 | return -EINVAL; | 1671 | return 0; |
1391 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: | 1672 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: |
1392 | 1673 | ||
1393 | last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 1674 | last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); |
@@ -1418,8 +1699,6 @@ static int ignore_request(struct wiphy *wiphy, | |||
1418 | return REG_INTERSECT; | 1699 | return REG_INTERSECT; |
1419 | case NL80211_REGDOM_SET_BY_DRIVER: | 1700 | case NL80211_REGDOM_SET_BY_DRIVER: |
1420 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { | 1701 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { |
1421 | if (is_old_static_regdom(cfg80211_regdomain)) | ||
1422 | return 0; | ||
1423 | if (regdom_changes(pending_request->alpha2)) | 1702 | if (regdom_changes(pending_request->alpha2)) |
1424 | return 0; | 1703 | return 0; |
1425 | return -EALREADY; | 1704 | return -EALREADY; |
@@ -1456,8 +1735,7 @@ static int ignore_request(struct wiphy *wiphy, | |||
1456 | return -EAGAIN; | 1735 | return -EAGAIN; |
1457 | } | 1736 | } |
1458 | 1737 | ||
1459 | if (!is_old_static_regdom(cfg80211_regdomain) && | 1738 | if (!regdom_changes(pending_request->alpha2)) |
1460 | !regdom_changes(pending_request->alpha2)) | ||
1461 | return -EALREADY; | 1739 | return -EALREADY; |
1462 | 1740 | ||
1463 | return 0; | 1741 | return 0; |
@@ -1529,6 +1807,11 @@ new_request: | |||
1529 | 1807 | ||
1530 | pending_request = NULL; | 1808 | pending_request = NULL; |
1531 | 1809 | ||
1810 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { | ||
1811 | user_alpha2[0] = last_request->alpha2[0]; | ||
1812 | user_alpha2[1] = last_request->alpha2[1]; | ||
1813 | } | ||
1814 | |||
1532 | /* When r == REG_INTERSECT we do need to call CRDA */ | 1815 | /* When r == REG_INTERSECT we do need to call CRDA */ |
1533 | if (r < 0) { | 1816 | if (r < 0) { |
1534 | /* | 1817 | /* |
@@ -1648,12 +1931,16 @@ static void queue_regulatory_request(struct regulatory_request *request) | |||
1648 | schedule_work(®_work); | 1931 | schedule_work(®_work); |
1649 | } | 1932 | } |
1650 | 1933 | ||
1651 | /* Core regulatory hint -- happens once during cfg80211_init() */ | 1934 | /* |
1935 | * Core regulatory hint -- happens during cfg80211_init() | ||
1936 | * and when we restore regulatory settings. | ||
1937 | */ | ||
1652 | static int regulatory_hint_core(const char *alpha2) | 1938 | static int regulatory_hint_core(const char *alpha2) |
1653 | { | 1939 | { |
1654 | struct regulatory_request *request; | 1940 | struct regulatory_request *request; |
1655 | 1941 | ||
1656 | BUG_ON(last_request); | 1942 | kfree(last_request); |
1943 | last_request = NULL; | ||
1657 | 1944 | ||
1658 | request = kzalloc(sizeof(struct regulatory_request), | 1945 | request = kzalloc(sizeof(struct regulatory_request), |
1659 | GFP_KERNEL); | 1946 | GFP_KERNEL); |
@@ -1664,14 +1951,12 @@ static int regulatory_hint_core(const char *alpha2) | |||
1664 | request->alpha2[1] = alpha2[1]; | 1951 | request->alpha2[1] = alpha2[1]; |
1665 | request->initiator = NL80211_REGDOM_SET_BY_CORE; | 1952 | request->initiator = NL80211_REGDOM_SET_BY_CORE; |
1666 | 1953 | ||
1667 | queue_regulatory_request(request); | ||
1668 | |||
1669 | /* | 1954 | /* |
1670 | * This ensures last_request is populated once modules | 1955 | * This ensures last_request is populated once modules |
1671 | * come swinging in and calling regulatory hints and | 1956 | * come swinging in and calling regulatory hints and |
1672 | * wiphy_apply_custom_regulatory(). | 1957 | * wiphy_apply_custom_regulatory(). |
1673 | */ | 1958 | */ |
1674 | flush_scheduled_work(); | 1959 | reg_process_hint(request); |
1675 | 1960 | ||
1676 | return 0; | 1961 | return 0; |
1677 | } | 1962 | } |
@@ -1758,8 +2043,9 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy, | |||
1758 | * therefore cannot iterate over the rdev list here. | 2043 | * therefore cannot iterate over the rdev list here. |
1759 | */ | 2044 | */ |
1760 | void regulatory_hint_11d(struct wiphy *wiphy, | 2045 | void regulatory_hint_11d(struct wiphy *wiphy, |
1761 | u8 *country_ie, | 2046 | enum ieee80211_band band, |
1762 | u8 country_ie_len) | 2047 | u8 *country_ie, |
2048 | u8 country_ie_len) | ||
1763 | { | 2049 | { |
1764 | struct ieee80211_regdomain *rd = NULL; | 2050 | struct ieee80211_regdomain *rd = NULL; |
1765 | char alpha2[2]; | 2051 | char alpha2[2]; |
@@ -1805,9 +2091,11 @@ void regulatory_hint_11d(struct wiphy *wiphy, | |||
1805 | wiphy_idx_valid(last_request->wiphy_idx))) | 2091 | wiphy_idx_valid(last_request->wiphy_idx))) |
1806 | goto out; | 2092 | goto out; |
1807 | 2093 | ||
1808 | rd = country_ie_2_rd(country_ie, country_ie_len, &checksum); | 2094 | rd = country_ie_2_rd(band, country_ie, country_ie_len, &checksum); |
1809 | if (!rd) | 2095 | if (!rd) { |
2096 | REG_DBG_PRINT("cfg80211: Ignoring bogus country IE\n"); | ||
1810 | goto out; | 2097 | goto out; |
2098 | } | ||
1811 | 2099 | ||
1812 | /* | 2100 | /* |
1813 | * This will not happen right now but we leave it here for the | 2101 | * This will not happen right now but we leave it here for the |
@@ -1850,6 +2138,123 @@ out: | |||
1850 | mutex_unlock(®_mutex); | 2138 | mutex_unlock(®_mutex); |
1851 | } | 2139 | } |
1852 | 2140 | ||
2141 | static void restore_alpha2(char *alpha2, bool reset_user) | ||
2142 | { | ||
2143 | /* indicates there is no alpha2 to consider for restoration */ | ||
2144 | alpha2[0] = '9'; | ||
2145 | alpha2[1] = '7'; | ||
2146 | |||
2147 | /* The user setting has precedence over the module parameter */ | ||
2148 | if (is_user_regdom_saved()) { | ||
2149 | /* Unless we're asked to ignore it and reset it */ | ||
2150 | if (reset_user) { | ||
2151 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings " | ||
2152 | "including user preference\n"); | ||
2153 | user_alpha2[0] = '9'; | ||
2154 | user_alpha2[1] = '7'; | ||
2155 | |||
2156 | /* | ||
2157 | * If we're ignoring user settings, we still need to | ||
2158 | * check the module parameter to ensure we put things | ||
2159 | * back as they were for a full restore. | ||
2160 | */ | ||
2161 | if (!is_world_regdom(ieee80211_regdom)) { | ||
2162 | REG_DBG_PRINT("cfg80211: Keeping preference on " | ||
2163 | "module parameter ieee80211_regdom: %c%c\n", | ||
2164 | ieee80211_regdom[0], | ||
2165 | ieee80211_regdom[1]); | ||
2166 | alpha2[0] = ieee80211_regdom[0]; | ||
2167 | alpha2[1] = ieee80211_regdom[1]; | ||
2168 | } | ||
2169 | } else { | ||
2170 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings " | ||
2171 | "while preserving user preference for: %c%c\n", | ||
2172 | user_alpha2[0], | ||
2173 | user_alpha2[1]); | ||
2174 | alpha2[0] = user_alpha2[0]; | ||
2175 | alpha2[1] = user_alpha2[1]; | ||
2176 | } | ||
2177 | } else if (!is_world_regdom(ieee80211_regdom)) { | ||
2178 | REG_DBG_PRINT("cfg80211: Keeping preference on " | ||
2179 | "module parameter ieee80211_regdom: %c%c\n", | ||
2180 | ieee80211_regdom[0], | ||
2181 | ieee80211_regdom[1]); | ||
2182 | alpha2[0] = ieee80211_regdom[0]; | ||
2183 | alpha2[1] = ieee80211_regdom[1]; | ||
2184 | } else | ||
2185 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings\n"); | ||
2186 | } | ||
2187 | |||
2188 | /* | ||
2189 | * Restoring regulatory settings involves ingoring any | ||
2190 | * possibly stale country IE information and user regulatory | ||
2191 | * settings if so desired, this includes any beacon hints | ||
2192 | * learned as we could have traveled outside to another country | ||
2193 | * after disconnection. To restore regulatory settings we do | ||
2194 | * exactly what we did at bootup: | ||
2195 | * | ||
2196 | * - send a core regulatory hint | ||
2197 | * - send a user regulatory hint if applicable | ||
2198 | * | ||
2199 | * Device drivers that send a regulatory hint for a specific country | ||
2200 | * keep their own regulatory domain on wiphy->regd so that does does | ||
2201 | * not need to be remembered. | ||
2202 | */ | ||
2203 | static void restore_regulatory_settings(bool reset_user) | ||
2204 | { | ||
2205 | char alpha2[2]; | ||
2206 | struct reg_beacon *reg_beacon, *btmp; | ||
2207 | |||
2208 | mutex_lock(&cfg80211_mutex); | ||
2209 | mutex_lock(®_mutex); | ||
2210 | |||
2211 | reset_regdomains(); | ||
2212 | restore_alpha2(alpha2, reset_user); | ||
2213 | |||
2214 | /* Clear beacon hints */ | ||
2215 | spin_lock_bh(®_pending_beacons_lock); | ||
2216 | if (!list_empty(®_pending_beacons)) { | ||
2217 | list_for_each_entry_safe(reg_beacon, btmp, | ||
2218 | ®_pending_beacons, list) { | ||
2219 | list_del(®_beacon->list); | ||
2220 | kfree(reg_beacon); | ||
2221 | } | ||
2222 | } | ||
2223 | spin_unlock_bh(®_pending_beacons_lock); | ||
2224 | |||
2225 | if (!list_empty(®_beacon_list)) { | ||
2226 | list_for_each_entry_safe(reg_beacon, btmp, | ||
2227 | ®_beacon_list, list) { | ||
2228 | list_del(®_beacon->list); | ||
2229 | kfree(reg_beacon); | ||
2230 | } | ||
2231 | } | ||
2232 | |||
2233 | /* First restore to the basic regulatory settings */ | ||
2234 | cfg80211_regdomain = cfg80211_world_regdom; | ||
2235 | |||
2236 | mutex_unlock(®_mutex); | ||
2237 | mutex_unlock(&cfg80211_mutex); | ||
2238 | |||
2239 | regulatory_hint_core(cfg80211_regdomain->alpha2); | ||
2240 | |||
2241 | /* | ||
2242 | * This restores the ieee80211_regdom module parameter | ||
2243 | * preference or the last user requested regulatory | ||
2244 | * settings, user regulatory settings takes precedence. | ||
2245 | */ | ||
2246 | if (is_an_alpha2(alpha2)) | ||
2247 | regulatory_hint_user(user_alpha2); | ||
2248 | } | ||
2249 | |||
2250 | |||
2251 | void regulatory_hint_disconnect(void) | ||
2252 | { | ||
2253 | REG_DBG_PRINT("cfg80211: All devices are disconnected, going to " | ||
2254 | "restore regulatory settings\n"); | ||
2255 | restore_regulatory_settings(false); | ||
2256 | } | ||
2257 | |||
1853 | static bool freq_is_chan_12_13_14(u16 freq) | 2258 | static bool freq_is_chan_12_13_14(u16 freq) |
1854 | { | 2259 | { |
1855 | if (freq == ieee80211_channel_to_frequency(12) || | 2260 | if (freq == ieee80211_channel_to_frequency(12) || |
@@ -1875,13 +2280,12 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, | |||
1875 | if (!reg_beacon) | 2280 | if (!reg_beacon) |
1876 | return -ENOMEM; | 2281 | return -ENOMEM; |
1877 | 2282 | ||
1878 | #ifdef CONFIG_CFG80211_REG_DEBUG | 2283 | REG_DBG_PRINT("cfg80211: Found new beacon on " |
1879 | printk(KERN_DEBUG "cfg80211: Found new beacon on " | 2284 | "frequency: %d MHz (Ch %d) on %s\n", |
1880 | "frequency: %d MHz (Ch %d) on %s\n", | 2285 | beacon_chan->center_freq, |
1881 | beacon_chan->center_freq, | 2286 | ieee80211_frequency_to_channel(beacon_chan->center_freq), |
1882 | ieee80211_frequency_to_channel(beacon_chan->center_freq), | 2287 | wiphy_name(wiphy)); |
1883 | wiphy_name(wiphy)); | 2288 | |
1884 | #endif | ||
1885 | memcpy(®_beacon->chan, beacon_chan, | 2289 | memcpy(®_beacon->chan, beacon_chan, |
1886 | sizeof(struct ieee80211_channel)); | 2290 | sizeof(struct ieee80211_channel)); |
1887 | 2291 | ||
@@ -2039,8 +2443,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2039 | * If someone else asked us to change the rd lets only bother | 2443 | * If someone else asked us to change the rd lets only bother |
2040 | * checking if the alpha2 changes if CRDA was already called | 2444 | * checking if the alpha2 changes if CRDA was already called |
2041 | */ | 2445 | */ |
2042 | if (!is_old_static_regdom(cfg80211_regdomain) && | 2446 | if (!regdom_changes(rd->alpha2)) |
2043 | !regdom_changes(rd->alpha2)) | ||
2044 | return -EINVAL; | 2447 | return -EINVAL; |
2045 | } | 2448 | } |
2046 | 2449 | ||
@@ -2239,15 +2642,11 @@ int regulatory_init(void) | |||
2239 | spin_lock_init(®_requests_lock); | 2642 | spin_lock_init(®_requests_lock); |
2240 | spin_lock_init(®_pending_beacons_lock); | 2643 | spin_lock_init(®_pending_beacons_lock); |
2241 | 2644 | ||
2242 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | ||
2243 | cfg80211_regdomain = static_regdom(ieee80211_regdom); | ||
2244 | |||
2245 | printk(KERN_INFO "cfg80211: Using static regulatory domain info\n"); | ||
2246 | print_regdomain_info(cfg80211_regdomain); | ||
2247 | #else | ||
2248 | cfg80211_regdomain = cfg80211_world_regdom; | 2645 | cfg80211_regdomain = cfg80211_world_regdom; |
2249 | 2646 | ||
2250 | #endif | 2647 | user_alpha2[0] = '9'; |
2648 | user_alpha2[1] = '7'; | ||
2649 | |||
2251 | /* We always try to get an update for the static regdomain */ | 2650 | /* We always try to get an update for the static regdomain */ |
2252 | err = regulatory_hint_core(cfg80211_regdomain->alpha2); | 2651 | err = regulatory_hint_core(cfg80211_regdomain->alpha2); |
2253 | if (err) { | 2652 | if (err) { |
diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 3362c7c069b2..b26224a9f3bc 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h | |||
@@ -41,15 +41,44 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, | |||
41 | * regulatory_hint_11d - hints a country IE as a regulatory domain | 41 | * regulatory_hint_11d - hints a country IE as a regulatory domain |
42 | * @wiphy: the wireless device giving the hint (used only for reporting | 42 | * @wiphy: the wireless device giving the hint (used only for reporting |
43 | * conflicts) | 43 | * conflicts) |
44 | * @band: the band on which the country IE was received on. This determines | ||
45 | * the band we'll process the country IE channel triplets for. | ||
44 | * @country_ie: pointer to the country IE | 46 | * @country_ie: pointer to the country IE |
45 | * @country_ie_len: length of the country IE | 47 | * @country_ie_len: length of the country IE |
46 | * | 48 | * |
47 | * We will intersect the rd with the what CRDA tells us should apply | 49 | * We will intersect the rd with the what CRDA tells us should apply |
48 | * for the alpha2 this country IE belongs to, this prevents APs from | 50 | * for the alpha2 this country IE belongs to, this prevents APs from |
49 | * sending us incorrect or outdated information against a country. | 51 | * sending us incorrect or outdated information against a country. |
52 | * | ||
53 | * The AP is expected to provide Country IE channel triplets for the | ||
54 | * band it is on. It is technically possible for APs to send channel | ||
55 | * country IE triplets even for channels outside of the band they are | ||
56 | * in but for that they would have to use the regulatory extension | ||
57 | * in combination with a triplet but this behaviour is currently | ||
58 | * not observed. For this reason if a triplet is seen with channel | ||
59 | * information for a band the BSS is not present in it will be ignored. | ||
50 | */ | 60 | */ |
51 | void regulatory_hint_11d(struct wiphy *wiphy, | 61 | void regulatory_hint_11d(struct wiphy *wiphy, |
62 | enum ieee80211_band band, | ||
52 | u8 *country_ie, | 63 | u8 *country_ie, |
53 | u8 country_ie_len); | 64 | u8 country_ie_len); |
54 | 65 | ||
66 | /** | ||
67 | * regulatory_hint_disconnect - informs all devices have been disconneted | ||
68 | * | ||
69 | * Regulotory rules can be enhanced further upon scanning and upon | ||
70 | * connection to an AP. These rules become stale if we disconnect | ||
71 | * and go to another country, whether or not we suspend and resume. | ||
72 | * If we suspend, go to another country and resume we'll automatically | ||
73 | * get disconnected shortly after resuming and things will be reset as well. | ||
74 | * This routine is a helper to restore regulatory settings to how they were | ||
75 | * prior to our first connect attempt. This includes ignoring country IE and | ||
76 | * beacon regulatory hints. The ieee80211_regdom module parameter will always | ||
77 | * be respected but if a user had set the regulatory domain that will take | ||
78 | * precedence. | ||
79 | * | ||
80 | * Must be called from process context. | ||
81 | */ | ||
82 | void regulatory_hint_disconnect(void); | ||
83 | |||
55 | #endif /* __NET_WIRELESS_REG_H */ | 84 | #endif /* __NET_WIRELESS_REG_H */ |
diff --git a/net/wireless/regdb.h b/net/wireless/regdb.h new file mode 100644 index 000000000000..818222c92513 --- /dev/null +++ b/net/wireless/regdb.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #ifndef __REGDB_H__ | ||
2 | #define __REGDB_H__ | ||
3 | |||
4 | extern const struct ieee80211_regdomain *reg_regdb[]; | ||
5 | extern int reg_regdb_size; | ||
6 | |||
7 | #endif /* __REGDB_H__ */ | ||
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 0c2cbbebca95..978cac3414b5 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
@@ -100,8 +100,10 @@ static void bss_release(struct kref *ref) | |||
100 | if (bss->pub.free_priv) | 100 | if (bss->pub.free_priv) |
101 | bss->pub.free_priv(&bss->pub); | 101 | bss->pub.free_priv(&bss->pub); |
102 | 102 | ||
103 | if (bss->ies_allocated) | 103 | if (bss->beacon_ies_allocated) |
104 | kfree(bss->pub.information_elements); | 104 | kfree(bss->pub.beacon_ies); |
105 | if (bss->proberesp_ies_allocated) | ||
106 | kfree(bss->pub.proberesp_ies); | ||
105 | 107 | ||
106 | BUG_ON(atomic_read(&bss->hold)); | 108 | BUG_ON(atomic_read(&bss->hold)); |
107 | 109 | ||
@@ -141,9 +143,9 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev) | |||
141 | dev->bss_generation++; | 143 | dev->bss_generation++; |
142 | } | 144 | } |
143 | 145 | ||
144 | static u8 *find_ie(u8 num, u8 *ies, int len) | 146 | const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) |
145 | { | 147 | { |
146 | while (len > 2 && ies[0] != num) { | 148 | while (len > 2 && ies[0] != eid) { |
147 | len -= ies[1] + 2; | 149 | len -= ies[1] + 2; |
148 | ies += ies[1] + 2; | 150 | ies += ies[1] + 2; |
149 | } | 151 | } |
@@ -153,11 +155,12 @@ static u8 *find_ie(u8 num, u8 *ies, int len) | |||
153 | return NULL; | 155 | return NULL; |
154 | return ies; | 156 | return ies; |
155 | } | 157 | } |
158 | EXPORT_SYMBOL(cfg80211_find_ie); | ||
156 | 159 | ||
157 | static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) | 160 | static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) |
158 | { | 161 | { |
159 | const u8 *ie1 = find_ie(num, ies1, len1); | 162 | const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); |
160 | const u8 *ie2 = find_ie(num, ies2, len2); | 163 | const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); |
161 | int r; | 164 | int r; |
162 | 165 | ||
163 | if (!ie1 && !ie2) | 166 | if (!ie1 && !ie2) |
@@ -183,9 +186,9 @@ static bool is_bss(struct cfg80211_bss *a, | |||
183 | if (!ssid) | 186 | if (!ssid) |
184 | return true; | 187 | return true; |
185 | 188 | ||
186 | ssidie = find_ie(WLAN_EID_SSID, | 189 | ssidie = cfg80211_find_ie(WLAN_EID_SSID, |
187 | a->information_elements, | 190 | a->information_elements, |
188 | a->len_information_elements); | 191 | a->len_information_elements); |
189 | if (!ssidie) | 192 | if (!ssidie) |
190 | return false; | 193 | return false; |
191 | if (ssidie[1] != ssid_len) | 194 | if (ssidie[1] != ssid_len) |
@@ -202,9 +205,9 @@ static bool is_mesh(struct cfg80211_bss *a, | |||
202 | if (!is_zero_ether_addr(a->bssid)) | 205 | if (!is_zero_ether_addr(a->bssid)) |
203 | return false; | 206 | return false; |
204 | 207 | ||
205 | ie = find_ie(WLAN_EID_MESH_ID, | 208 | ie = cfg80211_find_ie(WLAN_EID_MESH_ID, |
206 | a->information_elements, | 209 | a->information_elements, |
207 | a->len_information_elements); | 210 | a->len_information_elements); |
208 | if (!ie) | 211 | if (!ie) |
209 | return false; | 212 | return false; |
210 | if (ie[1] != meshidlen) | 213 | if (ie[1] != meshidlen) |
@@ -212,9 +215,9 @@ static bool is_mesh(struct cfg80211_bss *a, | |||
212 | if (memcmp(ie + 2, meshid, meshidlen)) | 215 | if (memcmp(ie + 2, meshid, meshidlen)) |
213 | return false; | 216 | return false; |
214 | 217 | ||
215 | ie = find_ie(WLAN_EID_MESH_CONFIG, | 218 | ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, |
216 | a->information_elements, | 219 | a->information_elements, |
217 | a->len_information_elements); | 220 | a->len_information_elements); |
218 | if (!ie) | 221 | if (!ie) |
219 | return false; | 222 | return false; |
220 | if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) | 223 | if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) |
@@ -375,8 +378,7 @@ rb_find_bss(struct cfg80211_registered_device *dev, | |||
375 | 378 | ||
376 | static struct cfg80211_internal_bss * | 379 | static struct cfg80211_internal_bss * |
377 | cfg80211_bss_update(struct cfg80211_registered_device *dev, | 380 | cfg80211_bss_update(struct cfg80211_registered_device *dev, |
378 | struct cfg80211_internal_bss *res, | 381 | struct cfg80211_internal_bss *res) |
379 | bool overwrite) | ||
380 | { | 382 | { |
381 | struct cfg80211_internal_bss *found = NULL; | 383 | struct cfg80211_internal_bss *found = NULL; |
382 | const u8 *meshid, *meshcfg; | 384 | const u8 *meshid, *meshcfg; |
@@ -394,11 +396,12 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
394 | 396 | ||
395 | if (is_zero_ether_addr(res->pub.bssid)) { | 397 | if (is_zero_ether_addr(res->pub.bssid)) { |
396 | /* must be mesh, verify */ | 398 | /* must be mesh, verify */ |
397 | meshid = find_ie(WLAN_EID_MESH_ID, res->pub.information_elements, | 399 | meshid = cfg80211_find_ie(WLAN_EID_MESH_ID, |
398 | res->pub.len_information_elements); | 400 | res->pub.information_elements, |
399 | meshcfg = find_ie(WLAN_EID_MESH_CONFIG, | 401 | res->pub.len_information_elements); |
400 | res->pub.information_elements, | 402 | meshcfg = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, |
401 | res->pub.len_information_elements); | 403 | res->pub.information_elements, |
404 | res->pub.len_information_elements); | ||
402 | if (!meshid || !meshcfg || | 405 | if (!meshid || !meshcfg || |
403 | meshcfg[1] != sizeof(struct ieee80211_meshconf_ie)) { | 406 | meshcfg[1] != sizeof(struct ieee80211_meshconf_ie)) { |
404 | /* bogus mesh */ | 407 | /* bogus mesh */ |
@@ -418,28 +421,64 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
418 | found->pub.capability = res->pub.capability; | 421 | found->pub.capability = res->pub.capability; |
419 | found->ts = res->ts; | 422 | found->ts = res->ts; |
420 | 423 | ||
421 | /* overwrite IEs */ | 424 | /* Update IEs */ |
422 | if (overwrite) { | 425 | if (res->pub.proberesp_ies) { |
423 | size_t used = dev->wiphy.bss_priv_size + sizeof(*res); | 426 | size_t used = dev->wiphy.bss_priv_size + sizeof(*res); |
424 | size_t ielen = res->pub.len_information_elements; | 427 | size_t ielen = res->pub.len_proberesp_ies; |
428 | |||
429 | if (found->pub.proberesp_ies && | ||
430 | !found->proberesp_ies_allocated && | ||
431 | ksize(found) >= used + ielen) { | ||
432 | memcpy(found->pub.proberesp_ies, | ||
433 | res->pub.proberesp_ies, ielen); | ||
434 | found->pub.len_proberesp_ies = ielen; | ||
435 | } else { | ||
436 | u8 *ies = found->pub.proberesp_ies; | ||
437 | |||
438 | if (found->proberesp_ies_allocated) | ||
439 | ies = krealloc(ies, ielen, GFP_ATOMIC); | ||
440 | else | ||
441 | ies = kmalloc(ielen, GFP_ATOMIC); | ||
442 | |||
443 | if (ies) { | ||
444 | memcpy(ies, res->pub.proberesp_ies, | ||
445 | ielen); | ||
446 | found->proberesp_ies_allocated = true; | ||
447 | found->pub.proberesp_ies = ies; | ||
448 | found->pub.len_proberesp_ies = ielen; | ||
449 | } | ||
450 | } | ||
425 | 451 | ||
426 | if (!found->ies_allocated && ksize(found) >= used + ielen) { | 452 | /* Override possible earlier Beacon frame IEs */ |
427 | memcpy(found->pub.information_elements, | 453 | found->pub.information_elements = |
428 | res->pub.information_elements, ielen); | 454 | found->pub.proberesp_ies; |
429 | found->pub.len_information_elements = ielen; | 455 | found->pub.len_information_elements = |
456 | found->pub.len_proberesp_ies; | ||
457 | } | ||
458 | if (res->pub.beacon_ies) { | ||
459 | size_t used = dev->wiphy.bss_priv_size + sizeof(*res); | ||
460 | size_t ielen = res->pub.len_beacon_ies; | ||
461 | |||
462 | if (found->pub.beacon_ies && | ||
463 | !found->beacon_ies_allocated && | ||
464 | ksize(found) >= used + ielen) { | ||
465 | memcpy(found->pub.beacon_ies, | ||
466 | res->pub.beacon_ies, ielen); | ||
467 | found->pub.len_beacon_ies = ielen; | ||
430 | } else { | 468 | } else { |
431 | u8 *ies = found->pub.information_elements; | 469 | u8 *ies = found->pub.beacon_ies; |
432 | 470 | ||
433 | if (found->ies_allocated) | 471 | if (found->beacon_ies_allocated) |
434 | ies = krealloc(ies, ielen, GFP_ATOMIC); | 472 | ies = krealloc(ies, ielen, GFP_ATOMIC); |
435 | else | 473 | else |
436 | ies = kmalloc(ielen, GFP_ATOMIC); | 474 | ies = kmalloc(ielen, GFP_ATOMIC); |
437 | 475 | ||
438 | if (ies) { | 476 | if (ies) { |
439 | memcpy(ies, res->pub.information_elements, ielen); | 477 | memcpy(ies, res->pub.beacon_ies, |
440 | found->ies_allocated = true; | 478 | ielen); |
441 | found->pub.information_elements = ies; | 479 | found->beacon_ies_allocated = true; |
442 | found->pub.len_information_elements = ielen; | 480 | found->pub.beacon_ies = ies; |
481 | found->pub.len_beacon_ies = ielen; | ||
443 | } | 482 | } |
444 | } | 483 | } |
445 | } | 484 | } |
@@ -489,14 +528,26 @@ cfg80211_inform_bss(struct wiphy *wiphy, | |||
489 | res->pub.tsf = timestamp; | 528 | res->pub.tsf = timestamp; |
490 | res->pub.beacon_interval = beacon_interval; | 529 | res->pub.beacon_interval = beacon_interval; |
491 | res->pub.capability = capability; | 530 | res->pub.capability = capability; |
492 | /* point to after the private area */ | 531 | /* |
493 | res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz; | 532 | * Since we do not know here whether the IEs are from a Beacon or Probe |
494 | memcpy(res->pub.information_elements, ie, ielen); | 533 | * Response frame, we need to pick one of the options and only use it |
495 | res->pub.len_information_elements = ielen; | 534 | * with the driver that does not provide the full Beacon/Probe Response |
535 | * frame. Use Beacon frame pointer to avoid indicating that this should | ||
536 | * override the information_elements pointer should we have received an | ||
537 | * earlier indication of Probe Response data. | ||
538 | * | ||
539 | * The initial buffer for the IEs is allocated with the BSS entry and | ||
540 | * is located after the private area. | ||
541 | */ | ||
542 | res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz; | ||
543 | memcpy(res->pub.beacon_ies, ie, ielen); | ||
544 | res->pub.len_beacon_ies = ielen; | ||
545 | res->pub.information_elements = res->pub.beacon_ies; | ||
546 | res->pub.len_information_elements = res->pub.len_beacon_ies; | ||
496 | 547 | ||
497 | kref_init(&res->ref); | 548 | kref_init(&res->ref); |
498 | 549 | ||
499 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, 0); | 550 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); |
500 | if (!res) | 551 | if (!res) |
501 | return NULL; | 552 | return NULL; |
502 | 553 | ||
@@ -517,7 +568,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
517 | struct cfg80211_internal_bss *res; | 568 | struct cfg80211_internal_bss *res; |
518 | size_t ielen = len - offsetof(struct ieee80211_mgmt, | 569 | size_t ielen = len - offsetof(struct ieee80211_mgmt, |
519 | u.probe_resp.variable); | 570 | u.probe_resp.variable); |
520 | bool overwrite; | ||
521 | size_t privsz = wiphy->bss_priv_size; | 571 | size_t privsz = wiphy->bss_priv_size; |
522 | 572 | ||
523 | if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC && | 573 | if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC && |
@@ -538,16 +588,28 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
538 | res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); | 588 | res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); |
539 | res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); | 589 | res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); |
540 | res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); | 590 | res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); |
541 | /* point to after the private area */ | 591 | /* |
542 | res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz; | 592 | * The initial buffer for the IEs is allocated with the BSS entry and |
543 | memcpy(res->pub.information_elements, mgmt->u.probe_resp.variable, ielen); | 593 | * is located after the private area. |
544 | res->pub.len_information_elements = ielen; | 594 | */ |
595 | if (ieee80211_is_probe_resp(mgmt->frame_control)) { | ||
596 | res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz; | ||
597 | memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable, | ||
598 | ielen); | ||
599 | res->pub.len_proberesp_ies = ielen; | ||
600 | res->pub.information_elements = res->pub.proberesp_ies; | ||
601 | res->pub.len_information_elements = res->pub.len_proberesp_ies; | ||
602 | } else { | ||
603 | res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz; | ||
604 | memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen); | ||
605 | res->pub.len_beacon_ies = ielen; | ||
606 | res->pub.information_elements = res->pub.beacon_ies; | ||
607 | res->pub.len_information_elements = res->pub.len_beacon_ies; | ||
608 | } | ||
545 | 609 | ||
546 | kref_init(&res->ref); | 610 | kref_init(&res->ref); |
547 | 611 | ||
548 | overwrite = ieee80211_is_probe_resp(mgmt->frame_control); | 612 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); |
549 | |||
550 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite); | ||
551 | if (!res) | 613 | if (!res) |
552 | return NULL; | 614 | return NULL; |
553 | 615 | ||
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index dc0fc4989d54..17fde0da1b08 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
@@ -34,6 +34,44 @@ struct cfg80211_conn { | |||
34 | bool auto_auth, prev_bssid_valid; | 34 | bool auto_auth, prev_bssid_valid; |
35 | }; | 35 | }; |
36 | 36 | ||
37 | bool cfg80211_is_all_idle(void) | ||
38 | { | ||
39 | struct cfg80211_registered_device *rdev; | ||
40 | struct wireless_dev *wdev; | ||
41 | bool is_all_idle = true; | ||
42 | |||
43 | mutex_lock(&cfg80211_mutex); | ||
44 | |||
45 | /* | ||
46 | * All devices must be idle as otherwise if you are actively | ||
47 | * scanning some new beacon hints could be learned and would | ||
48 | * count as new regulatory hints. | ||
49 | */ | ||
50 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | ||
51 | cfg80211_lock_rdev(rdev); | ||
52 | list_for_each_entry(wdev, &rdev->netdev_list, list) { | ||
53 | wdev_lock(wdev); | ||
54 | if (wdev->sme_state != CFG80211_SME_IDLE) | ||
55 | is_all_idle = false; | ||
56 | wdev_unlock(wdev); | ||
57 | } | ||
58 | cfg80211_unlock_rdev(rdev); | ||
59 | } | ||
60 | |||
61 | mutex_unlock(&cfg80211_mutex); | ||
62 | |||
63 | return is_all_idle; | ||
64 | } | ||
65 | |||
66 | static void disconnect_work(struct work_struct *work) | ||
67 | { | ||
68 | if (!cfg80211_is_all_idle()) | ||
69 | return; | ||
70 | |||
71 | regulatory_hint_disconnect(); | ||
72 | } | ||
73 | |||
74 | static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); | ||
37 | 75 | ||
38 | static int cfg80211_conn_scan(struct wireless_dev *wdev) | 76 | static int cfg80211_conn_scan(struct wireless_dev *wdev) |
39 | { | 77 | { |
@@ -454,6 +492,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
454 | * - and country_ie[1] which is the IE length | 492 | * - and country_ie[1] which is the IE length |
455 | */ | 493 | */ |
456 | regulatory_hint_11d(wdev->wiphy, | 494 | regulatory_hint_11d(wdev->wiphy, |
495 | bss->channel->band, | ||
457 | country_ie + 2, | 496 | country_ie + 2, |
458 | country_ie[1]); | 497 | country_ie[1]); |
459 | } | 498 | } |
@@ -657,6 +696,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, | |||
657 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | 696 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); |
658 | wdev->wext.connect.ssid_len = 0; | 697 | wdev->wext.connect.ssid_len = 0; |
659 | #endif | 698 | #endif |
699 | |||
700 | schedule_work(&cfg80211_disconnect_work); | ||
660 | } | 701 | } |
661 | 702 | ||
662 | void cfg80211_disconnected(struct net_device *dev, u16 reason, | 703 | void cfg80211_disconnected(struct net_device *dev, u16 reason, |
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index efe3c5c92b2d..9f2cef3e0ca0 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c | |||
@@ -33,10 +33,30 @@ static ssize_t name ## _show(struct device *dev, \ | |||
33 | 33 | ||
34 | SHOW_FMT(index, "%d", wiphy_idx); | 34 | SHOW_FMT(index, "%d", wiphy_idx); |
35 | SHOW_FMT(macaddress, "%pM", wiphy.perm_addr); | 35 | SHOW_FMT(macaddress, "%pM", wiphy.perm_addr); |
36 | SHOW_FMT(address_mask, "%pM", wiphy.addr_mask); | ||
37 | |||
38 | static ssize_t addresses_show(struct device *dev, | ||
39 | struct device_attribute *attr, | ||
40 | char *buf) | ||
41 | { | ||
42 | struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy; | ||
43 | char *start = buf; | ||
44 | int i; | ||
45 | |||
46 | if (!wiphy->addresses) | ||
47 | return sprintf(buf, "%pM\n", wiphy->perm_addr); | ||
48 | |||
49 | for (i = 0; i < wiphy->n_addresses; i++) | ||
50 | buf += sprintf(buf, "%pM\n", &wiphy->addresses[i].addr); | ||
51 | |||
52 | return buf - start; | ||
53 | } | ||
36 | 54 | ||
37 | static struct device_attribute ieee80211_dev_attrs[] = { | 55 | static struct device_attribute ieee80211_dev_attrs[] = { |
38 | __ATTR_RO(index), | 56 | __ATTR_RO(index), |
39 | __ATTR_RO(macaddress), | 57 | __ATTR_RO(macaddress), |
58 | __ATTR_RO(address_mask), | ||
59 | __ATTR_RO(addresses), | ||
40 | {} | 60 | {} |
41 | }; | 61 | }; |
42 | 62 | ||
diff --git a/net/wireless/util.c b/net/wireless/util.c index 59361fdcb5d0..be2ab8c59e3a 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -227,8 +227,11 @@ unsigned int ieee80211_hdrlen(__le16 fc) | |||
227 | if (ieee80211_is_data(fc)) { | 227 | if (ieee80211_is_data(fc)) { |
228 | if (ieee80211_has_a4(fc)) | 228 | if (ieee80211_has_a4(fc)) |
229 | hdrlen = 30; | 229 | hdrlen = 30; |
230 | if (ieee80211_is_data_qos(fc)) | 230 | if (ieee80211_is_data_qos(fc)) { |
231 | hdrlen += IEEE80211_QOS_CTL_LEN; | 231 | hdrlen += IEEE80211_QOS_CTL_LEN; |
232 | if (ieee80211_has_order(fc)) | ||
233 | hdrlen += IEEE80211_HT_CTL_LEN; | ||
234 | } | ||
232 | goto out; | 235 | goto out; |
233 | } | 236 | } |
234 | 237 | ||
@@ -285,7 +288,7 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) | |||
285 | } | 288 | } |
286 | } | 289 | } |
287 | 290 | ||
288 | int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, | 291 | int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, |
289 | enum nl80211_iftype iftype) | 292 | enum nl80211_iftype iftype) |
290 | { | 293 | { |
291 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | 294 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
@@ -383,7 +386,7 @@ int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, | |||
383 | } | 386 | } |
384 | EXPORT_SYMBOL(ieee80211_data_to_8023); | 387 | EXPORT_SYMBOL(ieee80211_data_to_8023); |
385 | 388 | ||
386 | int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, | 389 | int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, |
387 | enum nl80211_iftype iftype, u8 *bssid, bool qos) | 390 | enum nl80211_iftype iftype, u8 *bssid, bool qos) |
388 | { | 391 | { |
389 | struct ieee80211_hdr hdr; | 392 | struct ieee80211_hdr hdr; |
@@ -497,6 +500,101 @@ int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, | |||
497 | } | 500 | } |
498 | EXPORT_SYMBOL(ieee80211_data_from_8023); | 501 | EXPORT_SYMBOL(ieee80211_data_from_8023); |
499 | 502 | ||
503 | |||
504 | void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, | ||
505 | const u8 *addr, enum nl80211_iftype iftype, | ||
506 | const unsigned int extra_headroom) | ||
507 | { | ||
508 | struct sk_buff *frame = NULL; | ||
509 | u16 ethertype; | ||
510 | u8 *payload; | ||
511 | const struct ethhdr *eth; | ||
512 | int remaining, err; | ||
513 | u8 dst[ETH_ALEN], src[ETH_ALEN]; | ||
514 | |||
515 | err = ieee80211_data_to_8023(skb, addr, iftype); | ||
516 | if (err) | ||
517 | goto out; | ||
518 | |||
519 | /* skip the wrapping header */ | ||
520 | eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); | ||
521 | if (!eth) | ||
522 | goto out; | ||
523 | |||
524 | while (skb != frame) { | ||
525 | u8 padding; | ||
526 | __be16 len = eth->h_proto; | ||
527 | unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len); | ||
528 | |||
529 | remaining = skb->len; | ||
530 | memcpy(dst, eth->h_dest, ETH_ALEN); | ||
531 | memcpy(src, eth->h_source, ETH_ALEN); | ||
532 | |||
533 | padding = (4 - subframe_len) & 0x3; | ||
534 | /* the last MSDU has no padding */ | ||
535 | if (subframe_len > remaining) | ||
536 | goto purge; | ||
537 | |||
538 | skb_pull(skb, sizeof(struct ethhdr)); | ||
539 | /* reuse skb for the last subframe */ | ||
540 | if (remaining <= subframe_len + padding) | ||
541 | frame = skb; | ||
542 | else { | ||
543 | unsigned int hlen = ALIGN(extra_headroom, 4); | ||
544 | /* | ||
545 | * Allocate and reserve two bytes more for payload | ||
546 | * alignment since sizeof(struct ethhdr) is 14. | ||
547 | */ | ||
548 | frame = dev_alloc_skb(hlen + subframe_len + 2); | ||
549 | if (!frame) | ||
550 | goto purge; | ||
551 | |||
552 | skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2); | ||
553 | memcpy(skb_put(frame, ntohs(len)), skb->data, | ||
554 | ntohs(len)); | ||
555 | |||
556 | eth = (struct ethhdr *)skb_pull(skb, ntohs(len) + | ||
557 | padding); | ||
558 | if (!eth) { | ||
559 | dev_kfree_skb(frame); | ||
560 | goto purge; | ||
561 | } | ||
562 | } | ||
563 | |||
564 | skb_reset_network_header(frame); | ||
565 | frame->dev = skb->dev; | ||
566 | frame->priority = skb->priority; | ||
567 | |||
568 | payload = frame->data; | ||
569 | ethertype = (payload[6] << 8) | payload[7]; | ||
570 | |||
571 | if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && | ||
572 | ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || | ||
573 | compare_ether_addr(payload, | ||
574 | bridge_tunnel_header) == 0)) { | ||
575 | /* remove RFC1042 or Bridge-Tunnel | ||
576 | * encapsulation and replace EtherType */ | ||
577 | skb_pull(frame, 6); | ||
578 | memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); | ||
579 | memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); | ||
580 | } else { | ||
581 | memcpy(skb_push(frame, sizeof(__be16)), &len, | ||
582 | sizeof(__be16)); | ||
583 | memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); | ||
584 | memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); | ||
585 | } | ||
586 | __skb_queue_tail(list, frame); | ||
587 | } | ||
588 | |||
589 | return; | ||
590 | |||
591 | purge: | ||
592 | __skb_queue_purge(list); | ||
593 | out: | ||
594 | dev_kfree_skb(skb); | ||
595 | } | ||
596 | EXPORT_SYMBOL(ieee80211_amsdu_to_8023s); | ||
597 | |||
500 | /* Given a data frame determine the 802.1p/1d tag to use. */ | 598 | /* Given a data frame determine the 802.1p/1d tag to use. */ |
501 | unsigned int cfg80211_classify8021d(struct sk_buff *skb) | 599 | unsigned int cfg80211_classify8021d(struct sk_buff *skb) |
502 | { | 600 | { |
@@ -720,3 +818,36 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, | |||
720 | 818 | ||
721 | return err; | 819 | return err; |
722 | } | 820 | } |
821 | |||
822 | u16 cfg80211_calculate_bitrate(struct rate_info *rate) | ||
823 | { | ||
824 | int modulation, streams, bitrate; | ||
825 | |||
826 | if (!(rate->flags & RATE_INFO_FLAGS_MCS)) | ||
827 | return rate->legacy; | ||
828 | |||
829 | /* the formula below does only work for MCS values smaller than 32 */ | ||
830 | if (rate->mcs >= 32) | ||
831 | return 0; | ||
832 | |||
833 | modulation = rate->mcs & 7; | ||
834 | streams = (rate->mcs >> 3) + 1; | ||
835 | |||
836 | bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? | ||
837 | 13500000 : 6500000; | ||
838 | |||
839 | if (modulation < 4) | ||
840 | bitrate *= (modulation + 1); | ||
841 | else if (modulation == 4) | ||
842 | bitrate *= (modulation + 2); | ||
843 | else | ||
844 | bitrate *= (modulation + 3); | ||
845 | |||
846 | bitrate *= streams; | ||
847 | |||
848 | if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) | ||
849 | bitrate = (bitrate / 9) * 10; | ||
850 | |||
851 | /* do NOT round down here */ | ||
852 | return (bitrate + 50000) / 100000; | ||
853 | } | ||
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 54face3d4424..9ab51838849e 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c | |||
@@ -1099,8 +1099,8 @@ int cfg80211_wext_siwpower(struct net_device *dev, | |||
1099 | { | 1099 | { |
1100 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 1100 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
1101 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); | 1101 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); |
1102 | bool ps = wdev->wext.ps; | 1102 | bool ps = wdev->ps; |
1103 | int timeout = wdev->wext.ps_timeout; | 1103 | int timeout = wdev->ps_timeout; |
1104 | int err; | 1104 | int err; |
1105 | 1105 | ||
1106 | if (wdev->iftype != NL80211_IFTYPE_STATION) | 1106 | if (wdev->iftype != NL80211_IFTYPE_STATION) |
@@ -1133,8 +1133,8 @@ int cfg80211_wext_siwpower(struct net_device *dev, | |||
1133 | if (err) | 1133 | if (err) |
1134 | return err; | 1134 | return err; |
1135 | 1135 | ||
1136 | wdev->wext.ps = ps; | 1136 | wdev->ps = ps; |
1137 | wdev->wext.ps_timeout = timeout; | 1137 | wdev->ps_timeout = timeout; |
1138 | 1138 | ||
1139 | return 0; | 1139 | return 0; |
1140 | 1140 | ||
@@ -1147,7 +1147,7 @@ int cfg80211_wext_giwpower(struct net_device *dev, | |||
1147 | { | 1147 | { |
1148 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 1148 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
1149 | 1149 | ||
1150 | wrq->disabled = !wdev->wext.ps; | 1150 | wrq->disabled = !wdev->ps; |
1151 | 1151 | ||
1152 | return 0; | 1152 | return 0; |
1153 | } | 1153 | } |
@@ -1204,21 +1204,47 @@ int cfg80211_wext_siwrate(struct net_device *dev, | |||
1204 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 1204 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
1205 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); | 1205 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); |
1206 | struct cfg80211_bitrate_mask mask; | 1206 | struct cfg80211_bitrate_mask mask; |
1207 | u32 fixed, maxrate; | ||
1208 | struct ieee80211_supported_band *sband; | ||
1209 | int band, ridx; | ||
1210 | bool match = false; | ||
1207 | 1211 | ||
1208 | if (!rdev->ops->set_bitrate_mask) | 1212 | if (!rdev->ops->set_bitrate_mask) |
1209 | return -EOPNOTSUPP; | 1213 | return -EOPNOTSUPP; |
1210 | 1214 | ||
1211 | mask.fixed = 0; | 1215 | memset(&mask, 0, sizeof(mask)); |
1212 | mask.maxrate = 0; | 1216 | fixed = 0; |
1217 | maxrate = (u32)-1; | ||
1213 | 1218 | ||
1214 | if (rate->value < 0) { | 1219 | if (rate->value < 0) { |
1215 | /* nothing */ | 1220 | /* nothing */ |
1216 | } else if (rate->fixed) { | 1221 | } else if (rate->fixed) { |
1217 | mask.fixed = rate->value / 1000; /* kbps */ | 1222 | fixed = rate->value / 100000; |
1218 | } else { | 1223 | } else { |
1219 | mask.maxrate = rate->value / 1000; /* kbps */ | 1224 | maxrate = rate->value / 100000; |
1220 | } | 1225 | } |
1221 | 1226 | ||
1227 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
1228 | sband = wdev->wiphy->bands[band]; | ||
1229 | if (sband == NULL) | ||
1230 | continue; | ||
1231 | for (ridx = 0; ridx < sband->n_bitrates; ridx++) { | ||
1232 | struct ieee80211_rate *srate = &sband->bitrates[ridx]; | ||
1233 | if (fixed == srate->bitrate) { | ||
1234 | mask.control[band].legacy = 1 << ridx; | ||
1235 | match = true; | ||
1236 | break; | ||
1237 | } | ||
1238 | if (srate->bitrate <= maxrate) { | ||
1239 | mask.control[band].legacy |= 1 << ridx; | ||
1240 | match = true; | ||
1241 | } | ||
1242 | } | ||
1243 | } | ||
1244 | |||
1245 | if (!match) | ||
1246 | return -EINVAL; | ||
1247 | |||
1222 | return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask); | 1248 | return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask); |
1223 | } | 1249 | } |
1224 | EXPORT_SYMBOL_GPL(cfg80211_wext_siwrate); | 1250 | EXPORT_SYMBOL_GPL(cfg80211_wext_siwrate); |
@@ -1257,10 +1283,7 @@ int cfg80211_wext_giwrate(struct net_device *dev, | |||
1257 | if (!(sinfo.filled & STATION_INFO_TX_BITRATE)) | 1283 | if (!(sinfo.filled & STATION_INFO_TX_BITRATE)) |
1258 | return -EOPNOTSUPP; | 1284 | return -EOPNOTSUPP; |
1259 | 1285 | ||
1260 | rate->value = 0; | 1286 | rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); |
1261 | |||
1262 | if (!(sinfo.txrate.flags & RATE_INFO_FLAGS_MCS)) | ||
1263 | rate->value = 100000 * sinfo.txrate.legacy; | ||
1264 | 1287 | ||
1265 | return 0; | 1288 | return 0; |
1266 | } | 1289 | } |
diff --git a/net/wireless/wext-proc.c b/net/wireless/wext-proc.c index 273a7f77c834..8bafa31fa9f8 100644 --- a/net/wireless/wext-proc.c +++ b/net/wireless/wext-proc.c | |||
@@ -140,7 +140,7 @@ static const struct file_operations wireless_seq_fops = { | |||
140 | .release = seq_release_net, | 140 | .release = seq_release_net, |
141 | }; | 141 | }; |
142 | 142 | ||
143 | int wext_proc_init(struct net *net) | 143 | int __net_init wext_proc_init(struct net *net) |
144 | { | 144 | { |
145 | /* Create /proc/net/wireless entry */ | 145 | /* Create /proc/net/wireless entry */ |
146 | if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops)) | 146 | if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops)) |
@@ -149,7 +149,7 @@ int wext_proc_init(struct net *net) | |||
149 | return 0; | 149 | return 0; |
150 | } | 150 | } |
151 | 151 | ||
152 | void wext_proc_exit(struct net *net) | 152 | void __net_exit wext_proc_exit(struct net *net) |
153 | { | 153 | { |
154 | proc_net_remove(net, "wireless"); | 154 | proc_net_remove(net, "wireless"); |
155 | } | 155 | } |