diff options
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/core.c | 2 | ||||
-rw-r--r-- | net/wireless/lib80211_crypt_tkip.c | 99 | ||||
-rw-r--r-- | net/wireless/lib80211_crypt_wep.c | 46 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 2 | ||||
-rw-r--r-- | net/wireless/reg.c | 45 | ||||
-rw-r--r-- | net/wireless/sme.c | 6 | ||||
-rw-r--r-- | net/wireless/wext-core.c | 52 |
7 files changed, 156 insertions, 96 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index b0915515640e..8f0bac7e03c4 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1147,6 +1147,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, return NOTIFY_DONE; } + wireless_nlevent_flush(); + return NOTIFY_OK; } diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c index 3cd819539241..71447cf86306 100644 --- a/net/wireless/lib80211_crypt_tkip.c +++ b/net/wireless/lib80211_crypt_tkip.c @@ -29,7 +29,8 @@ #include <linux/ieee80211.h> #include <net/iw_handler.h> -#include <linux/crypto.h> +#include <crypto/hash.h> +#include <crypto/skcipher.h> #include <linux/crc32.h> #include <net/lib80211.h> @@ -63,10 +64,10 @@ struct lib80211_tkip_data { int key_idx; - struct crypto_blkcipher *rx_tfm_arc4; - struct crypto_hash *rx_tfm_michael; - struct crypto_blkcipher *tx_tfm_arc4; - struct crypto_hash *tx_tfm_michael; + struct crypto_skcipher *rx_tfm_arc4; + struct crypto_ahash *rx_tfm_michael; + struct crypto_skcipher *tx_tfm_arc4; + struct crypto_ahash *tx_tfm_michael; /* scratch buffers for virt_to_page() (crypto API) */ u8 rx_hdr[16], tx_hdr[16]; @@ -98,29 +99,29 @@ static void *lib80211_tkip_init(int key_idx) priv->key_idx = key_idx; - priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, - CRYPTO_ALG_ASYNC); + priv->tx_tfm_arc4 = crypto_alloc_skcipher("ecb(arc4)", 0, + CRYPTO_ALG_ASYNC); if (IS_ERR(priv->tx_tfm_arc4)) { priv->tx_tfm_arc4 = NULL; goto fail; } - priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0, - CRYPTO_ALG_ASYNC); + priv->tx_tfm_michael = crypto_alloc_ahash("michael_mic", 0, + CRYPTO_ALG_ASYNC); if (IS_ERR(priv->tx_tfm_michael)) { priv->tx_tfm_michael = NULL; goto fail; } - priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, - CRYPTO_ALG_ASYNC); + priv->rx_tfm_arc4 = crypto_alloc_skcipher("ecb(arc4)", 0, + CRYPTO_ALG_ASYNC); if (IS_ERR(priv->rx_tfm_arc4)) { priv->rx_tfm_arc4 = NULL; goto fail; } - priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0, - CRYPTO_ALG_ASYNC); + priv->rx_tfm_michael = crypto_alloc_ahash("michael_mic", 0, + CRYPTO_ALG_ASYNC); if (IS_ERR(priv->rx_tfm_michael)) { priv->rx_tfm_michael = NULL; goto fail; @@ -130,14 +131,10 @@ static void *lib80211_tkip_init(int key_idx) fail: if (priv) { - if (priv->tx_tfm_michael) - crypto_free_hash(priv->tx_tfm_michael); - if (priv->tx_tfm_arc4) - crypto_free_blkcipher(priv->tx_tfm_arc4); - if (priv->rx_tfm_michael) - crypto_free_hash(priv->rx_tfm_michael); - if (priv->rx_tfm_arc4) - crypto_free_blkcipher(priv->rx_tfm_arc4); + crypto_free_ahash(priv->tx_tfm_michael); + crypto_free_skcipher(priv->tx_tfm_arc4); + crypto_free_ahash(priv->rx_tfm_michael); + crypto_free_skcipher(priv->rx_tfm_arc4); kfree(priv); } @@ -148,14 +145,10 @@ static void lib80211_tkip_deinit(void *priv) { struct lib80211_tkip_data *_priv = priv; if (_priv) { - if (_priv->tx_tfm_michael) - crypto_free_hash(_priv->tx_tfm_michael); - if (_priv->tx_tfm_arc4) - crypto_free_blkcipher(_priv->tx_tfm_arc4); - if (_priv->rx_tfm_michael) - crypto_free_hash(_priv->rx_tfm_michael); - if (_priv->rx_tfm_arc4) - crypto_free_blkcipher(_priv->rx_tfm_arc4); + crypto_free_ahash(_priv->tx_tfm_michael); + crypto_free_skcipher(_priv->tx_tfm_arc4); + crypto_free_ahash(_priv->rx_tfm_michael); + crypto_free_skcipher(_priv->rx_tfm_arc4); } kfree(priv); } @@ -353,11 +346,12 @@ static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len, static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) { struct lib80211_tkip_data *tkey = priv; - struct blkcipher_desc desc = { .tfm = tkey->tx_tfm_arc4 }; + SKCIPHER_REQUEST_ON_STACK(req, tkey->tx_tfm_arc4); int len; u8 rc4key[16], *pos, *icv; u32 crc; struct scatterlist sg; + int err; if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; @@ -382,9 +376,14 @@ static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) icv[2] = crc >> 16; icv[3] = crc >> 24; - crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16); + crypto_skcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16); sg_init_one(&sg, pos, len + 4); - return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4); + skcipher_request_set_tfm(req, tkey->tx_tfm_arc4); + skcipher_request_set_callback(req, 0, NULL, NULL); + skcipher_request_set_crypt(req, &sg, &sg, len + 4, NULL); + err = crypto_skcipher_encrypt(req); + skcipher_request_zero(req); + return err; } /* @@ -403,7 +402,7 @@ static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n, static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) { struct lib80211_tkip_data *tkey = priv; - struct blkcipher_desc desc = { .tfm = tkey->rx_tfm_arc4 }; + SKCIPHER_REQUEST_ON_STACK(req, tkey->rx_tfm_arc4); u8 rc4key[16]; u8 keyidx, *pos; u32 iv32; @@ -413,6 +412,7 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) u32 crc; struct scatterlist sg; int plen; + int err; hdr = (struct ieee80211_hdr *)skb->data; @@ -465,9 +465,14 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) plen = skb->len - hdr_len - 12; - crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16); + crypto_skcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16); sg_init_one(&sg, pos, plen + 4); - if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) { + skcipher_request_set_tfm(req, tkey->rx_tfm_arc4); + skcipher_request_set_callback(req, 0, NULL, NULL); + skcipher_request_set_crypt(req, &sg, &sg, plen + 4, NULL); + err = crypto_skcipher_decrypt(req); + skcipher_request_zero(req); + if (err) { net_dbg_ratelimited("TKIP: failed to decrypt received packet from %pM\n", hdr->addr2); return -7; @@ -505,11 +510,12 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) return keyidx; } -static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr, +static int michael_mic(struct crypto_ahash *tfm_michael, u8 * key, u8 * hdr, u8 * data, size_t data_len, u8 * mic) { - struct hash_desc desc; + AHASH_REQUEST_ON_STACK(req, tfm_michael); struct scatterlist sg[2]; + int err; if (tfm_michael == NULL) { pr_warn("%s(): tfm_michael == NULL\n", __func__); @@ -519,12 +525,15 @@ static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr, sg_set_buf(&sg[0], hdr, 16); sg_set_buf(&sg[1], data, data_len); - if (crypto_hash_setkey(tfm_michael, key, 8)) + if (crypto_ahash_setkey(tfm_michael, key, 8)) return -1; - desc.tfm = tfm_michael; - desc.flags = 0; - return crypto_hash_digest(&desc, sg, data_len + 16, mic); + ahash_request_set_tfm(req, tfm_michael); + ahash_request_set_callback(req, 0, NULL, NULL); + ahash_request_set_crypt(req, sg, mic, data_len + 16); + err = crypto_ahash_digest(req); + ahash_request_zero(req); + return err; } static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr) @@ -645,10 +654,10 @@ static int lib80211_tkip_set_key(void *key, int len, u8 * seq, void *priv) { struct lib80211_tkip_data *tkey = priv; int keyidx; - struct crypto_hash *tfm = tkey->tx_tfm_michael; - struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4; - struct crypto_hash *tfm3 = tkey->rx_tfm_michael; - struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4; + struct crypto_ahash *tfm = tkey->tx_tfm_michael; + struct crypto_skcipher *tfm2 = tkey->tx_tfm_arc4; + struct crypto_ahash *tfm3 = tkey->rx_tfm_michael; + struct crypto_skcipher *tfm4 = tkey->rx_tfm_arc4; keyidx = tkey->key_idx; memset(tkey, 0, sizeof(*tkey)); diff --git a/net/wireless/lib80211_crypt_wep.c b/net/wireless/lib80211_crypt_wep.c index 1c292e4ea7b6..d05f58b0fd04 100644 --- a/net/wireless/lib80211_crypt_wep.c +++ b/net/wireless/lib80211_crypt_wep.c @@ -22,7 +22,7 @@ #include <net/lib80211.h> -#include <linux/crypto.h> +#include <crypto/skcipher.h> #include <linux/crc32.h> MODULE_AUTHOR("Jouni Malinen"); @@ -35,8 +35,8 @@ struct lib80211_wep_data { u8 key[WEP_KEY_LEN + 1]; u8 key_len; u8 key_idx; - struct crypto_blkcipher *tx_tfm; - struct crypto_blkcipher *rx_tfm; + struct crypto_skcipher *tx_tfm; + struct crypto_skcipher *rx_tfm; }; static void *lib80211_wep_init(int keyidx) @@ -48,13 +48,13 @@ static void *lib80211_wep_init(int keyidx) goto fail; priv->key_idx = keyidx; - priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); + priv->tx_tfm = crypto_alloc_skcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->tx_tfm)) { priv->tx_tfm = NULL; goto fail; } - priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); + priv->rx_tfm = crypto_alloc_skcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->rx_tfm)) { priv->rx_tfm = NULL; goto fail; @@ -66,10 +66,8 @@ static void *lib80211_wep_init(int keyidx) fail: if (priv) { - if (priv->tx_tfm) - crypto_free_blkcipher(priv->tx_tfm); - if (priv->rx_tfm) - crypto_free_blkcipher(priv->rx_tfm); + crypto_free_skcipher(priv->tx_tfm); + crypto_free_skcipher(priv->rx_tfm); kfree(priv); } return NULL; @@ -79,10 +77,8 @@ static void lib80211_wep_deinit(void *priv) { struct lib80211_wep_data *_priv = priv; if (_priv) { - if (_priv->tx_tfm) - crypto_free_blkcipher(_priv->tx_tfm); - if (_priv->rx_tfm) - crypto_free_blkcipher(_priv->rx_tfm); + crypto_free_skcipher(_priv->tx_tfm); + crypto_free_skcipher(_priv->rx_tfm); } kfree(priv); } @@ -133,11 +129,12 @@ static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len, static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) { struct lib80211_wep_data *wep = priv; - struct blkcipher_desc desc = { .tfm = wep->tx_tfm }; + SKCIPHER_REQUEST_ON_STACK(req, wep->tx_tfm); u32 crc, klen, len; u8 *pos, *icv; struct scatterlist sg; u8 key[WEP_KEY_LEN + 3]; + int err; /* other checks are in lib80211_wep_build_iv */ if (skb_tailroom(skb) < 4) @@ -165,9 +162,14 @@ static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) icv[2] = crc >> 16; icv[3] = crc >> 24; - crypto_blkcipher_setkey(wep->tx_tfm, key, klen); + crypto_skcipher_setkey(wep->tx_tfm, key, klen); sg_init_one(&sg, pos, len + 4); - return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4); + skcipher_request_set_tfm(req, wep->tx_tfm); + skcipher_request_set_callback(req, 0, NULL, NULL); + skcipher_request_set_crypt(req, &sg, &sg, len + 4, NULL); + err = crypto_skcipher_encrypt(req); + skcipher_request_zero(req); + return err; } /* Perform WEP decryption on given buffer. Buffer includes whole WEP part of @@ -180,11 +182,12 @@ static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) static int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) { struct lib80211_wep_data *wep = priv; - struct blkcipher_desc desc = { .tfm = wep->rx_tfm }; + SKCIPHER_REQUEST_ON_STACK(req, wep->rx_tfm); u32 crc, klen, plen; u8 key[WEP_KEY_LEN + 3]; u8 keyidx, *pos, icv[4]; struct scatterlist sg; + int err; if (skb->len < hdr_len + 8) return -1; @@ -205,9 +208,14 @@ static int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) /* Apply RC4 to data and compute CRC32 over decrypted data */ plen = skb->len - hdr_len - 8; - crypto_blkcipher_setkey(wep->rx_tfm, key, klen); + crypto_skcipher_setkey(wep->rx_tfm, key, klen); sg_init_one(&sg, pos, plen + 4); - if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) + skcipher_request_set_tfm(req, wep->rx_tfm); + skcipher_request_set_callback(req, 0, NULL, NULL); + skcipher_request_set_crypt(req, &sg, &sg, plen + 4, NULL); + err = crypto_skcipher_decrypt(req); + skcipher_request_zero(req); + if (err) return -7; crc = ~crc32_le(~0, pos, plen); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d4786f2802aa..711cb7ad6ae0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7547,7 +7547,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) && no_ht) { - kfree(connkeys); + kzfree(connkeys); return -EINVAL; } } diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 3b0ce1c484a3..547ceecc0523 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -231,20 +231,22 @@ static const struct ieee80211_regdomain world_regdom = { /* IEEE 802.11b/g, channels 1..11 */ REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), /* IEEE 802.11b/g, channels 12..13. */ - REG_RULE(2467-10, 2472+10, 40, 6, 20, - NL80211_RRF_NO_IR), + REG_RULE(2467-10, 2472+10, 20, 6, 20, + NL80211_RRF_NO_IR | NL80211_RRF_AUTO_BW), /* IEEE 802.11 channel 14 - Only JP enables * this and for 802.11b only */ REG_RULE(2484-10, 2484+10, 20, 6, 20, NL80211_RRF_NO_IR | NL80211_RRF_NO_OFDM), /* IEEE 802.11a, channel 36..48 */ - REG_RULE(5180-10, 5240+10, 160, 6, 20, - NL80211_RRF_NO_IR), + REG_RULE(5180-10, 5240+10, 80, 6, 20, + NL80211_RRF_NO_IR | + NL80211_RRF_AUTO_BW), /* IEEE 802.11a, channel 52..64 - DFS required */ - REG_RULE(5260-10, 5320+10, 160, 6, 20, + REG_RULE(5260-10, 5320+10, 80, 6, 20, NL80211_RRF_NO_IR | + NL80211_RRF_AUTO_BW | NL80211_RRF_DFS), /* IEEE 802.11a, channel 100..144 - DFS required */ @@ -2745,7 +2747,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) const struct ieee80211_power_rule *power_rule = NULL; char bw[32], cac_time[32]; - pr_info(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp), (dfs_cac_time)\n"); + pr_debug(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp), (dfs_cac_time)\n"); for (i = 0; i < rd->n_reg_rules; i++) { reg_rule = &rd->reg_rules[i]; @@ -2772,7 +2774,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) * in certain regions */ if (power_rule->max_antenna_gain) - pr_info(" (%d KHz - %d KHz @ %s), (%d mBi, %d mBm), (%s)\n", + pr_debug(" (%d KHz - %d KHz @ %s), (%d mBi, %d mBm), (%s)\n", freq_range->start_freq_khz, freq_range->end_freq_khz, bw, @@ -2780,7 +2782,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) power_rule->max_eirp, cac_time); else - pr_info(" (%d KHz - %d KHz @ %s), (N/A, %d mBm), (%s)\n", + pr_debug(" (%d KHz - %d KHz @ %s), (N/A, %d mBm), (%s)\n", freq_range->start_freq_khz, freq_range->end_freq_khz, bw, @@ -2813,35 +2815,35 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) struct cfg80211_registered_device *rdev; rdev = cfg80211_rdev_by_wiphy_idx(lr->wiphy_idx); if (rdev) { - pr_info("Current regulatory domain updated by AP to: %c%c\n", + pr_debug("Current regulatory domain updated by AP to: %c%c\n", rdev->country_ie_alpha2[0], rdev->country_ie_alpha2[1]); } else - pr_info("Current regulatory domain intersected:\n"); + pr_debug("Current regulatory domain intersected:\n"); } else - pr_info("Current regulatory domain intersected:\n"); + pr_debug("Current regulatory domain intersected:\n"); } else if (is_world_regdom(rd->alpha2)) { - pr_info("World regulatory domain updated:\n"); + pr_debug("World regulatory domain updated:\n"); } else { if (is_unknown_alpha2(rd->alpha2)) - pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); + pr_debug("Regulatory domain changed to driver built-in settings (unknown country)\n"); else { if (reg_request_cell_base(lr)) - pr_info("Regulatory domain changed to country: %c%c by Cell Station\n", + pr_debug("Regulatory domain changed to country: %c%c by Cell Station\n", rd->alpha2[0], rd->alpha2[1]); else - pr_info("Regulatory domain changed to country: %c%c\n", + pr_debug("Regulatory domain changed to country: %c%c\n", rd->alpha2[0], rd->alpha2[1]); } } - pr_info(" DFS Master region: %s", reg_dfs_region_str(rd->dfs_region)); + pr_debug(" DFS Master region: %s", reg_dfs_region_str(rd->dfs_region)); print_rd_rules(rd); } static void print_regdomain_info(const struct ieee80211_regdomain *rd) { - pr_info("Regulatory domain: %c%c\n", rd->alpha2[0], rd->alpha2[1]); + pr_debug("Regulatory domain: %c%c\n", rd->alpha2[0], rd->alpha2[1]); print_rd_rules(rd); } @@ -2862,7 +2864,8 @@ static int reg_set_rd_user(const struct ieee80211_regdomain *rd, return -EALREADY; if (!is_valid_rd(rd)) { - pr_err("Invalid regulatory domain detected:\n"); + pr_err("Invalid regulatory domain detected: %c%c\n", + rd->alpha2[0], rd->alpha2[1]); print_regdomain_info(rd); return -EINVAL; } @@ -2898,7 +2901,8 @@ static int reg_set_rd_driver(const struct ieee80211_regdomain *rd, return -EALREADY; if (!is_valid_rd(rd)) { - pr_err("Invalid regulatory domain detected:\n"); + pr_err("Invalid regulatory domain detected: %c%c\n", + rd->alpha2[0], rd->alpha2[1]); print_regdomain_info(rd); return -EINVAL; } @@ -2956,7 +2960,8 @@ static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd, */ if (!is_valid_rd(rd)) { - pr_err("Invalid regulatory domain detected:\n"); + pr_err("Invalid regulatory domain detected: %c%c\n", + rd->alpha2[0], rd->alpha2[1]); print_regdomain_info(rd); return -EINVAL; } diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 8020b5b094d4..d49ed7666d4c 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -917,6 +917,12 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); + /* stop critical protocol if supported */ + if (rdev->ops->crit_proto_stop && rdev->crit_proto_nlportid) { + rdev->crit_proto_nlportid = 0; + rdev_crit_proto_stop(rdev, wdev); + } + /* * Delete all the keys ... pairwise keys can't really * exist any more anyway, but default keys might. diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index c8717c1d082e..b50ee5d622e1 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -342,6 +342,40 @@ static const int compat_event_type_size[] = { /* IW event code */ +void wireless_nlevent_flush(void) +{ + struct sk_buff *skb; + struct net *net; + + ASSERT_RTNL(); + + for_each_net(net) { + while ((skb = skb_dequeue(&net->wext_nlevents))) + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, + GFP_KERNEL); + } +} +EXPORT_SYMBOL_GPL(wireless_nlevent_flush); + +static int wext_netdev_notifier_call(struct notifier_block *nb, + unsigned long state, void *ptr) +{ + /* + * When a netdev changes state in any way, flush all pending messages + * to avoid them going out in a strange order, e.g. RTM_NEWLINK after + * RTM_DELLINK, or with IFF_UP after without IFF_UP during dev_close() + * or similar - all of which could otherwise happen due to delays from + * schedule_work(). + */ + wireless_nlevent_flush(); + + return NOTIFY_OK; +} + +static struct notifier_block wext_netdev_notifier = { + .notifier_call = wext_netdev_notifier_call, +}; + static int __net_init wext_pernet_init(struct net *net) { skb_queue_head_init(&net->wext_nlevents); @@ -360,7 +394,12 @@ static struct pernet_operations wext_pernet_ops = { static int __init wireless_nlevent_init(void) { - return register_pernet_subsys(&wext_pernet_ops); + int err = register_pernet_subsys(&wext_pernet_ops); + + if (err) + return err; + + return register_netdevice_notifier(&wext_netdev_notifier); } subsys_initcall(wireless_nlevent_init); @@ -368,17 +407,8 @@ subsys_initcall(wireless_nlevent_init); /* Process events generated by the wireless layer or the driver. */ static void wireless_nlevent_process(struct work_struct *work) { - struct sk_buff *skb; - struct net *net; - rtnl_lock(); - - for_each_net(net) { - while ((skb = skb_dequeue(&net->wext_nlevents))) - rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, - GFP_KERNEL); - } - + wireless_nlevent_flush(); rtnl_unlock(); } |