mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 03:27:44 -04:00
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 <[email protected]> Signed-off-by: John W. Linville <[email protected]>
This commit is contained in:
parent
53f73c09d6
commit
e31b82136d
20 changed files with 293 additions and 109 deletions
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) &&
|
||||
|
|
|
|||
Loading…
Reference in a new issue