aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/key.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2008-02-25 10:27:45 -0500
committerJohn W. Linville <linville@tuxdriver.com>2008-02-29 15:42:04 -0500
commitdb4d1169d0b893bfb7923b6526748fe2c5a7373f (patch)
treeebd5ac06685bacc069b162b31f99b33c6191b4c3 /net/mac80211/key.c
parent6f48422a29714ed92f6136d9e7d3ff39c75607d7 (diff)
mac80211: split ieee80211_key_alloc/free
In order to RCU-ify sta_info, we need to be able to allocate a key without linking it to an sdata/sta structure (because allocation cannot be done in an rcu critical section). This patch splits up ieee80211_key_alloc() and updates all users appropriately. While at it, this patch fixes a number of race conditions such as finally making key replacement atomic, unfortunately at the expense of more complex code. Note that this patch documents /existing/ bugs with sta info and key interaction, there is currently a race condition when a sta info is freed without holding the RTNL. This will finally be fixed by a followup patch. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/key.c')
-rw-r--r--net/mac80211/key.c156
1 files changed, 113 insertions, 43 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index ed57fb8e82fc..60aaaf471544 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -13,6 +13,7 @@
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"
@@ -34,6 +35,10 @@
34 * 35 *
35 * All operations here are called under RTNL so no extra locking is 36 * All operations here are called under RTNL so no extra locking is
36 * required. 37 * required.
38 *
39 * NOTE: This code requires that sta info *destruction* is done under
40 * RTNL, otherwise it can try to access already freed STA structs
41 * when a STA key is being freed.
37 */ 42 */
38 43
39static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 44static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
@@ -84,16 +89,25 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
84 key->conf.keyidx, print_mac(mac, addr), ret); 89 key->conf.keyidx, print_mac(mac, addr), ret);
85} 90}
86 91
92static void ieee80211_key_mark_hw_accel_off(struct ieee80211_key *key)
93{
94 if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
95 key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
96 key->flags |= KEY_FLAG_REMOVE_FROM_HARDWARE;
97 }
98}
99
87static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) 100static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
88{ 101{
89 const u8 *addr; 102 const u8 *addr;
90 int ret; 103 int ret;
91 DECLARE_MAC_BUF(mac); 104 DECLARE_MAC_BUF(mac);
92 105
93 if (!key->local->ops->set_key) 106 if (!key || !key->local->ops->set_key)
94 return; 107 return;
95 108
96 if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) 109 if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
110 !(key->flags & KEY_FLAG_REMOVE_FROM_HARDWARE))
97 return; 111 return;
98 112
99 addr = get_mac_for_key(key); 113 addr = get_mac_for_key(key);
@@ -108,12 +122,11 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
108 wiphy_name(key->local->hw.wiphy), 122 wiphy_name(key->local->hw.wiphy),
109 key->conf.keyidx, print_mac(mac, addr), ret); 123 key->conf.keyidx, print_mac(mac, addr), ret);
110 124
111 key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; 125 key->flags &= ~(KEY_FLAG_UPLOADED_TO_HARDWARE |
126 KEY_FLAG_REMOVE_FROM_HARDWARE);
112} 127}
113 128
114struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata, 129struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
115 struct sta_info *sta,
116 enum ieee80211_key_alg alg,
117 int idx, 130 int idx,
118 size_t key_len, 131 size_t key_len,
119 const u8 *key_data) 132 const u8 *key_data)
@@ -138,10 +151,6 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
138 key->conf.keylen = key_len; 151 key->conf.keylen = key_len;
139 memcpy(key->conf.key, key_data, key_len); 152 memcpy(key->conf.key, key_data, key_len);
140 153
141 key->local = sdata->local;
142 key->sdata = sdata;
143 key->sta = sta;
144
145 if (alg == ALG_CCMP) { 154 if (alg == ALG_CCMP) {
146 /* 155 /*
147 * Initialize AES key state here as an optimization so that 156 * Initialize AES key state here as an optimization so that
@@ -154,13 +163,62 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
154 } 163 }
155 } 164 }
156 165
157 ieee80211_debugfs_key_add(key->local, key); 166 return key;
167}
158 168
159 /* remove key first */ 169static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
160 if (sta) 170 struct sta_info *sta,
161 ieee80211_key_free(sta->key); 171 struct ieee80211_key *key,
162 else 172 struct ieee80211_key *new)
163 ieee80211_key_free(sdata->keys[idx]); 173{
174 int idx, defkey;
175
176 if (sta) {
177 rcu_assign_pointer(sta->key, new);
178 } else {
179 WARN_ON(new && key && new->conf.keyidx != key->conf.keyidx);
180
181 if (key)
182 idx = key->conf.keyidx;
183 else
184 idx = new->conf.keyidx;
185
186 defkey = key && sdata->default_key == key;
187
188 if (defkey && !new)
189 ieee80211_set_default_key(sdata, -1);
190
191 rcu_assign_pointer(sdata->keys[idx], new);
192
193 if (defkey && new)
194 ieee80211_set_default_key(sdata, new->conf.keyidx);
195 }
196
197 if (key) {
198 ieee80211_key_mark_hw_accel_off(key);
199 list_del(&key->list);
200 }
201}
202
203void ieee80211_key_link(struct ieee80211_key *key,
204 struct ieee80211_sub_if_data *sdata,
205 struct sta_info *sta)
206{
207 struct ieee80211_key *old_key;
208 int idx;
209
210 ASSERT_RTNL();
211 might_sleep();
212
213 BUG_ON(!sdata);
214 BUG_ON(!key);
215
216 idx = key->conf.keyidx;
217 key->local = sdata->local;
218 key->sdata = sdata;
219 key->sta = sta;
220
221 ieee80211_debugfs_key_add(key->local, key);
164 222
165 if (sta) { 223 if (sta) {
166 ieee80211_debugfs_key_sta_link(key, sta); 224 ieee80211_debugfs_key_sta_link(key, sta);
@@ -186,50 +244,53 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
186 } 244 }
187 } 245 }
188 246
189 /* enable hwaccel if appropriate */
190 if (netif_running(key->sdata->dev))
191 ieee80211_key_enable_hw_accel(key);
192
193 if (sta) 247 if (sta)
194 rcu_assign_pointer(sta->key, key); 248 old_key = sta->key;
195 else 249 else
196 rcu_assign_pointer(sdata->keys[idx], key); 250 old_key = sdata->keys[idx];
251
252 __ieee80211_key_replace(sdata, sta, old_key, key);
197 253
198 list_add(&key->list, &sdata->key_list); 254 list_add(&key->list, &sdata->key_list);
199 255
200 return key; 256 synchronize_rcu();
257
258 ieee80211_key_free(old_key);
259 ieee80211_key_enable_hw_accel(key);
201} 260}
202 261
203void ieee80211_key_free(struct ieee80211_key *key) 262void ieee80211_key_free(struct ieee80211_key *key)
204{ 263{
264 ASSERT_RTNL();
265 might_sleep();
266
205 if (!key) 267 if (!key)
206 return; 268 return;
207 269
208 if (key->sta) { 270 if (key->sdata) {
209 rcu_assign_pointer(key->sta->key, NULL); 271 /*
210 } else { 272 * Replace key with nothingness.
211 if (key->sdata->default_key == key) 273 *
212 ieee80211_set_default_key(key->sdata, -1); 274 * Because other code may have key reference (RCU protected)
213 if (key->conf.keyidx >= 0 && 275 * right now, we then wait for a grace period before freeing
214 key->conf.keyidx < NUM_DEFAULT_KEYS) 276 * it.
215 rcu_assign_pointer(key->sdata->keys[key->conf.keyidx], 277 */
216 NULL); 278 __ieee80211_key_replace(key->sdata, key->sta, key, NULL);
217 else
218 WARN_ON(1);
219 }
220 279
221 /* wait for all key users to complete */ 280 synchronize_rcu();
222 synchronize_rcu();
223 281
224 /* remove from hwaccel if appropriate */ 282 /*
225 ieee80211_key_disable_hw_accel(key); 283 * Remove from hwaccel if appropriate, this will
284 * only happen when the key is actually unlinked,
285 * it will already be done when the key was replaced.
286 */
287 ieee80211_key_disable_hw_accel(key);
288 }
226 289
227 if (key->conf.alg == ALG_CCMP) 290 if (key->conf.alg == ALG_CCMP)
228 ieee80211_aes_key_free(key->u.ccmp.tfm); 291 ieee80211_aes_key_free(key->u.ccmp.tfm);
229 ieee80211_debugfs_key_remove(key); 292 ieee80211_debugfs_key_remove(key);
230 293
231 list_del(&key->list);
232
233 kfree(key); 294 kfree(key);
234} 295}
235 296
@@ -253,6 +314,10 @@ void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx)
253void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) 314void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
254{ 315{
255 struct ieee80211_key *key, *tmp; 316 struct ieee80211_key *key, *tmp;
317 LIST_HEAD(tmp_list);
318
319 ASSERT_RTNL();
320 might_sleep();
256 321
257 list_for_each_entry_safe(key, tmp, &sdata->key_list, list) 322 list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
258 ieee80211_key_free(key); 323 ieee80211_key_free(key);
@@ -262,8 +327,10 @@ void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata)
262{ 327{
263 struct ieee80211_key *key; 328 struct ieee80211_key *key;
264 329
265 WARN_ON(!netif_running(sdata->dev)); 330 ASSERT_RTNL();
266 if (!netif_running(sdata->dev)) 331 might_sleep();
332
333 if (WARN_ON(!netif_running(sdata->dev)))
267 return; 334 return;
268 335
269 list_for_each_entry(key, &sdata->key_list, list) 336 list_for_each_entry(key, &sdata->key_list, list)
@@ -274,6 +341,9 @@ void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata)
274{ 341{
275 struct ieee80211_key *key; 342 struct ieee80211_key *key;
276 343
344 ASSERT_RTNL();
345 might_sleep();
346
277 list_for_each_entry(key, &sdata->key_list, list) 347 list_for_each_entry(key, &sdata->key_list, list)
278 ieee80211_key_disable_hw_accel(key); 348 ieee80211_key_disable_hw_accel(key);
279} 349}