diff options
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/Kconfig | 30 | ||||
-rw-r--r-- | net/wireless/Makefile | 7 | ||||
-rw-r--r-- | net/wireless/core.c | 14 | ||||
-rw-r--r-- | net/wireless/core.h | 13 | ||||
-rw-r--r-- | net/wireless/lib80211.c | 284 | ||||
-rw-r--r-- | net/wireless/lib80211_crypt_ccmp.c | 492 | ||||
-rw-r--r-- | net/wireless/lib80211_crypt_tkip.c | 784 | ||||
-rw-r--r-- | net/wireless/lib80211_crypt_wep.c | 296 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 417 | ||||
-rw-r--r-- | net/wireless/reg.c | 1005 | ||||
-rw-r--r-- | net/wireless/reg.h | 33 | ||||
-rw-r--r-- | net/wireless/sysfs.c | 25 | ||||
-rw-r--r-- | net/wireless/util.c | 19 | ||||
-rw-r--r-- | net/wireless/wext-compat.c | 139 | ||||
-rw-r--r-- | net/wireless/wext.c | 2 |
15 files changed, 3281 insertions, 279 deletions
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 646c7121dbc0..e28e2b8fa436 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig | |||
@@ -1,6 +1,15 @@ | |||
1 | config CFG80211 | 1 | config CFG80211 |
2 | tristate "Improved wireless configuration API" | 2 | tristate "Improved wireless configuration API" |
3 | 3 | ||
4 | config CFG80211_REG_DEBUG | ||
5 | bool "cfg80211 regulatory debugging" | ||
6 | depends on CFG80211 | ||
7 | default n | ||
8 | ---help--- | ||
9 | You can enable this if you want to debug regulatory changes. | ||
10 | |||
11 | If unsure, say N. | ||
12 | |||
4 | config NL80211 | 13 | config NL80211 |
5 | bool "nl80211 new netlink interface support" | 14 | bool "nl80211 new netlink interface support" |
6 | depends on CFG80211 | 15 | depends on CFG80211 |
@@ -40,6 +49,8 @@ config WIRELESS_OLD_REGULATORY | |||
40 | ieee80211_regdom module parameter. This is being phased out and you | 49 | ieee80211_regdom module parameter. This is being phased out and you |
41 | should stop using them ASAP. | 50 | should stop using them ASAP. |
42 | 51 | ||
52 | Note: You will need CRDA if you want 802.11d support | ||
53 | |||
43 | Say Y unless you have installed a new userspace application. | 54 | Say Y unless you have installed a new userspace application. |
44 | Also say Y if have one currently depending on the ieee80211_regdom | 55 | Also say Y if have one currently depending on the ieee80211_regdom |
45 | module parameter and cannot port it to use the new userspace | 56 | module parameter and cannot port it to use the new userspace |
@@ -72,3 +83,22 @@ config WIRELESS_EXT_SYSFS | |||
72 | 83 | ||
73 | Say Y if you have programs using it, like old versions of | 84 | Say Y if you have programs using it, like old versions of |
74 | hal. | 85 | hal. |
86 | |||
87 | config LIB80211 | ||
88 | tristate "Common routines for IEEE802.11 drivers" | ||
89 | default n | ||
90 | help | ||
91 | This options enables a library of common routines used | ||
92 | by IEEE802.11 wireless LAN drivers. | ||
93 | |||
94 | Drivers should select this themselves if needed. Say Y if | ||
95 | you want this built into your kernel. | ||
96 | |||
97 | config LIB80211_CRYPT_WEP | ||
98 | tristate | ||
99 | |||
100 | config LIB80211_CRYPT_CCMP | ||
101 | tristate | ||
102 | |||
103 | config LIB80211_CRYPT_TKIP | ||
104 | tristate | ||
diff --git a/net/wireless/Makefile b/net/wireless/Makefile index b9f943c45f3b..938a334c8dbc 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile | |||
@@ -1,5 +1,12 @@ | |||
1 | obj-$(CONFIG_WIRELESS_EXT) += wext.o | 1 | obj-$(CONFIG_WIRELESS_EXT) += wext.o |
2 | obj-$(CONFIG_CFG80211) += cfg80211.o | 2 | obj-$(CONFIG_CFG80211) += cfg80211.o |
3 | obj-$(CONFIG_LIB80211) += lib80211.o | ||
4 | obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o | ||
5 | obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o | ||
6 | obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o | ||
3 | 7 | ||
4 | cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o | 8 | cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o |
9 | cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o | ||
5 | cfg80211-$(CONFIG_NL80211) += nl80211.o | 10 | cfg80211-$(CONFIG_NL80211) += nl80211.o |
11 | |||
12 | ccflags-y += -D__CHECK_ENDIAN__ | ||
diff --git a/net/wireless/core.c b/net/wireless/core.c index 5031db7b275b..b96fc0c3f1c4 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -19,7 +19,6 @@ | |||
19 | #include "nl80211.h" | 19 | #include "nl80211.h" |
20 | #include "core.h" | 20 | #include "core.h" |
21 | #include "sysfs.h" | 21 | #include "sysfs.h" |
22 | #include "reg.h" | ||
23 | 22 | ||
24 | /* name for sysfs, %d is appended */ | 23 | /* name for sysfs, %d is appended */ |
25 | #define PHY_NAME "phy" | 24 | #define PHY_NAME "phy" |
@@ -236,8 +235,7 @@ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv) | |||
236 | mutex_unlock(&cfg80211_drv_mutex); | 235 | mutex_unlock(&cfg80211_drv_mutex); |
237 | 236 | ||
238 | /* give it a proper name */ | 237 | /* give it a proper name */ |
239 | snprintf(drv->wiphy.dev.bus_id, BUS_ID_SIZE, | 238 | dev_set_name(&drv->wiphy.dev, PHY_NAME "%d", drv->idx); |
240 | PHY_NAME "%d", drv->idx); | ||
241 | 239 | ||
242 | mutex_init(&drv->mtx); | 240 | mutex_init(&drv->mtx); |
243 | mutex_init(&drv->devlist_mtx); | 241 | mutex_init(&drv->devlist_mtx); |
@@ -301,12 +299,10 @@ int wiphy_register(struct wiphy *wiphy) | |||
301 | /* check and set up bitrates */ | 299 | /* check and set up bitrates */ |
302 | ieee80211_set_bitrate_flags(wiphy); | 300 | ieee80211_set_bitrate_flags(wiphy); |
303 | 301 | ||
302 | mutex_lock(&cfg80211_drv_mutex); | ||
303 | |||
304 | /* set up regulatory info */ | 304 | /* set up regulatory info */ |
305 | mutex_lock(&cfg80211_reg_mutex); | ||
306 | wiphy_update_regulatory(wiphy, REGDOM_SET_BY_CORE); | 305 | wiphy_update_regulatory(wiphy, REGDOM_SET_BY_CORE); |
307 | mutex_unlock(&cfg80211_reg_mutex); | ||
308 | |||
309 | mutex_lock(&cfg80211_drv_mutex); | ||
310 | 306 | ||
311 | res = device_add(&drv->wiphy.dev); | 307 | res = device_add(&drv->wiphy.dev); |
312 | if (res) | 308 | if (res) |
@@ -351,6 +347,10 @@ void wiphy_unregister(struct wiphy *wiphy) | |||
351 | /* unlock again before freeing */ | 347 | /* unlock again before freeing */ |
352 | mutex_unlock(&drv->mtx); | 348 | mutex_unlock(&drv->mtx); |
353 | 349 | ||
350 | /* If this device got a regulatory hint tell core its | ||
351 | * free to listen now to a new shiny device regulatory hint */ | ||
352 | reg_device_remove(wiphy); | ||
353 | |||
354 | list_del(&drv->list); | 354 | list_del(&drv->list); |
355 | device_del(&drv->wiphy.dev); | 355 | device_del(&drv->wiphy.dev); |
356 | debugfs_remove(drv->wiphy.debugfsdir); | 356 | debugfs_remove(drv->wiphy.debugfsdir); |
diff --git a/net/wireless/core.h b/net/wireless/core.h index 771cc5cc7658..f7fb9f413028 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <net/genetlink.h> | 11 | #include <net/genetlink.h> |
12 | #include <net/wireless.h> | 12 | #include <net/wireless.h> |
13 | #include <net/cfg80211.h> | 13 | #include <net/cfg80211.h> |
14 | #include "reg.h" | ||
14 | 15 | ||
15 | struct cfg80211_registered_device { | 16 | struct cfg80211_registered_device { |
16 | struct cfg80211_ops *ops; | 17 | struct cfg80211_ops *ops; |
@@ -21,6 +22,18 @@ struct cfg80211_registered_device { | |||
21 | * any call is in progress */ | 22 | * any call is in progress */ |
22 | struct mutex mtx; | 23 | struct mutex mtx; |
23 | 24 | ||
25 | /* ISO / IEC 3166 alpha2 for which this device is receiving | ||
26 | * country IEs on, this can help disregard country IEs from APs | ||
27 | * on the same alpha2 quickly. The alpha2 may differ from | ||
28 | * cfg80211_regdomain's alpha2 when an intersection has occurred. | ||
29 | * If the AP is reconfigured this can also be used to tell us if | ||
30 | * the country on the country IE changed. */ | ||
31 | char country_ie_alpha2[2]; | ||
32 | |||
33 | /* If a Country IE has been received this tells us the environment | ||
34 | * which its telling us its in. This defaults to ENVIRON_ANY */ | ||
35 | enum environment_cap env; | ||
36 | |||
24 | /* wiphy index, internal only */ | 37 | /* wiphy index, internal only */ |
25 | int idx; | 38 | int idx; |
26 | 39 | ||
diff --git a/net/wireless/lib80211.c b/net/wireless/lib80211.c new file mode 100644 index 000000000000..97d411f74507 --- /dev/null +++ b/net/wireless/lib80211.c | |||
@@ -0,0 +1,284 @@ | |||
1 | /* | ||
2 | * lib80211 -- common bits for IEEE802.11 drivers | ||
3 | * | ||
4 | * Copyright(c) 2008 John W. Linville <linville@tuxdriver.com> | ||
5 | * | ||
6 | * Portions copied from old ieee80211 component, w/ original copyright | ||
7 | * notices below: | ||
8 | * | ||
9 | * Host AP crypto routines | ||
10 | * | ||
11 | * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> | ||
12 | * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com> | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/ctype.h> | ||
18 | #include <linux/ieee80211.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/string.h> | ||
23 | |||
24 | #include <net/lib80211.h> | ||
25 | |||
26 | #define DRV_NAME "lib80211" | ||
27 | |||
28 | #define DRV_DESCRIPTION "common routines for IEEE802.11 drivers" | ||
29 | |||
30 | MODULE_DESCRIPTION(DRV_DESCRIPTION); | ||
31 | MODULE_AUTHOR("John W. Linville <linville@tuxdriver.com>"); | ||
32 | MODULE_LICENSE("GPL"); | ||
33 | |||
34 | struct lib80211_crypto_alg { | ||
35 | struct list_head list; | ||
36 | struct lib80211_crypto_ops *ops; | ||
37 | }; | ||
38 | |||
39 | static LIST_HEAD(lib80211_crypto_algs); | ||
40 | static DEFINE_SPINLOCK(lib80211_crypto_lock); | ||
41 | |||
42 | const char *print_ssid(char *buf, const char *ssid, u8 ssid_len) | ||
43 | { | ||
44 | const char *s = ssid; | ||
45 | char *d = buf; | ||
46 | |||
47 | ssid_len = min_t(u8, ssid_len, IEEE80211_MAX_SSID_LEN); | ||
48 | while (ssid_len--) { | ||
49 | if (isprint(*s)) { | ||
50 | *d++ = *s++; | ||
51 | continue; | ||
52 | } | ||
53 | |||
54 | *d++ = '\\'; | ||
55 | if (*s == '\0') | ||
56 | *d++ = '0'; | ||
57 | else if (*s == '\n') | ||
58 | *d++ = 'n'; | ||
59 | else if (*s == '\r') | ||
60 | *d++ = 'r'; | ||
61 | else if (*s == '\t') | ||
62 | *d++ = 't'; | ||
63 | else if (*s == '\\') | ||
64 | *d++ = '\\'; | ||
65 | else | ||
66 | d += snprintf(d, 3, "%03o", *s); | ||
67 | s++; | ||
68 | } | ||
69 | *d = '\0'; | ||
70 | return buf; | ||
71 | } | ||
72 | EXPORT_SYMBOL(print_ssid); | ||
73 | |||
74 | int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name, | ||
75 | spinlock_t *lock) | ||
76 | { | ||
77 | memset(info, 0, sizeof(*info)); | ||
78 | |||
79 | info->name = name; | ||
80 | info->lock = lock; | ||
81 | |||
82 | INIT_LIST_HEAD(&info->crypt_deinit_list); | ||
83 | setup_timer(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler, | ||
84 | (unsigned long)info); | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | EXPORT_SYMBOL(lib80211_crypt_info_init); | ||
89 | |||
90 | void lib80211_crypt_info_free(struct lib80211_crypt_info *info) | ||
91 | { | ||
92 | int i; | ||
93 | |||
94 | lib80211_crypt_quiescing(info); | ||
95 | del_timer_sync(&info->crypt_deinit_timer); | ||
96 | lib80211_crypt_deinit_entries(info, 1); | ||
97 | |||
98 | for (i = 0; i < NUM_WEP_KEYS; i++) { | ||
99 | struct lib80211_crypt_data *crypt = info->crypt[i]; | ||
100 | if (crypt) { | ||
101 | if (crypt->ops) { | ||
102 | crypt->ops->deinit(crypt->priv); | ||
103 | module_put(crypt->ops->owner); | ||
104 | } | ||
105 | kfree(crypt); | ||
106 | info->crypt[i] = NULL; | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | EXPORT_SYMBOL(lib80211_crypt_info_free); | ||
111 | |||
112 | void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, int force) | ||
113 | { | ||
114 | struct lib80211_crypt_data *entry, *next; | ||
115 | unsigned long flags; | ||
116 | |||
117 | spin_lock_irqsave(info->lock, flags); | ||
118 | list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) { | ||
119 | if (atomic_read(&entry->refcnt) != 0 && !force) | ||
120 | continue; | ||
121 | |||
122 | list_del(&entry->list); | ||
123 | |||
124 | if (entry->ops) { | ||
125 | entry->ops->deinit(entry->priv); | ||
126 | module_put(entry->ops->owner); | ||
127 | } | ||
128 | kfree(entry); | ||
129 | } | ||
130 | spin_unlock_irqrestore(info->lock, flags); | ||
131 | } | ||
132 | EXPORT_SYMBOL(lib80211_crypt_deinit_entries); | ||
133 | |||
134 | /* After this, crypt_deinit_list won't accept new members */ | ||
135 | void lib80211_crypt_quiescing(struct lib80211_crypt_info *info) | ||
136 | { | ||
137 | unsigned long flags; | ||
138 | |||
139 | spin_lock_irqsave(info->lock, flags); | ||
140 | info->crypt_quiesced = 1; | ||
141 | spin_unlock_irqrestore(info->lock, flags); | ||
142 | } | ||
143 | EXPORT_SYMBOL(lib80211_crypt_quiescing); | ||
144 | |||
145 | void lib80211_crypt_deinit_handler(unsigned long data) | ||
146 | { | ||
147 | struct lib80211_crypt_info *info = (struct lib80211_crypt_info *)data; | ||
148 | unsigned long flags; | ||
149 | |||
150 | lib80211_crypt_deinit_entries(info, 0); | ||
151 | |||
152 | spin_lock_irqsave(info->lock, flags); | ||
153 | if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) { | ||
154 | printk(KERN_DEBUG "%s: entries remaining in delayed crypt " | ||
155 | "deletion list\n", info->name); | ||
156 | info->crypt_deinit_timer.expires = jiffies + HZ; | ||
157 | add_timer(&info->crypt_deinit_timer); | ||
158 | } | ||
159 | spin_unlock_irqrestore(info->lock, flags); | ||
160 | } | ||
161 | EXPORT_SYMBOL(lib80211_crypt_deinit_handler); | ||
162 | |||
163 | void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info, | ||
164 | struct lib80211_crypt_data **crypt) | ||
165 | { | ||
166 | struct lib80211_crypt_data *tmp; | ||
167 | unsigned long flags; | ||
168 | |||
169 | if (*crypt == NULL) | ||
170 | return; | ||
171 | |||
172 | tmp = *crypt; | ||
173 | *crypt = NULL; | ||
174 | |||
175 | /* must not run ops->deinit() while there may be pending encrypt or | ||
176 | * decrypt operations. Use a list of delayed deinits to avoid needing | ||
177 | * locking. */ | ||
178 | |||
179 | spin_lock_irqsave(info->lock, flags); | ||
180 | if (!info->crypt_quiesced) { | ||
181 | list_add(&tmp->list, &info->crypt_deinit_list); | ||
182 | if (!timer_pending(&info->crypt_deinit_timer)) { | ||
183 | info->crypt_deinit_timer.expires = jiffies + HZ; | ||
184 | add_timer(&info->crypt_deinit_timer); | ||
185 | } | ||
186 | } | ||
187 | spin_unlock_irqrestore(info->lock, flags); | ||
188 | } | ||
189 | EXPORT_SYMBOL(lib80211_crypt_delayed_deinit); | ||
190 | |||
191 | int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops) | ||
192 | { | ||
193 | unsigned long flags; | ||
194 | struct lib80211_crypto_alg *alg; | ||
195 | |||
196 | alg = kzalloc(sizeof(*alg), GFP_KERNEL); | ||
197 | if (alg == NULL) | ||
198 | return -ENOMEM; | ||
199 | |||
200 | alg->ops = ops; | ||
201 | |||
202 | spin_lock_irqsave(&lib80211_crypto_lock, flags); | ||
203 | list_add(&alg->list, &lib80211_crypto_algs); | ||
204 | spin_unlock_irqrestore(&lib80211_crypto_lock, flags); | ||
205 | |||
206 | printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'\n", | ||
207 | ops->name); | ||
208 | |||
209 | return 0; | ||
210 | } | ||
211 | EXPORT_SYMBOL(lib80211_register_crypto_ops); | ||
212 | |||
213 | int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops) | ||
214 | { | ||
215 | struct lib80211_crypto_alg *alg; | ||
216 | unsigned long flags; | ||
217 | |||
218 | spin_lock_irqsave(&lib80211_crypto_lock, flags); | ||
219 | list_for_each_entry(alg, &lib80211_crypto_algs, list) { | ||
220 | if (alg->ops == ops) | ||
221 | goto found; | ||
222 | } | ||
223 | spin_unlock_irqrestore(&lib80211_crypto_lock, flags); | ||
224 | return -EINVAL; | ||
225 | |||
226 | found: | ||
227 | printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm " | ||
228 | "'%s'\n", ops->name); | ||
229 | list_del(&alg->list); | ||
230 | spin_unlock_irqrestore(&lib80211_crypto_lock, flags); | ||
231 | kfree(alg); | ||
232 | return 0; | ||
233 | } | ||
234 | EXPORT_SYMBOL(lib80211_unregister_crypto_ops); | ||
235 | |||
236 | struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name) | ||
237 | { | ||
238 | struct lib80211_crypto_alg *alg; | ||
239 | unsigned long flags; | ||
240 | |||
241 | spin_lock_irqsave(&lib80211_crypto_lock, flags); | ||
242 | list_for_each_entry(alg, &lib80211_crypto_algs, list) { | ||
243 | if (strcmp(alg->ops->name, name) == 0) | ||
244 | goto found; | ||
245 | } | ||
246 | spin_unlock_irqrestore(&lib80211_crypto_lock, flags); | ||
247 | return NULL; | ||
248 | |||
249 | found: | ||
250 | spin_unlock_irqrestore(&lib80211_crypto_lock, flags); | ||
251 | return alg->ops; | ||
252 | } | ||
253 | EXPORT_SYMBOL(lib80211_get_crypto_ops); | ||
254 | |||
255 | static void *lib80211_crypt_null_init(int keyidx) | ||
256 | { | ||
257 | return (void *)1; | ||
258 | } | ||
259 | |||
260 | static void lib80211_crypt_null_deinit(void *priv) | ||
261 | { | ||
262 | } | ||
263 | |||
264 | static struct lib80211_crypto_ops lib80211_crypt_null = { | ||
265 | .name = "NULL", | ||
266 | .init = lib80211_crypt_null_init, | ||
267 | .deinit = lib80211_crypt_null_deinit, | ||
268 | .owner = THIS_MODULE, | ||
269 | }; | ||
270 | |||
271 | static int __init lib80211_init(void) | ||
272 | { | ||
273 | printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION "\n"); | ||
274 | return lib80211_register_crypto_ops(&lib80211_crypt_null); | ||
275 | } | ||
276 | |||
277 | static void __exit lib80211_exit(void) | ||
278 | { | ||
279 | lib80211_unregister_crypto_ops(&lib80211_crypt_null); | ||
280 | BUG_ON(!list_empty(&lib80211_crypto_algs)); | ||
281 | } | ||
282 | |||
283 | module_init(lib80211_init); | ||
284 | module_exit(lib80211_exit); | ||
diff --git a/net/wireless/lib80211_crypt_ccmp.c b/net/wireless/lib80211_crypt_ccmp.c new file mode 100644 index 000000000000..db428194c16a --- /dev/null +++ b/net/wireless/lib80211_crypt_ccmp.c | |||
@@ -0,0 +1,492 @@ | |||
1 | /* | ||
2 | * lib80211 crypt: host-based CCMP encryption implementation for lib80211 | ||
3 | * | ||
4 | * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi> | ||
5 | * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com> | ||
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. See README and COPYING for | ||
10 | * more details. | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/err.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/random.h> | ||
19 | #include <linux/skbuff.h> | ||
20 | #include <linux/netdevice.h> | ||
21 | #include <linux/if_ether.h> | ||
22 | #include <linux/if_arp.h> | ||
23 | #include <asm/string.h> | ||
24 | #include <linux/wireless.h> | ||
25 | |||
26 | #include <linux/ieee80211.h> | ||
27 | |||
28 | #include <linux/crypto.h> | ||
29 | |||
30 | #include <net/lib80211.h> | ||
31 | |||
32 | MODULE_AUTHOR("Jouni Malinen"); | ||
33 | MODULE_DESCRIPTION("Host AP crypt: CCMP"); | ||
34 | MODULE_LICENSE("GPL"); | ||
35 | |||
36 | #define AES_BLOCK_LEN 16 | ||
37 | #define CCMP_HDR_LEN 8 | ||
38 | #define CCMP_MIC_LEN 8 | ||
39 | #define CCMP_TK_LEN 16 | ||
40 | #define CCMP_PN_LEN 6 | ||
41 | |||
42 | struct lib80211_ccmp_data { | ||
43 | u8 key[CCMP_TK_LEN]; | ||
44 | int key_set; | ||
45 | |||
46 | u8 tx_pn[CCMP_PN_LEN]; | ||
47 | u8 rx_pn[CCMP_PN_LEN]; | ||
48 | |||
49 | u32 dot11RSNAStatsCCMPFormatErrors; | ||
50 | u32 dot11RSNAStatsCCMPReplays; | ||
51 | u32 dot11RSNAStatsCCMPDecryptErrors; | ||
52 | |||
53 | int key_idx; | ||
54 | |||
55 | struct crypto_cipher *tfm; | ||
56 | |||
57 | /* scratch buffers for virt_to_page() (crypto API) */ | ||
58 | u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN], | ||
59 | tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN]; | ||
60 | u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN]; | ||
61 | }; | ||
62 | |||
63 | static inline void lib80211_ccmp_aes_encrypt(struct crypto_cipher *tfm, | ||
64 | const u8 pt[16], u8 ct[16]) | ||
65 | { | ||
66 | crypto_cipher_encrypt_one(tfm, ct, pt); | ||
67 | } | ||
68 | |||
69 | static void *lib80211_ccmp_init(int key_idx) | ||
70 | { | ||
71 | struct lib80211_ccmp_data *priv; | ||
72 | |||
73 | priv = kzalloc(sizeof(*priv), GFP_ATOMIC); | ||
74 | if (priv == NULL) | ||
75 | goto fail; | ||
76 | priv->key_idx = key_idx; | ||
77 | |||
78 | priv->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); | ||
79 | if (IS_ERR(priv->tfm)) { | ||
80 | printk(KERN_DEBUG "lib80211_crypt_ccmp: could not allocate " | ||
81 | "crypto API aes\n"); | ||
82 | priv->tfm = NULL; | ||
83 | goto fail; | ||
84 | } | ||
85 | |||
86 | return priv; | ||
87 | |||
88 | fail: | ||
89 | if (priv) { | ||
90 | if (priv->tfm) | ||
91 | crypto_free_cipher(priv->tfm); | ||
92 | kfree(priv); | ||
93 | } | ||
94 | |||
95 | return NULL; | ||
96 | } | ||
97 | |||
98 | static void lib80211_ccmp_deinit(void *priv) | ||
99 | { | ||
100 | struct lib80211_ccmp_data *_priv = priv; | ||
101 | if (_priv && _priv->tfm) | ||
102 | crypto_free_cipher(_priv->tfm); | ||
103 | kfree(priv); | ||
104 | } | ||
105 | |||
106 | static inline void xor_block(u8 * b, u8 * a, size_t len) | ||
107 | { | ||
108 | int i; | ||
109 | for (i = 0; i < len; i++) | ||
110 | b[i] ^= a[i]; | ||
111 | } | ||
112 | |||
113 | static void ccmp_init_blocks(struct crypto_cipher *tfm, | ||
114 | struct ieee80211_hdr *hdr, | ||
115 | u8 * pn, size_t dlen, u8 * b0, u8 * auth, u8 * s0) | ||
116 | { | ||
117 | u8 *pos, qc = 0; | ||
118 | size_t aad_len; | ||
119 | int a4_included, qc_included; | ||
120 | u8 aad[2 * AES_BLOCK_LEN]; | ||
121 | |||
122 | a4_included = ieee80211_has_a4(hdr->frame_control); | ||
123 | qc_included = ieee80211_is_data_qos(hdr->frame_control); | ||
124 | |||
125 | aad_len = 22; | ||
126 | if (a4_included) | ||
127 | aad_len += 6; | ||
128 | if (qc_included) { | ||
129 | pos = (u8 *) & hdr->addr4; | ||
130 | if (a4_included) | ||
131 | pos += 6; | ||
132 | qc = *pos & 0x0f; | ||
133 | aad_len += 2; | ||
134 | } | ||
135 | |||
136 | /* CCM Initial Block: | ||
137 | * Flag (Include authentication header, M=3 (8-octet MIC), | ||
138 | * L=1 (2-octet Dlen)) | ||
139 | * Nonce: 0x00 | A2 | PN | ||
140 | * Dlen */ | ||
141 | b0[0] = 0x59; | ||
142 | b0[1] = qc; | ||
143 | memcpy(b0 + 2, hdr->addr2, ETH_ALEN); | ||
144 | memcpy(b0 + 8, pn, CCMP_PN_LEN); | ||
145 | b0[14] = (dlen >> 8) & 0xff; | ||
146 | b0[15] = dlen & 0xff; | ||
147 | |||
148 | /* AAD: | ||
149 | * FC with bits 4..6 and 11..13 masked to zero; 14 is always one | ||
150 | * A1 | A2 | A3 | ||
151 | * SC with bits 4..15 (seq#) masked to zero | ||
152 | * A4 (if present) | ||
153 | * QC (if present) | ||
154 | */ | ||
155 | pos = (u8 *) hdr; | ||
156 | aad[0] = 0; /* aad_len >> 8 */ | ||
157 | aad[1] = aad_len & 0xff; | ||
158 | aad[2] = pos[0] & 0x8f; | ||
159 | aad[3] = pos[1] & 0xc7; | ||
160 | memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN); | ||
161 | pos = (u8 *) & hdr->seq_ctrl; | ||
162 | aad[22] = pos[0] & 0x0f; | ||
163 | aad[23] = 0; /* all bits masked */ | ||
164 | memset(aad + 24, 0, 8); | ||
165 | if (a4_included) | ||
166 | memcpy(aad + 24, hdr->addr4, ETH_ALEN); | ||
167 | if (qc_included) { | ||
168 | aad[a4_included ? 30 : 24] = qc; | ||
169 | /* rest of QC masked */ | ||
170 | } | ||
171 | |||
172 | /* Start with the first block and AAD */ | ||
173 | lib80211_ccmp_aes_encrypt(tfm, b0, auth); | ||
174 | xor_block(auth, aad, AES_BLOCK_LEN); | ||
175 | lib80211_ccmp_aes_encrypt(tfm, auth, auth); | ||
176 | xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); | ||
177 | lib80211_ccmp_aes_encrypt(tfm, auth, auth); | ||
178 | b0[0] &= 0x07; | ||
179 | b0[14] = b0[15] = 0; | ||
180 | lib80211_ccmp_aes_encrypt(tfm, b0, s0); | ||
181 | } | ||
182 | |||
183 | static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len, | ||
184 | u8 *aeskey, int keylen, void *priv) | ||
185 | { | ||
186 | struct lib80211_ccmp_data *key = priv; | ||
187 | int i; | ||
188 | u8 *pos; | ||
189 | |||
190 | if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len) | ||
191 | return -1; | ||
192 | |||
193 | if (aeskey != NULL && keylen >= CCMP_TK_LEN) | ||
194 | memcpy(aeskey, key->key, CCMP_TK_LEN); | ||
195 | |||
196 | pos = skb_push(skb, CCMP_HDR_LEN); | ||
197 | memmove(pos, pos + CCMP_HDR_LEN, hdr_len); | ||
198 | pos += hdr_len; | ||
199 | |||
200 | i = CCMP_PN_LEN - 1; | ||
201 | while (i >= 0) { | ||
202 | key->tx_pn[i]++; | ||
203 | if (key->tx_pn[i] != 0) | ||
204 | break; | ||
205 | i--; | ||
206 | } | ||
207 | |||
208 | *pos++ = key->tx_pn[5]; | ||
209 | *pos++ = key->tx_pn[4]; | ||
210 | *pos++ = 0; | ||
211 | *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */ ; | ||
212 | *pos++ = key->tx_pn[3]; | ||
213 | *pos++ = key->tx_pn[2]; | ||
214 | *pos++ = key->tx_pn[1]; | ||
215 | *pos++ = key->tx_pn[0]; | ||
216 | |||
217 | return CCMP_HDR_LEN; | ||
218 | } | ||
219 | |||
220 | static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) | ||
221 | { | ||
222 | struct lib80211_ccmp_data *key = priv; | ||
223 | int data_len, i, blocks, last, len; | ||
224 | u8 *pos, *mic; | ||
225 | struct ieee80211_hdr *hdr; | ||
226 | u8 *b0 = key->tx_b0; | ||
227 | u8 *b = key->tx_b; | ||
228 | u8 *e = key->tx_e; | ||
229 | u8 *s0 = key->tx_s0; | ||
230 | |||
231 | if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len) | ||
232 | return -1; | ||
233 | |||
234 | data_len = skb->len - hdr_len; | ||
235 | len = lib80211_ccmp_hdr(skb, hdr_len, NULL, 0, priv); | ||
236 | if (len < 0) | ||
237 | return -1; | ||
238 | |||
239 | pos = skb->data + hdr_len + CCMP_HDR_LEN; | ||
240 | mic = skb_put(skb, CCMP_MIC_LEN); | ||
241 | hdr = (struct ieee80211_hdr *)skb->data; | ||
242 | ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); | ||
243 | |||
244 | blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); | ||
245 | last = data_len % AES_BLOCK_LEN; | ||
246 | |||
247 | for (i = 1; i <= blocks; i++) { | ||
248 | len = (i == blocks && last) ? last : AES_BLOCK_LEN; | ||
249 | /* Authentication */ | ||
250 | xor_block(b, pos, len); | ||
251 | lib80211_ccmp_aes_encrypt(key->tfm, b, b); | ||
252 | /* Encryption, with counter */ | ||
253 | b0[14] = (i >> 8) & 0xff; | ||
254 | b0[15] = i & 0xff; | ||
255 | lib80211_ccmp_aes_encrypt(key->tfm, b0, e); | ||
256 | xor_block(pos, e, len); | ||
257 | pos += len; | ||
258 | } | ||
259 | |||
260 | for (i = 0; i < CCMP_MIC_LEN; i++) | ||
261 | mic[i] = b[i] ^ s0[i]; | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | /* | ||
267 | * deal with seq counter wrapping correctly. | ||
268 | * refer to timer_after() for jiffies wrapping handling | ||
269 | */ | ||
270 | static inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o) | ||
271 | { | ||
272 | u32 iv32_n, iv16_n; | ||
273 | u32 iv32_o, iv16_o; | ||
274 | |||
275 | iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3]; | ||
276 | iv16_n = (pn_n[4] << 8) | pn_n[5]; | ||
277 | |||
278 | iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3]; | ||
279 | iv16_o = (pn_o[4] << 8) | pn_o[5]; | ||
280 | |||
281 | if ((s32)iv32_n - (s32)iv32_o < 0 || | ||
282 | (iv32_n == iv32_o && iv16_n <= iv16_o)) | ||
283 | return 1; | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) | ||
288 | { | ||
289 | struct lib80211_ccmp_data *key = priv; | ||
290 | u8 keyidx, *pos; | ||
291 | struct ieee80211_hdr *hdr; | ||
292 | u8 *b0 = key->rx_b0; | ||
293 | u8 *b = key->rx_b; | ||
294 | u8 *a = key->rx_a; | ||
295 | u8 pn[6]; | ||
296 | int i, blocks, last, len; | ||
297 | size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN; | ||
298 | u8 *mic = skb->data + skb->len - CCMP_MIC_LEN; | ||
299 | |||
300 | if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) { | ||
301 | key->dot11RSNAStatsCCMPFormatErrors++; | ||
302 | return -1; | ||
303 | } | ||
304 | |||
305 | hdr = (struct ieee80211_hdr *)skb->data; | ||
306 | pos = skb->data + hdr_len; | ||
307 | keyidx = pos[3]; | ||
308 | if (!(keyidx & (1 << 5))) { | ||
309 | if (net_ratelimit()) { | ||
310 | printk(KERN_DEBUG "CCMP: received packet without ExtIV" | ||
311 | " flag from %pM\n", hdr->addr2); | ||
312 | } | ||
313 | key->dot11RSNAStatsCCMPFormatErrors++; | ||
314 | return -2; | ||
315 | } | ||
316 | keyidx >>= 6; | ||
317 | if (key->key_idx != keyidx) { | ||
318 | printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame " | ||
319 | "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv); | ||
320 | return -6; | ||
321 | } | ||
322 | if (!key->key_set) { | ||
323 | if (net_ratelimit()) { | ||
324 | printk(KERN_DEBUG "CCMP: received packet from %pM" | ||
325 | " with keyid=%d that does not have a configured" | ||
326 | " key\n", hdr->addr2, keyidx); | ||
327 | } | ||
328 | return -3; | ||
329 | } | ||
330 | |||
331 | pn[0] = pos[7]; | ||
332 | pn[1] = pos[6]; | ||
333 | pn[2] = pos[5]; | ||
334 | pn[3] = pos[4]; | ||
335 | pn[4] = pos[1]; | ||
336 | pn[5] = pos[0]; | ||
337 | pos += 8; | ||
338 | |||
339 | if (ccmp_replay_check(pn, key->rx_pn)) { | ||
340 | if (net_ratelimit()) { | ||
341 | printk(KERN_DEBUG "CCMP: replay detected: STA=%pM " | ||
342 | "previous PN %02x%02x%02x%02x%02x%02x " | ||
343 | "received PN %02x%02x%02x%02x%02x%02x\n", | ||
344 | hdr->addr2, | ||
345 | key->rx_pn[0], key->rx_pn[1], key->rx_pn[2], | ||
346 | key->rx_pn[3], key->rx_pn[4], key->rx_pn[5], | ||
347 | pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]); | ||
348 | } | ||
349 | key->dot11RSNAStatsCCMPReplays++; | ||
350 | return -4; | ||
351 | } | ||
352 | |||
353 | ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b); | ||
354 | xor_block(mic, b, CCMP_MIC_LEN); | ||
355 | |||
356 | blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); | ||
357 | last = data_len % AES_BLOCK_LEN; | ||
358 | |||
359 | for (i = 1; i <= blocks; i++) { | ||
360 | len = (i == blocks && last) ? last : AES_BLOCK_LEN; | ||
361 | /* Decrypt, with counter */ | ||
362 | b0[14] = (i >> 8) & 0xff; | ||
363 | b0[15] = i & 0xff; | ||
364 | lib80211_ccmp_aes_encrypt(key->tfm, b0, b); | ||
365 | xor_block(pos, b, len); | ||
366 | /* Authentication */ | ||
367 | xor_block(a, pos, len); | ||
368 | lib80211_ccmp_aes_encrypt(key->tfm, a, a); | ||
369 | pos += len; | ||
370 | } | ||
371 | |||
372 | if (memcmp(mic, a, CCMP_MIC_LEN) != 0) { | ||
373 | if (net_ratelimit()) { | ||
374 | printk(KERN_DEBUG "CCMP: decrypt failed: STA=" | ||
375 | "%pM\n", hdr->addr2); | ||
376 | } | ||
377 | key->dot11RSNAStatsCCMPDecryptErrors++; | ||
378 | return -5; | ||
379 | } | ||
380 | |||
381 | memcpy(key->rx_pn, pn, CCMP_PN_LEN); | ||
382 | |||
383 | /* Remove hdr and MIC */ | ||
384 | memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len); | ||
385 | skb_pull(skb, CCMP_HDR_LEN); | ||
386 | skb_trim(skb, skb->len - CCMP_MIC_LEN); | ||
387 | |||
388 | return keyidx; | ||
389 | } | ||
390 | |||
391 | static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv) | ||
392 | { | ||
393 | struct lib80211_ccmp_data *data = priv; | ||
394 | int keyidx; | ||
395 | struct crypto_cipher *tfm = data->tfm; | ||
396 | |||
397 | keyidx = data->key_idx; | ||
398 | memset(data, 0, sizeof(*data)); | ||
399 | data->key_idx = keyidx; | ||
400 | data->tfm = tfm; | ||
401 | if (len == CCMP_TK_LEN) { | ||
402 | memcpy(data->key, key, CCMP_TK_LEN); | ||
403 | data->key_set = 1; | ||
404 | if (seq) { | ||
405 | data->rx_pn[0] = seq[5]; | ||
406 | data->rx_pn[1] = seq[4]; | ||
407 | data->rx_pn[2] = seq[3]; | ||
408 | data->rx_pn[3] = seq[2]; | ||
409 | data->rx_pn[4] = seq[1]; | ||
410 | data->rx_pn[5] = seq[0]; | ||
411 | } | ||
412 | crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN); | ||
413 | } else if (len == 0) | ||
414 | data->key_set = 0; | ||
415 | else | ||
416 | return -1; | ||
417 | |||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | static int lib80211_ccmp_get_key(void *key, int len, u8 * seq, void *priv) | ||
422 | { | ||
423 | struct lib80211_ccmp_data *data = priv; | ||
424 | |||
425 | if (len < CCMP_TK_LEN) | ||
426 | return -1; | ||
427 | |||
428 | if (!data->key_set) | ||
429 | return 0; | ||
430 | memcpy(key, data->key, CCMP_TK_LEN); | ||
431 | |||
432 | if (seq) { | ||
433 | seq[0] = data->tx_pn[5]; | ||
434 | seq[1] = data->tx_pn[4]; | ||
435 | seq[2] = data->tx_pn[3]; | ||
436 | seq[3] = data->tx_pn[2]; | ||
437 | seq[4] = data->tx_pn[1]; | ||
438 | seq[5] = data->tx_pn[0]; | ||
439 | } | ||
440 | |||
441 | return CCMP_TK_LEN; | ||
442 | } | ||
443 | |||
444 | static char *lib80211_ccmp_print_stats(char *p, void *priv) | ||
445 | { | ||
446 | struct lib80211_ccmp_data *ccmp = priv; | ||
447 | |||
448 | p += sprintf(p, "key[%d] alg=CCMP key_set=%d " | ||
449 | "tx_pn=%02x%02x%02x%02x%02x%02x " | ||
450 | "rx_pn=%02x%02x%02x%02x%02x%02x " | ||
451 | "format_errors=%d replays=%d decrypt_errors=%d\n", | ||
452 | ccmp->key_idx, ccmp->key_set, | ||
453 | ccmp->tx_pn[0], ccmp->tx_pn[1], ccmp->tx_pn[2], | ||
454 | ccmp->tx_pn[3], ccmp->tx_pn[4], ccmp->tx_pn[5], | ||
455 | ccmp->rx_pn[0], ccmp->rx_pn[1], ccmp->rx_pn[2], | ||
456 | ccmp->rx_pn[3], ccmp->rx_pn[4], ccmp->rx_pn[5], | ||
457 | ccmp->dot11RSNAStatsCCMPFormatErrors, | ||
458 | ccmp->dot11RSNAStatsCCMPReplays, | ||
459 | ccmp->dot11RSNAStatsCCMPDecryptErrors); | ||
460 | |||
461 | return p; | ||
462 | } | ||
463 | |||
464 | static struct lib80211_crypto_ops lib80211_crypt_ccmp = { | ||
465 | .name = "CCMP", | ||
466 | .init = lib80211_ccmp_init, | ||
467 | .deinit = lib80211_ccmp_deinit, | ||
468 | .build_iv = lib80211_ccmp_hdr, | ||
469 | .encrypt_mpdu = lib80211_ccmp_encrypt, | ||
470 | .decrypt_mpdu = lib80211_ccmp_decrypt, | ||
471 | .encrypt_msdu = NULL, | ||
472 | .decrypt_msdu = NULL, | ||
473 | .set_key = lib80211_ccmp_set_key, | ||
474 | .get_key = lib80211_ccmp_get_key, | ||
475 | .print_stats = lib80211_ccmp_print_stats, | ||
476 | .extra_mpdu_prefix_len = CCMP_HDR_LEN, | ||
477 | .extra_mpdu_postfix_len = CCMP_MIC_LEN, | ||
478 | .owner = THIS_MODULE, | ||
479 | }; | ||
480 | |||
481 | static int __init lib80211_crypto_ccmp_init(void) | ||
482 | { | ||
483 | return lib80211_register_crypto_ops(&lib80211_crypt_ccmp); | ||
484 | } | ||
485 | |||
486 | static void __exit lib80211_crypto_ccmp_exit(void) | ||
487 | { | ||
488 | lib80211_unregister_crypto_ops(&lib80211_crypt_ccmp); | ||
489 | } | ||
490 | |||
491 | module_init(lib80211_crypto_ccmp_init); | ||
492 | module_exit(lib80211_crypto_ccmp_exit); | ||
diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c new file mode 100644 index 000000000000..7e8e22bfed90 --- /dev/null +++ b/net/wireless/lib80211_crypt_tkip.c | |||
@@ -0,0 +1,784 @@ | |||
1 | /* | ||
2 | * lib80211 crypt: host-based TKIP encryption implementation for lib80211 | ||
3 | * | ||
4 | * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi> | ||
5 | * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com> | ||
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. See README and COPYING for | ||
10 | * more details. | ||
11 | */ | ||
12 | |||
13 | #include <linux/err.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/random.h> | ||
18 | #include <linux/scatterlist.h> | ||
19 | #include <linux/skbuff.h> | ||
20 | #include <linux/netdevice.h> | ||
21 | #include <linux/mm.h> | ||
22 | #include <linux/if_ether.h> | ||
23 | #include <linux/if_arp.h> | ||
24 | #include <asm/string.h> | ||
25 | |||
26 | #include <linux/wireless.h> | ||
27 | #include <linux/ieee80211.h> | ||
28 | #include <net/iw_handler.h> | ||
29 | |||
30 | #include <linux/crypto.h> | ||
31 | #include <linux/crc32.h> | ||
32 | |||
33 | #include <net/lib80211.h> | ||
34 | |||
35 | MODULE_AUTHOR("Jouni Malinen"); | ||
36 | MODULE_DESCRIPTION("lib80211 crypt: TKIP"); | ||
37 | MODULE_LICENSE("GPL"); | ||
38 | |||
39 | struct lib80211_tkip_data { | ||
40 | #define TKIP_KEY_LEN 32 | ||
41 | u8 key[TKIP_KEY_LEN]; | ||
42 | int key_set; | ||
43 | |||
44 | u32 tx_iv32; | ||
45 | u16 tx_iv16; | ||
46 | u16 tx_ttak[5]; | ||
47 | int tx_phase1_done; | ||
48 | |||
49 | u32 rx_iv32; | ||
50 | u16 rx_iv16; | ||
51 | u16 rx_ttak[5]; | ||
52 | int rx_phase1_done; | ||
53 | u32 rx_iv32_new; | ||
54 | u16 rx_iv16_new; | ||
55 | |||
56 | u32 dot11RSNAStatsTKIPReplays; | ||
57 | u32 dot11RSNAStatsTKIPICVErrors; | ||
58 | u32 dot11RSNAStatsTKIPLocalMICFailures; | ||
59 | |||
60 | int key_idx; | ||
61 | |||
62 | struct crypto_blkcipher *rx_tfm_arc4; | ||
63 | struct crypto_hash *rx_tfm_michael; | ||
64 | struct crypto_blkcipher *tx_tfm_arc4; | ||
65 | struct crypto_hash *tx_tfm_michael; | ||
66 | |||
67 | /* scratch buffers for virt_to_page() (crypto API) */ | ||
68 | u8 rx_hdr[16], tx_hdr[16]; | ||
69 | |||
70 | unsigned long flags; | ||
71 | }; | ||
72 | |||
73 | static unsigned long lib80211_tkip_set_flags(unsigned long flags, void *priv) | ||
74 | { | ||
75 | struct lib80211_tkip_data *_priv = priv; | ||
76 | unsigned long old_flags = _priv->flags; | ||
77 | _priv->flags = flags; | ||
78 | return old_flags; | ||
79 | } | ||
80 | |||
81 | static unsigned long lib80211_tkip_get_flags(void *priv) | ||
82 | { | ||
83 | struct lib80211_tkip_data *_priv = priv; | ||
84 | return _priv->flags; | ||
85 | } | ||
86 | |||
87 | static void *lib80211_tkip_init(int key_idx) | ||
88 | { | ||
89 | struct lib80211_tkip_data *priv; | ||
90 | |||
91 | priv = kzalloc(sizeof(*priv), GFP_ATOMIC); | ||
92 | if (priv == NULL) | ||
93 | goto fail; | ||
94 | |||
95 | priv->key_idx = key_idx; | ||
96 | |||
97 | priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, | ||
98 | CRYPTO_ALG_ASYNC); | ||
99 | if (IS_ERR(priv->tx_tfm_arc4)) { | ||
100 | printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate " | ||
101 | "crypto API arc4\n"); | ||
102 | priv->tx_tfm_arc4 = NULL; | ||
103 | goto fail; | ||
104 | } | ||
105 | |||
106 | priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0, | ||
107 | CRYPTO_ALG_ASYNC); | ||
108 | if (IS_ERR(priv->tx_tfm_michael)) { | ||
109 | printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate " | ||
110 | "crypto API michael_mic\n"); | ||
111 | priv->tx_tfm_michael = NULL; | ||
112 | goto fail; | ||
113 | } | ||
114 | |||
115 | priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, | ||
116 | CRYPTO_ALG_ASYNC); | ||
117 | if (IS_ERR(priv->rx_tfm_arc4)) { | ||
118 | printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate " | ||
119 | "crypto API arc4\n"); | ||
120 | priv->rx_tfm_arc4 = NULL; | ||
121 | goto fail; | ||
122 | } | ||
123 | |||
124 | priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0, | ||
125 | CRYPTO_ALG_ASYNC); | ||
126 | if (IS_ERR(priv->rx_tfm_michael)) { | ||
127 | printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate " | ||
128 | "crypto API michael_mic\n"); | ||
129 | priv->rx_tfm_michael = NULL; | ||
130 | goto fail; | ||
131 | } | ||
132 | |||
133 | return priv; | ||
134 | |||
135 | fail: | ||
136 | if (priv) { | ||
137 | if (priv->tx_tfm_michael) | ||
138 | crypto_free_hash(priv->tx_tfm_michael); | ||
139 | if (priv->tx_tfm_arc4) | ||
140 | crypto_free_blkcipher(priv->tx_tfm_arc4); | ||
141 | if (priv->rx_tfm_michael) | ||
142 | crypto_free_hash(priv->rx_tfm_michael); | ||
143 | if (priv->rx_tfm_arc4) | ||
144 | crypto_free_blkcipher(priv->rx_tfm_arc4); | ||
145 | kfree(priv); | ||
146 | } | ||
147 | |||
148 | return NULL; | ||
149 | } | ||
150 | |||
151 | static void lib80211_tkip_deinit(void *priv) | ||
152 | { | ||
153 | struct lib80211_tkip_data *_priv = priv; | ||
154 | if (_priv) { | ||
155 | if (_priv->tx_tfm_michael) | ||
156 | crypto_free_hash(_priv->tx_tfm_michael); | ||
157 | if (_priv->tx_tfm_arc4) | ||
158 | crypto_free_blkcipher(_priv->tx_tfm_arc4); | ||
159 | if (_priv->rx_tfm_michael) | ||
160 | crypto_free_hash(_priv->rx_tfm_michael); | ||
161 | if (_priv->rx_tfm_arc4) | ||
162 | crypto_free_blkcipher(_priv->rx_tfm_arc4); | ||
163 | } | ||
164 | kfree(priv); | ||
165 | } | ||
166 | |||
167 | static inline u16 RotR1(u16 val) | ||
168 | { | ||
169 | return (val >> 1) | (val << 15); | ||
170 | } | ||
171 | |||
172 | static inline u8 Lo8(u16 val) | ||
173 | { | ||
174 | return val & 0xff; | ||
175 | } | ||
176 | |||
177 | static inline u8 Hi8(u16 val) | ||
178 | { | ||
179 | return val >> 8; | ||
180 | } | ||
181 | |||
182 | static inline u16 Lo16(u32 val) | ||
183 | { | ||
184 | return val & 0xffff; | ||
185 | } | ||
186 | |||
187 | static inline u16 Hi16(u32 val) | ||
188 | { | ||
189 | return val >> 16; | ||
190 | } | ||
191 | |||
192 | static inline u16 Mk16(u8 hi, u8 lo) | ||
193 | { | ||
194 | return lo | (((u16) hi) << 8); | ||
195 | } | ||
196 | |||
197 | static inline u16 Mk16_le(__le16 * v) | ||
198 | { | ||
199 | return le16_to_cpu(*v); | ||
200 | } | ||
201 | |||
202 | static const u16 Sbox[256] = { | ||
203 | 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, | ||
204 | 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, | ||
205 | 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, | ||
206 | 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, | ||
207 | 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, | ||
208 | 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, | ||
209 | 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, | ||
210 | 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, | ||
211 | 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, | ||
212 | 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, | ||
213 | 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, | ||
214 | 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, | ||
215 | 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, | ||
216 | 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, | ||
217 | 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, | ||
218 | 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, | ||
219 | 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, | ||
220 | 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, | ||
221 | 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, | ||
222 | 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, | ||
223 | 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, | ||
224 | 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, | ||
225 | 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, | ||
226 | 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, | ||
227 | 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, | ||
228 | 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, | ||
229 | 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, | ||
230 | 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, | ||
231 | 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, | ||
232 | 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, | ||
233 | 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, | ||
234 | 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, | ||
235 | }; | ||
236 | |||
237 | static inline u16 _S_(u16 v) | ||
238 | { | ||
239 | u16 t = Sbox[Hi8(v)]; | ||
240 | return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8)); | ||
241 | } | ||
242 | |||
243 | #define PHASE1_LOOP_COUNT 8 | ||
244 | |||
245 | static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA, | ||
246 | u32 IV32) | ||
247 | { | ||
248 | int i, j; | ||
249 | |||
250 | /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */ | ||
251 | TTAK[0] = Lo16(IV32); | ||
252 | TTAK[1] = Hi16(IV32); | ||
253 | TTAK[2] = Mk16(TA[1], TA[0]); | ||
254 | TTAK[3] = Mk16(TA[3], TA[2]); | ||
255 | TTAK[4] = Mk16(TA[5], TA[4]); | ||
256 | |||
257 | for (i = 0; i < PHASE1_LOOP_COUNT; i++) { | ||
258 | j = 2 * (i & 1); | ||
259 | TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j])); | ||
260 | TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j])); | ||
261 | TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j])); | ||
262 | TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j])); | ||
263 | TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK, | ||
268 | u16 IV16) | ||
269 | { | ||
270 | /* Make temporary area overlap WEP seed so that the final copy can be | ||
271 | * avoided on little endian hosts. */ | ||
272 | u16 *PPK = (u16 *) & WEPSeed[4]; | ||
273 | |||
274 | /* Step 1 - make copy of TTAK and bring in TSC */ | ||
275 | PPK[0] = TTAK[0]; | ||
276 | PPK[1] = TTAK[1]; | ||
277 | PPK[2] = TTAK[2]; | ||
278 | PPK[3] = TTAK[3]; | ||
279 | PPK[4] = TTAK[4]; | ||
280 | PPK[5] = TTAK[4] + IV16; | ||
281 | |||
282 | /* Step 2 - 96-bit bijective mixing using S-box */ | ||
283 | PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0])); | ||
284 | PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2])); | ||
285 | PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4])); | ||
286 | PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6])); | ||
287 | PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8])); | ||
288 | PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10])); | ||
289 | |||
290 | PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12])); | ||
291 | PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14])); | ||
292 | PPK[2] += RotR1(PPK[1]); | ||
293 | PPK[3] += RotR1(PPK[2]); | ||
294 | PPK[4] += RotR1(PPK[3]); | ||
295 | PPK[5] += RotR1(PPK[4]); | ||
296 | |||
297 | /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value | ||
298 | * WEPSeed[0..2] is transmitted as WEP IV */ | ||
299 | WEPSeed[0] = Hi8(IV16); | ||
300 | WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F; | ||
301 | WEPSeed[2] = Lo8(IV16); | ||
302 | WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1); | ||
303 | |||
304 | #ifdef __BIG_ENDIAN | ||
305 | { | ||
306 | int i; | ||
307 | for (i = 0; i < 6; i++) | ||
308 | PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8); | ||
309 | } | ||
310 | #endif | ||
311 | } | ||
312 | |||
313 | static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len, | ||
314 | u8 * rc4key, int keylen, void *priv) | ||
315 | { | ||
316 | struct lib80211_tkip_data *tkey = priv; | ||
317 | int len; | ||
318 | u8 *pos; | ||
319 | struct ieee80211_hdr *hdr; | ||
320 | |||
321 | hdr = (struct ieee80211_hdr *)skb->data; | ||
322 | |||
323 | if (skb_headroom(skb) < 8 || skb->len < hdr_len) | ||
324 | return -1; | ||
325 | |||
326 | if (rc4key == NULL || keylen < 16) | ||
327 | return -1; | ||
328 | |||
329 | if (!tkey->tx_phase1_done) { | ||
330 | tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2, | ||
331 | tkey->tx_iv32); | ||
332 | tkey->tx_phase1_done = 1; | ||
333 | } | ||
334 | tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16); | ||
335 | |||
336 | len = skb->len - hdr_len; | ||
337 | pos = skb_push(skb, 8); | ||
338 | memmove(pos, pos + 8, hdr_len); | ||
339 | pos += hdr_len; | ||
340 | |||
341 | *pos++ = *rc4key; | ||
342 | *pos++ = *(rc4key + 1); | ||
343 | *pos++ = *(rc4key + 2); | ||
344 | *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ; | ||
345 | *pos++ = tkey->tx_iv32 & 0xff; | ||
346 | *pos++ = (tkey->tx_iv32 >> 8) & 0xff; | ||
347 | *pos++ = (tkey->tx_iv32 >> 16) & 0xff; | ||
348 | *pos++ = (tkey->tx_iv32 >> 24) & 0xff; | ||
349 | |||
350 | tkey->tx_iv16++; | ||
351 | if (tkey->tx_iv16 == 0) { | ||
352 | tkey->tx_phase1_done = 0; | ||
353 | tkey->tx_iv32++; | ||
354 | } | ||
355 | |||
356 | return 8; | ||
357 | } | ||
358 | |||
359 | static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) | ||
360 | { | ||
361 | struct lib80211_tkip_data *tkey = priv; | ||
362 | struct blkcipher_desc desc = { .tfm = tkey->tx_tfm_arc4 }; | ||
363 | int len; | ||
364 | u8 rc4key[16], *pos, *icv; | ||
365 | u32 crc; | ||
366 | struct scatterlist sg; | ||
367 | |||
368 | if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) { | ||
369 | if (net_ratelimit()) { | ||
370 | struct ieee80211_hdr *hdr = | ||
371 | (struct ieee80211_hdr *)skb->data; | ||
372 | printk(KERN_DEBUG ": TKIP countermeasures: dropped " | ||
373 | "TX packet to %pM\n", hdr->addr1); | ||
374 | } | ||
375 | return -1; | ||
376 | } | ||
377 | |||
378 | if (skb_tailroom(skb) < 4 || skb->len < hdr_len) | ||
379 | return -1; | ||
380 | |||
381 | len = skb->len - hdr_len; | ||
382 | pos = skb->data + hdr_len; | ||
383 | |||
384 | if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0) | ||
385 | return -1; | ||
386 | |||
387 | icv = skb_put(skb, 4); | ||
388 | |||
389 | crc = ~crc32_le(~0, pos, len); | ||
390 | icv[0] = crc; | ||
391 | icv[1] = crc >> 8; | ||
392 | icv[2] = crc >> 16; | ||
393 | icv[3] = crc >> 24; | ||
394 | |||
395 | crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16); | ||
396 | sg_init_one(&sg, pos, len + 4); | ||
397 | return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4); | ||
398 | } | ||
399 | |||
400 | /* | ||
401 | * deal with seq counter wrapping correctly. | ||
402 | * refer to timer_after() for jiffies wrapping handling | ||
403 | */ | ||
404 | static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n, | ||
405 | u32 iv32_o, u16 iv16_o) | ||
406 | { | ||
407 | if ((s32)iv32_n - (s32)iv32_o < 0 || | ||
408 | (iv32_n == iv32_o && iv16_n <= iv16_o)) | ||
409 | return 1; | ||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) | ||
414 | { | ||
415 | struct lib80211_tkip_data *tkey = priv; | ||
416 | struct blkcipher_desc desc = { .tfm = tkey->rx_tfm_arc4 }; | ||
417 | u8 rc4key[16]; | ||
418 | u8 keyidx, *pos; | ||
419 | u32 iv32; | ||
420 | u16 iv16; | ||
421 | struct ieee80211_hdr *hdr; | ||
422 | u8 icv[4]; | ||
423 | u32 crc; | ||
424 | struct scatterlist sg; | ||
425 | int plen; | ||
426 | |||
427 | hdr = (struct ieee80211_hdr *)skb->data; | ||
428 | |||
429 | if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) { | ||
430 | if (net_ratelimit()) { | ||
431 | printk(KERN_DEBUG ": TKIP countermeasures: dropped " | ||
432 | "received packet from %pM\n", hdr->addr2); | ||
433 | } | ||
434 | return -1; | ||
435 | } | ||
436 | |||
437 | if (skb->len < hdr_len + 8 + 4) | ||
438 | return -1; | ||
439 | |||
440 | pos = skb->data + hdr_len; | ||
441 | keyidx = pos[3]; | ||
442 | if (!(keyidx & (1 << 5))) { | ||
443 | if (net_ratelimit()) { | ||
444 | printk(KERN_DEBUG "TKIP: received packet without ExtIV" | ||
445 | " flag from %pM\n", hdr->addr2); | ||
446 | } | ||
447 | return -2; | ||
448 | } | ||
449 | keyidx >>= 6; | ||
450 | if (tkey->key_idx != keyidx) { | ||
451 | printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame " | ||
452 | "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv); | ||
453 | return -6; | ||
454 | } | ||
455 | if (!tkey->key_set) { | ||
456 | if (net_ratelimit()) { | ||
457 | printk(KERN_DEBUG "TKIP: received packet from %pM" | ||
458 | " with keyid=%d that does not have a configured" | ||
459 | " key\n", hdr->addr2, keyidx); | ||
460 | } | ||
461 | return -3; | ||
462 | } | ||
463 | iv16 = (pos[0] << 8) | pos[2]; | ||
464 | iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); | ||
465 | pos += 8; | ||
466 | |||
467 | if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) { | ||
468 | if (net_ratelimit()) { | ||
469 | printk(KERN_DEBUG "TKIP: replay detected: STA=%pM" | ||
470 | " previous TSC %08x%04x received TSC " | ||
471 | "%08x%04x\n", hdr->addr2, | ||
472 | tkey->rx_iv32, tkey->rx_iv16, iv32, iv16); | ||
473 | } | ||
474 | tkey->dot11RSNAStatsTKIPReplays++; | ||
475 | return -4; | ||
476 | } | ||
477 | |||
478 | if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) { | ||
479 | tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32); | ||
480 | tkey->rx_phase1_done = 1; | ||
481 | } | ||
482 | tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16); | ||
483 | |||
484 | plen = skb->len - hdr_len - 12; | ||
485 | |||
486 | crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16); | ||
487 | sg_init_one(&sg, pos, plen + 4); | ||
488 | if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) { | ||
489 | if (net_ratelimit()) { | ||
490 | printk(KERN_DEBUG ": TKIP: failed to decrypt " | ||
491 | "received packet from %pM\n", | ||
492 | hdr->addr2); | ||
493 | } | ||
494 | return -7; | ||
495 | } | ||
496 | |||
497 | crc = ~crc32_le(~0, pos, plen); | ||
498 | icv[0] = crc; | ||
499 | icv[1] = crc >> 8; | ||
500 | icv[2] = crc >> 16; | ||
501 | icv[3] = crc >> 24; | ||
502 | if (memcmp(icv, pos + plen, 4) != 0) { | ||
503 | if (iv32 != tkey->rx_iv32) { | ||
504 | /* Previously cached Phase1 result was already lost, so | ||
505 | * it needs to be recalculated for the next packet. */ | ||
506 | tkey->rx_phase1_done = 0; | ||
507 | } | ||
508 | if (net_ratelimit()) { | ||
509 | printk(KERN_DEBUG "TKIP: ICV error detected: STA=" | ||
510 | "%pM\n", hdr->addr2); | ||
511 | } | ||
512 | tkey->dot11RSNAStatsTKIPICVErrors++; | ||
513 | return -5; | ||
514 | } | ||
515 | |||
516 | /* Update real counters only after Michael MIC verification has | ||
517 | * completed */ | ||
518 | tkey->rx_iv32_new = iv32; | ||
519 | tkey->rx_iv16_new = iv16; | ||
520 | |||
521 | /* Remove IV and ICV */ | ||
522 | memmove(skb->data + 8, skb->data, hdr_len); | ||
523 | skb_pull(skb, 8); | ||
524 | skb_trim(skb, skb->len - 4); | ||
525 | |||
526 | return keyidx; | ||
527 | } | ||
528 | |||
529 | static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr, | ||
530 | u8 * data, size_t data_len, u8 * mic) | ||
531 | { | ||
532 | struct hash_desc desc; | ||
533 | struct scatterlist sg[2]; | ||
534 | |||
535 | if (tfm_michael == NULL) { | ||
536 | printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n"); | ||
537 | return -1; | ||
538 | } | ||
539 | sg_init_table(sg, 2); | ||
540 | sg_set_buf(&sg[0], hdr, 16); | ||
541 | sg_set_buf(&sg[1], data, data_len); | ||
542 | |||
543 | if (crypto_hash_setkey(tfm_michael, key, 8)) | ||
544 | return -1; | ||
545 | |||
546 | desc.tfm = tfm_michael; | ||
547 | desc.flags = 0; | ||
548 | return crypto_hash_digest(&desc, sg, data_len + 16, mic); | ||
549 | } | ||
550 | |||
551 | static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr) | ||
552 | { | ||
553 | struct ieee80211_hdr *hdr11; | ||
554 | |||
555 | hdr11 = (struct ieee80211_hdr *)skb->data; | ||
556 | |||
557 | switch (le16_to_cpu(hdr11->frame_control) & | ||
558 | (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { | ||
559 | case IEEE80211_FCTL_TODS: | ||
560 | memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ | ||
561 | memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ | ||
562 | break; | ||
563 | case IEEE80211_FCTL_FROMDS: | ||
564 | memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ | ||
565 | memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */ | ||
566 | break; | ||
567 | case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: | ||
568 | memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ | ||
569 | memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */ | ||
570 | break; | ||
571 | case 0: | ||
572 | memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ | ||
573 | memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ | ||
574 | break; | ||
575 | } | ||
576 | |||
577 | if (ieee80211_is_data_qos(hdr11->frame_control)) { | ||
578 | hdr[12] = le16_to_cpu(*ieee80211_get_qos_ctl(hdr11)) | ||
579 | & IEEE80211_QOS_CTL_TID_MASK; | ||
580 | } else | ||
581 | hdr[12] = 0; /* priority */ | ||
582 | |||
583 | hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ | ||
584 | } | ||
585 | |||
586 | static int lib80211_michael_mic_add(struct sk_buff *skb, int hdr_len, | ||
587 | void *priv) | ||
588 | { | ||
589 | struct lib80211_tkip_data *tkey = priv; | ||
590 | u8 *pos; | ||
591 | |||
592 | if (skb_tailroom(skb) < 8 || skb->len < hdr_len) { | ||
593 | printk(KERN_DEBUG "Invalid packet for Michael MIC add " | ||
594 | "(tailroom=%d hdr_len=%d skb->len=%d)\n", | ||
595 | skb_tailroom(skb), hdr_len, skb->len); | ||
596 | return -1; | ||
597 | } | ||
598 | |||
599 | michael_mic_hdr(skb, tkey->tx_hdr); | ||
600 | pos = skb_put(skb, 8); | ||
601 | if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr, | ||
602 | skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) | ||
603 | return -1; | ||
604 | |||
605 | return 0; | ||
606 | } | ||
607 | |||
608 | static void lib80211_michael_mic_failure(struct net_device *dev, | ||
609 | struct ieee80211_hdr *hdr, | ||
610 | int keyidx) | ||
611 | { | ||
612 | union iwreq_data wrqu; | ||
613 | struct iw_michaelmicfailure ev; | ||
614 | |||
615 | /* TODO: needed parameters: count, keyid, key type, TSC */ | ||
616 | memset(&ev, 0, sizeof(ev)); | ||
617 | ev.flags = keyidx & IW_MICFAILURE_KEY_ID; | ||
618 | if (hdr->addr1[0] & 0x01) | ||
619 | ev.flags |= IW_MICFAILURE_GROUP; | ||
620 | else | ||
621 | ev.flags |= IW_MICFAILURE_PAIRWISE; | ||
622 | ev.src_addr.sa_family = ARPHRD_ETHER; | ||
623 | memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN); | ||
624 | memset(&wrqu, 0, sizeof(wrqu)); | ||
625 | wrqu.data.length = sizeof(ev); | ||
626 | wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev); | ||
627 | } | ||
628 | |||
629 | static int lib80211_michael_mic_verify(struct sk_buff *skb, int keyidx, | ||
630 | int hdr_len, void *priv) | ||
631 | { | ||
632 | struct lib80211_tkip_data *tkey = priv; | ||
633 | u8 mic[8]; | ||
634 | |||
635 | if (!tkey->key_set) | ||
636 | return -1; | ||
637 | |||
638 | michael_mic_hdr(skb, tkey->rx_hdr); | ||
639 | if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr, | ||
640 | skb->data + hdr_len, skb->len - 8 - hdr_len, mic)) | ||
641 | return -1; | ||
642 | if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) { | ||
643 | struct ieee80211_hdr *hdr; | ||
644 | hdr = (struct ieee80211_hdr *)skb->data; | ||
645 | printk(KERN_DEBUG "%s: Michael MIC verification failed for " | ||
646 | "MSDU from %pM keyidx=%d\n", | ||
647 | skb->dev ? skb->dev->name : "N/A", hdr->addr2, | ||
648 | keyidx); | ||
649 | if (skb->dev) | ||
650 | lib80211_michael_mic_failure(skb->dev, hdr, keyidx); | ||
651 | tkey->dot11RSNAStatsTKIPLocalMICFailures++; | ||
652 | return -1; | ||
653 | } | ||
654 | |||
655 | /* Update TSC counters for RX now that the packet verification has | ||
656 | * completed. */ | ||
657 | tkey->rx_iv32 = tkey->rx_iv32_new; | ||
658 | tkey->rx_iv16 = tkey->rx_iv16_new; | ||
659 | |||
660 | skb_trim(skb, skb->len - 8); | ||
661 | |||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | static int lib80211_tkip_set_key(void *key, int len, u8 * seq, void *priv) | ||
666 | { | ||
667 | struct lib80211_tkip_data *tkey = priv; | ||
668 | int keyidx; | ||
669 | struct crypto_hash *tfm = tkey->tx_tfm_michael; | ||
670 | struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4; | ||
671 | struct crypto_hash *tfm3 = tkey->rx_tfm_michael; | ||
672 | struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4; | ||
673 | |||
674 | keyidx = tkey->key_idx; | ||
675 | memset(tkey, 0, sizeof(*tkey)); | ||
676 | tkey->key_idx = keyidx; | ||
677 | tkey->tx_tfm_michael = tfm; | ||
678 | tkey->tx_tfm_arc4 = tfm2; | ||
679 | tkey->rx_tfm_michael = tfm3; | ||
680 | tkey->rx_tfm_arc4 = tfm4; | ||
681 | if (len == TKIP_KEY_LEN) { | ||
682 | memcpy(tkey->key, key, TKIP_KEY_LEN); | ||
683 | tkey->key_set = 1; | ||
684 | tkey->tx_iv16 = 1; /* TSC is initialized to 1 */ | ||
685 | if (seq) { | ||
686 | tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) | | ||
687 | (seq[3] << 8) | seq[2]; | ||
688 | tkey->rx_iv16 = (seq[1] << 8) | seq[0]; | ||
689 | } | ||
690 | } else if (len == 0) | ||
691 | tkey->key_set = 0; | ||
692 | else | ||
693 | return -1; | ||
694 | |||
695 | return 0; | ||
696 | } | ||
697 | |||
698 | static int lib80211_tkip_get_key(void *key, int len, u8 * seq, void *priv) | ||
699 | { | ||
700 | struct lib80211_tkip_data *tkey = priv; | ||
701 | |||
702 | if (len < TKIP_KEY_LEN) | ||
703 | return -1; | ||
704 | |||
705 | if (!tkey->key_set) | ||
706 | return 0; | ||
707 | memcpy(key, tkey->key, TKIP_KEY_LEN); | ||
708 | |||
709 | if (seq) { | ||
710 | /* Return the sequence number of the last transmitted frame. */ | ||
711 | u16 iv16 = tkey->tx_iv16; | ||
712 | u32 iv32 = tkey->tx_iv32; | ||
713 | if (iv16 == 0) | ||
714 | iv32--; | ||
715 | iv16--; | ||
716 | seq[0] = tkey->tx_iv16; | ||
717 | seq[1] = tkey->tx_iv16 >> 8; | ||
718 | seq[2] = tkey->tx_iv32; | ||
719 | seq[3] = tkey->tx_iv32 >> 8; | ||
720 | seq[4] = tkey->tx_iv32 >> 16; | ||
721 | seq[5] = tkey->tx_iv32 >> 24; | ||
722 | } | ||
723 | |||
724 | return TKIP_KEY_LEN; | ||
725 | } | ||
726 | |||
727 | static char *lib80211_tkip_print_stats(char *p, void *priv) | ||
728 | { | ||
729 | struct lib80211_tkip_data *tkip = priv; | ||
730 | p += sprintf(p, "key[%d] alg=TKIP key_set=%d " | ||
731 | "tx_pn=%02x%02x%02x%02x%02x%02x " | ||
732 | "rx_pn=%02x%02x%02x%02x%02x%02x " | ||
733 | "replays=%d icv_errors=%d local_mic_failures=%d\n", | ||
734 | tkip->key_idx, tkip->key_set, | ||
735 | (tkip->tx_iv32 >> 24) & 0xff, | ||
736 | (tkip->tx_iv32 >> 16) & 0xff, | ||
737 | (tkip->tx_iv32 >> 8) & 0xff, | ||
738 | tkip->tx_iv32 & 0xff, | ||
739 | (tkip->tx_iv16 >> 8) & 0xff, | ||
740 | tkip->tx_iv16 & 0xff, | ||
741 | (tkip->rx_iv32 >> 24) & 0xff, | ||
742 | (tkip->rx_iv32 >> 16) & 0xff, | ||
743 | (tkip->rx_iv32 >> 8) & 0xff, | ||
744 | tkip->rx_iv32 & 0xff, | ||
745 | (tkip->rx_iv16 >> 8) & 0xff, | ||
746 | tkip->rx_iv16 & 0xff, | ||
747 | tkip->dot11RSNAStatsTKIPReplays, | ||
748 | tkip->dot11RSNAStatsTKIPICVErrors, | ||
749 | tkip->dot11RSNAStatsTKIPLocalMICFailures); | ||
750 | return p; | ||
751 | } | ||
752 | |||
753 | static struct lib80211_crypto_ops lib80211_crypt_tkip = { | ||
754 | .name = "TKIP", | ||
755 | .init = lib80211_tkip_init, | ||
756 | .deinit = lib80211_tkip_deinit, | ||
757 | .build_iv = lib80211_tkip_hdr, | ||
758 | .encrypt_mpdu = lib80211_tkip_encrypt, | ||
759 | .decrypt_mpdu = lib80211_tkip_decrypt, | ||
760 | .encrypt_msdu = lib80211_michael_mic_add, | ||
761 | .decrypt_msdu = lib80211_michael_mic_verify, | ||
762 | .set_key = lib80211_tkip_set_key, | ||
763 | .get_key = lib80211_tkip_get_key, | ||
764 | .print_stats = lib80211_tkip_print_stats, | ||
765 | .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */ | ||
766 | .extra_mpdu_postfix_len = 4, /* ICV */ | ||
767 | .extra_msdu_postfix_len = 8, /* MIC */ | ||
768 | .get_flags = lib80211_tkip_get_flags, | ||
769 | .set_flags = lib80211_tkip_set_flags, | ||
770 | .owner = THIS_MODULE, | ||
771 | }; | ||
772 | |||
773 | static int __init lib80211_crypto_tkip_init(void) | ||
774 | { | ||
775 | return lib80211_register_crypto_ops(&lib80211_crypt_tkip); | ||
776 | } | ||
777 | |||
778 | static void __exit lib80211_crypto_tkip_exit(void) | ||
779 | { | ||
780 | lib80211_unregister_crypto_ops(&lib80211_crypt_tkip); | ||
781 | } | ||
782 | |||
783 | module_init(lib80211_crypto_tkip_init); | ||
784 | module_exit(lib80211_crypto_tkip_exit); | ||
diff --git a/net/wireless/lib80211_crypt_wep.c b/net/wireless/lib80211_crypt_wep.c new file mode 100644 index 000000000000..6d41e05ca33b --- /dev/null +++ b/net/wireless/lib80211_crypt_wep.c | |||
@@ -0,0 +1,296 @@ | |||
1 | /* | ||
2 | * lib80211 crypt: host-based WEP encryption implementation for lib80211 | ||
3 | * | ||
4 | * Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi> | ||
5 | * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com> | ||
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. See README and COPYING for | ||
10 | * more details. | ||
11 | */ | ||
12 | |||
13 | #include <linux/err.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/random.h> | ||
18 | #include <linux/scatterlist.h> | ||
19 | #include <linux/skbuff.h> | ||
20 | #include <linux/mm.h> | ||
21 | #include <asm/string.h> | ||
22 | |||
23 | #include <net/lib80211.h> | ||
24 | |||
25 | #include <linux/crypto.h> | ||
26 | #include <linux/crc32.h> | ||
27 | |||
28 | MODULE_AUTHOR("Jouni Malinen"); | ||
29 | MODULE_DESCRIPTION("lib80211 crypt: WEP"); | ||
30 | MODULE_LICENSE("GPL"); | ||
31 | |||
32 | struct lib80211_wep_data { | ||
33 | u32 iv; | ||
34 | #define WEP_KEY_LEN 13 | ||
35 | u8 key[WEP_KEY_LEN + 1]; | ||
36 | u8 key_len; | ||
37 | u8 key_idx; | ||
38 | struct crypto_blkcipher *tx_tfm; | ||
39 | struct crypto_blkcipher *rx_tfm; | ||
40 | }; | ||
41 | |||
42 | static void *lib80211_wep_init(int keyidx) | ||
43 | { | ||
44 | struct lib80211_wep_data *priv; | ||
45 | |||
46 | priv = kzalloc(sizeof(*priv), GFP_ATOMIC); | ||
47 | if (priv == NULL) | ||
48 | goto fail; | ||
49 | priv->key_idx = keyidx; | ||
50 | |||
51 | priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); | ||
52 | if (IS_ERR(priv->tx_tfm)) { | ||
53 | printk(KERN_DEBUG "lib80211_crypt_wep: could not allocate " | ||
54 | "crypto API arc4\n"); | ||
55 | priv->tx_tfm = NULL; | ||
56 | goto fail; | ||
57 | } | ||
58 | |||
59 | priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); | ||
60 | if (IS_ERR(priv->rx_tfm)) { | ||
61 | printk(KERN_DEBUG "lib80211_crypt_wep: could not allocate " | ||
62 | "crypto API arc4\n"); | ||
63 | priv->rx_tfm = NULL; | ||
64 | goto fail; | ||
65 | } | ||
66 | /* start WEP IV from a random value */ | ||
67 | get_random_bytes(&priv->iv, 4); | ||
68 | |||
69 | return priv; | ||
70 | |||
71 | fail: | ||
72 | if (priv) { | ||
73 | if (priv->tx_tfm) | ||
74 | crypto_free_blkcipher(priv->tx_tfm); | ||
75 | if (priv->rx_tfm) | ||
76 | crypto_free_blkcipher(priv->rx_tfm); | ||
77 | kfree(priv); | ||
78 | } | ||
79 | return NULL; | ||
80 | } | ||
81 | |||
82 | static void lib80211_wep_deinit(void *priv) | ||
83 | { | ||
84 | struct lib80211_wep_data *_priv = priv; | ||
85 | if (_priv) { | ||
86 | if (_priv->tx_tfm) | ||
87 | crypto_free_blkcipher(_priv->tx_tfm); | ||
88 | if (_priv->rx_tfm) | ||
89 | crypto_free_blkcipher(_priv->rx_tfm); | ||
90 | } | ||
91 | kfree(priv); | ||
92 | } | ||
93 | |||
94 | /* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */ | ||
95 | static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len, | ||
96 | u8 *key, int keylen, void *priv) | ||
97 | { | ||
98 | struct lib80211_wep_data *wep = priv; | ||
99 | u32 klen, len; | ||
100 | u8 *pos; | ||
101 | |||
102 | if (skb_headroom(skb) < 4 || skb->len < hdr_len) | ||
103 | return -1; | ||
104 | |||
105 | len = skb->len - hdr_len; | ||
106 | pos = skb_push(skb, 4); | ||
107 | memmove(pos, pos + 4, hdr_len); | ||
108 | pos += hdr_len; | ||
109 | |||
110 | klen = 3 + wep->key_len; | ||
111 | |||
112 | wep->iv++; | ||
113 | |||
114 | /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key | ||
115 | * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N) | ||
116 | * can be used to speedup attacks, so avoid using them. */ | ||
117 | if ((wep->iv & 0xff00) == 0xff00) { | ||
118 | u8 B = (wep->iv >> 16) & 0xff; | ||
119 | if (B >= 3 && B < klen) | ||
120 | wep->iv += 0x0100; | ||
121 | } | ||
122 | |||
123 | /* Prepend 24-bit IV to RC4 key and TX frame */ | ||
124 | *pos++ = (wep->iv >> 16) & 0xff; | ||
125 | *pos++ = (wep->iv >> 8) & 0xff; | ||
126 | *pos++ = wep->iv & 0xff; | ||
127 | *pos++ = wep->key_idx << 6; | ||
128 | |||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | /* Perform WEP encryption on given skb that has at least 4 bytes of headroom | ||
133 | * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted, | ||
134 | * so the payload length increases with 8 bytes. | ||
135 | * | ||
136 | * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) | ||
137 | */ | ||
138 | static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) | ||
139 | { | ||
140 | struct lib80211_wep_data *wep = priv; | ||
141 | struct blkcipher_desc desc = { .tfm = wep->tx_tfm }; | ||
142 | u32 crc, klen, len; | ||
143 | u8 *pos, *icv; | ||
144 | struct scatterlist sg; | ||
145 | u8 key[WEP_KEY_LEN + 3]; | ||
146 | |||
147 | /* other checks are in lib80211_wep_build_iv */ | ||
148 | if (skb_tailroom(skb) < 4) | ||
149 | return -1; | ||
150 | |||
151 | /* add the IV to the frame */ | ||
152 | if (lib80211_wep_build_iv(skb, hdr_len, NULL, 0, priv)) | ||
153 | return -1; | ||
154 | |||
155 | /* Copy the IV into the first 3 bytes of the key */ | ||
156 | skb_copy_from_linear_data_offset(skb, hdr_len, key, 3); | ||
157 | |||
158 | /* Copy rest of the WEP key (the secret part) */ | ||
159 | memcpy(key + 3, wep->key, wep->key_len); | ||
160 | |||
161 | len = skb->len - hdr_len - 4; | ||
162 | pos = skb->data + hdr_len + 4; | ||
163 | klen = 3 + wep->key_len; | ||
164 | |||
165 | /* Append little-endian CRC32 over only the data and encrypt it to produce ICV */ | ||
166 | crc = ~crc32_le(~0, pos, len); | ||
167 | icv = skb_put(skb, 4); | ||
168 | icv[0] = crc; | ||
169 | icv[1] = crc >> 8; | ||
170 | icv[2] = crc >> 16; | ||
171 | icv[3] = crc >> 24; | ||
172 | |||
173 | crypto_blkcipher_setkey(wep->tx_tfm, key, klen); | ||
174 | sg_init_one(&sg, pos, len + 4); | ||
175 | return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4); | ||
176 | } | ||
177 | |||
178 | /* Perform WEP decryption on given buffer. Buffer includes whole WEP part of | ||
179 | * the frame: IV (4 bytes), encrypted payload (including SNAP header), | ||
180 | * ICV (4 bytes). len includes both IV and ICV. | ||
181 | * | ||
182 | * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on | ||
183 | * failure. If frame is OK, IV and ICV will be removed. | ||
184 | */ | ||
185 | static int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) | ||
186 | { | ||
187 | struct lib80211_wep_data *wep = priv; | ||
188 | struct blkcipher_desc desc = { .tfm = wep->rx_tfm }; | ||
189 | u32 crc, klen, plen; | ||
190 | u8 key[WEP_KEY_LEN + 3]; | ||
191 | u8 keyidx, *pos, icv[4]; | ||
192 | struct scatterlist sg; | ||
193 | |||
194 | if (skb->len < hdr_len + 8) | ||
195 | return -1; | ||
196 | |||
197 | pos = skb->data + hdr_len; | ||
198 | key[0] = *pos++; | ||
199 | key[1] = *pos++; | ||
200 | key[2] = *pos++; | ||
201 | keyidx = *pos++ >> 6; | ||
202 | if (keyidx != wep->key_idx) | ||
203 | return -1; | ||
204 | |||
205 | klen = 3 + wep->key_len; | ||
206 | |||
207 | /* Copy rest of the WEP key (the secret part) */ | ||
208 | memcpy(key + 3, wep->key, wep->key_len); | ||
209 | |||
210 | /* Apply RC4 to data and compute CRC32 over decrypted data */ | ||
211 | plen = skb->len - hdr_len - 8; | ||
212 | |||
213 | crypto_blkcipher_setkey(wep->rx_tfm, key, klen); | ||
214 | sg_init_one(&sg, pos, plen + 4); | ||
215 | if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) | ||
216 | return -7; | ||
217 | |||
218 | crc = ~crc32_le(~0, pos, plen); | ||
219 | icv[0] = crc; | ||
220 | icv[1] = crc >> 8; | ||
221 | icv[2] = crc >> 16; | ||
222 | icv[3] = crc >> 24; | ||
223 | if (memcmp(icv, pos + plen, 4) != 0) { | ||
224 | /* ICV mismatch - drop frame */ | ||
225 | return -2; | ||
226 | } | ||
227 | |||
228 | /* Remove IV and ICV */ | ||
229 | memmove(skb->data + 4, skb->data, hdr_len); | ||
230 | skb_pull(skb, 4); | ||
231 | skb_trim(skb, skb->len - 4); | ||
232 | |||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | static int lib80211_wep_set_key(void *key, int len, u8 * seq, void *priv) | ||
237 | { | ||
238 | struct lib80211_wep_data *wep = priv; | ||
239 | |||
240 | if (len < 0 || len > WEP_KEY_LEN) | ||
241 | return -1; | ||
242 | |||
243 | memcpy(wep->key, key, len); | ||
244 | wep->key_len = len; | ||
245 | |||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static int lib80211_wep_get_key(void *key, int len, u8 * seq, void *priv) | ||
250 | { | ||
251 | struct lib80211_wep_data *wep = priv; | ||
252 | |||
253 | if (len < wep->key_len) | ||
254 | return -1; | ||
255 | |||
256 | memcpy(key, wep->key, wep->key_len); | ||
257 | |||
258 | return wep->key_len; | ||
259 | } | ||
260 | |||
261 | static char *lib80211_wep_print_stats(char *p, void *priv) | ||
262 | { | ||
263 | struct lib80211_wep_data *wep = priv; | ||
264 | p += sprintf(p, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len); | ||
265 | return p; | ||
266 | } | ||
267 | |||
268 | static struct lib80211_crypto_ops lib80211_crypt_wep = { | ||
269 | .name = "WEP", | ||
270 | .init = lib80211_wep_init, | ||
271 | .deinit = lib80211_wep_deinit, | ||
272 | .build_iv = lib80211_wep_build_iv, | ||
273 | .encrypt_mpdu = lib80211_wep_encrypt, | ||
274 | .decrypt_mpdu = lib80211_wep_decrypt, | ||
275 | .encrypt_msdu = NULL, | ||
276 | .decrypt_msdu = NULL, | ||
277 | .set_key = lib80211_wep_set_key, | ||
278 | .get_key = lib80211_wep_get_key, | ||
279 | .print_stats = lib80211_wep_print_stats, | ||
280 | .extra_mpdu_prefix_len = 4, /* IV */ | ||
281 | .extra_mpdu_postfix_len = 4, /* ICV */ | ||
282 | .owner = THIS_MODULE, | ||
283 | }; | ||
284 | |||
285 | static int __init lib80211_crypto_wep_init(void) | ||
286 | { | ||
287 | return lib80211_register_crypto_ops(&lib80211_crypt_wep); | ||
288 | } | ||
289 | |||
290 | static void __exit lib80211_crypto_wep_exit(void) | ||
291 | { | ||
292 | lib80211_unregister_crypto_ops(&lib80211_crypt_wep); | ||
293 | } | ||
294 | |||
295 | module_init(lib80211_crypto_wep_init); | ||
296 | module_exit(lib80211_crypto_wep_exit); | ||
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 572793c8c7ab..1e728fff474e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -58,6 +58,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
58 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, | 58 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, |
59 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, | 59 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, |
60 | .len = BUS_ID_SIZE-1 }, | 60 | .len = BUS_ID_SIZE-1 }, |
61 | [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, | ||
62 | [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, | ||
63 | [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 }, | ||
61 | 64 | ||
62 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, | 65 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, |
63 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, | 66 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, |
@@ -84,7 +87,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
84 | .len = NL80211_MAX_SUPP_RATES }, | 87 | .len = NL80211_MAX_SUPP_RATES }, |
85 | [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 }, | 88 | [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 }, |
86 | [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, | 89 | [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, |
87 | [NL80211_ATTR_MNTR_FLAGS] = { .type = NLA_NESTED }, | 90 | [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ }, |
88 | [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, | 91 | [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, |
89 | .len = IEEE80211_MAX_MESH_ID_LEN }, | 92 | .len = IEEE80211_MAX_MESH_ID_LEN }, |
90 | [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, | 93 | [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, |
@@ -95,6 +98,10 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
95 | [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, | 98 | [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, |
96 | [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, | 99 | [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, |
97 | [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, | 100 | [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, |
101 | [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY, | ||
102 | .len = NL80211_MAX_SUPP_RATES }, | ||
103 | |||
104 | [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED }, | ||
98 | 105 | ||
99 | [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, | 106 | [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, |
100 | .len = NL80211_HT_CAPABILITY_LEN }, | 107 | .len = NL80211_HT_CAPABILITY_LEN }, |
@@ -157,6 +164,19 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
157 | if (!nl_band) | 164 | if (!nl_band) |
158 | goto nla_put_failure; | 165 | goto nla_put_failure; |
159 | 166 | ||
167 | /* add HT info */ | ||
168 | if (dev->wiphy.bands[band]->ht_cap.ht_supported) { | ||
169 | NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET, | ||
170 | sizeof(dev->wiphy.bands[band]->ht_cap.mcs), | ||
171 | &dev->wiphy.bands[band]->ht_cap.mcs); | ||
172 | NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA, | ||
173 | dev->wiphy.bands[band]->ht_cap.cap); | ||
174 | NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR, | ||
175 | dev->wiphy.bands[band]->ht_cap.ampdu_factor); | ||
176 | NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY, | ||
177 | dev->wiphy.bands[band]->ht_cap.ampdu_density); | ||
178 | } | ||
179 | |||
160 | /* add frequencies */ | 180 | /* add frequencies */ |
161 | nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS); | 181 | nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS); |
162 | if (!nl_freqs) | 182 | if (!nl_freqs) |
@@ -180,6 +200,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
180 | if (chan->flags & IEEE80211_CHAN_RADAR) | 200 | if (chan->flags & IEEE80211_CHAN_RADAR) |
181 | NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR); | 201 | NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR); |
182 | 202 | ||
203 | NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, | ||
204 | DBM_TO_MBM(chan->max_power)); | ||
205 | |||
183 | nla_nest_end(msg, nl_freq); | 206 | nla_nest_end(msg, nl_freq); |
184 | } | 207 | } |
185 | 208 | ||
@@ -269,20 +292,142 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
269 | return -ENOBUFS; | 292 | return -ENOBUFS; |
270 | } | 293 | } |
271 | 294 | ||
295 | static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = { | ||
296 | [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 }, | ||
297 | [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 }, | ||
298 | [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 }, | ||
299 | [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 }, | ||
300 | [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 }, | ||
301 | }; | ||
302 | |||
303 | static int parse_txq_params(struct nlattr *tb[], | ||
304 | struct ieee80211_txq_params *txq_params) | ||
305 | { | ||
306 | if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] || | ||
307 | !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] || | ||
308 | !tb[NL80211_TXQ_ATTR_AIFS]) | ||
309 | return -EINVAL; | ||
310 | |||
311 | txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]); | ||
312 | txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]); | ||
313 | txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]); | ||
314 | txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]); | ||
315 | txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]); | ||
316 | |||
317 | return 0; | ||
318 | } | ||
319 | |||
272 | static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | 320 | static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) |
273 | { | 321 | { |
274 | struct cfg80211_registered_device *rdev; | 322 | struct cfg80211_registered_device *rdev; |
275 | int result; | 323 | int result = 0, rem_txq_params = 0; |
276 | 324 | struct nlattr *nl_txq_params; | |
277 | if (!info->attrs[NL80211_ATTR_WIPHY_NAME]) | ||
278 | return -EINVAL; | ||
279 | 325 | ||
280 | rdev = cfg80211_get_dev_from_info(info); | 326 | rdev = cfg80211_get_dev_from_info(info); |
281 | if (IS_ERR(rdev)) | 327 | if (IS_ERR(rdev)) |
282 | return PTR_ERR(rdev); | 328 | return PTR_ERR(rdev); |
283 | 329 | ||
284 | result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); | 330 | if (info->attrs[NL80211_ATTR_WIPHY_NAME]) { |
331 | result = cfg80211_dev_rename( | ||
332 | rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); | ||
333 | if (result) | ||
334 | goto bad_res; | ||
335 | } | ||
336 | |||
337 | if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) { | ||
338 | struct ieee80211_txq_params txq_params; | ||
339 | struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1]; | ||
340 | |||
341 | if (!rdev->ops->set_txq_params) { | ||
342 | result = -EOPNOTSUPP; | ||
343 | goto bad_res; | ||
344 | } | ||
345 | |||
346 | nla_for_each_nested(nl_txq_params, | ||
347 | info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], | ||
348 | rem_txq_params) { | ||
349 | nla_parse(tb, NL80211_TXQ_ATTR_MAX, | ||
350 | nla_data(nl_txq_params), | ||
351 | nla_len(nl_txq_params), | ||
352 | txq_params_policy); | ||
353 | result = parse_txq_params(tb, &txq_params); | ||
354 | if (result) | ||
355 | goto bad_res; | ||
356 | |||
357 | result = rdev->ops->set_txq_params(&rdev->wiphy, | ||
358 | &txq_params); | ||
359 | if (result) | ||
360 | goto bad_res; | ||
361 | } | ||
362 | } | ||
363 | |||
364 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | ||
365 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
366 | struct ieee80211_channel *chan; | ||
367 | struct ieee80211_sta_ht_cap *ht_cap; | ||
368 | u32 freq, sec_freq; | ||
369 | |||
370 | if (!rdev->ops->set_channel) { | ||
371 | result = -EOPNOTSUPP; | ||
372 | goto bad_res; | ||
373 | } | ||
374 | |||
375 | result = -EINVAL; | ||
376 | |||
377 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
378 | channel_type = nla_get_u32(info->attrs[ | ||
379 | NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
380 | if (channel_type != NL80211_CHAN_NO_HT && | ||
381 | channel_type != NL80211_CHAN_HT20 && | ||
382 | channel_type != NL80211_CHAN_HT40PLUS && | ||
383 | channel_type != NL80211_CHAN_HT40MINUS) | ||
384 | goto bad_res; | ||
385 | } | ||
386 | |||
387 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
388 | chan = ieee80211_get_channel(&rdev->wiphy, freq); | ||
389 | |||
390 | /* Primary channel not allowed */ | ||
391 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) | ||
392 | goto bad_res; | ||
393 | |||
394 | if (channel_type == NL80211_CHAN_HT40MINUS) | ||
395 | sec_freq = freq - 20; | ||
396 | else if (channel_type == NL80211_CHAN_HT40PLUS) | ||
397 | sec_freq = freq + 20; | ||
398 | else | ||
399 | sec_freq = 0; | ||
400 | |||
401 | ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; | ||
285 | 402 | ||
403 | /* no HT capabilities */ | ||
404 | if (channel_type != NL80211_CHAN_NO_HT && | ||
405 | !ht_cap->ht_supported) | ||
406 | goto bad_res; | ||
407 | |||
408 | if (sec_freq) { | ||
409 | struct ieee80211_channel *schan; | ||
410 | |||
411 | /* no 40 MHz capabilities */ | ||
412 | if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || | ||
413 | (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) | ||
414 | goto bad_res; | ||
415 | |||
416 | schan = ieee80211_get_channel(&rdev->wiphy, sec_freq); | ||
417 | |||
418 | /* Secondary channel not allowed */ | ||
419 | if (!schan || schan->flags & IEEE80211_CHAN_DISABLED) | ||
420 | goto bad_res; | ||
421 | } | ||
422 | |||
423 | result = rdev->ops->set_channel(&rdev->wiphy, chan, | ||
424 | channel_type); | ||
425 | if (result) | ||
426 | goto bad_res; | ||
427 | } | ||
428 | |||
429 | |||
430 | bad_res: | ||
286 | cfg80211_put_dev(rdev); | 431 | cfg80211_put_dev(rdev); |
287 | return result; | 432 | return result; |
288 | } | 433 | } |
@@ -945,12 +1090,46 @@ static int parse_station_flags(struct nlattr *nla, u32 *staflags) | |||
945 | return 0; | 1090 | return 0; |
946 | } | 1091 | } |
947 | 1092 | ||
1093 | static u16 nl80211_calculate_bitrate(struct rate_info *rate) | ||
1094 | { | ||
1095 | int modulation, streams, bitrate; | ||
1096 | |||
1097 | if (!(rate->flags & RATE_INFO_FLAGS_MCS)) | ||
1098 | return rate->legacy; | ||
1099 | |||
1100 | /* the formula below does only work for MCS values smaller than 32 */ | ||
1101 | if (rate->mcs >= 32) | ||
1102 | return 0; | ||
1103 | |||
1104 | modulation = rate->mcs & 7; | ||
1105 | streams = (rate->mcs >> 3) + 1; | ||
1106 | |||
1107 | bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? | ||
1108 | 13500000 : 6500000; | ||
1109 | |||
1110 | if (modulation < 4) | ||
1111 | bitrate *= (modulation + 1); | ||
1112 | else if (modulation == 4) | ||
1113 | bitrate *= (modulation + 2); | ||
1114 | else | ||
1115 | bitrate *= (modulation + 3); | ||
1116 | |||
1117 | bitrate *= streams; | ||
1118 | |||
1119 | if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) | ||
1120 | bitrate = (bitrate / 9) * 10; | ||
1121 | |||
1122 | /* do NOT round down here */ | ||
1123 | return (bitrate + 50000) / 100000; | ||
1124 | } | ||
1125 | |||
948 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | 1126 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, |
949 | int flags, struct net_device *dev, | 1127 | int flags, struct net_device *dev, |
950 | u8 *mac_addr, struct station_info *sinfo) | 1128 | u8 *mac_addr, struct station_info *sinfo) |
951 | { | 1129 | { |
952 | void *hdr; | 1130 | void *hdr; |
953 | struct nlattr *sinfoattr; | 1131 | struct nlattr *sinfoattr, *txrate; |
1132 | u16 bitrate; | ||
954 | 1133 | ||
955 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); | 1134 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); |
956 | if (!hdr) | 1135 | if (!hdr) |
@@ -980,7 +1159,29 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | |||
980 | if (sinfo->filled & STATION_INFO_PLINK_STATE) | 1159 | if (sinfo->filled & STATION_INFO_PLINK_STATE) |
981 | NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE, | 1160 | NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE, |
982 | sinfo->plink_state); | 1161 | sinfo->plink_state); |
1162 | if (sinfo->filled & STATION_INFO_SIGNAL) | ||
1163 | NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL, | ||
1164 | sinfo->signal); | ||
1165 | if (sinfo->filled & STATION_INFO_TX_BITRATE) { | ||
1166 | txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE); | ||
1167 | if (!txrate) | ||
1168 | goto nla_put_failure; | ||
1169 | |||
1170 | /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */ | ||
1171 | bitrate = nl80211_calculate_bitrate(&sinfo->txrate); | ||
1172 | if (bitrate > 0) | ||
1173 | NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); | ||
1174 | |||
1175 | if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS) | ||
1176 | NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, | ||
1177 | sinfo->txrate.mcs); | ||
1178 | if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) | ||
1179 | NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH); | ||
1180 | if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI) | ||
1181 | NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI); | ||
983 | 1182 | ||
1183 | nla_nest_end(msg, txrate); | ||
1184 | } | ||
984 | nla_nest_end(msg, sinfoattr); | 1185 | nla_nest_end(msg, sinfoattr); |
985 | 1186 | ||
986 | return genlmsg_end(msg, hdr); | 1187 | return genlmsg_end(msg, hdr); |
@@ -1598,6 +1799,12 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
1598 | if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) | 1799 | if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) |
1599 | params.use_short_slot_time = | 1800 | params.use_short_slot_time = |
1600 | nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); | 1801 | nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); |
1802 | if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { | ||
1803 | params.basic_rates = | ||
1804 | nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | ||
1805 | params.basic_rates_len = | ||
1806 | nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | ||
1807 | } | ||
1601 | 1808 | ||
1602 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | 1809 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); |
1603 | if (err) | 1810 | if (err) |
@@ -1680,11 +1887,188 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
1680 | return -EINVAL; | 1887 | return -EINVAL; |
1681 | #endif | 1888 | #endif |
1682 | mutex_lock(&cfg80211_drv_mutex); | 1889 | mutex_lock(&cfg80211_drv_mutex); |
1683 | r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, NULL); | 1890 | r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY); |
1684 | mutex_unlock(&cfg80211_drv_mutex); | 1891 | mutex_unlock(&cfg80211_drv_mutex); |
1685 | return r; | 1892 | return r; |
1686 | } | 1893 | } |
1687 | 1894 | ||
1895 | static int nl80211_get_mesh_params(struct sk_buff *skb, | ||
1896 | struct genl_info *info) | ||
1897 | { | ||
1898 | struct cfg80211_registered_device *drv; | ||
1899 | struct mesh_config cur_params; | ||
1900 | int err; | ||
1901 | struct net_device *dev; | ||
1902 | void *hdr; | ||
1903 | struct nlattr *pinfoattr; | ||
1904 | struct sk_buff *msg; | ||
1905 | |||
1906 | /* Look up our device */ | ||
1907 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | ||
1908 | if (err) | ||
1909 | return err; | ||
1910 | |||
1911 | /* Get the mesh params */ | ||
1912 | rtnl_lock(); | ||
1913 | err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params); | ||
1914 | rtnl_unlock(); | ||
1915 | if (err) | ||
1916 | goto out; | ||
1917 | |||
1918 | /* Draw up a netlink message to send back */ | ||
1919 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
1920 | if (!msg) { | ||
1921 | err = -ENOBUFS; | ||
1922 | goto out; | ||
1923 | } | ||
1924 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
1925 | NL80211_CMD_GET_MESH_PARAMS); | ||
1926 | if (!hdr) | ||
1927 | goto nla_put_failure; | ||
1928 | pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS); | ||
1929 | if (!pinfoattr) | ||
1930 | goto nla_put_failure; | ||
1931 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | ||
1932 | NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT, | ||
1933 | cur_params.dot11MeshRetryTimeout); | ||
1934 | NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT, | ||
1935 | cur_params.dot11MeshConfirmTimeout); | ||
1936 | NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT, | ||
1937 | cur_params.dot11MeshHoldingTimeout); | ||
1938 | NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS, | ||
1939 | cur_params.dot11MeshMaxPeerLinks); | ||
1940 | NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES, | ||
1941 | cur_params.dot11MeshMaxRetries); | ||
1942 | NLA_PUT_U8(msg, NL80211_MESHCONF_TTL, | ||
1943 | cur_params.dot11MeshTTL); | ||
1944 | NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, | ||
1945 | cur_params.auto_open_plinks); | ||
1946 | NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, | ||
1947 | cur_params.dot11MeshHWMPmaxPREQretries); | ||
1948 | NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME, | ||
1949 | cur_params.path_refresh_time); | ||
1950 | NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, | ||
1951 | cur_params.min_discovery_timeout); | ||
1952 | NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, | ||
1953 | cur_params.dot11MeshHWMPactivePathTimeout); | ||
1954 | NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, | ||
1955 | cur_params.dot11MeshHWMPpreqMinInterval); | ||
1956 | NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | ||
1957 | cur_params.dot11MeshHWMPnetDiameterTraversalTime); | ||
1958 | nla_nest_end(msg, pinfoattr); | ||
1959 | genlmsg_end(msg, hdr); | ||
1960 | err = genlmsg_unicast(msg, info->snd_pid); | ||
1961 | goto out; | ||
1962 | |||
1963 | nla_put_failure: | ||
1964 | genlmsg_cancel(msg, hdr); | ||
1965 | err = -EMSGSIZE; | ||
1966 | out: | ||
1967 | /* Cleanup */ | ||
1968 | cfg80211_put_dev(drv); | ||
1969 | dev_put(dev); | ||
1970 | return err; | ||
1971 | } | ||
1972 | |||
1973 | #define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \ | ||
1974 | do {\ | ||
1975 | if (table[attr_num]) {\ | ||
1976 | cfg.param = nla_fn(table[attr_num]); \ | ||
1977 | mask |= (1 << (attr_num - 1)); \ | ||
1978 | } \ | ||
1979 | } while (0);\ | ||
1980 | |||
1981 | static struct nla_policy | ||
1982 | nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = { | ||
1983 | [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, | ||
1984 | [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, | ||
1985 | [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 }, | ||
1986 | [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 }, | ||
1987 | [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 }, | ||
1988 | [NL80211_MESHCONF_TTL] = { .type = NLA_U8 }, | ||
1989 | [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 }, | ||
1990 | |||
1991 | [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 }, | ||
1992 | [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 }, | ||
1993 | [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 }, | ||
1994 | [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 }, | ||
1995 | [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 }, | ||
1996 | [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 }, | ||
1997 | }; | ||
1998 | |||
1999 | static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) | ||
2000 | { | ||
2001 | int err; | ||
2002 | u32 mask; | ||
2003 | struct cfg80211_registered_device *drv; | ||
2004 | struct net_device *dev; | ||
2005 | struct mesh_config cfg; | ||
2006 | struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; | ||
2007 | struct nlattr *parent_attr; | ||
2008 | |||
2009 | parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS]; | ||
2010 | if (!parent_attr) | ||
2011 | return -EINVAL; | ||
2012 | if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX, | ||
2013 | parent_attr, nl80211_meshconf_params_policy)) | ||
2014 | return -EINVAL; | ||
2015 | |||
2016 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | ||
2017 | if (err) | ||
2018 | return err; | ||
2019 | |||
2020 | /* This makes sure that there aren't more than 32 mesh config | ||
2021 | * parameters (otherwise our bitfield scheme would not work.) */ | ||
2022 | BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32); | ||
2023 | |||
2024 | /* Fill in the params struct */ | ||
2025 | mask = 0; | ||
2026 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, | ||
2027 | mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16); | ||
2028 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, | ||
2029 | mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16); | ||
2030 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, | ||
2031 | mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16); | ||
2032 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, | ||
2033 | mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16); | ||
2034 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, | ||
2035 | mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8); | ||
2036 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, | ||
2037 | mask, NL80211_MESHCONF_TTL, nla_get_u8); | ||
2038 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, | ||
2039 | mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8); | ||
2040 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, | ||
2041 | mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, | ||
2042 | nla_get_u8); | ||
2043 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, | ||
2044 | mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32); | ||
2045 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, | ||
2046 | mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, | ||
2047 | nla_get_u16); | ||
2048 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, | ||
2049 | mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, | ||
2050 | nla_get_u32); | ||
2051 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, | ||
2052 | mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, | ||
2053 | nla_get_u16); | ||
2054 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, | ||
2055 | dot11MeshHWMPnetDiameterTraversalTime, | ||
2056 | mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | ||
2057 | nla_get_u16); | ||
2058 | |||
2059 | /* Apply changes */ | ||
2060 | rtnl_lock(); | ||
2061 | err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask); | ||
2062 | rtnl_unlock(); | ||
2063 | |||
2064 | /* cleanup */ | ||
2065 | cfg80211_put_dev(drv); | ||
2066 | dev_put(dev); | ||
2067 | return err; | ||
2068 | } | ||
2069 | |||
2070 | #undef FILL_IN_MESH_PARAM_IF_SET | ||
2071 | |||
1688 | static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | 2072 | static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) |
1689 | { | 2073 | { |
1690 | struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; | 2074 | struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; |
@@ -1743,12 +2127,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
1743 | mutex_lock(&cfg80211_drv_mutex); | 2127 | mutex_lock(&cfg80211_drv_mutex); |
1744 | r = set_regdom(rd); | 2128 | r = set_regdom(rd); |
1745 | mutex_unlock(&cfg80211_drv_mutex); | 2129 | mutex_unlock(&cfg80211_drv_mutex); |
1746 | if (r) | ||
1747 | goto bad_reg; | ||
1748 | |||
1749 | return r; | 2130 | return r; |
1750 | 2131 | ||
1751 | bad_reg: | 2132 | bad_reg: |
1752 | kfree(rd); | 2133 | kfree(rd); |
1753 | return -EINVAL; | 2134 | return -EINVAL; |
1754 | } | 2135 | } |
@@ -1902,6 +2283,18 @@ static struct genl_ops nl80211_ops[] = { | |||
1902 | .policy = nl80211_policy, | 2283 | .policy = nl80211_policy, |
1903 | .flags = GENL_ADMIN_PERM, | 2284 | .flags = GENL_ADMIN_PERM, |
1904 | }, | 2285 | }, |
2286 | { | ||
2287 | .cmd = NL80211_CMD_GET_MESH_PARAMS, | ||
2288 | .doit = nl80211_get_mesh_params, | ||
2289 | .policy = nl80211_policy, | ||
2290 | /* can be retrieved by unprivileged users */ | ||
2291 | }, | ||
2292 | { | ||
2293 | .cmd = NL80211_CMD_SET_MESH_PARAMS, | ||
2294 | .doit = nl80211_set_mesh_params, | ||
2295 | .policy = nl80211_policy, | ||
2296 | .flags = GENL_ADMIN_PERM, | ||
2297 | }, | ||
1905 | }; | 2298 | }; |
1906 | 2299 | ||
1907 | /* multicast groups */ | 2300 | /* multicast groups */ |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index eb3b1a9f9b12..4f877535e666 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -42,17 +42,40 @@ | |||
42 | #include "core.h" | 42 | #include "core.h" |
43 | #include "reg.h" | 43 | #include "reg.h" |
44 | 44 | ||
45 | /* wiphy is set if this request's initiator is REGDOM_SET_BY_DRIVER */ | 45 | /** |
46 | * struct regulatory_request - receipt of last regulatory request | ||
47 | * | ||
48 | * @wiphy: this is set if this request's initiator is | ||
49 | * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This | ||
50 | * can be used by the wireless core to deal with conflicts | ||
51 | * and potentially inform users of which devices specifically | ||
52 | * cased the conflicts. | ||
53 | * @initiator: indicates who sent this request, could be any of | ||
54 | * of those set in reg_set_by, %REGDOM_SET_BY_* | ||
55 | * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested | ||
56 | * regulatory domain. We have a few special codes: | ||
57 | * 00 - World regulatory domain | ||
58 | * 99 - built by driver but a specific alpha2 cannot be determined | ||
59 | * 98 - result of an intersection between two regulatory domains | ||
60 | * @intersect: indicates whether the wireless core should intersect | ||
61 | * the requested regulatory domain with the presently set regulatory | ||
62 | * domain. | ||
63 | * @country_ie_checksum: checksum of the last processed and accepted | ||
64 | * country IE | ||
65 | * @country_ie_env: lets us know if the AP is telling us we are outdoor, | ||
66 | * indoor, or if it doesn't matter | ||
67 | */ | ||
46 | struct regulatory_request { | 68 | struct regulatory_request { |
47 | struct list_head list; | ||
48 | struct wiphy *wiphy; | 69 | struct wiphy *wiphy; |
49 | int granted; | ||
50 | enum reg_set_by initiator; | 70 | enum reg_set_by initiator; |
51 | char alpha2[2]; | 71 | char alpha2[2]; |
72 | bool intersect; | ||
73 | u32 country_ie_checksum; | ||
74 | enum environment_cap country_ie_env; | ||
52 | }; | 75 | }; |
53 | 76 | ||
54 | static LIST_HEAD(regulatory_requests); | 77 | /* Receipt of information from last regulatory request */ |
55 | DEFINE_MUTEX(cfg80211_reg_mutex); | 78 | static struct regulatory_request *last_request; |
56 | 79 | ||
57 | /* To trigger userspace events */ | 80 | /* To trigger userspace events */ |
58 | static struct platform_device *reg_pdev; | 81 | static struct platform_device *reg_pdev; |
@@ -63,13 +86,16 @@ static u32 supported_bandwidths[] = { | |||
63 | MHZ_TO_KHZ(20), | 86 | MHZ_TO_KHZ(20), |
64 | }; | 87 | }; |
65 | 88 | ||
66 | static struct list_head regulatory_requests; | ||
67 | |||
68 | /* Central wireless core regulatory domains, we only need two, | 89 | /* Central wireless core regulatory domains, we only need two, |
69 | * the current one and a world regulatory domain in case we have no | 90 | * the current one and a world regulatory domain in case we have no |
70 | * information to give us an alpha2 */ | 91 | * information to give us an alpha2 */ |
71 | static const struct ieee80211_regdomain *cfg80211_regdomain; | 92 | static const struct ieee80211_regdomain *cfg80211_regdomain; |
72 | 93 | ||
94 | /* We use this as a place for the rd structure built from the | ||
95 | * last parsed country IE to rest until CRDA gets back to us with | ||
96 | * what it thinks should apply for the same country */ | ||
97 | static const struct ieee80211_regdomain *country_ie_regdomain; | ||
98 | |||
73 | /* We keep a static world regulatory domain in case of the absence of CRDA */ | 99 | /* We keep a static world regulatory domain in case of the absence of CRDA */ |
74 | static const struct ieee80211_regdomain world_regdom = { | 100 | static const struct ieee80211_regdomain world_regdom = { |
75 | .n_reg_rules = 1, | 101 | .n_reg_rules = 1, |
@@ -204,7 +230,7 @@ static void reset_regdomains(void) | |||
204 | * core upon initialization */ | 230 | * core upon initialization */ |
205 | static void update_world_regdomain(const struct ieee80211_regdomain *rd) | 231 | static void update_world_regdomain(const struct ieee80211_regdomain *rd) |
206 | { | 232 | { |
207 | BUG_ON(list_empty(®ulatory_requests)); | 233 | BUG_ON(!last_request); |
208 | 234 | ||
209 | reset_regdomains(); | 235 | reset_regdomains(); |
210 | 236 | ||
@@ -249,6 +275,18 @@ static bool is_unknown_alpha2(const char *alpha2) | |||
249 | return false; | 275 | return false; |
250 | } | 276 | } |
251 | 277 | ||
278 | static bool is_intersected_alpha2(const char *alpha2) | ||
279 | { | ||
280 | if (!alpha2) | ||
281 | return false; | ||
282 | /* Special case where regulatory domain is the | ||
283 | * result of an intersection between two regulatory domain | ||
284 | * structures */ | ||
285 | if (alpha2[0] == '9' && alpha2[1] == '8') | ||
286 | return true; | ||
287 | return false; | ||
288 | } | ||
289 | |||
252 | static bool is_an_alpha2(const char *alpha2) | 290 | static bool is_an_alpha2(const char *alpha2) |
253 | { | 291 | { |
254 | if (!alpha2) | 292 | if (!alpha2) |
@@ -277,6 +315,25 @@ static bool regdom_changed(const char *alpha2) | |||
277 | return true; | 315 | return true; |
278 | } | 316 | } |
279 | 317 | ||
318 | /** | ||
319 | * country_ie_integrity_changes - tells us if the country IE has changed | ||
320 | * @checksum: checksum of country IE of fields we are interested in | ||
321 | * | ||
322 | * If the country IE has not changed you can ignore it safely. This is | ||
323 | * useful to determine if two devices are seeing two different country IEs | ||
324 | * even on the same alpha2. Note that this will return false if no IE has | ||
325 | * been set on the wireless core yet. | ||
326 | */ | ||
327 | static bool country_ie_integrity_changes(u32 checksum) | ||
328 | { | ||
329 | /* If no IE has been set then the checksum doesn't change */ | ||
330 | if (unlikely(!last_request->country_ie_checksum)) | ||
331 | return false; | ||
332 | if (unlikely(last_request->country_ie_checksum != checksum)) | ||
333 | return true; | ||
334 | return false; | ||
335 | } | ||
336 | |||
280 | /* This lets us keep regulatory code which is updated on a regulatory | 337 | /* This lets us keep regulatory code which is updated on a regulatory |
281 | * basis in userspace. */ | 338 | * basis in userspace. */ |
282 | static int call_crda(const char *alpha2) | 339 | static int call_crda(const char *alpha2) |
@@ -300,121 +357,13 @@ static int call_crda(const char *alpha2) | |||
300 | return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, envp); | 357 | return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, envp); |
301 | } | 358 | } |
302 | 359 | ||
303 | /* This has the logic which determines when a new request | ||
304 | * should be ignored. */ | ||
305 | static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | ||
306 | char *alpha2, struct ieee80211_regdomain *rd) | ||
307 | { | ||
308 | struct regulatory_request *last_request = NULL; | ||
309 | |||
310 | /* All initial requests are respected */ | ||
311 | if (list_empty(®ulatory_requests)) | ||
312 | return 0; | ||
313 | |||
314 | last_request = list_first_entry(®ulatory_requests, | ||
315 | struct regulatory_request, list); | ||
316 | |||
317 | switch (set_by) { | ||
318 | case REGDOM_SET_BY_INIT: | ||
319 | return -EINVAL; | ||
320 | case REGDOM_SET_BY_CORE: | ||
321 | /* Always respect new wireless core hints, should only | ||
322 | * come in for updating the world regulatory domain at init | ||
323 | * anyway */ | ||
324 | return 0; | ||
325 | case REGDOM_SET_BY_COUNTRY_IE: | ||
326 | if (last_request->initiator == set_by) { | ||
327 | if (last_request->wiphy != wiphy) { | ||
328 | /* Two cards with two APs claiming different | ||
329 | * different Country IE alpha2s! | ||
330 | * You're special!! */ | ||
331 | if (!alpha2_equal(last_request->alpha2, | ||
332 | cfg80211_regdomain->alpha2)) { | ||
333 | /* XXX: Deal with conflict, consider | ||
334 | * building a new one out of the | ||
335 | * intersection */ | ||
336 | WARN_ON(1); | ||
337 | return -EOPNOTSUPP; | ||
338 | } | ||
339 | return -EALREADY; | ||
340 | } | ||
341 | /* Two consecutive Country IE hints on the same wiphy */ | ||
342 | if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) | ||
343 | return 0; | ||
344 | return -EALREADY; | ||
345 | } | ||
346 | if (WARN(!is_alpha2_set(alpha2) || !is_an_alpha2(alpha2), | ||
347 | "Invalid Country IE regulatory hint passed " | ||
348 | "to the wireless core\n")) | ||
349 | return -EINVAL; | ||
350 | /* We ignore Country IE hints for now, as we haven't yet | ||
351 | * added the dot11MultiDomainCapabilityEnabled flag | ||
352 | * for wiphys */ | ||
353 | return 1; | ||
354 | case REGDOM_SET_BY_DRIVER: | ||
355 | BUG_ON(!wiphy); | ||
356 | if (last_request->initiator == set_by) { | ||
357 | /* Two separate drivers hinting different things, | ||
358 | * this is possible if you have two devices present | ||
359 | * on a system with different EEPROM regulatory | ||
360 | * readings. XXX: Do intersection, we support only | ||
361 | * the first regulatory hint for now */ | ||
362 | if (last_request->wiphy != wiphy) | ||
363 | return -EALREADY; | ||
364 | if (rd) | ||
365 | return -EALREADY; | ||
366 | /* Driver should not be trying to hint different | ||
367 | * regulatory domains! */ | ||
368 | BUG_ON(!alpha2_equal(alpha2, | ||
369 | cfg80211_regdomain->alpha2)); | ||
370 | return -EALREADY; | ||
371 | } | ||
372 | if (last_request->initiator == REGDOM_SET_BY_CORE) | ||
373 | return 0; | ||
374 | /* XXX: Handle intersection, and add the | ||
375 | * dot11MultiDomainCapabilityEnabled flag to wiphy. For now | ||
376 | * we assume the driver has this set to false, following the | ||
377 | * 802.11d dot11MultiDomainCapabilityEnabled documentation */ | ||
378 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) | ||
379 | return 0; | ||
380 | return 0; | ||
381 | case REGDOM_SET_BY_USER: | ||
382 | if (last_request->initiator == set_by || | ||
383 | last_request->initiator == REGDOM_SET_BY_CORE) | ||
384 | return 0; | ||
385 | /* Drivers can use their wiphy's reg_notifier() | ||
386 | * to override any information */ | ||
387 | if (last_request->initiator == REGDOM_SET_BY_DRIVER) | ||
388 | return 0; | ||
389 | /* XXX: Handle intersection */ | ||
390 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) | ||
391 | return -EOPNOTSUPP; | ||
392 | return 0; | ||
393 | default: | ||
394 | return -EINVAL; | ||
395 | } | ||
396 | } | ||
397 | |||
398 | static bool __reg_is_valid_request(const char *alpha2, | ||
399 | struct regulatory_request **request) | ||
400 | { | ||
401 | struct regulatory_request *req; | ||
402 | if (list_empty(®ulatory_requests)) | ||
403 | return false; | ||
404 | list_for_each_entry(req, ®ulatory_requests, list) { | ||
405 | if (alpha2_equal(req->alpha2, alpha2)) { | ||
406 | *request = req; | ||
407 | return true; | ||
408 | } | ||
409 | } | ||
410 | return false; | ||
411 | } | ||
412 | |||
413 | /* Used by nl80211 before kmalloc'ing our regulatory domain */ | 360 | /* Used by nl80211 before kmalloc'ing our regulatory domain */ |
414 | bool reg_is_valid_request(const char *alpha2) | 361 | bool reg_is_valid_request(const char *alpha2) |
415 | { | 362 | { |
416 | struct regulatory_request *request = NULL; | 363 | if (!last_request) |
417 | return __reg_is_valid_request(alpha2, &request); | 364 | return false; |
365 | |||
366 | return alpha2_equal(last_request->alpha2, alpha2); | ||
418 | } | 367 | } |
419 | 368 | ||
420 | /* Sanity check on a regulatory rule */ | 369 | /* Sanity check on a regulatory rule */ |
@@ -423,7 +372,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) | |||
423 | const struct ieee80211_freq_range *freq_range = &rule->freq_range; | 372 | const struct ieee80211_freq_range *freq_range = &rule->freq_range; |
424 | u32 freq_diff; | 373 | u32 freq_diff; |
425 | 374 | ||
426 | if (freq_range->start_freq_khz == 0 || freq_range->end_freq_khz == 0) | 375 | if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0) |
427 | return false; | 376 | return false; |
428 | 377 | ||
429 | if (freq_range->start_freq_khz > freq_range->end_freq_khz) | 378 | if (freq_range->start_freq_khz > freq_range->end_freq_khz) |
@@ -431,7 +380,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) | |||
431 | 380 | ||
432 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; | 381 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; |
433 | 382 | ||
434 | if (freq_range->max_bandwidth_khz > freq_diff) | 383 | if (freq_diff <= 0 || freq_range->max_bandwidth_khz > freq_diff) |
435 | return false; | 384 | return false; |
436 | 385 | ||
437 | return true; | 386 | return true; |
@@ -445,6 +394,9 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd) | |||
445 | if (!rd->n_reg_rules) | 394 | if (!rd->n_reg_rules) |
446 | return false; | 395 | return false; |
447 | 396 | ||
397 | if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) | ||
398 | return false; | ||
399 | |||
448 | for (i = 0; i < rd->n_reg_rules; i++) { | 400 | for (i = 0; i < rd->n_reg_rules; i++) { |
449 | reg_rule = &rd->reg_rules[i]; | 401 | reg_rule = &rd->reg_rules[i]; |
450 | if (!is_valid_reg_rule(reg_rule)) | 402 | if (!is_valid_reg_rule(reg_rule)) |
@@ -469,6 +421,311 @@ static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range, | |||
469 | return 0; | 421 | return 0; |
470 | } | 422 | } |
471 | 423 | ||
424 | /* Converts a country IE to a regulatory domain. A regulatory domain | ||
425 | * structure has a lot of information which the IE doesn't yet have, | ||
426 | * so for the other values we use upper max values as we will intersect | ||
427 | * with our userspace regulatory agent to get lower bounds. */ | ||
428 | static struct ieee80211_regdomain *country_ie_2_rd( | ||
429 | u8 *country_ie, | ||
430 | u8 country_ie_len, | ||
431 | u32 *checksum) | ||
432 | { | ||
433 | struct ieee80211_regdomain *rd = NULL; | ||
434 | unsigned int i = 0; | ||
435 | char alpha2[2]; | ||
436 | u32 flags = 0; | ||
437 | u32 num_rules = 0, size_of_regd = 0; | ||
438 | u8 *triplets_start = NULL; | ||
439 | u8 len_at_triplet = 0; | ||
440 | /* the last channel we have registered in a subband (triplet) */ | ||
441 | int last_sub_max_channel = 0; | ||
442 | |||
443 | *checksum = 0xDEADBEEF; | ||
444 | |||
445 | /* Country IE requirements */ | ||
446 | BUG_ON(country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN || | ||
447 | country_ie_len & 0x01); | ||
448 | |||
449 | alpha2[0] = country_ie[0]; | ||
450 | alpha2[1] = country_ie[1]; | ||
451 | |||
452 | /* | ||
453 | * Third octet can be: | ||
454 | * 'I' - Indoor | ||
455 | * 'O' - Outdoor | ||
456 | * | ||
457 | * anything else we assume is no restrictions | ||
458 | */ | ||
459 | if (country_ie[2] == 'I') | ||
460 | flags = NL80211_RRF_NO_OUTDOOR; | ||
461 | else if (country_ie[2] == 'O') | ||
462 | flags = NL80211_RRF_NO_INDOOR; | ||
463 | |||
464 | country_ie += 3; | ||
465 | country_ie_len -= 3; | ||
466 | |||
467 | triplets_start = country_ie; | ||
468 | len_at_triplet = country_ie_len; | ||
469 | |||
470 | *checksum ^= ((flags ^ alpha2[0] ^ alpha2[1]) << 8); | ||
471 | |||
472 | /* We need to build a reg rule for each triplet, but first we must | ||
473 | * calculate the number of reg rules we will need. We will need one | ||
474 | * for each channel subband */ | ||
475 | while (country_ie_len >= 3) { | ||
476 | struct ieee80211_country_ie_triplet *triplet = | ||
477 | (struct ieee80211_country_ie_triplet *) country_ie; | ||
478 | int cur_sub_max_channel = 0, cur_channel = 0; | ||
479 | |||
480 | if (triplet->ext.reg_extension_id >= | ||
481 | IEEE80211_COUNTRY_EXTENSION_ID) { | ||
482 | country_ie += 3; | ||
483 | country_ie_len -= 3; | ||
484 | continue; | ||
485 | } | ||
486 | |||
487 | cur_channel = triplet->chans.first_channel; | ||
488 | cur_sub_max_channel = ieee80211_channel_to_frequency( | ||
489 | cur_channel + triplet->chans.num_channels); | ||
490 | |||
491 | /* Basic sanity check */ | ||
492 | if (cur_sub_max_channel < cur_channel) | ||
493 | return NULL; | ||
494 | |||
495 | /* Do not allow overlapping channels. Also channels | ||
496 | * passed in each subband must be monotonically | ||
497 | * increasing */ | ||
498 | if (last_sub_max_channel) { | ||
499 | if (cur_channel <= last_sub_max_channel) | ||
500 | return NULL; | ||
501 | if (cur_sub_max_channel <= last_sub_max_channel) | ||
502 | return NULL; | ||
503 | } | ||
504 | |||
505 | /* When dot11RegulatoryClassesRequired is supported | ||
506 | * we can throw ext triplets as part of this soup, | ||
507 | * for now we don't care when those change as we | ||
508 | * don't support them */ | ||
509 | *checksum ^= ((cur_channel ^ cur_sub_max_channel) << 8) | | ||
510 | ((cur_sub_max_channel ^ cur_sub_max_channel) << 16) | | ||
511 | ((triplet->chans.max_power ^ cur_sub_max_channel) << 24); | ||
512 | |||
513 | last_sub_max_channel = cur_sub_max_channel; | ||
514 | |||
515 | country_ie += 3; | ||
516 | country_ie_len -= 3; | ||
517 | num_rules++; | ||
518 | |||
519 | /* Note: this is not a IEEE requirement but | ||
520 | * simply a memory requirement */ | ||
521 | if (num_rules > NL80211_MAX_SUPP_REG_RULES) | ||
522 | return NULL; | ||
523 | } | ||
524 | |||
525 | country_ie = triplets_start; | ||
526 | country_ie_len = len_at_triplet; | ||
527 | |||
528 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
529 | (num_rules * sizeof(struct ieee80211_reg_rule)); | ||
530 | |||
531 | rd = kzalloc(size_of_regd, GFP_KERNEL); | ||
532 | if (!rd) | ||
533 | return NULL; | ||
534 | |||
535 | rd->n_reg_rules = num_rules; | ||
536 | rd->alpha2[0] = alpha2[0]; | ||
537 | rd->alpha2[1] = alpha2[1]; | ||
538 | |||
539 | /* This time around we fill in the rd */ | ||
540 | while (country_ie_len >= 3) { | ||
541 | struct ieee80211_country_ie_triplet *triplet = | ||
542 | (struct ieee80211_country_ie_triplet *) country_ie; | ||
543 | struct ieee80211_reg_rule *reg_rule = NULL; | ||
544 | struct ieee80211_freq_range *freq_range = NULL; | ||
545 | struct ieee80211_power_rule *power_rule = NULL; | ||
546 | |||
547 | /* Must parse if dot11RegulatoryClassesRequired is true, | ||
548 | * we don't support this yet */ | ||
549 | if (triplet->ext.reg_extension_id >= | ||
550 | IEEE80211_COUNTRY_EXTENSION_ID) { | ||
551 | country_ie += 3; | ||
552 | country_ie_len -= 3; | ||
553 | continue; | ||
554 | } | ||
555 | |||
556 | reg_rule = &rd->reg_rules[i]; | ||
557 | freq_range = ®_rule->freq_range; | ||
558 | power_rule = ®_rule->power_rule; | ||
559 | |||
560 | reg_rule->flags = flags; | ||
561 | |||
562 | /* The +10 is since the regulatory domain expects | ||
563 | * the actual band edge, not the center of freq for | ||
564 | * its start and end freqs, assuming 20 MHz bandwidth on | ||
565 | * the channels passed */ | ||
566 | freq_range->start_freq_khz = | ||
567 | MHZ_TO_KHZ(ieee80211_channel_to_frequency( | ||
568 | triplet->chans.first_channel) - 10); | ||
569 | freq_range->end_freq_khz = | ||
570 | MHZ_TO_KHZ(ieee80211_channel_to_frequency( | ||
571 | triplet->chans.first_channel + | ||
572 | triplet->chans.num_channels) + 10); | ||
573 | |||
574 | /* Large arbitrary values, we intersect later */ | ||
575 | /* Increment this if we ever support >= 40 MHz channels | ||
576 | * in IEEE 802.11 */ | ||
577 | freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40); | ||
578 | power_rule->max_antenna_gain = DBI_TO_MBI(100); | ||
579 | power_rule->max_eirp = DBM_TO_MBM(100); | ||
580 | |||
581 | country_ie += 3; | ||
582 | country_ie_len -= 3; | ||
583 | i++; | ||
584 | |||
585 | BUG_ON(i > NL80211_MAX_SUPP_REG_RULES); | ||
586 | } | ||
587 | |||
588 | return rd; | ||
589 | } | ||
590 | |||
591 | |||
592 | /* Helper for regdom_intersect(), this does the real | ||
593 | * mathematical intersection fun */ | ||
594 | static int reg_rules_intersect( | ||
595 | const struct ieee80211_reg_rule *rule1, | ||
596 | const struct ieee80211_reg_rule *rule2, | ||
597 | struct ieee80211_reg_rule *intersected_rule) | ||
598 | { | ||
599 | const struct ieee80211_freq_range *freq_range1, *freq_range2; | ||
600 | struct ieee80211_freq_range *freq_range; | ||
601 | const struct ieee80211_power_rule *power_rule1, *power_rule2; | ||
602 | struct ieee80211_power_rule *power_rule; | ||
603 | u32 freq_diff; | ||
604 | |||
605 | freq_range1 = &rule1->freq_range; | ||
606 | freq_range2 = &rule2->freq_range; | ||
607 | freq_range = &intersected_rule->freq_range; | ||
608 | |||
609 | power_rule1 = &rule1->power_rule; | ||
610 | power_rule2 = &rule2->power_rule; | ||
611 | power_rule = &intersected_rule->power_rule; | ||
612 | |||
613 | freq_range->start_freq_khz = max(freq_range1->start_freq_khz, | ||
614 | freq_range2->start_freq_khz); | ||
615 | freq_range->end_freq_khz = min(freq_range1->end_freq_khz, | ||
616 | freq_range2->end_freq_khz); | ||
617 | freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, | ||
618 | freq_range2->max_bandwidth_khz); | ||
619 | |||
620 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; | ||
621 | if (freq_range->max_bandwidth_khz > freq_diff) | ||
622 | freq_range->max_bandwidth_khz = freq_diff; | ||
623 | |||
624 | power_rule->max_eirp = min(power_rule1->max_eirp, | ||
625 | power_rule2->max_eirp); | ||
626 | power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, | ||
627 | power_rule2->max_antenna_gain); | ||
628 | |||
629 | intersected_rule->flags = (rule1->flags | rule2->flags); | ||
630 | |||
631 | if (!is_valid_reg_rule(intersected_rule)) | ||
632 | return -EINVAL; | ||
633 | |||
634 | return 0; | ||
635 | } | ||
636 | |||
637 | /** | ||
638 | * regdom_intersect - do the intersection between two regulatory domains | ||
639 | * @rd1: first regulatory domain | ||
640 | * @rd2: second regulatory domain | ||
641 | * | ||
642 | * Use this function to get the intersection between two regulatory domains. | ||
643 | * Once completed we will mark the alpha2 for the rd as intersected, "98", | ||
644 | * as no one single alpha2 can represent this regulatory domain. | ||
645 | * | ||
646 | * Returns a pointer to the regulatory domain structure which will hold the | ||
647 | * resulting intersection of rules between rd1 and rd2. We will | ||
648 | * kzalloc() this structure for you. | ||
649 | */ | ||
650 | static struct ieee80211_regdomain *regdom_intersect( | ||
651 | const struct ieee80211_regdomain *rd1, | ||
652 | const struct ieee80211_regdomain *rd2) | ||
653 | { | ||
654 | int r, size_of_regd; | ||
655 | unsigned int x, y; | ||
656 | unsigned int num_rules = 0, rule_idx = 0; | ||
657 | const struct ieee80211_reg_rule *rule1, *rule2; | ||
658 | struct ieee80211_reg_rule *intersected_rule; | ||
659 | struct ieee80211_regdomain *rd; | ||
660 | /* This is just a dummy holder to help us count */ | ||
661 | struct ieee80211_reg_rule irule; | ||
662 | |||
663 | /* Uses the stack temporarily for counter arithmetic */ | ||
664 | intersected_rule = &irule; | ||
665 | |||
666 | memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); | ||
667 | |||
668 | if (!rd1 || !rd2) | ||
669 | return NULL; | ||
670 | |||
671 | /* First we get a count of the rules we'll need, then we actually | ||
672 | * build them. This is to so we can malloc() and free() a | ||
673 | * regdomain once. The reason we use reg_rules_intersect() here | ||
674 | * is it will return -EINVAL if the rule computed makes no sense. | ||
675 | * All rules that do check out OK are valid. */ | ||
676 | |||
677 | for (x = 0; x < rd1->n_reg_rules; x++) { | ||
678 | rule1 = &rd1->reg_rules[x]; | ||
679 | for (y = 0; y < rd2->n_reg_rules; y++) { | ||
680 | rule2 = &rd2->reg_rules[y]; | ||
681 | if (!reg_rules_intersect(rule1, rule2, | ||
682 | intersected_rule)) | ||
683 | num_rules++; | ||
684 | memset(intersected_rule, 0, | ||
685 | sizeof(struct ieee80211_reg_rule)); | ||
686 | } | ||
687 | } | ||
688 | |||
689 | if (!num_rules) | ||
690 | return NULL; | ||
691 | |||
692 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
693 | ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
694 | |||
695 | rd = kzalloc(size_of_regd, GFP_KERNEL); | ||
696 | if (!rd) | ||
697 | return NULL; | ||
698 | |||
699 | for (x = 0; x < rd1->n_reg_rules; x++) { | ||
700 | rule1 = &rd1->reg_rules[x]; | ||
701 | for (y = 0; y < rd2->n_reg_rules; y++) { | ||
702 | rule2 = &rd2->reg_rules[y]; | ||
703 | /* This time around instead of using the stack lets | ||
704 | * write to the target rule directly saving ourselves | ||
705 | * a memcpy() */ | ||
706 | intersected_rule = &rd->reg_rules[rule_idx]; | ||
707 | r = reg_rules_intersect(rule1, rule2, | ||
708 | intersected_rule); | ||
709 | /* No need to memset here the intersected rule here as | ||
710 | * we're not using the stack anymore */ | ||
711 | if (r) | ||
712 | continue; | ||
713 | rule_idx++; | ||
714 | } | ||
715 | } | ||
716 | |||
717 | if (rule_idx != num_rules) { | ||
718 | kfree(rd); | ||
719 | return NULL; | ||
720 | } | ||
721 | |||
722 | rd->n_reg_rules = num_rules; | ||
723 | rd->alpha2[0] = '9'; | ||
724 | rd->alpha2[1] = '8'; | ||
725 | |||
726 | return rd; | ||
727 | } | ||
728 | |||
472 | /* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may | 729 | /* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may |
473 | * want to just have the channel structure use these */ | 730 | * want to just have the channel structure use these */ |
474 | static u32 map_regdom_flags(u32 rd_flags) | 731 | static u32 map_regdom_flags(u32 rd_flags) |
@@ -559,12 +816,23 @@ static void handle_band(struct ieee80211_supported_band *sband) | |||
559 | handle_channel(&sband->channels[i]); | 816 | handle_channel(&sband->channels[i]); |
560 | } | 817 | } |
561 | 818 | ||
819 | static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby) | ||
820 | { | ||
821 | if (!last_request) | ||
822 | return true; | ||
823 | if (setby == REGDOM_SET_BY_CORE && | ||
824 | wiphy->fw_handles_regulatory) | ||
825 | return true; | ||
826 | return false; | ||
827 | } | ||
828 | |||
562 | static void update_all_wiphy_regulatory(enum reg_set_by setby) | 829 | static void update_all_wiphy_regulatory(enum reg_set_by setby) |
563 | { | 830 | { |
564 | struct cfg80211_registered_device *drv; | 831 | struct cfg80211_registered_device *drv; |
565 | 832 | ||
566 | list_for_each_entry(drv, &cfg80211_drv_list, list) | 833 | list_for_each_entry(drv, &cfg80211_drv_list, list) |
567 | wiphy_update_regulatory(&drv->wiphy, setby); | 834 | if (!ignore_reg_update(&drv->wiphy, setby)) |
835 | wiphy_update_regulatory(&drv->wiphy, setby); | ||
568 | } | 836 | } |
569 | 837 | ||
570 | void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) | 838 | void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) |
@@ -578,78 +846,237 @@ void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) | |||
578 | } | 846 | } |
579 | } | 847 | } |
580 | 848 | ||
581 | /* Caller must hold &cfg80211_drv_mutex */ | 849 | /* Return value which can be used by ignore_request() to indicate |
582 | int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | 850 | * it has been determined we should intersect two regulatory domains */ |
583 | const char *alpha2, struct ieee80211_regdomain *rd) | 851 | #define REG_INTERSECT 1 |
584 | { | ||
585 | struct regulatory_request *request; | ||
586 | char *rd_alpha2; | ||
587 | int r = 0; | ||
588 | |||
589 | r = ignore_request(wiphy, set_by, (char *) alpha2, rd); | ||
590 | if (r) | ||
591 | return r; | ||
592 | 852 | ||
593 | if (rd) | 853 | /* This has the logic which determines when a new request |
594 | rd_alpha2 = rd->alpha2; | 854 | * should be ignored. */ |
595 | else | 855 | static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, |
596 | rd_alpha2 = (char *) alpha2; | 856 | const char *alpha2) |
857 | { | ||
858 | /* All initial requests are respected */ | ||
859 | if (!last_request) | ||
860 | return 0; | ||
597 | 861 | ||
598 | switch (set_by) { | 862 | switch (set_by) { |
863 | case REGDOM_SET_BY_INIT: | ||
864 | return -EINVAL; | ||
599 | case REGDOM_SET_BY_CORE: | 865 | case REGDOM_SET_BY_CORE: |
866 | /* | ||
867 | * Always respect new wireless core hints, should only happen | ||
868 | * when updating the world regulatory domain at init. | ||
869 | */ | ||
870 | return 0; | ||
600 | case REGDOM_SET_BY_COUNTRY_IE: | 871 | case REGDOM_SET_BY_COUNTRY_IE: |
872 | if (unlikely(!is_an_alpha2(alpha2))) | ||
873 | return -EINVAL; | ||
874 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { | ||
875 | if (last_request->wiphy != wiphy) { | ||
876 | /* | ||
877 | * Two cards with two APs claiming different | ||
878 | * different Country IE alpha2s. We could | ||
879 | * intersect them, but that seems unlikely | ||
880 | * to be correct. Reject second one for now. | ||
881 | */ | ||
882 | if (!alpha2_equal(alpha2, | ||
883 | cfg80211_regdomain->alpha2)) | ||
884 | return -EOPNOTSUPP; | ||
885 | return -EALREADY; | ||
886 | } | ||
887 | /* Two consecutive Country IE hints on the same wiphy. | ||
888 | * This should be picked up early by the driver/stack */ | ||
889 | if (WARN_ON(!alpha2_equal(cfg80211_regdomain->alpha2, | ||
890 | alpha2))) | ||
891 | return 0; | ||
892 | return -EALREADY; | ||
893 | } | ||
894 | return REG_INTERSECT; | ||
601 | case REGDOM_SET_BY_DRIVER: | 895 | case REGDOM_SET_BY_DRIVER: |
896 | if (last_request->initiator == REGDOM_SET_BY_DRIVER) | ||
897 | return -EALREADY; | ||
898 | return 0; | ||
602 | case REGDOM_SET_BY_USER: | 899 | case REGDOM_SET_BY_USER: |
603 | request = kzalloc(sizeof(struct regulatory_request), | 900 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) |
604 | GFP_KERNEL); | 901 | return REG_INTERSECT; |
605 | if (!request) | 902 | /* If the user knows better the user should set the regdom |
606 | return -ENOMEM; | 903 | * to their country before the IE is picked up */ |
607 | 904 | if (last_request->initiator == REGDOM_SET_BY_USER && | |
608 | request->alpha2[0] = rd_alpha2[0]; | 905 | last_request->intersect) |
609 | request->alpha2[1] = rd_alpha2[1]; | 906 | return -EOPNOTSUPP; |
610 | request->initiator = set_by; | 907 | return 0; |
611 | request->wiphy = wiphy; | ||
612 | |||
613 | list_add_tail(&request->list, ®ulatory_requests); | ||
614 | if (rd) | ||
615 | break; | ||
616 | r = call_crda(alpha2); | ||
617 | #ifndef CONFIG_WIRELESS_OLD_REGULATORY | ||
618 | if (r) | ||
619 | printk(KERN_ERR "cfg80211: Failed calling CRDA\n"); | ||
620 | #endif | ||
621 | break; | ||
622 | default: | ||
623 | r = -ENOTSUPP; | ||
624 | break; | ||
625 | } | 908 | } |
626 | 909 | ||
627 | return r; | 910 | return -EINVAL; |
628 | } | 911 | } |
629 | 912 | ||
630 | /* If rd is not NULL and if this call fails the caller must free it */ | 913 | /* Caller must hold &cfg80211_drv_mutex */ |
631 | int regulatory_hint(struct wiphy *wiphy, const char *alpha2, | 914 | int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, |
632 | struct ieee80211_regdomain *rd) | 915 | const char *alpha2, |
916 | u32 country_ie_checksum, | ||
917 | enum environment_cap env) | ||
633 | { | 918 | { |
634 | int r; | 919 | struct regulatory_request *request; |
635 | BUG_ON(!rd && !alpha2); | 920 | bool intersect = false; |
921 | int r = 0; | ||
636 | 922 | ||
637 | mutex_lock(&cfg80211_drv_mutex); | 923 | r = ignore_request(wiphy, set_by, alpha2); |
638 | 924 | ||
639 | r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, rd); | 925 | if (r == REG_INTERSECT) |
640 | if (r || !rd) | 926 | intersect = true; |
641 | goto unlock_and_exit; | 927 | else if (r) |
928 | return r; | ||
642 | 929 | ||
643 | /* If the driver passed a regulatory domain we skipped asking | 930 | request = kzalloc(sizeof(struct regulatory_request), |
644 | * userspace for one so we can now go ahead and set it */ | 931 | GFP_KERNEL); |
645 | r = set_regdom(rd); | 932 | if (!request) |
933 | return -ENOMEM; | ||
934 | |||
935 | request->alpha2[0] = alpha2[0]; | ||
936 | request->alpha2[1] = alpha2[1]; | ||
937 | request->initiator = set_by; | ||
938 | request->wiphy = wiphy; | ||
939 | request->intersect = intersect; | ||
940 | request->country_ie_checksum = country_ie_checksum; | ||
941 | request->country_ie_env = env; | ||
942 | |||
943 | kfree(last_request); | ||
944 | last_request = request; | ||
945 | /* | ||
946 | * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled | ||
947 | * AND if CRDA is NOT present nothing will happen, if someone | ||
948 | * wants to bother with 11d with OLD_REG you can add a timer. | ||
949 | * If after x amount of time nothing happens you can call: | ||
950 | * | ||
951 | * return set_regdom(country_ie_regdomain); | ||
952 | * | ||
953 | * to intersect with the static rd | ||
954 | */ | ||
955 | return call_crda(alpha2); | ||
956 | } | ||
957 | |||
958 | void regulatory_hint(struct wiphy *wiphy, const char *alpha2) | ||
959 | { | ||
960 | BUG_ON(!alpha2); | ||
646 | 961 | ||
647 | unlock_and_exit: | 962 | mutex_lock(&cfg80211_drv_mutex); |
963 | __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, 0, ENVIRON_ANY); | ||
648 | mutex_unlock(&cfg80211_drv_mutex); | 964 | mutex_unlock(&cfg80211_drv_mutex); |
649 | return r; | ||
650 | } | 965 | } |
651 | EXPORT_SYMBOL(regulatory_hint); | 966 | EXPORT_SYMBOL(regulatory_hint); |
652 | 967 | ||
968 | static bool reg_same_country_ie_hint(struct wiphy *wiphy, | ||
969 | u32 country_ie_checksum) | ||
970 | { | ||
971 | if (!last_request->wiphy) | ||
972 | return false; | ||
973 | if (likely(last_request->wiphy != wiphy)) | ||
974 | return !country_ie_integrity_changes(country_ie_checksum); | ||
975 | /* We should not have let these through at this point, they | ||
976 | * should have been picked up earlier by the first alpha2 check | ||
977 | * on the device */ | ||
978 | if (WARN_ON(!country_ie_integrity_changes(country_ie_checksum))) | ||
979 | return true; | ||
980 | return false; | ||
981 | } | ||
982 | |||
983 | void regulatory_hint_11d(struct wiphy *wiphy, | ||
984 | u8 *country_ie, | ||
985 | u8 country_ie_len) | ||
986 | { | ||
987 | struct ieee80211_regdomain *rd = NULL; | ||
988 | char alpha2[2]; | ||
989 | u32 checksum = 0; | ||
990 | enum environment_cap env = ENVIRON_ANY; | ||
991 | |||
992 | if (!last_request) | ||
993 | return; | ||
994 | |||
995 | mutex_lock(&cfg80211_drv_mutex); | ||
996 | |||
997 | /* IE len must be evenly divisible by 2 */ | ||
998 | if (country_ie_len & 0x01) | ||
999 | goto out; | ||
1000 | |||
1001 | if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) | ||
1002 | goto out; | ||
1003 | |||
1004 | /* Pending country IE processing, this can happen after we | ||
1005 | * call CRDA and wait for a response if a beacon was received before | ||
1006 | * we were able to process the last regulatory_hint_11d() call */ | ||
1007 | if (country_ie_regdomain) | ||
1008 | goto out; | ||
1009 | |||
1010 | alpha2[0] = country_ie[0]; | ||
1011 | alpha2[1] = country_ie[1]; | ||
1012 | |||
1013 | if (country_ie[2] == 'I') | ||
1014 | env = ENVIRON_INDOOR; | ||
1015 | else if (country_ie[2] == 'O') | ||
1016 | env = ENVIRON_OUTDOOR; | ||
1017 | |||
1018 | /* We will run this for *every* beacon processed for the BSSID, so | ||
1019 | * we optimize an early check to exit out early if we don't have to | ||
1020 | * do anything */ | ||
1021 | if (likely(last_request->wiphy)) { | ||
1022 | struct cfg80211_registered_device *drv_last_ie; | ||
1023 | |||
1024 | drv_last_ie = wiphy_to_dev(last_request->wiphy); | ||
1025 | |||
1026 | /* Lets keep this simple -- we trust the first AP | ||
1027 | * after we intersect with CRDA */ | ||
1028 | if (likely(last_request->wiphy == wiphy)) { | ||
1029 | /* Ignore IEs coming in on this wiphy with | ||
1030 | * the same alpha2 and environment cap */ | ||
1031 | if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2, | ||
1032 | alpha2) && | ||
1033 | env == drv_last_ie->env)) { | ||
1034 | goto out; | ||
1035 | } | ||
1036 | /* the wiphy moved on to another BSSID or the AP | ||
1037 | * was reconfigured. XXX: We need to deal with the | ||
1038 | * case where the user suspends and goes to goes | ||
1039 | * to another country, and then gets IEs from an | ||
1040 | * AP with different settings */ | ||
1041 | goto out; | ||
1042 | } else { | ||
1043 | /* Ignore IEs coming in on two separate wiphys with | ||
1044 | * the same alpha2 and environment cap */ | ||
1045 | if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2, | ||
1046 | alpha2) && | ||
1047 | env == drv_last_ie->env)) { | ||
1048 | goto out; | ||
1049 | } | ||
1050 | /* We could potentially intersect though */ | ||
1051 | goto out; | ||
1052 | } | ||
1053 | } | ||
1054 | |||
1055 | rd = country_ie_2_rd(country_ie, country_ie_len, &checksum); | ||
1056 | if (!rd) | ||
1057 | goto out; | ||
1058 | |||
1059 | /* This will not happen right now but we leave it here for the | ||
1060 | * the future when we want to add suspend/resume support and having | ||
1061 | * the user move to another country after doing so, or having the user | ||
1062 | * move to another AP. Right now we just trust the first AP. This is why | ||
1063 | * this is marked as likley(). If we hit this before we add this support | ||
1064 | * we want to be informed of it as it would indicate a mistake in the | ||
1065 | * current design */ | ||
1066 | if (likely(WARN_ON(reg_same_country_ie_hint(wiphy, checksum)))) | ||
1067 | goto out; | ||
1068 | |||
1069 | /* We keep this around for when CRDA comes back with a response so | ||
1070 | * we can intersect with that */ | ||
1071 | country_ie_regdomain = rd; | ||
1072 | |||
1073 | __regulatory_hint(wiphy, REGDOM_SET_BY_COUNTRY_IE, | ||
1074 | country_ie_regdomain->alpha2, checksum, env); | ||
1075 | |||
1076 | out: | ||
1077 | mutex_unlock(&cfg80211_drv_mutex); | ||
1078 | } | ||
1079 | EXPORT_SYMBOL(regulatory_hint_11d); | ||
653 | 1080 | ||
654 | static void print_rd_rules(const struct ieee80211_regdomain *rd) | 1081 | static void print_rd_rules(const struct ieee80211_regdomain *rd) |
655 | { | 1082 | { |
@@ -689,7 +1116,25 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) | |||
689 | static void print_regdomain(const struct ieee80211_regdomain *rd) | 1116 | static void print_regdomain(const struct ieee80211_regdomain *rd) |
690 | { | 1117 | { |
691 | 1118 | ||
692 | if (is_world_regdom(rd->alpha2)) | 1119 | if (is_intersected_alpha2(rd->alpha2)) { |
1120 | struct wiphy *wiphy = NULL; | ||
1121 | struct cfg80211_registered_device *drv; | ||
1122 | |||
1123 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { | ||
1124 | if (last_request->wiphy) { | ||
1125 | wiphy = last_request->wiphy; | ||
1126 | drv = wiphy_to_dev(wiphy); | ||
1127 | printk(KERN_INFO "cfg80211: Current regulatory " | ||
1128 | "domain updated by AP to: %c%c\n", | ||
1129 | drv->country_ie_alpha2[0], | ||
1130 | drv->country_ie_alpha2[1]); | ||
1131 | } else | ||
1132 | printk(KERN_INFO "cfg80211: Current regulatory " | ||
1133 | "domain intersected: \n"); | ||
1134 | } else | ||
1135 | printk(KERN_INFO "cfg80211: Current regulatory " | ||
1136 | "intersected: \n"); | ||
1137 | } else if (is_world_regdom(rd->alpha2)) | ||
693 | printk(KERN_INFO "cfg80211: World regulatory " | 1138 | printk(KERN_INFO "cfg80211: World regulatory " |
694 | "domain updated:\n"); | 1139 | "domain updated:\n"); |
695 | else { | 1140 | else { |
@@ -705,21 +1150,50 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) | |||
705 | print_rd_rules(rd); | 1150 | print_rd_rules(rd); |
706 | } | 1151 | } |
707 | 1152 | ||
708 | void print_regdomain_info(const struct ieee80211_regdomain *rd) | 1153 | static void print_regdomain_info(const struct ieee80211_regdomain *rd) |
709 | { | 1154 | { |
710 | printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n", | 1155 | printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n", |
711 | rd->alpha2[0], rd->alpha2[1]); | 1156 | rd->alpha2[0], rd->alpha2[1]); |
712 | print_rd_rules(rd); | 1157 | print_rd_rules(rd); |
713 | } | 1158 | } |
714 | 1159 | ||
715 | static int __set_regdom(const struct ieee80211_regdomain *rd) | 1160 | #ifdef CONFIG_CFG80211_REG_DEBUG |
1161 | static void reg_country_ie_process_debug( | ||
1162 | const struct ieee80211_regdomain *rd, | ||
1163 | const struct ieee80211_regdomain *country_ie_regdomain, | ||
1164 | const struct ieee80211_regdomain *intersected_rd) | ||
716 | { | 1165 | { |
717 | struct regulatory_request *request = NULL; | 1166 | printk(KERN_DEBUG "cfg80211: Received country IE:\n"); |
1167 | print_regdomain_info(country_ie_regdomain); | ||
1168 | printk(KERN_DEBUG "cfg80211: CRDA thinks this should applied:\n"); | ||
1169 | print_regdomain_info(rd); | ||
1170 | if (intersected_rd) { | ||
1171 | printk(KERN_DEBUG "cfg80211: We intersect both of these " | ||
1172 | "and get:\n"); | ||
1173 | print_regdomain_info(rd); | ||
1174 | return; | ||
1175 | } | ||
1176 | printk(KERN_DEBUG "cfg80211: Intersection between both failed\n"); | ||
1177 | } | ||
1178 | #else | ||
1179 | static inline void reg_country_ie_process_debug( | ||
1180 | const struct ieee80211_regdomain *rd, | ||
1181 | const struct ieee80211_regdomain *country_ie_regdomain, | ||
1182 | const struct ieee80211_regdomain *intersected_rd) | ||
1183 | { | ||
1184 | } | ||
1185 | #endif | ||
718 | 1186 | ||
1187 | /* Takes ownership of rd only if it doesn't fail */ | ||
1188 | static int __set_regdom(const struct ieee80211_regdomain *rd) | ||
1189 | { | ||
1190 | const struct ieee80211_regdomain *intersected_rd = NULL; | ||
1191 | struct cfg80211_registered_device *drv = NULL; | ||
1192 | struct wiphy *wiphy = NULL; | ||
719 | /* Some basic sanity checks first */ | 1193 | /* Some basic sanity checks first */ |
720 | 1194 | ||
721 | if (is_world_regdom(rd->alpha2)) { | 1195 | if (is_world_regdom(rd->alpha2)) { |
722 | if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request))) | 1196 | if (WARN_ON(!reg_is_valid_request(rd->alpha2))) |
723 | return -EINVAL; | 1197 | return -EINVAL; |
724 | update_world_regdomain(rd); | 1198 | update_world_regdomain(rd); |
725 | return 0; | 1199 | return 0; |
@@ -729,45 +1203,102 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
729 | !is_unknown_alpha2(rd->alpha2)) | 1203 | !is_unknown_alpha2(rd->alpha2)) |
730 | return -EINVAL; | 1204 | return -EINVAL; |
731 | 1205 | ||
732 | if (list_empty(®ulatory_requests)) | 1206 | if (!last_request) |
733 | return -EINVAL; | 1207 | return -EINVAL; |
734 | 1208 | ||
735 | /* allow overriding the static definitions if CRDA is present */ | 1209 | /* Lets only bother proceeding on the same alpha2 if the current |
736 | if (!is_old_static_regdom(cfg80211_regdomain) && | 1210 | * rd is non static (it means CRDA was present and was used last) |
737 | !regdom_changed(rd->alpha2)) | 1211 | * and the pending request came in from a country IE */ |
738 | return -EINVAL; | 1212 | if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) { |
1213 | /* If someone else asked us to change the rd lets only bother | ||
1214 | * checking if the alpha2 changes if CRDA was already called */ | ||
1215 | if (!is_old_static_regdom(cfg80211_regdomain) && | ||
1216 | !regdom_changed(rd->alpha2)) | ||
1217 | return -EINVAL; | ||
1218 | } | ||
1219 | |||
1220 | wiphy = last_request->wiphy; | ||
739 | 1221 | ||
740 | /* Now lets set the regulatory domain, update all driver channels | 1222 | /* Now lets set the regulatory domain, update all driver channels |
741 | * and finally inform them of what we have done, in case they want | 1223 | * and finally inform them of what we have done, in case they want |
742 | * to review or adjust their own settings based on their own | 1224 | * to review or adjust their own settings based on their own |
743 | * internal EEPROM data */ | 1225 | * internal EEPROM data */ |
744 | 1226 | ||
745 | if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request))) | 1227 | if (WARN_ON(!reg_is_valid_request(rd->alpha2))) |
746 | return -EINVAL; | 1228 | return -EINVAL; |
747 | 1229 | ||
748 | reset_regdomains(); | 1230 | if (!is_valid_rd(rd)) { |
1231 | printk(KERN_ERR "cfg80211: Invalid " | ||
1232 | "regulatory domain detected:\n"); | ||
1233 | print_regdomain_info(rd); | ||
1234 | return -EINVAL; | ||
1235 | } | ||
749 | 1236 | ||
750 | /* Country IE parsing coming soon */ | 1237 | if (!last_request->intersect) { |
751 | switch (request->initiator) { | 1238 | reset_regdomains(); |
752 | case REGDOM_SET_BY_CORE: | 1239 | cfg80211_regdomain = rd; |
753 | case REGDOM_SET_BY_DRIVER: | 1240 | return 0; |
754 | case REGDOM_SET_BY_USER: | 1241 | } |
755 | if (!is_valid_rd(rd)) { | 1242 | |
756 | printk(KERN_ERR "cfg80211: Invalid " | 1243 | /* Intersection requires a bit more work */ |
757 | "regulatory domain detected:\n"); | 1244 | |
758 | print_regdomain_info(rd); | 1245 | if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) { |
1246 | |||
1247 | intersected_rd = regdom_intersect(rd, cfg80211_regdomain); | ||
1248 | if (!intersected_rd) | ||
759 | return -EINVAL; | 1249 | return -EINVAL; |
760 | } | 1250 | |
761 | break; | 1251 | /* We can trash what CRDA provided now */ |
762 | case REGDOM_SET_BY_COUNTRY_IE: /* Not yet */ | 1252 | kfree(rd); |
763 | WARN_ON(1); | 1253 | rd = NULL; |
764 | default: | 1254 | |
765 | return -EOPNOTSUPP; | 1255 | reset_regdomains(); |
1256 | cfg80211_regdomain = intersected_rd; | ||
1257 | |||
1258 | return 0; | ||
766 | } | 1259 | } |
767 | 1260 | ||
768 | /* Tada! */ | 1261 | /* |
769 | cfg80211_regdomain = rd; | 1262 | * Country IE requests are handled a bit differently, we intersect |
770 | request->granted = 1; | 1263 | * the country IE rd with what CRDA believes that country should have |
1264 | */ | ||
1265 | |||
1266 | BUG_ON(!country_ie_regdomain); | ||
1267 | |||
1268 | if (rd != country_ie_regdomain) { | ||
1269 | /* Intersect what CRDA returned and our what we | ||
1270 | * had built from the Country IE received */ | ||
1271 | |||
1272 | intersected_rd = regdom_intersect(rd, country_ie_regdomain); | ||
1273 | |||
1274 | reg_country_ie_process_debug(rd, country_ie_regdomain, | ||
1275 | intersected_rd); | ||
1276 | |||
1277 | kfree(country_ie_regdomain); | ||
1278 | country_ie_regdomain = NULL; | ||
1279 | } else { | ||
1280 | /* This would happen when CRDA was not present and | ||
1281 | * OLD_REGULATORY was enabled. We intersect our Country | ||
1282 | * IE rd and what was set on cfg80211 originally */ | ||
1283 | intersected_rd = regdom_intersect(rd, cfg80211_regdomain); | ||
1284 | } | ||
1285 | |||
1286 | if (!intersected_rd) | ||
1287 | return -EINVAL; | ||
1288 | |||
1289 | drv = wiphy_to_dev(wiphy); | ||
1290 | |||
1291 | drv->country_ie_alpha2[0] = rd->alpha2[0]; | ||
1292 | drv->country_ie_alpha2[1] = rd->alpha2[1]; | ||
1293 | drv->env = last_request->country_ie_env; | ||
1294 | |||
1295 | BUG_ON(intersected_rd == rd); | ||
1296 | |||
1297 | kfree(rd); | ||
1298 | rd = NULL; | ||
1299 | |||
1300 | reset_regdomains(); | ||
1301 | cfg80211_regdomain = intersected_rd; | ||
771 | 1302 | ||
772 | return 0; | 1303 | return 0; |
773 | } | 1304 | } |
@@ -775,52 +1306,41 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
775 | 1306 | ||
776 | /* Use this call to set the current regulatory domain. Conflicts with | 1307 | /* Use this call to set the current regulatory domain. Conflicts with |
777 | * multiple drivers can be ironed out later. Caller must've already | 1308 | * multiple drivers can be ironed out later. Caller must've already |
778 | * kmalloc'd the rd structure. If this calls fails you should kfree() | 1309 | * kmalloc'd the rd structure. Caller must hold cfg80211_drv_mutex */ |
779 | * the passed rd. Caller must hold cfg80211_drv_mutex */ | ||
780 | int set_regdom(const struct ieee80211_regdomain *rd) | 1310 | int set_regdom(const struct ieee80211_regdomain *rd) |
781 | { | 1311 | { |
782 | struct regulatory_request *this_request = NULL, *prev_request = NULL; | ||
783 | int r; | 1312 | int r; |
784 | 1313 | ||
785 | if (!list_empty(®ulatory_requests)) | ||
786 | prev_request = list_first_entry(®ulatory_requests, | ||
787 | struct regulatory_request, list); | ||
788 | |||
789 | /* Note that this doesn't update the wiphys, this is done below */ | 1314 | /* Note that this doesn't update the wiphys, this is done below */ |
790 | r = __set_regdom(rd); | 1315 | r = __set_regdom(rd); |
791 | if (r) | 1316 | if (r) { |
1317 | kfree(rd); | ||
792 | return r; | 1318 | return r; |
793 | |||
794 | BUG_ON((!__reg_is_valid_request(rd->alpha2, &this_request))); | ||
795 | |||
796 | /* The initial standard core update of the world regulatory domain, no | ||
797 | * need to keep that request info around if it didn't fail. */ | ||
798 | if (is_world_regdom(rd->alpha2) && | ||
799 | this_request->initiator == REGDOM_SET_BY_CORE && | ||
800 | this_request->granted) { | ||
801 | list_del(&this_request->list); | ||
802 | kfree(this_request); | ||
803 | this_request = NULL; | ||
804 | } | ||
805 | |||
806 | /* Remove old requests, we only leave behind the last one */ | ||
807 | if (prev_request) { | ||
808 | list_del(&prev_request->list); | ||
809 | kfree(prev_request); | ||
810 | prev_request = NULL; | ||
811 | } | 1319 | } |
812 | 1320 | ||
813 | /* This would make this whole thing pointless */ | 1321 | /* This would make this whole thing pointless */ |
814 | BUG_ON(rd != cfg80211_regdomain); | 1322 | if (!last_request->intersect) |
1323 | BUG_ON(rd != cfg80211_regdomain); | ||
815 | 1324 | ||
816 | /* update all wiphys now with the new established regulatory domain */ | 1325 | /* update all wiphys now with the new established regulatory domain */ |
817 | update_all_wiphy_regulatory(this_request->initiator); | 1326 | update_all_wiphy_regulatory(last_request->initiator); |
818 | 1327 | ||
819 | print_regdomain(rd); | 1328 | print_regdomain(cfg80211_regdomain); |
820 | 1329 | ||
821 | return r; | 1330 | return r; |
822 | } | 1331 | } |
823 | 1332 | ||
1333 | /* Caller must hold cfg80211_drv_mutex */ | ||
1334 | void reg_device_remove(struct wiphy *wiphy) | ||
1335 | { | ||
1336 | if (!last_request || !last_request->wiphy) | ||
1337 | return; | ||
1338 | if (last_request->wiphy != wiphy) | ||
1339 | return; | ||
1340 | last_request->wiphy = NULL; | ||
1341 | last_request->country_ie_env = ENVIRON_ANY; | ||
1342 | } | ||
1343 | |||
824 | int regulatory_init(void) | 1344 | int regulatory_init(void) |
825 | { | 1345 | { |
826 | int err; | 1346 | int err; |
@@ -838,13 +1358,13 @@ int regulatory_init(void) | |||
838 | * you have CRDA you get it updated, otherwise you get | 1358 | * you have CRDA you get it updated, otherwise you get |
839 | * stuck with the static values. We ignore "EU" code as | 1359 | * stuck with the static values. We ignore "EU" code as |
840 | * that is not a valid ISO / IEC 3166 alpha2 */ | 1360 | * that is not a valid ISO / IEC 3166 alpha2 */ |
841 | if (ieee80211_regdom[0] != 'E' && ieee80211_regdom[1] != 'U') | 1361 | if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U') |
842 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, | 1362 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, |
843 | ieee80211_regdom, NULL); | 1363 | ieee80211_regdom, 0, ENVIRON_ANY); |
844 | #else | 1364 | #else |
845 | cfg80211_regdomain = cfg80211_world_regdom; | 1365 | cfg80211_regdomain = cfg80211_world_regdom; |
846 | 1366 | ||
847 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", NULL); | 1367 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", 0, ENVIRON_ANY); |
848 | if (err) | 1368 | if (err) |
849 | printk(KERN_ERR "cfg80211: calling CRDA failed - " | 1369 | printk(KERN_ERR "cfg80211: calling CRDA failed - " |
850 | "unable to update world regulatory domain, " | 1370 | "unable to update world regulatory domain, " |
@@ -856,16 +1376,15 @@ int regulatory_init(void) | |||
856 | 1376 | ||
857 | void regulatory_exit(void) | 1377 | void regulatory_exit(void) |
858 | { | 1378 | { |
859 | struct regulatory_request *req, *req_tmp; | ||
860 | |||
861 | mutex_lock(&cfg80211_drv_mutex); | 1379 | mutex_lock(&cfg80211_drv_mutex); |
862 | 1380 | ||
863 | reset_regdomains(); | 1381 | reset_regdomains(); |
864 | 1382 | ||
865 | list_for_each_entry_safe(req, req_tmp, ®ulatory_requests, list) { | 1383 | kfree(country_ie_regdomain); |
866 | list_del(&req->list); | 1384 | country_ie_regdomain = NULL; |
867 | kfree(req); | 1385 | |
868 | } | 1386 | kfree(last_request); |
1387 | |||
869 | platform_device_unregister(reg_pdev); | 1388 | platform_device_unregister(reg_pdev); |
870 | 1389 | ||
871 | mutex_unlock(&cfg80211_drv_mutex); | 1390 | mutex_unlock(&cfg80211_drv_mutex); |
diff --git a/net/wireless/reg.h b/net/wireless/reg.h index a33362872f3c..a76ea3ff7cd6 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h | |||
@@ -1,13 +1,44 @@ | |||
1 | #ifndef __NET_WIRELESS_REG_H | 1 | #ifndef __NET_WIRELESS_REG_H |
2 | #define __NET_WIRELESS_REG_H | 2 | #define __NET_WIRELESS_REG_H |
3 | 3 | ||
4 | extern struct mutex cfg80211_reg_mutex; | ||
5 | bool is_world_regdom(const char *alpha2); | 4 | bool is_world_regdom(const char *alpha2); |
6 | bool reg_is_valid_request(const char *alpha2); | 5 | bool reg_is_valid_request(const char *alpha2); |
7 | 6 | ||
7 | void reg_device_remove(struct wiphy *wiphy); | ||
8 | |||
8 | int regulatory_init(void); | 9 | int regulatory_init(void); |
9 | void regulatory_exit(void); | 10 | void regulatory_exit(void); |
10 | 11 | ||
11 | int set_regdom(const struct ieee80211_regdomain *rd); | 12 | int set_regdom(const struct ieee80211_regdomain *rd); |
12 | 13 | ||
14 | enum environment_cap { | ||
15 | ENVIRON_ANY, | ||
16 | ENVIRON_INDOOR, | ||
17 | ENVIRON_OUTDOOR, | ||
18 | }; | ||
19 | |||
20 | |||
21 | /** | ||
22 | * __regulatory_hint - hint to the wireless core a regulatory domain | ||
23 | * @wiphy: if the hint comes from country information from an AP, this | ||
24 | * is required to be set to the wiphy that received the information | ||
25 | * @alpha2: the ISO/IEC 3166 alpha2 being claimed the regulatory domain | ||
26 | * should be in. | ||
27 | * @country_ie_checksum: checksum of processed country IE, set this to 0 | ||
28 | * if the hint did not come from a country IE | ||
29 | * @country_ie_env: the environment the IE told us we are in, %ENVIRON_* | ||
30 | * | ||
31 | * The Wireless subsystem can use this function to hint to the wireless core | ||
32 | * what it believes should be the current regulatory domain by giving it an | ||
33 | * ISO/IEC 3166 alpha2 country code it knows its regulatory domain should be | ||
34 | * in. | ||
35 | * | ||
36 | * Returns zero if all went fine, %-EALREADY if a regulatory domain had | ||
37 | * already been set or other standard error codes. | ||
38 | * | ||
39 | */ | ||
40 | extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | ||
41 | const char *alpha2, u32 country_ie_checksum, | ||
42 | enum environment_cap country_ie_env); | ||
43 | |||
13 | #endif /* __NET_WIRELESS_REG_H */ | 44 | #endif /* __NET_WIRELESS_REG_H */ |
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 29f820e18251..79a382877641 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c | |||
@@ -23,25 +23,20 @@ static inline struct cfg80211_registered_device *dev_to_rdev( | |||
23 | return container_of(dev, struct cfg80211_registered_device, wiphy.dev); | 23 | return container_of(dev, struct cfg80211_registered_device, wiphy.dev); |
24 | } | 24 | } |
25 | 25 | ||
26 | static ssize_t _show_index(struct device *dev, struct device_attribute *attr, | 26 | #define SHOW_FMT(name, fmt, member) \ |
27 | char *buf) | 27 | static ssize_t name ## _show(struct device *dev, \ |
28 | { | 28 | struct device_attribute *attr, \ |
29 | return sprintf(buf, "%d\n", dev_to_rdev(dev)->idx); | 29 | char *buf) \ |
30 | { \ | ||
31 | return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \ | ||
30 | } | 32 | } |
31 | 33 | ||
32 | static ssize_t _show_permaddr(struct device *dev, | 34 | SHOW_FMT(index, "%d", idx); |
33 | struct device_attribute *attr, | 35 | SHOW_FMT(macaddress, "%pM", wiphy.perm_addr); |
34 | char *buf) | ||
35 | { | ||
36 | unsigned char *addr = dev_to_rdev(dev)->wiphy.perm_addr; | ||
37 | |||
38 | return sprintf(buf, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", | ||
39 | addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); | ||
40 | } | ||
41 | 36 | ||
42 | static struct device_attribute ieee80211_dev_attrs[] = { | 37 | static struct device_attribute ieee80211_dev_attrs[] = { |
43 | __ATTR(index, S_IRUGO, _show_index, NULL), | 38 | __ATTR_RO(index), |
44 | __ATTR(macaddress, S_IRUGO, _show_permaddr, NULL), | 39 | __ATTR_RO(macaddress), |
45 | {} | 40 | {} |
46 | }; | 41 | }; |
47 | 42 | ||
diff --git a/net/wireless/util.c b/net/wireless/util.c index f54424693a38..e76cc28b0345 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -7,6 +7,25 @@ | |||
7 | #include <asm/bitops.h> | 7 | #include <asm/bitops.h> |
8 | #include "core.h" | 8 | #include "core.h" |
9 | 9 | ||
10 | struct ieee80211_rate * | ||
11 | ieee80211_get_response_rate(struct ieee80211_supported_band *sband, | ||
12 | u64 basic_rates, int bitrate) | ||
13 | { | ||
14 | struct ieee80211_rate *result = &sband->bitrates[0]; | ||
15 | int i; | ||
16 | |||
17 | for (i = 0; i < sband->n_bitrates; i++) { | ||
18 | if (!(basic_rates & BIT(i))) | ||
19 | continue; | ||
20 | if (sband->bitrates[i].bitrate > bitrate) | ||
21 | continue; | ||
22 | result = &sband->bitrates[i]; | ||
23 | } | ||
24 | |||
25 | return result; | ||
26 | } | ||
27 | EXPORT_SYMBOL(ieee80211_get_response_rate); | ||
28 | |||
10 | int ieee80211_channel_to_frequency(int chan) | 29 | int ieee80211_channel_to_frequency(int chan) |
11 | { | 30 | { |
12 | if (chan < 14) | 31 | if (chan < 14) |
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c new file mode 100644 index 000000000000..58e489fd4aed --- /dev/null +++ b/net/wireless/wext-compat.c | |||
@@ -0,0 +1,139 @@ | |||
1 | /* | ||
2 | * cfg80211 - wext compat code | ||
3 | * | ||
4 | * This is temporary code until all wireless functionality is migrated | ||
5 | * into cfg80211, when that happens all the exports here go away and | ||
6 | * we directly assign the wireless handlers of wireless interfaces. | ||
7 | * | ||
8 | * Copyright 2008 Johannes Berg <johannes@sipsolutions.net> | ||
9 | */ | ||
10 | |||
11 | #include <linux/wireless.h> | ||
12 | #include <linux/nl80211.h> | ||
13 | #include <net/iw_handler.h> | ||
14 | #include <net/wireless.h> | ||
15 | #include <net/cfg80211.h> | ||
16 | #include "core.h" | ||
17 | |||
18 | int cfg80211_wext_giwname(struct net_device *dev, | ||
19 | struct iw_request_info *info, | ||
20 | char *name, char *extra) | ||
21 | { | ||
22 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
23 | struct ieee80211_supported_band *sband; | ||
24 | bool is_ht = false, is_a = false, is_b = false, is_g = false; | ||
25 | |||
26 | if (!wdev) | ||
27 | return -EOPNOTSUPP; | ||
28 | |||
29 | sband = wdev->wiphy->bands[IEEE80211_BAND_5GHZ]; | ||
30 | if (sband) { | ||
31 | is_a = true; | ||
32 | is_ht |= sband->ht_cap.ht_supported; | ||
33 | } | ||
34 | |||
35 | sband = wdev->wiphy->bands[IEEE80211_BAND_2GHZ]; | ||
36 | if (sband) { | ||
37 | int i; | ||
38 | /* Check for mandatory rates */ | ||
39 | for (i = 0; i < sband->n_bitrates; i++) { | ||
40 | if (sband->bitrates[i].bitrate == 10) | ||
41 | is_b = true; | ||
42 | if (sband->bitrates[i].bitrate == 60) | ||
43 | is_g = true; | ||
44 | } | ||
45 | is_ht |= sband->ht_cap.ht_supported; | ||
46 | } | ||
47 | |||
48 | strcpy(name, "IEEE 802.11"); | ||
49 | if (is_a) | ||
50 | strcat(name, "a"); | ||
51 | if (is_b) | ||
52 | strcat(name, "b"); | ||
53 | if (is_g) | ||
54 | strcat(name, "g"); | ||
55 | if (is_ht) | ||
56 | strcat(name, "n"); | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | EXPORT_SYMBOL(cfg80211_wext_giwname); | ||
61 | |||
62 | int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, | ||
63 | u32 *mode, char *extra) | ||
64 | { | ||
65 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
66 | struct cfg80211_registered_device *rdev; | ||
67 | struct vif_params vifparams; | ||
68 | enum nl80211_iftype type; | ||
69 | |||
70 | if (!wdev) | ||
71 | return -EOPNOTSUPP; | ||
72 | |||
73 | rdev = wiphy_to_dev(wdev->wiphy); | ||
74 | |||
75 | if (!rdev->ops->change_virtual_intf) | ||
76 | return -EOPNOTSUPP; | ||
77 | |||
78 | /* don't support changing VLANs, you just re-create them */ | ||
79 | if (wdev->iftype == NL80211_IFTYPE_AP_VLAN) | ||
80 | return -EOPNOTSUPP; | ||
81 | |||
82 | switch (*mode) { | ||
83 | case IW_MODE_INFRA: | ||
84 | type = NL80211_IFTYPE_STATION; | ||
85 | break; | ||
86 | case IW_MODE_ADHOC: | ||
87 | type = NL80211_IFTYPE_ADHOC; | ||
88 | break; | ||
89 | case IW_MODE_REPEAT: | ||
90 | type = NL80211_IFTYPE_WDS; | ||
91 | break; | ||
92 | case IW_MODE_MONITOR: | ||
93 | type = NL80211_IFTYPE_MONITOR; | ||
94 | break; | ||
95 | default: | ||
96 | return -EINVAL; | ||
97 | } | ||
98 | |||
99 | memset(&vifparams, 0, sizeof(vifparams)); | ||
100 | |||
101 | return rdev->ops->change_virtual_intf(wdev->wiphy, dev->ifindex, type, | ||
102 | NULL, &vifparams); | ||
103 | } | ||
104 | EXPORT_SYMBOL(cfg80211_wext_siwmode); | ||
105 | |||
106 | int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info, | ||
107 | u32 *mode, char *extra) | ||
108 | { | ||
109 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
110 | |||
111 | if (!wdev) | ||
112 | return -EOPNOTSUPP; | ||
113 | |||
114 | switch (wdev->iftype) { | ||
115 | case NL80211_IFTYPE_AP: | ||
116 | *mode = IW_MODE_MASTER; | ||
117 | break; | ||
118 | case NL80211_IFTYPE_STATION: | ||
119 | *mode = IW_MODE_INFRA; | ||
120 | break; | ||
121 | case NL80211_IFTYPE_ADHOC: | ||
122 | *mode = IW_MODE_ADHOC; | ||
123 | break; | ||
124 | case NL80211_IFTYPE_MONITOR: | ||
125 | *mode = IW_MODE_MONITOR; | ||
126 | break; | ||
127 | case NL80211_IFTYPE_WDS: | ||
128 | *mode = IW_MODE_REPEAT; | ||
129 | break; | ||
130 | case NL80211_IFTYPE_AP_VLAN: | ||
131 | *mode = IW_MODE_SECOND; /* FIXME */ | ||
132 | break; | ||
133 | default: | ||
134 | *mode = IW_MODE_AUTO; | ||
135 | break; | ||
136 | } | ||
137 | return 0; | ||
138 | } | ||
139 | EXPORT_SYMBOL(cfg80211_wext_giwmode); | ||
diff --git a/net/wireless/wext.c b/net/wireless/wext.c index d98ffb75119a..e49a2d1ef1e4 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c | |||
@@ -64,7 +64,7 @@ | |||
64 | * o Remove spy_offset from struct iw_handler_def | 64 | * o Remove spy_offset from struct iw_handler_def |
65 | * o Start deprecating dev->get_wireless_stats, output a warning | 65 | * o Start deprecating dev->get_wireless_stats, output a warning |
66 | * o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless | 66 | * o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless |
67 | * o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats) | 67 | * o Don't lose INVALID/DBM flags when clearing UPDATED flags (iwstats) |
68 | * | 68 | * |
69 | * v8 - 17.02.06 - Jean II | 69 | * v8 - 17.02.06 - Jean II |
70 | * o RtNetlink requests support (SET/GET) | 70 | * o RtNetlink requests support (SET/GET) |