aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/net/mac80211.h59
-rw-r--r--net/mac80211/debugfs_key.c4
-rw-r--r--net/mac80211/ieee80211.c6
-rw-r--r--net/mac80211/ieee80211_i.h8
-rw-r--r--net/mac80211/ieee80211_iface.c21
-rw-r--r--net/mac80211/ieee80211_ioctl.c177
-rw-r--r--net/mac80211/ieee80211_key.h26
-rw-r--r--net/mac80211/key.c243
-rw-r--r--net/mac80211/rx.c7
-rw-r--r--net/mac80211/sta_info.c10
-rw-r--r--net/mac80211/tx.c4
-rw-r--r--net/mac80211/wpa.c30
12 files changed, 366 insertions, 229 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 3437fa16eea5..ec8c7393956b 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -399,19 +399,34 @@ typedef enum {
ALG_CCMP,
} ieee80211_key_alg;
+/*
+ * This flag indiciates that the station this key is being
+ * configured for may use QoS. If your hardware cannot handle
+ * that situation it should reject that key.
+ */
+#define IEEE80211_KEY_FLAG_WMM_STA (1<<0)
+
struct ieee80211_key_conf {
- /* shall be changed by the driver to anything but HW_KEY_IDX_INVALID */
+ /*
+ * To be set by the driver to the key index it would like to
+ * get in the ieee80211_tx_control.key_idx which defaults
+ * to HW_KEY_IDX_INVALID so that shouldn't be used.
+ */
int hw_key_idx;
+ /* key algorithm, ALG_NONE should never be seen by the driver */
ieee80211_key_alg alg;
- int keylen;
+ /* key flags, see above */
+ u8 flags;
+
+ /* key index: 0-3 */
+ s8 keyidx;
-#define IEEE80211_KEY_FORCE_SW_ENCRYPT (1<<0) /* to be cleared by low-level
- driver */
- u32 flags; /* key configuration flags defined above */
+ /* length of key material */
+ u8 keylen;
- s8 keyidx; /* WEP key index */
+ /* the key material */
u8 key[0];
};
@@ -419,7 +434,7 @@ struct ieee80211_key_conf {
#define IEEE80211_SEQ_COUNTER_TX 1
typedef enum {
- SET_KEY, DISABLE_KEY, REMOVE_ALL_KEYS,
+ SET_KEY, DISABLE_KEY,
} set_key_cmd;
/* This is driver-visible part of the per-hw state the stack keeps. */
@@ -492,8 +507,7 @@ struct ieee80211_hw {
/* hole at 6 */
- /* Force software encryption for TKIP packets if WMM is enabled. */
-#define IEEE80211_HW_NO_TKIP_WMM_HWACCEL (1<<7)
+/* hole at 7 */
/*
* Some devices handle Michael MIC internally and do not include MIC in
@@ -627,12 +641,31 @@ struct ieee80211_ops {
*
* This is called to enable hardware acceleration of encryption and
* decryption. The address will be the broadcast address for default
- * keys and the other station's hardware address for individual keys.
+ * keys, the other station's hardware address for individual keys or
+ * the zero address for keys that will be used only for transmission.
+ *
+ * The local_address parameter will always be set to our own address,
+ * this is only relevant if you support multiple local addresses.
+ *
* When transmitting, the TX control data will use the hw_key_idx
* selected by the low-level driver.
+ *
+ * Return 0 if the key is now in use, -EOPNOTSUPP or -ENOSPC if it
+ * couldn't be added; if you return 0 then hw_key_idx must be
+ * assigned to something other than HW_KEY_IDX_INVALID. When the cmd
+ * is DISABLE_KEY then it must succeed.
+ *
+ * This callback can sleep, and is only called between add_interface
+ * and remove_interface calls, i.e. while the interface with the
+ * given local_address is enabled.
+ *
+ * The ieee80211_key_conf structure pointed to by the key parameter
+ * is guaranteed to be valid until another call to set_key removes
+ * it, but it can only be used as a cookie to differentiate keys.
*/
int (*set_key)(struct ieee80211_hw *hw, set_key_cmd cmd,
- u8 *address, struct ieee80211_key_conf *key);
+ const u8 *local_address, const u8 *address,
+ struct ieee80211_key_conf *key);
/*
* Set TX key index for default/broadcast keys. This is needed in cases
@@ -640,6 +673,10 @@ struct ieee80211_ops {
* is not set), in other cases, this function pointer can be set to
* NULL since the IEEE 802.11 module takes care of selecting the key
* index for each TX frame.
+ *
+ * TODO: If you use this callback in your driver tell us if you need
+ * any other information from it to make it easier, like the
+ * key_conf instead.
*/
int (*set_key_idx)(struct ieee80211_hw *hw, int idx);
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 246938c32d4d..36e7812da556 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -25,6 +25,7 @@ static ssize_t key_##name##_read(struct file *file, \
return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
}
#define KEY_READ_D(name) KEY_READ(name, name, 20, "%d\n")
+#define KEY_READ_X(name) KEY_READ(name, name, 20, "0x%x\n")
#define KEY_OPS(name) \
static const struct file_operations key_ ##name## _ops = { \
@@ -39,7 +40,6 @@ static const struct file_operations key_ ##name## _ops = { \
#define KEY_CONF_READ(name, buflen, format_string) \
KEY_READ(conf_##name, conf.name, buflen, format_string)
#define KEY_CONF_READ_D(name) KEY_CONF_READ(name, 20, "%d\n")
-#define KEY_CONF_READ_X(name) KEY_CONF_READ(name, 20, "0x%x\n")
#define KEY_CONF_OPS(name) \
static const struct file_operations key_ ##name## _ops = { \
@@ -54,7 +54,7 @@ static const struct file_operations key_ ##name## _ops = { \
KEY_CONF_FILE(keylen, D);
KEY_CONF_FILE(keyidx, D);
KEY_CONF_FILE(hw_key_idx, D);
-KEY_CONF_FILE(flags, X);
+KEY_FILE(flags, X);
KEY_FILE(tx_rx_count, D);
static ssize_t key_algorithm_read(struct file *file,
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
index 5d5034f36fde..73e314e33de2 100644
--- a/net/mac80211/ieee80211.c
+++ b/net/mac80211/ieee80211.c
@@ -442,6 +442,7 @@ static int ieee80211_open(struct net_device *dev)
} else {
ieee80211_if_config(dev);
ieee80211_reset_erp_info(dev);
+ ieee80211_enable_keys(sdata);
}
if (sdata->type == IEEE80211_IF_TYPE_STA &&
@@ -510,6 +511,9 @@ static int ieee80211_stop(struct net_device *dev)
local->monitors--;
if (!local->monitors)
local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
+ } else {
+ /* disable all keys for as long as this netdev is down */
+ ieee80211_disable_keys(sdata);
}
local->open_count--;
@@ -908,7 +912,7 @@ static void ieee80211_remove_tx_extra(struct ieee80211_local *local,
}
if (skb->len >= mic_len &&
- (key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT))
+ !(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
skb_trim(skb, skb->len - mic_len);
if (skb->len >= iv_len && skb->len > hdrlen) {
memmove(skb->data + iv_len, skb->data, hdrlen);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 7b5cc146c81b..0149f9055918 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -291,6 +291,9 @@ struct ieee80211_sub_if_data {
struct wireless_dev wdev;
+ /* keys */
+ struct list_head key_list;
+
struct net_device *dev;
struct ieee80211_local *local;
@@ -810,11 +813,6 @@ int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
int ieee80211_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev);
-/* key handling */
-struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
- int idx, size_t key_len, gfp_t flags);
-void ieee80211_key_free(struct ieee80211_key *key);
-
/* utility functions/constants */
extern void *mac80211_wiphy_privid; /* for wiphy privid */
extern const unsigned char rfc1042_header[6];
diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c
index 8bb85f194385..f9c74bb09d31 100644
--- a/net/mac80211/ieee80211_iface.c
+++ b/net/mac80211/ieee80211_iface.c
@@ -25,6 +25,8 @@ void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata)
sdata->eapol = 1;
for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
skb_queue_head_init(&sdata->fragments[i].skb_list);
+
+ INIT_LIST_HEAD(&sdata->key_list);
}
static void ieee80211_if_sdata_deinit(struct ieee80211_sub_if_data *sdata)
@@ -210,25 +212,12 @@ void ieee80211_if_reinit(struct net_device *dev)
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct sta_info *sta;
- int i;
ASSERT_RTNL();
+
+ ieee80211_free_keys(sdata);
+
ieee80211_if_sdata_deinit(sdata);
- for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
- if (!sdata->keys[i])
- continue;
-#if 0
- /* The interface is down at the moment, so there is not
- * really much point in disabling the keys at this point. */
- memset(addr, 0xff, ETH_ALEN);
- if (local->ops->set_key)
- local->ops->set_key(local_to_hw(local), DISABLE_KEY, addr,
- local->keys[i],
- local->default_wep_only);
-#endif
- ieee80211_key_free(sdata->keys[i]);
- sdata->keys[i] = NULL;
- }
switch (sdata->type) {
case IEEE80211_IF_TYPE_AP: {
diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c
index dc05bc66fbb8..8296e7de12c7 100644
--- a/net/mac80211/ieee80211_ioctl.c
+++ b/net/mac80211/ieee80211_ioctl.c
@@ -25,28 +25,6 @@
#include "ieee80211_rate.h"
#include "wpa.h"
#include "aes_ccm.h"
-#include "debugfs_key.h"
-
-static void ieee80211_set_hw_encryption(struct net_device *dev,
- struct sta_info *sta, u8 addr[ETH_ALEN],
- struct ieee80211_key *key)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
- /* default to sw encryption; this will be cleared by low-level
- * driver if the hw supports requested encryption */
- if (key)
- key->conf.flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT;
-
- if (key && local->ops->set_key) {
- if (local->ops->set_key(local_to_hw(local), SET_KEY, addr,
- &key->conf)) {
- key->conf.flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT;
- key->conf.hw_key_idx = HW_KEY_IDX_INVALID;
- }
- }
-}
-
static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
int idx, int alg, int set_tx_key,
@@ -55,8 +33,7 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
int ret = 0;
struct sta_info *sta;
- struct ieee80211_key *key, *old_key;
- int try_hwaccel = 1;
+ struct ieee80211_key *key;
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -69,16 +46,6 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
return -EINVAL;
}
key = sdata->keys[idx];
-
- /* TODO: consider adding hwaccel support for these; at least
- * Atheros key cache should be able to handle this since AP is
- * only transmitting frames with default keys. */
- /* FIX: hw key cache can be used when only one virtual
- * STA is associated with each AP. If more than one STA
- * is associated to the same AP, software encryption
- * must be used. This should be done automatically
- * based on configured station devices. For the time
- * being, this can be only set at compile time. */
} else {
set_tx_key = 0;
if (idx != 0) {
@@ -101,139 +68,28 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
key = sta->key;
}
- /* FIX:
- * Cannot configure default hwaccel keys with WEP algorithm, if
- * any of the virtual interfaces is using static WEP
- * configuration because hwaccel would otherwise try to decrypt
- * these frames.
- *
- * For now, just disable WEP hwaccel for broadcast when there is
- * possibility of conflict with default keys. This can maybe later be
- * optimized by using non-default keys (at least with Atheros ar521x).
- */
- if (!sta && alg == ALG_WEP &&
- sdata->type != IEEE80211_IF_TYPE_IBSS &&
- sdata->type != IEEE80211_IF_TYPE_AP) {
- try_hwaccel = 0;
- }
-
- if (local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP) {
- /* Software encryption cannot be used with devices that hide
- * encryption from the host system, so always try to use
- * hardware acceleration with such devices. */
- try_hwaccel = 1;
- }
-
- if ((local->hw.flags & IEEE80211_HW_NO_TKIP_WMM_HWACCEL) &&
- alg == ALG_TKIP) {
- if (sta && (sta->flags & WLAN_STA_WME)) {
- /* Hardware does not support hwaccel with TKIP when using WMM.
- */
- try_hwaccel = 0;
- }
- else if (sdata->type == IEEE80211_IF_TYPE_STA) {
- sta = sta_info_get(local, sdata->u.sta.bssid);
- if (sta) {
- if (sta->flags & WLAN_STA_WME) {
- try_hwaccel = 0;
- }
- sta_info_put(sta);
- sta = NULL;
- }
- }
- }
-
if (alg == ALG_NONE) {
- if (try_hwaccel && key &&
- key->conf.hw_key_idx != HW_KEY_IDX_INVALID &&
- local->ops->set_key &&
- local->ops->set_key(local_to_hw(local), DISABLE_KEY,
- sta_addr, &key->conf)) {
- printk(KERN_DEBUG "%s: set_encrypt - low-level disable"
- " failed\n", dev->name);
- ret = -EINVAL;
- }
-
- if (set_tx_key || sdata->default_key == key) {
- ieee80211_debugfs_key_remove_default(sdata);
- sdata->default_key = NULL;
- }
- ieee80211_debugfs_key_remove(key);
- if (sta)
- sta->key = NULL;
- else
- sdata->keys[idx] = NULL;
ieee80211_key_free(key);
key = NULL;
} else {
- old_key = key;
- key = ieee80211_key_alloc(sta ? NULL : sdata, idx, key_len,
- GFP_KERNEL);
+ /*
+ * Need to free it before allocating a new one with
+ * with the same index or the ordering to the driver's
+ * set_key() callback becomes confused.
+ */
+ ieee80211_key_free(key);
+ key = ieee80211_key_alloc(sdata, sta, alg, idx, key_len, _key);
if (!key) {
ret = -ENOMEM;
goto err_out;
}
-
- /* default to sw encryption; low-level driver sets these if the
- * requested encryption is supported */
- key->conf.hw_key_idx = HW_KEY_IDX_INVALID;
- key->conf.flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT;
-
- key->conf.alg = alg;
- key->conf.keyidx = idx;
- key->conf.keylen = key_len;
- memcpy(key->conf.key, _key, key_len);
-
- if (alg == ALG_CCMP) {
- /* Initialize AES key state here as an optimization
- * so that it does not need to be initialized for every
- * packet. */
- key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(
- key->conf.key);
- if (!key->u.ccmp.tfm) {
- ret = -ENOMEM;
- goto err_free;
- }
- }
-
- if (set_tx_key || sdata->default_key == old_key) {
- ieee80211_debugfs_key_remove_default(sdata);
- sdata->default_key = NULL;
- }
- ieee80211_debugfs_key_remove(old_key);
- if (sta)
- sta->key = key;
- else
- sdata->keys[idx] = key;
- ieee80211_key_free(old_key);
- ieee80211_debugfs_key_add(local, key);
- if (sta)
- ieee80211_debugfs_key_sta_link(key, sta);
-
- if (try_hwaccel &&
- (alg == ALG_WEP || alg == ALG_TKIP || alg == ALG_CCMP))
- ieee80211_set_hw_encryption(dev, sta, sta_addr, key);
- }
-
- if (set_tx_key || (!sta && !sdata->default_key && key)) {
- sdata->default_key = key;
- if (key)
- ieee80211_debugfs_key_add_default(sdata);
-
- if (local->ops->set_key_idx &&
- local->ops->set_key_idx(local_to_hw(local), idx))
- printk(KERN_DEBUG "%s: failed to set TX key idx for "
- "low-level driver\n", dev->name);
}
- if (sta)
- sta_info_put(sta);
+ if (set_tx_key || (!sta && !sdata->default_key && key))
+ ieee80211_set_default_key(sdata, idx);
- return 0;
-
-err_free:
- ieee80211_key_free(key);
-err_out:
+ ret = 0;
+ err_out:
if (sta)
sta_info_put(sta);
return ret;
@@ -1181,12 +1037,7 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
alg = ALG_NONE;
else if (erq->length == 0) {
/* No key data - just set the default TX key index */
- if (sdata->default_key != sdata->keys[idx]) {
- ieee80211_debugfs_key_remove_default(sdata);
- sdata->default_key = sdata->keys[idx];
- if (sdata->default_key)
- ieee80211_debugfs_key_add_default(sdata);
- }
+ ieee80211_set_default_key(sdata, idx);
return 0;
}
@@ -1232,7 +1083,7 @@ static int ieee80211_ioctl_giwencode(struct net_device *dev,
}
memcpy(key, sdata->keys[idx]->conf.key,
- min((int)erq->length, sdata->keys[idx]->conf.keylen));
+ min_t(int, erq->length, sdata->keys[idx]->conf.keylen));
erq->length = sdata->keys[idx]->conf.keylen;
erq->flags |= IW_ENCODE_ENABLED;
diff --git a/net/mac80211/ieee80211_key.h b/net/mac80211/ieee80211_key.h
index 58e192530826..a4e5fbbe045c 100644
--- a/net/mac80211/ieee80211_key.h
+++ b/net/mac80211/ieee80211_key.h
@@ -41,7 +41,21 @@
#define NUM_RX_DATA_QUEUES 17
+struct ieee80211_local;
+struct ieee80211_sub_if_data;
+struct sta_info;
+
+#define KEY_FLAG_UPLOADED_TO_HARDWARE (1<<0)
+
struct ieee80211_key {
+ struct ieee80211_local *local;
+ struct ieee80211_sub_if_data *sdata;
+ struct sta_info *sta;
+
+ struct list_head list;
+
+ unsigned int flags;
+
union {
struct {
/* last used TSC */
@@ -97,4 +111,16 @@ struct ieee80211_key {
struct ieee80211_key_conf conf;
};
+struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ ieee80211_key_alg alg,
+ int idx,
+ size_t key_len,
+ const u8 *key_data);
+void ieee80211_key_free(struct ieee80211_key *key);
+void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx);
+void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata);
+void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata);
+void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata);
+
#endif /* IEEE80211_KEY_H */
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 843d1577f00f..178f00cf61b9 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -2,25 +2,198 @@
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <linux/if_ether.h>
+#include <linux/etherdevice.h>
+#include <linux/list.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
#include "debugfs_key.h"
#include "aes_ccm.h"
+
+/*
+ * Key handling basics
+ *
+ * Key handling in mac80211 is done based on per-interface (sub_if_data)
+ * keys and per-station keys. Since each station belongs to an interface,
+ * each station key also belongs to that interface.
+ *
+ * Hardware acceleration is done on a best-effort basis, for each key
+ * that is eligible the hardware is asked to enable that key but if
+ * it cannot do that they key is simply kept for software encryption.
+ * There is currently no way of knowing this except by looking into
+ * debugfs.
+ *
+ * All operations here are called under RTNL so no extra locking is
+ * required.
+ */
+
+static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+static const u8 zero_addr[ETH_ALEN];
+
+static const u8 *get_mac_for_key(struct ieee80211_key *key)
+{
+ const u8 *addr = bcast_addr;
+
+ /*
+ * If we're an AP we won't ever receive frames with a non-WEP
+ * group key so we tell the driver that by using the zero MAC
+ * address to indicate a transmit-only key.
+ */
+ if (key->conf.alg != ALG_WEP &&
+ (key->sdata->type == IEEE80211_IF_TYPE_AP ||
+ key->sdata->type == IEEE80211_IF_TYPE_VLAN))
+ addr = zero_addr;
+
+ if (key->sta)
+ addr = key->sta->addr;
+
+ return addr;
+}
+
+static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
+{
+ const u8 *addr;
+ int ret;
+
+ if (!key->local->ops->set_key)
+ return;
+
+ addr = get_mac_for_key(key);
+
+ ret = key->local->ops->set_key(local_to_hw(key->local), SET_KEY,
+ key->sdata->dev->dev_addr, addr,
+ &key->conf);
+
+ WARN_ON(!ret && (key->conf.hw_key_idx == HW_KEY_IDX_INVALID));
+
+ if (!ret)
+ key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
+
+ if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP)
+ printk(KERN_ERR "mac80211-%s: failed to set key "
+ "(%d, " MAC_FMT ") to hardware (%d)\n",
+ wiphy_name(key->local->hw.wiphy),
+ key->conf.keyidx, MAC_ARG(addr), ret);
+}
+
+static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
+{
+ const u8 *addr;
+ int ret;
+
+ if (!key->local->ops->set_key)
+ return;
+
+ if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
+ return;
+
+ addr = get_mac_for_key(key);
+
+ ret = key->local->ops->set_key(local_to_hw(key->local), DISABLE_KEY,
+ key->sdata->dev->dev_addr, addr,
+ &key->conf);
+
+ if (ret)
+ printk(KERN_ERR "mac80211-%s: failed to remove key "
+ "(%d, " MAC_FMT ") from hardware (%d)\n",
+ wiphy_name(key->local->hw.wiphy),
+ key->conf.keyidx, MAC_ARG(addr), ret);
+
+ key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
+ key->conf.hw_key_idx = HW_KEY_IDX_INVALID;
+}
+
struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
- int idx, size_t key_len, gfp_t flags)
+ struct sta_info *sta,
+ ieee80211_key_alg alg,
+ int idx,
+ size_t key_len,
+ const u8 *key_data)
{
struct ieee80211_key *key;
- key = kzalloc(sizeof(struct ieee80211_key) + key_len, flags);
+ BUG_ON(alg == ALG_NONE);
+
+ key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL);
if (!key)
return NULL;
+
+ /*
+ * Default to software encryption; we'll later upload the
+ * key to the hardware if possible.
+ */
+ key->conf.hw_key_idx = HW_KEY_IDX_INVALID;
+ key->conf.flags = 0;
+ key->flags = 0;
+
+ key->conf.alg = alg;
+ key->conf.keyidx = idx;
+ key->conf.keylen = key_len;
+ memcpy(key->conf.key, key_data, key_len);
+
+ key->local = sdata->local;
+ key->sdata = sdata;
+ key->sta = sta;
+
+ if (alg == ALG_CCMP) {
+ /*
+ * Initialize AES key state here as an optimization so that
+ * it does not need to be initialized for every packet.
+ */
+ key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data);
+ if (!key->u.ccmp.tfm) {
+ ieee80211_key_free(key);
+ return NULL;
+ }
+ }
+
+ ieee80211_debugfs_key_add(key->local, key);
+
+ if (sta) {
+ ieee80211_debugfs_key_sta_link(key, sta);
+ sta->key = key;
+ /*
+ * some hardware cannot handle TKIP with QoS, so
+ * we indicate whether QoS could be in use.
+ */
+ if (sta->flags & WLAN_STA_WME)
+ key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA;
+ } else {
+ if (sdata->type == IEEE80211_IF_TYPE_STA) {
+ struct sta_info *ap;
+
+ /* same here, the AP could be using QoS */
+ ap = sta_info_get(key->local, key->sdata->u.sta.bssid);
+ if (ap) {
+ if (ap->flags & WLAN_STA_WME)
+ key->conf.flags |=
+ IEEE80211_KEY_FLAG_WMM_STA;
+ sta_info_put(ap);
+ }
+ }
+
+ if (idx >= 0 && idx < NUM_DEFAULT_KEYS) {
+ if (!sdata->keys[idx])
+ sdata->keys[idx] = key;
+ else
+ WARN_ON(1);
+ } else
+ WARN_ON(1);
+ }
+
+ list_add(&key->list, &sdata->key_list);
+
+ if (netif_running(key->sdata->dev))
+ ieee80211_key_enable_hw_accel(key);
+
return key;
}
@@ -29,8 +202,74 @@ void ieee80211_key_free(struct ieee80211_key *key)
if (!key)
return;
+ ieee80211_key_disable_hw_accel(key);
+
+ if (key->sta) {
+ key->sta->key = NULL;
+ } else {
+ if (key->sdata->default_key == key)
+ ieee80211_set_default_key(key->sdata, -1);
+ if (key->conf.keyidx >= 0 &&
+ key->conf.keyidx < NUM_DEFAULT_KEYS)
+ key->sdata->keys[key->conf.keyidx] = NULL;
+ else
+ WARN_ON(1);
+ }
+
if (key->conf.alg == ALG_CCMP)
ieee80211_aes_key_free(key->u.ccmp.tfm);
ieee80211_debugfs_key_remove(key);
+
+ list_del(&key->list);
+
kfree(key);
}
+
+void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx)
+{
+ struct ieee80211_key *key = NULL;
+
+ if (idx >= 0 && idx < NUM_DEFAULT_KEYS)
+ key = sdata->keys[idx];
+
+ if (sdata->default_key != key) {
+ ieee80211_debugfs_key_remove_default(sdata);
+
+ sdata->default_key = key;
+
+ if (sdata->default_key)
+ ieee80211_debugfs_key_add_default(sdata);
+
+ if (sdata->local->ops->set_key_idx)
+ sdata->local->ops->set_key_idx(
+ local_to_hw(sdata->local), idx);
+ }
+}
+
+void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_key *key, *tmp;
+
+ list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
+ ieee80211_key_free(key);
+}
+
+void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_key *key;
+
+ WARN_ON(!netif_running(sdata->dev));
+ if (!netif_running(sdata->dev))
+ return;
+
+ list_for_each_entry(key, &sdata->key_list, list)
+ ieee80211_key_enable_hw_accel(key);
+}
+
+void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_key *key;
+
+ list_for_each_entry(key, &sdata->key_list, list)
+ ieee80211_key_disable_hw_accel(key);
+}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 976b646a40de..ba94f58ba02e 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -528,7 +528,7 @@ ieee80211_rx_h_wep_weak_iv_detection(struct ieee80211_txrx_data *rx)
/* Check for weak IVs, if hwaccel did not remove IV from the frame */
if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) ||
- (rx->key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT))
+ !(rx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
if (ieee80211_wep_is_weak_iv(rx->skb, rx->key))
rx->sta->wep_weak_iv_count++;
@@ -553,7 +553,7 @@ ieee80211_rx_h_wep_decrypt(struct ieee80211_txrx_data *rx)
}
if (!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED) ||
- (rx->key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT)) {
+ !(rx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) {
if (net_ratelimit())
printk(KERN_DEBUG "%s: RX WEP frame, decrypt "
@@ -897,8 +897,7 @@ ieee80211_rx_h_drop_unencrypted(struct ieee80211_txrx_data *rx)
* uploaded to the hardware.
*/
if ((rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP) &&
- (!rx->key ||
- !(rx->key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT)))
+ (!rx->key || (rx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)))
return TXRX_CONTINUE;
/* Drop unencrypted frames if key is set. */
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index fba2d79e4d2b..c17172abb21c 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -19,7 +19,6 @@
#include "ieee80211_i.h"
#include "ieee80211_rate.h"
#include "sta_info.h"
-#include "debugfs_key.h"
#include "debugfs_sta.h"
/* Caller must hold local->sta_lock */
@@ -118,8 +117,6 @@ static void sta_info_release(struct kref *kref)
}
rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv);
rate_control_put(sta->rate_ctrl);
- if (sta->key)
- ieee80211_debugfs_key_sta_del(sta->key, sta);
kfree(sta);
}
@@ -230,11 +227,8 @@ void sta_info_free(struct sta_info *sta)
local->mdev->name, MAC_ARG(sta->addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
- if (sta->key) {
- ieee80211_debugfs_key_remove(sta->key);
- ieee80211_key_free(sta->key);
- sta->key = NULL;
- }
+ ieee80211_key_free(sta->key);
+ sta->key = NULL;
rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index d70140cbd66a..b65ff6536244 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -536,7 +536,7 @@ ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
static int wep_encrypt_skb(struct ieee80211_txrx_data *tx, struct sk_buff *skb)
{
- if (tx->key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT) {
+ if (!(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
if (ieee80211_wep_encrypt(tx->local, skb, tx->key))
return -1;
} else {
@@ -832,7 +832,7 @@ __ieee80211_parse_tx_radiotap(
*/
control->retry_limit = 1; /* no retry */
- control->key_idx = -1; /* no encryption key */
+ control->key_idx = HW_KEY_IDX_INVALID;
control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
IEEE80211_TXCTL_USE_CTS_PROTECT);
control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT |
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 4a2a9aa638b3..b6cd66e0ee58 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -89,7 +89,7 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_txrx_data *tx)
if (ieee80211_get_hdr_info(skb, &sa, &da, &qos_tid, &data, &data_len))
return TXRX_DROP;
- if (!(tx->key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT) &&
+ if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
!(tx->flags & IEEE80211_TXRXD_FRAGMENTED) &&
!(tx->local->hw.flags & IEEE80211_HW_TKIP_INCLUDE_MMIC) &&
!wpa_test) {
@@ -146,7 +146,7 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_txrx_data *rx)
return TXRX_CONTINUE;
if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) &&
- !(rx->key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT)) {
+ (rx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
if (rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) {
if (skb->len < MICHAEL_MIC_LEN)
return TXRX_DROP;
@@ -205,10 +205,10 @@ static int tkip_encrypt_skb(struct ieee80211_txrx_data *tx,
hdrlen = ieee80211_get_hdrlen(fc);
len = skb->len - hdrlen;
- if (tx->key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT)
- tailneed = TKIP_ICV_LEN;
- else
+ if (tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)
tailneed = 0;
+ else
+ tailneed = TKIP_ICV_LEN;
if ((skb_headroom(skb) < TKIP_IV_LEN ||
skb_tailroom(skb) < tailneed)) {
@@ -227,7 +227,7 @@ static int tkip_encrypt_skb(struct ieee80211_txrx_data *tx,
if (key->u.tkip.iv16 == 0)
key->u.tkip.iv32++;
- if (!(tx->key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT)) {
+ if (tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
u32 flags = tx->local->hw.flags;
hdr = (struct ieee80211_hdr *)skb->data;
@@ -286,7 +286,7 @@ ieee80211_tx_h_tkip_encrypt(struct ieee80211_txrx_data *tx)
tx->u.tx.control->iv_len = TKIP_IV_LEN;
ieee80211_tx_set_iswep(tx);
- if (!(tx->key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT) &&
+ if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
!(tx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) &&
!wpa_test) {
/* hwaccel - with no need for preallocated room for IV/ICV */
@@ -331,7 +331,7 @@ ieee80211_rx_h_tkip_decrypt(struct ieee80211_txrx_data *rx)
return TXRX_DROP;
if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) &&
- !(key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT)) {
+ (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
if (!(rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV)) {
/* Hardware takes care of all processing, including
* replay protection, so no need to continue here. */
@@ -475,10 +475,10 @@ static int ccmp_encrypt_skb(struct ieee80211_txrx_data *tx,
hdrlen = ieee80211_get_hdrlen(fc);
len = skb->len - hdrlen;
- if (key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT)
- tailneed = CCMP_MIC_LEN;
- else
+ if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)
tailneed = 0;
+ else
+ tailneed = CCMP_MIC_LEN;
if ((skb_headroom(skb) < CCMP_HDR_LEN ||
skb_tailroom(skb) < tailneed)) {
@@ -504,7 +504,7 @@ static int ccmp_encrypt_skb(struct ieee80211_txrx_data *tx,
ccmp_pn2hdr(pos, pn, key->conf.keyidx);
- if (!(key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT)) {
+ if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
/* hwaccel - with preallocated room for CCMP header */
tx->u.tx.control->key_idx = key->conf.hw_key_idx;
return 0;
@@ -537,7 +537,7 @@ ieee80211_tx_h_ccmp_encrypt(struct ieee80211_txrx_data *tx)
tx->u.tx.control->iv_len = CCMP_HDR_LEN;
ieee80211_tx_set_iswep(tx);
- if (!(tx->key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT) &&
+ if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
!(tx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV)) {
/* hwaccel - with no need for preallocated room for CCMP "
* header or MIC fields */
@@ -586,7 +586,7 @@ ieee80211_rx_h_ccmp_decrypt(struct ieee80211_txrx_data *rx)
return TXRX_DROP;
if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) &&
- !(key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT) &&
+ (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
!(rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV))
return TXRX_CONTINUE;
@@ -607,7 +607,7 @@ ieee80211_rx_h_ccmp_decrypt(struct ieee80211_txrx_data *rx)
}
if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) &&
- !(key->conf.flags & IEEE80211_KEY_FORCE_SW_ENCRYPT)) {
+ (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
/* hwaccel has already decrypted frame and verified MIC */
} else {
u8 *scratch, *b_0, *aad;