diff options
Diffstat (limited to 'net/mac80211/key.c')
-rw-r--r-- | net/mac80211/key.c | 368 |
1 files changed, 293 insertions, 75 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c index ed57fb8e82fc..711e36e54ff8 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * Copyright 2002-2005, Instant802 Networks, Inc. | 2 | * Copyright 2002-2005, Instant802 Networks, Inc. |
3 | * Copyright 2005-2006, Devicescape Software, Inc. | 3 | * Copyright 2005-2006, Devicescape Software, Inc. |
4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
5 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> | 5 | * Copyright 2007-2008 Johannes Berg <johannes@sipsolutions.net> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 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 | 8 | * it under the terms of the GNU General Public License version 2 as |
@@ -13,14 +13,15 @@ | |||
13 | #include <linux/etherdevice.h> | 13 | #include <linux/etherdevice.h> |
14 | #include <linux/list.h> | 14 | #include <linux/list.h> |
15 | #include <linux/rcupdate.h> | 15 | #include <linux/rcupdate.h> |
16 | #include <linux/rtnetlink.h> | ||
16 | #include <net/mac80211.h> | 17 | #include <net/mac80211.h> |
17 | #include "ieee80211_i.h" | 18 | #include "ieee80211_i.h" |
18 | #include "debugfs_key.h" | 19 | #include "debugfs_key.h" |
19 | #include "aes_ccm.h" | 20 | #include "aes_ccm.h" |
20 | 21 | ||
21 | 22 | ||
22 | /* | 23 | /** |
23 | * Key handling basics | 24 | * DOC: Key handling basics |
24 | * | 25 | * |
25 | * Key handling in mac80211 is done based on per-interface (sub_if_data) | 26 | * Key handling in mac80211 is done based on per-interface (sub_if_data) |
26 | * keys and per-station keys. Since each station belongs to an interface, | 27 | * keys and per-station keys. Since each station belongs to an interface, |
@@ -32,13 +33,78 @@ | |||
32 | * There is currently no way of knowing this except by looking into | 33 | * There is currently no way of knowing this except by looking into |
33 | * debugfs. | 34 | * debugfs. |
34 | * | 35 | * |
35 | * All operations here are called under RTNL so no extra locking is | 36 | * All key operations are protected internally so you can call them at |
36 | * required. | 37 | * any time. |
38 | * | ||
39 | * Within mac80211, key references are, just as STA structure references, | ||
40 | * protected by RCU. Note, however, that some things are unprotected, | ||
41 | * namely the key->sta dereferences within the hardware acceleration | ||
42 | * functions. This means that sta_info_destroy() must flush the key todo | ||
43 | * list. | ||
44 | * | ||
45 | * All the direct key list manipulation functions must not sleep because | ||
46 | * they can operate on STA info structs that are protected by RCU. | ||
37 | */ | 47 | */ |
38 | 48 | ||
39 | static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | 49 | static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; |
40 | static const u8 zero_addr[ETH_ALEN]; | 50 | static const u8 zero_addr[ETH_ALEN]; |
41 | 51 | ||
52 | /* key mutex: used to synchronise todo runners */ | ||
53 | static DEFINE_MUTEX(key_mutex); | ||
54 | static DEFINE_SPINLOCK(todo_lock); | ||
55 | static LIST_HEAD(todo_list); | ||
56 | |||
57 | static void key_todo(struct work_struct *work) | ||
58 | { | ||
59 | ieee80211_key_todo(); | ||
60 | } | ||
61 | |||
62 | static DECLARE_WORK(todo_work, key_todo); | ||
63 | |||
64 | /** | ||
65 | * add_todo - add todo item for a key | ||
66 | * | ||
67 | * @key: key to add to do item for | ||
68 | * @flag: todo flag(s) | ||
69 | */ | ||
70 | static void add_todo(struct ieee80211_key *key, u32 flag) | ||
71 | { | ||
72 | if (!key) | ||
73 | return; | ||
74 | |||
75 | spin_lock(&todo_lock); | ||
76 | key->flags |= flag; | ||
77 | /* only add if not already added */ | ||
78 | if (list_empty(&key->todo)) | ||
79 | list_add(&key->todo, &todo_list); | ||
80 | schedule_work(&todo_work); | ||
81 | spin_unlock(&todo_lock); | ||
82 | } | ||
83 | |||
84 | /** | ||
85 | * ieee80211_key_lock - lock the mac80211 key operation lock | ||
86 | * | ||
87 | * This locks the (global) mac80211 key operation lock, all | ||
88 | * key operations must be done under this lock. | ||
89 | */ | ||
90 | static void ieee80211_key_lock(void) | ||
91 | { | ||
92 | mutex_lock(&key_mutex); | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * ieee80211_key_unlock - unlock the mac80211 key operation lock | ||
97 | */ | ||
98 | static void ieee80211_key_unlock(void) | ||
99 | { | ||
100 | mutex_unlock(&key_mutex); | ||
101 | } | ||
102 | |||
103 | static void assert_key_lock(void) | ||
104 | { | ||
105 | WARN_ON(!mutex_is_locked(&key_mutex)); | ||
106 | } | ||
107 | |||
42 | static const u8 *get_mac_for_key(struct ieee80211_key *key) | 108 | static const u8 *get_mac_for_key(struct ieee80211_key *key) |
43 | { | 109 | { |
44 | const u8 *addr = bcast_addr; | 110 | const u8 *addr = bcast_addr; |
@@ -65,6 +131,9 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) | |||
65 | int ret; | 131 | int ret; |
66 | DECLARE_MAC_BUF(mac); | 132 | DECLARE_MAC_BUF(mac); |
67 | 133 | ||
134 | assert_key_lock(); | ||
135 | might_sleep(); | ||
136 | |||
68 | if (!key->local->ops->set_key) | 137 | if (!key->local->ops->set_key) |
69 | return; | 138 | return; |
70 | 139 | ||
@@ -74,8 +143,11 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) | |||
74 | key->sdata->dev->dev_addr, addr, | 143 | key->sdata->dev->dev_addr, addr, |
75 | &key->conf); | 144 | &key->conf); |
76 | 145 | ||
77 | if (!ret) | 146 | if (!ret) { |
147 | spin_lock(&todo_lock); | ||
78 | key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; | 148 | key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; |
149 | spin_unlock(&todo_lock); | ||
150 | } | ||
79 | 151 | ||
80 | if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP) | 152 | if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP) |
81 | printk(KERN_ERR "mac80211-%s: failed to set key " | 153 | printk(KERN_ERR "mac80211-%s: failed to set key " |
@@ -90,11 +162,18 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) | |||
90 | int ret; | 162 | int ret; |
91 | DECLARE_MAC_BUF(mac); | 163 | DECLARE_MAC_BUF(mac); |
92 | 164 | ||
93 | if (!key->local->ops->set_key) | 165 | assert_key_lock(); |
166 | might_sleep(); | ||
167 | |||
168 | if (!key || !key->local->ops->set_key) | ||
94 | return; | 169 | return; |
95 | 170 | ||
96 | if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) | 171 | spin_lock(&todo_lock); |
172 | if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) { | ||
173 | spin_unlock(&todo_lock); | ||
97 | return; | 174 | return; |
175 | } | ||
176 | spin_unlock(&todo_lock); | ||
98 | 177 | ||
99 | addr = get_mac_for_key(key); | 178 | addr = get_mac_for_key(key); |
100 | 179 | ||
@@ -108,12 +187,75 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) | |||
108 | wiphy_name(key->local->hw.wiphy), | 187 | wiphy_name(key->local->hw.wiphy), |
109 | key->conf.keyidx, print_mac(mac, addr), ret); | 188 | key->conf.keyidx, print_mac(mac, addr), ret); |
110 | 189 | ||
190 | spin_lock(&todo_lock); | ||
111 | key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; | 191 | key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; |
192 | spin_unlock(&todo_lock); | ||
112 | } | 193 | } |
113 | 194 | ||
114 | struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata, | 195 | static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, |
115 | struct sta_info *sta, | 196 | int idx) |
116 | enum ieee80211_key_alg alg, | 197 | { |
198 | struct ieee80211_key *key = NULL; | ||
199 | |||
200 | if (idx >= 0 && idx < NUM_DEFAULT_KEYS) | ||
201 | key = sdata->keys[idx]; | ||
202 | |||
203 | rcu_assign_pointer(sdata->default_key, key); | ||
204 | |||
205 | if (key) | ||
206 | add_todo(key, KEY_FLAG_TODO_DEFKEY); | ||
207 | } | ||
208 | |||
209 | void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx) | ||
210 | { | ||
211 | unsigned long flags; | ||
212 | |||
213 | spin_lock_irqsave(&sdata->local->sta_lock, flags); | ||
214 | __ieee80211_set_default_key(sdata, idx); | ||
215 | spin_unlock_irqrestore(&sdata->local->sta_lock, flags); | ||
216 | } | ||
217 | |||
218 | |||
219 | static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, | ||
220 | struct sta_info *sta, | ||
221 | struct ieee80211_key *old, | ||
222 | struct ieee80211_key *new) | ||
223 | { | ||
224 | int idx, defkey; | ||
225 | |||
226 | if (new) | ||
227 | list_add(&new->list, &sdata->key_list); | ||
228 | |||
229 | if (sta) { | ||
230 | rcu_assign_pointer(sta->key, new); | ||
231 | } else { | ||
232 | WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx); | ||
233 | |||
234 | if (old) | ||
235 | idx = old->conf.keyidx; | ||
236 | else | ||
237 | idx = new->conf.keyidx; | ||
238 | |||
239 | defkey = old && sdata->default_key == old; | ||
240 | |||
241 | if (defkey && !new) | ||
242 | __ieee80211_set_default_key(sdata, -1); | ||
243 | |||
244 | rcu_assign_pointer(sdata->keys[idx], new); | ||
245 | if (defkey && new) | ||
246 | __ieee80211_set_default_key(sdata, new->conf.keyidx); | ||
247 | } | ||
248 | |||
249 | if (old) { | ||
250 | /* | ||
251 | * We'll use an empty list to indicate that the key | ||
252 | * has already been removed. | ||
253 | */ | ||
254 | list_del_init(&old->list); | ||
255 | } | ||
256 | } | ||
257 | |||
258 | struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, | ||
117 | int idx, | 259 | int idx, |
118 | size_t key_len, | 260 | size_t key_len, |
119 | const u8 *key_data) | 261 | const u8 *key_data) |
@@ -137,10 +279,8 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata, | |||
137 | key->conf.keyidx = idx; | 279 | key->conf.keyidx = idx; |
138 | key->conf.keylen = key_len; | 280 | key->conf.keylen = key_len; |
139 | memcpy(key->conf.key, key_data, key_len); | 281 | memcpy(key->conf.key, key_data, key_len); |
140 | 282 | INIT_LIST_HEAD(&key->list); | |
141 | key->local = sdata->local; | 283 | INIT_LIST_HEAD(&key->todo); |
142 | key->sdata = sdata; | ||
143 | key->sta = sta; | ||
144 | 284 | ||
145 | if (alg == ALG_CCMP) { | 285 | if (alg == ALG_CCMP) { |
146 | /* | 286 | /* |
@@ -149,22 +289,31 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata, | |||
149 | */ | 289 | */ |
150 | key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data); | 290 | key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data); |
151 | if (!key->u.ccmp.tfm) { | 291 | if (!key->u.ccmp.tfm) { |
152 | ieee80211_key_free(key); | 292 | kfree(key); |
153 | return NULL; | 293 | return NULL; |
154 | } | 294 | } |
155 | } | 295 | } |
156 | 296 | ||
157 | ieee80211_debugfs_key_add(key->local, key); | 297 | return key; |
298 | } | ||
158 | 299 | ||
159 | /* remove key first */ | 300 | void ieee80211_key_link(struct ieee80211_key *key, |
160 | if (sta) | 301 | struct ieee80211_sub_if_data *sdata, |
161 | ieee80211_key_free(sta->key); | 302 | struct sta_info *sta) |
162 | else | 303 | { |
163 | ieee80211_key_free(sdata->keys[idx]); | 304 | struct ieee80211_key *old_key; |
305 | unsigned long flags; | ||
306 | int idx; | ||
164 | 307 | ||
165 | if (sta) { | 308 | BUG_ON(!sdata); |
166 | ieee80211_debugfs_key_sta_link(key, sta); | 309 | BUG_ON(!key); |
310 | |||
311 | idx = key->conf.keyidx; | ||
312 | key->local = sdata->local; | ||
313 | key->sdata = sdata; | ||
314 | key->sta = sta; | ||
167 | 315 | ||
316 | if (sta) { | ||
168 | /* | 317 | /* |
169 | * some hardware cannot handle TKIP with QoS, so | 318 | * some hardware cannot handle TKIP with QoS, so |
170 | * we indicate whether QoS could be in use. | 319 | * we indicate whether QoS could be in use. |
@@ -175,105 +324,174 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata, | |||
175 | if (sdata->vif.type == IEEE80211_IF_TYPE_STA) { | 324 | if (sdata->vif.type == IEEE80211_IF_TYPE_STA) { |
176 | struct sta_info *ap; | 325 | struct sta_info *ap; |
177 | 326 | ||
327 | /* | ||
328 | * We're getting a sta pointer in, | ||
329 | * so must be under RCU read lock. | ||
330 | */ | ||
331 | |||
178 | /* same here, the AP could be using QoS */ | 332 | /* same here, the AP could be using QoS */ |
179 | ap = sta_info_get(key->local, key->sdata->u.sta.bssid); | 333 | ap = sta_info_get(key->local, key->sdata->u.sta.bssid); |
180 | if (ap) { | 334 | if (ap) { |
181 | if (ap->flags & WLAN_STA_WME) | 335 | if (ap->flags & WLAN_STA_WME) |
182 | key->conf.flags |= | 336 | key->conf.flags |= |
183 | IEEE80211_KEY_FLAG_WMM_STA; | 337 | IEEE80211_KEY_FLAG_WMM_STA; |
184 | sta_info_put(ap); | ||
185 | } | 338 | } |
186 | } | 339 | } |
187 | } | 340 | } |
188 | 341 | ||
189 | /* enable hwaccel if appropriate */ | 342 | spin_lock_irqsave(&sdata->local->sta_lock, flags); |
190 | if (netif_running(key->sdata->dev)) | ||
191 | ieee80211_key_enable_hw_accel(key); | ||
192 | 343 | ||
193 | if (sta) | 344 | if (sta) |
194 | rcu_assign_pointer(sta->key, key); | 345 | old_key = sta->key; |
195 | else | 346 | else |
196 | rcu_assign_pointer(sdata->keys[idx], key); | 347 | old_key = sdata->keys[idx]; |
197 | 348 | ||
198 | list_add(&key->list, &sdata->key_list); | 349 | __ieee80211_key_replace(sdata, sta, old_key, key); |
199 | 350 | ||
200 | return key; | 351 | spin_unlock_irqrestore(&sdata->local->sta_lock, flags); |
352 | |||
353 | /* free old key later */ | ||
354 | add_todo(old_key, KEY_FLAG_TODO_DELETE); | ||
355 | |||
356 | add_todo(key, KEY_FLAG_TODO_ADD_DEBUGFS); | ||
357 | if (netif_running(sdata->dev)) | ||
358 | add_todo(key, KEY_FLAG_TODO_HWACCEL); | ||
201 | } | 359 | } |
202 | 360 | ||
203 | void ieee80211_key_free(struct ieee80211_key *key) | 361 | void ieee80211_key_free(struct ieee80211_key *key) |
204 | { | 362 | { |
363 | unsigned long flags; | ||
364 | |||
205 | if (!key) | 365 | if (!key) |
206 | return; | 366 | return; |
207 | 367 | ||
208 | if (key->sta) { | 368 | /* |
209 | rcu_assign_pointer(key->sta->key, NULL); | 369 | * Replace key with nothingness if it was ever used. |
210 | } else { | 370 | */ |
211 | if (key->sdata->default_key == key) | 371 | if (key->sdata) { |
212 | ieee80211_set_default_key(key->sdata, -1); | 372 | spin_lock_irqsave(&key->sdata->local->sta_lock, flags); |
213 | if (key->conf.keyidx >= 0 && | 373 | __ieee80211_key_replace(key->sdata, key->sta, |
214 | key->conf.keyidx < NUM_DEFAULT_KEYS) | 374 | key, NULL); |
215 | rcu_assign_pointer(key->sdata->keys[key->conf.keyidx], | 375 | spin_unlock_irqrestore(&key->sdata->local->sta_lock, flags); |
216 | NULL); | ||
217 | else | ||
218 | WARN_ON(1); | ||
219 | } | 376 | } |
220 | 377 | ||
221 | /* wait for all key users to complete */ | 378 | add_todo(key, KEY_FLAG_TODO_DELETE); |
222 | synchronize_rcu(); | 379 | } |
380 | |||
381 | void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata) | ||
382 | { | ||
383 | struct ieee80211_key *key; | ||
384 | |||
385 | might_sleep(); | ||
386 | |||
387 | if (WARN_ON(!netif_running(sdata->dev))) | ||
388 | return; | ||
389 | |||
390 | ieee80211_key_lock(); | ||
391 | |||
392 | list_for_each_entry(key, &sdata->key_list, list) | ||
393 | ieee80211_key_enable_hw_accel(key); | ||
394 | |||
395 | ieee80211_key_unlock(); | ||
396 | } | ||
397 | |||
398 | void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata) | ||
399 | { | ||
400 | struct ieee80211_key *key; | ||
401 | |||
402 | might_sleep(); | ||
403 | |||
404 | ieee80211_key_lock(); | ||
405 | |||
406 | list_for_each_entry(key, &sdata->key_list, list) | ||
407 | ieee80211_key_disable_hw_accel(key); | ||
408 | |||
409 | ieee80211_key_unlock(); | ||
410 | } | ||
411 | |||
412 | static void __ieee80211_key_free(struct ieee80211_key *key) | ||
413 | { | ||
414 | if (!key) | ||
415 | return; | ||
223 | 416 | ||
224 | /* remove from hwaccel if appropriate */ | ||
225 | ieee80211_key_disable_hw_accel(key); | 417 | ieee80211_key_disable_hw_accel(key); |
226 | 418 | ||
227 | if (key->conf.alg == ALG_CCMP) | 419 | if (key->conf.alg == ALG_CCMP) |
228 | ieee80211_aes_key_free(key->u.ccmp.tfm); | 420 | ieee80211_aes_key_free(key->u.ccmp.tfm); |
229 | ieee80211_debugfs_key_remove(key); | 421 | ieee80211_debugfs_key_remove(key); |
230 | 422 | ||
231 | list_del(&key->list); | ||
232 | |||
233 | kfree(key); | 423 | kfree(key); |
234 | } | 424 | } |
235 | 425 | ||
236 | void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx) | 426 | static void __ieee80211_key_todo(void) |
237 | { | 427 | { |
238 | struct ieee80211_key *key = NULL; | 428 | struct ieee80211_key *key; |
429 | bool work_done; | ||
430 | u32 todoflags; | ||
239 | 431 | ||
240 | if (idx >= 0 && idx < NUM_DEFAULT_KEYS) | 432 | /* |
241 | key = sdata->keys[idx]; | 433 | * NB: sta_info_destroy relies on this! |
434 | */ | ||
435 | synchronize_rcu(); | ||
242 | 436 | ||
243 | if (sdata->default_key != key) { | 437 | spin_lock(&todo_lock); |
244 | ieee80211_debugfs_key_remove_default(sdata); | 438 | while (!list_empty(&todo_list)) { |
439 | key = list_first_entry(&todo_list, struct ieee80211_key, todo); | ||
440 | list_del_init(&key->todo); | ||
441 | todoflags = key->flags & (KEY_FLAG_TODO_ADD_DEBUGFS | | ||
442 | KEY_FLAG_TODO_DEFKEY | | ||
443 | KEY_FLAG_TODO_HWACCEL | | ||
444 | KEY_FLAG_TODO_DELETE); | ||
445 | key->flags &= ~todoflags; | ||
446 | spin_unlock(&todo_lock); | ||
447 | |||
448 | work_done = false; | ||
449 | |||
450 | if (todoflags & KEY_FLAG_TODO_ADD_DEBUGFS) { | ||
451 | ieee80211_debugfs_key_add(key); | ||
452 | work_done = true; | ||
453 | } | ||
454 | if (todoflags & KEY_FLAG_TODO_DEFKEY) { | ||
455 | ieee80211_debugfs_key_remove_default(key->sdata); | ||
456 | ieee80211_debugfs_key_add_default(key->sdata); | ||
457 | work_done = true; | ||
458 | } | ||
459 | if (todoflags & KEY_FLAG_TODO_HWACCEL) { | ||
460 | ieee80211_key_enable_hw_accel(key); | ||
461 | work_done = true; | ||
462 | } | ||
463 | if (todoflags & KEY_FLAG_TODO_DELETE) { | ||
464 | __ieee80211_key_free(key); | ||
465 | work_done = true; | ||
466 | } | ||
245 | 467 | ||
246 | rcu_assign_pointer(sdata->default_key, key); | 468 | WARN_ON(!work_done); |
247 | 469 | ||
248 | if (sdata->default_key) | 470 | spin_lock(&todo_lock); |
249 | ieee80211_debugfs_key_add_default(sdata); | ||
250 | } | 471 | } |
472 | spin_unlock(&todo_lock); | ||
251 | } | 473 | } |
252 | 474 | ||
253 | void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) | 475 | void ieee80211_key_todo(void) |
254 | { | 476 | { |
255 | struct ieee80211_key *key, *tmp; | 477 | ieee80211_key_lock(); |
256 | 478 | __ieee80211_key_todo(); | |
257 | list_for_each_entry_safe(key, tmp, &sdata->key_list, list) | 479 | ieee80211_key_unlock(); |
258 | ieee80211_key_free(key); | ||
259 | } | 480 | } |
260 | 481 | ||
261 | void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata) | 482 | void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) |
262 | { | 483 | { |
263 | struct ieee80211_key *key; | 484 | struct ieee80211_key *key, *tmp; |
485 | LIST_HEAD(tmp_list); | ||
264 | 486 | ||
265 | WARN_ON(!netif_running(sdata->dev)); | 487 | ieee80211_key_lock(); |
266 | if (!netif_running(sdata->dev)) | ||
267 | return; | ||
268 | 488 | ||
269 | list_for_each_entry(key, &sdata->key_list, list) | 489 | ieee80211_debugfs_key_remove_default(sdata); |
270 | ieee80211_key_enable_hw_accel(key); | ||
271 | } | ||
272 | 490 | ||
273 | void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata) | 491 | list_for_each_entry_safe(key, tmp, &sdata->key_list, list) |
274 | { | 492 | ieee80211_key_free(key); |
275 | struct ieee80211_key *key; | ||
276 | 493 | ||
277 | list_for_each_entry(key, &sdata->key_list, list) | 494 | __ieee80211_key_todo(); |
278 | ieee80211_key_disable_hw_accel(key); | 495 | |
496 | ieee80211_key_unlock(); | ||
279 | } | 497 | } |