diff options
Diffstat (limited to 'net/mac80211/key.c')
-rw-r--r-- | net/mac80211/key.c | 43 |
1 files changed, 27 insertions, 16 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 178f00cf61b9..19e77f626d84 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/if_ether.h> | 12 | #include <linux/if_ether.h> |
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 <net/mac80211.h> | 16 | #include <net/mac80211.h> |
16 | #include "ieee80211_i.h" | 17 | #include "ieee80211_i.h" |
17 | #include "debugfs_key.h" | 18 | #include "debugfs_key.h" |
@@ -120,6 +121,7 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata, | |||
120 | { | 121 | { |
121 | struct ieee80211_key *key; | 122 | struct ieee80211_key *key; |
122 | 123 | ||
124 | BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS); | ||
123 | BUG_ON(alg == ALG_NONE); | 125 | BUG_ON(alg == ALG_NONE); |
124 | 126 | ||
125 | key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL); | 127 | key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL); |
@@ -157,9 +159,15 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata, | |||
157 | 159 | ||
158 | ieee80211_debugfs_key_add(key->local, key); | 160 | ieee80211_debugfs_key_add(key->local, key); |
159 | 161 | ||
162 | /* remove key first */ | ||
163 | if (sta) | ||
164 | ieee80211_key_free(sta->key); | ||
165 | else | ||
166 | ieee80211_key_free(sdata->keys[idx]); | ||
167 | |||
160 | if (sta) { | 168 | if (sta) { |
161 | ieee80211_debugfs_key_sta_link(key, sta); | 169 | ieee80211_debugfs_key_sta_link(key, sta); |
162 | sta->key = key; | 170 | |
163 | /* | 171 | /* |
164 | * some hardware cannot handle TKIP with QoS, so | 172 | * some hardware cannot handle TKIP with QoS, so |
165 | * we indicate whether QoS could be in use. | 173 | * we indicate whether QoS could be in use. |
@@ -179,21 +187,19 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata, | |||
179 | sta_info_put(ap); | 187 | sta_info_put(ap); |
180 | } | 188 | } |
181 | } | 189 | } |
182 | |||
183 | if (idx >= 0 && idx < NUM_DEFAULT_KEYS) { | ||
184 | if (!sdata->keys[idx]) | ||
185 | sdata->keys[idx] = key; | ||
186 | else | ||
187 | WARN_ON(1); | ||
188 | } else | ||
189 | WARN_ON(1); | ||
190 | } | 190 | } |
191 | 191 | ||
192 | list_add(&key->list, &sdata->key_list); | 192 | /* enable hwaccel if appropriate */ |
193 | |||
194 | if (netif_running(key->sdata->dev)) | 193 | if (netif_running(key->sdata->dev)) |
195 | ieee80211_key_enable_hw_accel(key); | 194 | ieee80211_key_enable_hw_accel(key); |
196 | 195 | ||
196 | if (sta) | ||
197 | rcu_assign_pointer(sta->key, key); | ||
198 | else | ||
199 | rcu_assign_pointer(sdata->keys[idx], key); | ||
200 | |||
201 | list_add(&key->list, &sdata->key_list); | ||
202 | |||
197 | return key; | 203 | return key; |
198 | } | 204 | } |
199 | 205 | ||
@@ -202,20 +208,25 @@ void ieee80211_key_free(struct ieee80211_key *key) | |||
202 | if (!key) | 208 | if (!key) |
203 | return; | 209 | return; |
204 | 210 | ||
205 | ieee80211_key_disable_hw_accel(key); | ||
206 | |||
207 | if (key->sta) { | 211 | if (key->sta) { |
208 | key->sta->key = NULL; | 212 | rcu_assign_pointer(key->sta->key, NULL); |
209 | } else { | 213 | } else { |
210 | if (key->sdata->default_key == key) | 214 | if (key->sdata->default_key == key) |
211 | ieee80211_set_default_key(key->sdata, -1); | 215 | ieee80211_set_default_key(key->sdata, -1); |
212 | if (key->conf.keyidx >= 0 && | 216 | if (key->conf.keyidx >= 0 && |
213 | key->conf.keyidx < NUM_DEFAULT_KEYS) | 217 | key->conf.keyidx < NUM_DEFAULT_KEYS) |
214 | key->sdata->keys[key->conf.keyidx] = NULL; | 218 | rcu_assign_pointer(key->sdata->keys[key->conf.keyidx], |
219 | NULL); | ||
215 | else | 220 | else |
216 | WARN_ON(1); | 221 | WARN_ON(1); |
217 | } | 222 | } |
218 | 223 | ||
224 | /* wait for all key users to complete */ | ||
225 | synchronize_rcu(); | ||
226 | |||
227 | /* remove from hwaccel if appropriate */ | ||
228 | ieee80211_key_disable_hw_accel(key); | ||
229 | |||
219 | if (key->conf.alg == ALG_CCMP) | 230 | if (key->conf.alg == ALG_CCMP) |
220 | ieee80211_aes_key_free(key->u.ccmp.tfm); | 231 | ieee80211_aes_key_free(key->u.ccmp.tfm); |
221 | ieee80211_debugfs_key_remove(key); | 232 | ieee80211_debugfs_key_remove(key); |
@@ -235,7 +246,7 @@ void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx) | |||
235 | if (sdata->default_key != key) { | 246 | if (sdata->default_key != key) { |
236 | ieee80211_debugfs_key_remove_default(sdata); | 247 | ieee80211_debugfs_key_remove_default(sdata); |
237 | 248 | ||
238 | sdata->default_key = key; | 249 | rcu_assign_pointer(sdata->default_key, key); |
239 | 250 | ||
240 | if (sdata->default_key) | 251 | if (sdata->default_key) |
241 | ieee80211_debugfs_key_add_default(sdata); | 252 | ieee80211_debugfs_key_add_default(sdata); |