diff options
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/ieee80211_ioctl.c | 5 | ||||
-rw-r--r-- | net/mac80211/key.c | 43 | ||||
-rw-r--r-- | net/mac80211/rx.c | 20 | ||||
-rw-r--r-- | net/mac80211/tx.c | 20 |
4 files changed, 61 insertions, 27 deletions
diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c index 1d585cc2c8c7..10ec05624a67 100644 --- a/net/mac80211/ieee80211_ioctl.c +++ b/net/mac80211/ieee80211_ioctl.c | |||
@@ -73,11 +73,8 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, | |||
73 | key = NULL; | 73 | key = NULL; |
74 | } else { | 74 | } else { |
75 | /* | 75 | /* |
76 | * Need to free it before allocating a new one with | 76 | * Automatically frees any old key if present. |
77 | * with the same index or the ordering to the driver's | ||
78 | * set_key() callback becomes confused. | ||
79 | */ | 77 | */ |
80 | ieee80211_key_free(key); | ||
81 | key = ieee80211_key_alloc(sdata, sta, alg, idx, key_len, _key); | 78 | key = ieee80211_key_alloc(sdata, sta, alg, idx, key_len, _key); |
82 | if (!key) { | 79 | if (!key) { |
83 | ret = -ENOMEM; | 80 | ret = -ENOMEM; |
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); |
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 4fb8c7026f11..91b7886bf797 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/skbuff.h> | 13 | #include <linux/skbuff.h> |
14 | #include <linux/netdevice.h> | 14 | #include <linux/netdevice.h> |
15 | #include <linux/etherdevice.h> | 15 | #include <linux/etherdevice.h> |
16 | #include <linux/rcupdate.h> | ||
16 | #include <net/mac80211.h> | 17 | #include <net/mac80211.h> |
17 | #include <net/ieee80211_radiotap.h> | 18 | #include <net/ieee80211_radiotap.h> |
18 | 19 | ||
@@ -311,6 +312,7 @@ ieee80211_rx_h_load_key(struct ieee80211_txrx_data *rx) | |||
311 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; | 312 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; |
312 | int keyidx; | 313 | int keyidx; |
313 | int hdrlen; | 314 | int hdrlen; |
315 | struct ieee80211_key *stakey = NULL; | ||
314 | 316 | ||
315 | /* | 317 | /* |
316 | * Key selection 101 | 318 | * Key selection 101 |
@@ -348,8 +350,11 @@ ieee80211_rx_h_load_key(struct ieee80211_txrx_data *rx) | |||
348 | if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) | 350 | if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) |
349 | return TXRX_CONTINUE; | 351 | return TXRX_CONTINUE; |
350 | 352 | ||
351 | if (!is_multicast_ether_addr(hdr->addr1) && rx->sta && rx->sta->key) { | 353 | if (rx->sta) |
352 | rx->key = rx->sta->key; | 354 | stakey = rcu_dereference(rx->sta->key); |
355 | |||
356 | if (!is_multicast_ether_addr(hdr->addr1) && stakey) { | ||
357 | rx->key = stakey; | ||
353 | } else { | 358 | } else { |
354 | /* | 359 | /* |
355 | * The device doesn't give us the IV so we won't be | 360 | * The device doesn't give us the IV so we won't be |
@@ -374,7 +379,7 @@ ieee80211_rx_h_load_key(struct ieee80211_txrx_data *rx) | |||
374 | */ | 379 | */ |
375 | keyidx = rx->skb->data[hdrlen + 3] >> 6; | 380 | keyidx = rx->skb->data[hdrlen + 3] >> 6; |
376 | 381 | ||
377 | rx->key = rx->sdata->keys[keyidx]; | 382 | rx->key = rcu_dereference(rx->sdata->keys[keyidx]); |
378 | 383 | ||
379 | /* | 384 | /* |
380 | * RSNA-protected unicast frames should always be sent with | 385 | * RSNA-protected unicast frames should always be sent with |
@@ -1364,6 +1369,12 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, | |||
1364 | skb_pull(skb, radiotap_len); | 1369 | skb_pull(skb, radiotap_len); |
1365 | } | 1370 | } |
1366 | 1371 | ||
1372 | /* | ||
1373 | * key references are protected using RCU and this requires that | ||
1374 | * we are in a read-site RCU section during receive processing | ||
1375 | */ | ||
1376 | rcu_read_lock(); | ||
1377 | |||
1367 | hdr = (struct ieee80211_hdr *) skb->data; | 1378 | hdr = (struct ieee80211_hdr *) skb->data; |
1368 | memset(&rx, 0, sizeof(rx)); | 1379 | memset(&rx, 0, sizeof(rx)); |
1369 | rx.skb = skb; | 1380 | rx.skb = skb; |
@@ -1404,6 +1415,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, | |||
1404 | ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx, | 1415 | ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx, |
1405 | rx.sta); | 1416 | rx.sta); |
1406 | sta_info_put(sta); | 1417 | sta_info_put(sta); |
1418 | rcu_read_unlock(); | ||
1407 | return; | 1419 | return; |
1408 | } | 1420 | } |
1409 | 1421 | ||
@@ -1465,6 +1477,8 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, | |||
1465 | read_unlock(&local->sub_if_lock); | 1477 | read_unlock(&local->sub_if_lock); |
1466 | 1478 | ||
1467 | end: | 1479 | end: |
1480 | rcu_read_unlock(); | ||
1481 | |||
1468 | if (sta) | 1482 | if (sta) |
1469 | sta_info_put(sta); | 1483 | sta_info_put(sta); |
1470 | } | 1484 | } |
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0820f127da2b..b29dc70b2f01 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/skbuff.h> | 17 | #include <linux/skbuff.h> |
18 | #include <linux/etherdevice.h> | 18 | #include <linux/etherdevice.h> |
19 | #include <linux/bitmap.h> | 19 | #include <linux/bitmap.h> |
20 | #include <linux/rcupdate.h> | ||
20 | #include <net/net_namespace.h> | 21 | #include <net/net_namespace.h> |
21 | #include <net/ieee80211_radiotap.h> | 22 | #include <net/ieee80211_radiotap.h> |
22 | #include <net/cfg80211.h> | 23 | #include <net/cfg80211.h> |
@@ -427,14 +428,16 @@ ieee80211_tx_h_ps_buf(struct ieee80211_txrx_data *tx) | |||
427 | static ieee80211_txrx_result | 428 | static ieee80211_txrx_result |
428 | ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx) | 429 | ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx) |
429 | { | 430 | { |
431 | struct ieee80211_key *key; | ||
432 | |||
430 | tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID; | 433 | tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID; |
431 | 434 | ||
432 | if (unlikely(tx->u.tx.control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)) | 435 | if (unlikely(tx->u.tx.control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)) |
433 | tx->key = NULL; | 436 | tx->key = NULL; |
434 | else if (tx->sta && tx->sta->key) | 437 | else if (tx->sta && (key = rcu_dereference(tx->sta->key))) |
435 | tx->key = tx->sta->key; | 438 | tx->key = key; |
436 | else if (tx->sdata->default_key) | 439 | else if ((key = rcu_dereference(tx->sdata->default_key))) |
437 | tx->key = tx->sdata->default_key; | 440 | tx->key = key; |
438 | else if (tx->sdata->drop_unencrypted && | 441 | else if (tx->sdata->drop_unencrypted && |
439 | !(tx->sdata->eapol && ieee80211_is_eapol(tx->skb))) { | 442 | !(tx->sdata->eapol && ieee80211_is_eapol(tx->skb))) { |
440 | I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted); | 443 | I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted); |
@@ -1112,6 +1115,12 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb, | |||
1112 | return 0; | 1115 | return 0; |
1113 | } | 1116 | } |
1114 | 1117 | ||
1118 | /* | ||
1119 | * key references are protected using RCU and this requires that | ||
1120 | * we are in a read-site RCU section during receive processing | ||
1121 | */ | ||
1122 | rcu_read_lock(); | ||
1123 | |||
1115 | sta = tx.sta; | 1124 | sta = tx.sta; |
1116 | tx.u.tx.mgmt_interface = mgmt; | 1125 | tx.u.tx.mgmt_interface = mgmt; |
1117 | tx.u.tx.mode = local->hw.conf.mode; | 1126 | tx.u.tx.mode = local->hw.conf.mode; |
@@ -1139,6 +1148,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb, | |||
1139 | 1148 | ||
1140 | if (unlikely(res == TXRX_QUEUED)) { | 1149 | if (unlikely(res == TXRX_QUEUED)) { |
1141 | I802_DEBUG_INC(local->tx_handlers_queued); | 1150 | I802_DEBUG_INC(local->tx_handlers_queued); |
1151 | rcu_read_unlock(); | ||
1142 | return 0; | 1152 | return 0; |
1143 | } | 1153 | } |
1144 | 1154 | ||
@@ -1196,6 +1206,7 @@ retry: | |||
1196 | store->last_frag_rate_ctrl_probe = | 1206 | store->last_frag_rate_ctrl_probe = |
1197 | !!(tx.flags & IEEE80211_TXRXD_TXPROBE_LAST_FRAG); | 1207 | !!(tx.flags & IEEE80211_TXRXD_TXPROBE_LAST_FRAG); |
1198 | } | 1208 | } |
1209 | rcu_read_unlock(); | ||
1199 | return 0; | 1210 | return 0; |
1200 | 1211 | ||
1201 | drop: | 1212 | drop: |
@@ -1205,6 +1216,7 @@ retry: | |||
1205 | if (tx.u.tx.extra_frag[i]) | 1216 | if (tx.u.tx.extra_frag[i]) |
1206 | dev_kfree_skb(tx.u.tx.extra_frag[i]); | 1217 | dev_kfree_skb(tx.u.tx.extra_frag[i]); |
1207 | kfree(tx.u.tx.extra_frag); | 1218 | kfree(tx.u.tx.extra_frag); |
1219 | rcu_read_unlock(); | ||
1208 | return 0; | 1220 | return 0; |
1209 | } | 1221 | } |
1210 | 1222 | ||