summaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-10-05 19:39:30 +0200
committerJohn W. Linville <linville@tuxdriver.com>2010-10-06 16:30:40 -0400
commite31b82136d1adc7a599b6e99d3321e5831841f5a (patch)
treec72d78d4cccfd08587e909c7efe59956f1cbc23e /net/mac80211
parent53f73c09d64f1fa7d7e6e8b6bb7468d42eddc92d (diff)
downloadop-kernel-dev-e31b82136d1adc7a599b6e99d3321e5831841f5a.zip
op-kernel-dev-e31b82136d1adc7a599b6e99d3321e5831841f5a.tar.gz
cfg80211/mac80211: allow per-station GTKs
This adds API to allow adding per-station GTKs, updates mac80211 to support it, and also allows drivers to remove a key from hwaccel again when this may be necessary due to multiple GTKs. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/cfg.c32
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/key.c95
-rw-r--r--net/mac80211/key.h3
-rw-r--r--net/mac80211/rx.c41
-rw-r--r--net/mac80211/sta_info.c10
-rw-r--r--net/mac80211/sta_info.h6
-rw-r--r--net/mac80211/tx.c2
8 files changed, 123 insertions, 68 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 94bf550..8b0e874 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -103,7 +103,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
}
static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
- u8 key_idx, const u8 *mac_addr,
+ u8 key_idx, bool pairwise, const u8 *mac_addr,
struct key_params *params)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -131,6 +131,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
if (IS_ERR(key))
return PTR_ERR(key);
+ if (pairwise)
+ key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
+
mutex_lock(&sdata->local->sta_mtx);
if (mac_addr) {
@@ -153,7 +156,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
}
static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
- u8 key_idx, const u8 *mac_addr)
+ u8 key_idx, bool pairwise, const u8 *mac_addr)
{
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
@@ -170,10 +173,17 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
if (!sta)
goto out_unlock;
- if (sta->key) {
- ieee80211_key_free(sdata->local, sta->key);
- WARN_ON(sta->key);
- ret = 0;
+ if (pairwise) {
+ if (sta->ptk) {
+ ieee80211_key_free(sdata->local, sta->ptk);
+ ret = 0;
+ }
+ } else {
+ if (sta->gtk[key_idx]) {
+ ieee80211_key_free(sdata->local,
+ sta->gtk[key_idx]);
+ ret = 0;
+ }
}
goto out_unlock;
@@ -195,7 +205,8 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
}
static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
- u8 key_idx, const u8 *mac_addr, void *cookie,
+ u8 key_idx, bool pairwise, const u8 *mac_addr,
+ void *cookie,
void (*callback)(void *cookie,
struct key_params *params))
{
@@ -203,7 +214,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
struct sta_info *sta = NULL;
u8 seq[6] = {0};
struct key_params params;
- struct ieee80211_key *key;
+ struct ieee80211_key *key = NULL;
u32 iv32;
u16 iv16;
int err = -ENOENT;
@@ -217,7 +228,10 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
if (!sta)
goto out;
- key = sta->key;
+ if (pairwise)
+ key = sta->ptk;
+ else if (key_idx < NUM_DEFAULT_KEYS)
+ key = sta->gtk[key_idx];
} else
key = sdata->keys[key_idx];
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 76c2b50..f0610fa 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -549,8 +549,6 @@ struct ieee80211_sub_if_data {
struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX];
unsigned int fragment_next;
-#define NUM_DEFAULT_KEYS 4
-#define NUM_DEFAULT_MGMT_KEYS 2
struct ieee80211_key *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
struct ieee80211_key *default_key;
struct ieee80211_key *default_mgmt_key;
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 6a63d1a..ccd676b 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -68,15 +68,21 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
might_sleep();
- if (!key->local->ops->set_key) {
- ret = -EOPNOTSUPP;
+ if (!key->local->ops->set_key)
goto out_unsupported;
- }
assert_key_lock(key->local);
sta = get_sta_for_key(key);
+ /*
+ * If this is a per-STA GTK, check if it
+ * is supported; if not, return.
+ */
+ if (sta && !(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE) &&
+ !(key->local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK))
+ goto out_unsupported;
+
sdata = key->sdata;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
@@ -85,31 +91,28 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
ret = drv_set_key(key->local, SET_KEY, sdata, sta, &key->conf);
- if (!ret)
+ if (!ret) {
key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
+ return 0;
+ }
- if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP)
+ if (ret != -ENOSPC && ret != -EOPNOTSUPP)
wiphy_err(key->local->hw.wiphy,
"failed to set key (%d, %pM) to hardware (%d)\n",
key->conf.keyidx, sta ? sta->addr : bcast_addr, ret);
-out_unsupported:
- if (ret) {
- switch (key->conf.cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- case WLAN_CIPHER_SUITE_TKIP:
- case WLAN_CIPHER_SUITE_CCMP:
- case WLAN_CIPHER_SUITE_AES_CMAC:
- /* all of these we can do in software */
- ret = 0;
- break;
- default:
- ret = -EINVAL;
- }
+ out_unsupported:
+ switch (key->conf.cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ /* all of these we can do in software */
+ return 0;
+ default:
+ return -EINVAL;
}
-
- return ret;
}
static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
@@ -147,6 +150,26 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
}
+void ieee80211_key_removed(struct ieee80211_key_conf *key_conf)
+{
+ struct ieee80211_key *key;
+
+ key = container_of(key_conf, struct ieee80211_key, conf);
+
+ might_sleep();
+ assert_key_lock(key->local);
+
+ key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
+
+ /*
+ * Flush TX path to avoid attempts to use this key
+ * after this function returns. Until then, drivers
+ * must be prepared to handle the key.
+ */
+ synchronize_rcu();
+}
+EXPORT_SYMBOL_GPL(ieee80211_key_removed);
+
static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
int idx)
{
@@ -202,6 +225,7 @@ void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,
static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
+ bool pairwise,
struct ieee80211_key *old,
struct ieee80211_key *new)
{
@@ -210,8 +234,14 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
if (new)
list_add(&new->list, &sdata->key_list);
- if (sta) {
- rcu_assign_pointer(sta->key, new);
+ if (sta && pairwise) {
+ rcu_assign_pointer(sta->ptk, new);
+ } else if (sta) {
+ if (old)
+ idx = old->conf.keyidx;
+ else
+ idx = new->conf.keyidx;
+ rcu_assign_pointer(sta->gtk[idx], new);
} else {
WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);
@@ -355,6 +385,7 @@ int ieee80211_key_link(struct ieee80211_key *key,
{
struct ieee80211_key *old_key;
int idx, ret;
+ bool pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;
BUG_ON(!sdata);
BUG_ON(!key);
@@ -371,13 +402,6 @@ int ieee80211_key_link(struct ieee80211_key *key,
*/
if (test_sta_flags(sta, WLAN_STA_WME))
key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA;
-
- /*
- * This key is for a specific sta interface,
- * inform the driver that it should try to store
- * this key as pairwise key.
- */
- key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
} else {
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
struct sta_info *ap;
@@ -399,12 +423,14 @@ int ieee80211_key_link(struct ieee80211_key *key,
mutex_lock(&sdata->local->key_mtx);
- if (sta)
- old_key = sta->key;
+ if (sta && pairwise)
+ old_key = sta->ptk;
+ else if (sta)
+ old_key = sta->gtk[idx];
else
old_key = sdata->keys[idx];
- __ieee80211_key_replace(sdata, sta, old_key, key);
+ __ieee80211_key_replace(sdata, sta, pairwise, old_key, key);
__ieee80211_key_destroy(old_key);
ieee80211_debugfs_key_add(key);
@@ -423,7 +449,8 @@ static void __ieee80211_key_free(struct ieee80211_key *key)
*/
if (key->sdata)
__ieee80211_key_replace(key->sdata, key->sta,
- key, NULL);
+ key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
+ key, NULL);
__ieee80211_key_destroy(key);
}
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index cb9a4a6..0db1c0f 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -16,6 +16,9 @@
#include <linux/rcupdate.h>
#include <net/mac80211.h>
+#define NUM_DEFAULT_KEYS 4
+#define NUM_DEFAULT_MGMT_KEYS 2
+
#define WEP_IV_LEN 4
#define WEP_ICV_LEN 4
#define ALG_TKIP_KEY_LEN 32
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index b3e161f..b67221d 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -846,7 +846,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
int keyidx;
int hdrlen;
ieee80211_rx_result result = RX_DROP_UNUSABLE;
- struct ieee80211_key *stakey = NULL;
+ struct ieee80211_key *sta_ptk = NULL;
int mmie_keyidx = -1;
__le16 fc;
@@ -888,15 +888,15 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
rx->key = NULL;
if (rx->sta)
- stakey = rcu_dereference(rx->sta->key);
+ sta_ptk = rcu_dereference(rx->sta->ptk);
fc = hdr->frame_control;
if (!ieee80211_has_protected(fc))
mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb);
- if (!is_multicast_ether_addr(hdr->addr1) && stakey) {
- rx->key = stakey;
+ if (!is_multicast_ether_addr(hdr->addr1) && sta_ptk) {
+ rx->key = sta_ptk;
if ((status->flag & RX_FLAG_DECRYPTED) &&
(status->flag & RX_FLAG_IV_STRIPPED))
return RX_CONTINUE;
@@ -912,7 +912,10 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
if (mmie_keyidx < NUM_DEFAULT_KEYS ||
mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
return RX_DROP_MONITOR; /* unexpected BIP keyidx */
- rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]);
+ if (rx->sta)
+ rx->key = rcu_dereference(rx->sta->gtk[mmie_keyidx]);
+ if (!rx->key)
+ rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]);
} else if (!ieee80211_has_protected(fc)) {
/*
* The frame was not protected, so skip decryption. However, we
@@ -955,17 +958,25 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
keyidx = keyid >> 6;
- rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
+ /* check per-station GTK first, if multicast packet */
+ if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
+ rx->key = rcu_dereference(rx->sta->gtk[keyidx]);
- /*
- * RSNA-protected unicast frames should always be sent with
- * pairwise or station-to-station keys, but for WEP we allow
- * using a key index as well.
- */
- if (rx->key && rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 &&
- rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 &&
- !is_multicast_ether_addr(hdr->addr1))
- rx->key = NULL;
+ /* if not found, try default key */
+ if (!rx->key) {
+ rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
+
+ /*
+ * RSNA-protected unicast frames should always be
+ * sent with pairwise or station-to-station keys,
+ * but for WEP we allow using a key index as well.
+ */
+ if (rx->key &&
+ rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 &&
+ rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 &&
+ !is_multicast_ether_addr(hdr->addr1))
+ rx->key = NULL;
+ }
}
if (rx->key) {
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index aeaf2d6..6d8f897 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -616,7 +616,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
struct ieee80211_sub_if_data *sdata;
struct sk_buff *skb;
unsigned long flags;
- int ret;
+ int ret, i;
might_sleep();
@@ -644,10 +644,10 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
if (ret)
return ret;
- if (sta->key) {
- ieee80211_key_free(local, sta->key);
- WARN_ON(sta->key);
- }
+ for (i = 0; i < NUM_DEFAULT_KEYS; i++)
+ ieee80211_key_free(local, sta->gtk[i]);
+ if (sta->ptk)
+ ieee80211_key_free(local, sta->ptk);
sta->dead = true;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index cf21a2e..9265aca 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -199,7 +199,8 @@ enum plink_state {
* @hnext: hash table linked list pointer
* @local: pointer to the global information
* @sdata: virtual interface this station belongs to
- * @key: peer key negotiated with this station, if any
+ * @ptk: peer key negotiated with this station, if any
+ * @gtk: group keys negotiated with this station, if any
* @rate_ctrl: rate control algorithm reference
* @rate_ctrl_priv: rate control private per-STA pointer
* @last_tx_rate: rate used for last transmit, to report to userspace as
@@ -254,7 +255,8 @@ struct sta_info {
struct sta_info *hnext;
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
- struct ieee80211_key *key;
+ struct ieee80211_key *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
+ struct ieee80211_key *ptk;
struct rate_control_ref *rate_ctrl;
void *rate_ctrl_priv;
spinlock_t lock;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 258fbdb..96c5943 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -532,7 +532,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT))
tx->key = NULL;
- else if (tx->sta && (key = rcu_dereference(tx->sta->key)))
+ else if (tx->sta && (key = rcu_dereference(tx->sta->ptk)))
tx->key = key;
else if (ieee80211_is_mgmt(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr1) &&
OpenPOWER on IntegriCloud