summaryrefslogtreecommitdiffstats
path: root/net/mac80211/ieee80211_ioctl.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2008-02-25 16:27:45 +0100
committerJohn W. Linville <linville@tuxdriver.com>2008-02-29 15:42:04 -0500
commitdb4d1169d0b893bfb7923b6526748fe2c5a7373f (patch)
treeebd5ac06685bacc069b162b31f99b33c6191b4c3 /net/mac80211/ieee80211_ioctl.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/ieee80211_ioctl.c')
-rw-r--r--net/mac80211/ieee80211_ioctl.c90
1 files changed, 48 insertions, 42 deletions
diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c
index 54ad07aafe2..7551db3f3ab 100644
--- a/net/mac80211/ieee80211_ioctl.c
+++ b/net/mac80211/ieee80211_ioctl.c
@@ -33,8 +33,8 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
size_t key_len)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- int ret = 0;
- struct sta_info *sta;
+ int ret;
+ struct sta_info *sta = NULL;
struct ieee80211_key *key;
struct ieee80211_sub_if_data *sdata;
@@ -46,58 +46,64 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
return -EINVAL;
}
- if (is_broadcast_ether_addr(sta_addr)) {
- sta = NULL;
- key = sdata->keys[idx];
- } else {
- set_tx_key = 0;
- /*
- * According to the standard, the key index of a pairwise
- * key must be zero. However, some AP are broken when it
- * comes to WEP key indices, so we work around this.
- */
- if (idx != 0 && alg != ALG_WEP) {
- printk(KERN_DEBUG "%s: set_encrypt - non-zero idx for "
- "individual key\n", dev->name);
- return -EINVAL;
+ if (remove) {
+ if (is_broadcast_ether_addr(sta_addr)) {
+ key = sdata->keys[idx];
+ } else {
+ sta = sta_info_get(local, sta_addr);
+ if (!sta) {
+ ret = -ENOENT;
+ key = NULL;
+ goto err_out;
+ }
+
+ key = sta->key;
}
- sta = sta_info_get(local, sta_addr);
- if (!sta) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- DECLARE_MAC_BUF(mac);
- printk(KERN_DEBUG "%s: set_encrypt - unknown addr "
- "%s\n",
- dev->name, print_mac(mac, sta_addr));
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+ if (!key)
+ ret = -ENOENT;
+ else
+ ret = 0;
+ } else {
+ key = ieee80211_key_alloc(alg, idx, key_len, _key);
+ if (!key)
+ return -ENOMEM;
+
+ if (!is_broadcast_ether_addr(sta_addr)) {
+ set_tx_key = 0;
+ /*
+ * According to the standard, the key index of a
+ * pairwise key must be zero. However, some AP are
+ * broken when it comes to WEP key indices, so we
+ * work around this.
+ */
+ if (idx != 0 && alg != ALG_WEP) {
+ ret = -EINVAL;
+ goto err_out;
+ }
- return -ENOENT;
+ sta = sta_info_get(local, sta_addr);
+ if (!sta) {
+ ret = -ENOENT;
+ goto err_out;
+ }
}
- key = sta->key;
- }
+ ieee80211_key_link(key, sdata, sta);
- if (remove) {
- ieee80211_key_free(key);
+ if (set_tx_key || (!sta && !sdata->default_key && key))
+ ieee80211_set_default_key(sdata, idx);
+
+ /* don't free key later */
key = NULL;
- } else {
- /*
- * Automatically frees any old key if present.
- */
- key = ieee80211_key_alloc(sdata, sta, alg, idx, key_len, _key);
- if (!key) {
- ret = -ENOMEM;
- goto err_out;
- }
- }
- if (set_tx_key || (!sta && !sdata->default_key && key))
- ieee80211_set_default_key(sdata, idx);
+ ret = 0;
+ }
- ret = 0;
err_out:
if (sta)
sta_info_put(sta);
+ ieee80211_key_free(key);
return ret;
}