wl12xx: AP mode - encryption support

Encryption key configuration is different for AP/STA modes.

AP encryption keys are recorded when the BSS is not started. On BSS
start they are propagated to the AP (in wl1271_ap_init_hwenc).

Signed-off-by: Arik Nemtsov <arik@wizery.com>
Reviewed-by: Luciano Coelho <coelho@ti.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
This commit is contained in:
Arik Nemtsov 2010-10-16 21:39:06 +02:00 committed by Luciano Coelho
parent 488fc54047
commit 7f179b4689
4 changed files with 223 additions and 44 deletions

View File

@ -291,7 +291,6 @@ struct wl1271_cmd_ps_params {
/* HW encryption keys */
#define NUM_ACCESS_CATEGORIES_COPY 4
#define MAX_KEY_SIZE 32
enum wl1271_cmd_key_action {
KEY_ADD_OR_REPLACE = 1,

View File

@ -296,6 +296,7 @@ static struct conf_drv_settings default_conf = {
};
static void __wl1271_op_remove_interface(struct wl1271 *wl);
static void wl1271_free_ap_keys(struct wl1271 *wl);
static void wl1271_device_release(struct device *dev)
@ -1193,6 +1194,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
wl->flags = 0;
wl->vif = NULL;
wl->filters = 0;
wl1271_free_ap_keys(wl);
memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
for (i = 0; i < NUM_TX_QUEUES; i++)
@ -1627,38 +1629,192 @@ out:
kfree(fp);
}
static int wl1271_record_ap_key(struct wl1271 *wl, u8 id, u8 key_type,
u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
u16 tx_seq_16)
{
struct wl1271_ap_key *ap_key;
int i;
wl1271_debug(DEBUG_CRYPT, "record ap key id %d", (int)id);
if (key_size > MAX_KEY_SIZE)
return -EINVAL;
/*
* Find next free entry in ap_keys. Also check we are not replacing
* an existing key.
*/
for (i = 0; i < MAX_NUM_KEYS; i++) {
if (wl->recorded_ap_keys[i] == NULL)
break;
if (wl->recorded_ap_keys[i]->id == id) {
wl1271_warning("trying to record key replacement");
return -EINVAL;
}
}
if (i == MAX_NUM_KEYS)
return -EBUSY;
ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL);
if (!ap_key)
return -ENOMEM;
ap_key->id = id;
ap_key->key_type = key_type;
ap_key->key_size = key_size;
memcpy(ap_key->key, key, key_size);
ap_key->hlid = hlid;
ap_key->tx_seq_32 = tx_seq_32;
ap_key->tx_seq_16 = tx_seq_16;
wl->recorded_ap_keys[i] = ap_key;
return 0;
}
static void wl1271_free_ap_keys(struct wl1271 *wl)
{
int i;
for (i = 0; i < MAX_NUM_KEYS; i++) {
kfree(wl->recorded_ap_keys[i]);
wl->recorded_ap_keys[i] = NULL;
}
}
static int wl1271_ap_init_hwenc(struct wl1271 *wl)
{
int i, ret = 0;
struct wl1271_ap_key *key;
bool wep_key_added = false;
for (i = 0; i < MAX_NUM_KEYS; i++) {
if (wl->recorded_ap_keys[i] == NULL)
break;
key = wl->recorded_ap_keys[i];
ret = wl1271_cmd_set_ap_key(wl, KEY_ADD_OR_REPLACE,
key->id, key->key_type,
key->key_size, key->key,
key->hlid, key->tx_seq_32,
key->tx_seq_16);
if (ret < 0)
goto out;
if (key->key_type == KEY_WEP)
wep_key_added = true;
}
if (wep_key_added) {
ret = wl1271_cmd_set_ap_default_wep_key(wl, wl->default_key);
if (ret < 0)
goto out;
}
out:
wl1271_free_ap_keys(wl);
return ret;
}
static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
u8 key_size, const u8 *key, u32 tx_seq_32,
u16 tx_seq_16, struct ieee80211_sta *sta)
{
int ret;
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
if (is_ap) {
struct wl1271_station *wl_sta;
u8 hlid;
if (sta) {
wl_sta = (struct wl1271_station *)sta->drv_priv;
hlid = wl_sta->hlid;
} else {
hlid = WL1271_AP_BROADCAST_HLID;
}
if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
/*
* We do not support removing keys after AP shutdown.
* Pretend we do to make mac80211 happy.
*/
if (action != KEY_ADD_OR_REPLACE)
return 0;
ret = wl1271_record_ap_key(wl, id,
key_type, key_size,
key, hlid, tx_seq_32,
tx_seq_16);
} else {
ret = wl1271_cmd_set_ap_key(wl, action,
id, key_type, key_size,
key, hlid, tx_seq_32,
tx_seq_16);
}
if (ret < 0)
return ret;
} else {
const u8 *addr;
static const u8 bcast_addr[ETH_ALEN] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
addr = sta ? sta->addr : bcast_addr;
if (is_zero_ether_addr(addr)) {
/* We dont support TX only encryption */
return -EOPNOTSUPP;
}
/* The wl1271 does not allow to remove unicast keys - they
will be cleared automatically on next CMD_JOIN. Ignore the
request silently, as we dont want the mac80211 to emit
an error message. */
if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr))
return 0;
ret = wl1271_cmd_set_sta_key(wl, action,
id, key_type, key_size,
key, addr, tx_seq_32,
tx_seq_16);
if (ret < 0)
return ret;
/* the default WEP key needs to be configured at least once */
if (key_type == KEY_WEP) {
ret = wl1271_cmd_set_sta_default_wep_key(wl,
wl->default_key);
if (ret < 0)
return ret;
}
}
return 0;
}
static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf)
{
struct wl1271 *wl = hw->priv;
const u8 *addr;
int ret;
u32 tx_seq_32 = 0;
u16 tx_seq_16 = 0;
u8 key_type;
static const u8 bcast_addr[ETH_ALEN] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
addr = sta ? sta->addr : bcast_addr;
wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd);
wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN);
wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta);
wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
key_conf->cipher, key_conf->keyidx,
key_conf->keylen, key_conf->flags);
wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
if (is_zero_ether_addr(addr)) {
/* We dont support TX only encryption */
ret = -EOPNOTSUPP;
goto out;
}
mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF)) {
@ -1705,36 +1861,21 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
switch (cmd) {
case SET_KEY:
ret = wl1271_cmd_set_sta_key(wl, KEY_ADD_OR_REPLACE,
key_conf->keyidx, key_type,
key_conf->keylen, key_conf->key,
addr, tx_seq_32, tx_seq_16);
ret = wl1271_set_key(wl, KEY_ADD_OR_REPLACE,
key_conf->keyidx, key_type,
key_conf->keylen, key_conf->key,
tx_seq_32, tx_seq_16, sta);
if (ret < 0) {
wl1271_error("Could not add or replace key");
goto out_sleep;
}
/* the default WEP key needs to be configured at least once */
if (key_type == KEY_WEP) {
ret = wl1271_cmd_set_sta_default_wep_key(wl,
wl->default_key);
if (ret < 0)
goto out_sleep;
}
break;
case DISABLE_KEY:
/* The wl1271 does not allow to remove unicast keys - they
will be cleared automatically on next CMD_JOIN. Ignore the
request silently, as we dont want the mac80211 to emit
an error message. */
if (!is_broadcast_ether_addr(addr))
break;
ret = wl1271_cmd_set_sta_key(wl, KEY_REMOVE,
key_conf->keyidx, key_type,
key_conf->keylen, key_conf->key,
addr, 0, 0);
ret = wl1271_set_key(wl, KEY_REMOVE,
key_conf->keyidx, key_type,
key_conf->keylen, key_conf->key,
0, 0, sta);
if (ret < 0) {
wl1271_error("Could not remove key");
goto out_sleep;
@ -1753,7 +1894,6 @@ out_sleep:
out_unlock:
mutex_unlock(&wl->mutex);
out:
return ret;
}
@ -2019,6 +2159,10 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
set_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
wl1271_debug(DEBUG_AP, "started AP");
ret = wl1271_ap_init_hwenc(wl);
if (ret < 0)
goto out;
}
} else {
if (test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {

View File

@ -31,6 +31,23 @@
#include "ps.h"
#include "tx.h"
static int wl1271_set_default_wep_key(struct wl1271 *wl, u8 id)
{
int ret;
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
if (is_ap)
ret = wl1271_cmd_set_ap_default_wep_key(wl, id);
else
ret = wl1271_cmd_set_sta_default_wep_key(wl, id);
if (ret < 0)
return ret;
wl1271_debug(DEBUG_CRYPT, "default wep key idx: %d", (int)id);
return 0;
}
static int wl1271_alloc_tx_id(struct wl1271 *wl, struct sk_buff *skb)
{
int id;
@ -190,7 +207,6 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
struct ieee80211_tx_info *info;
u32 extra = 0;
int ret = 0;
u8 idx;
u32 total_len;
if (!skb)
@ -203,11 +219,15 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
extra = WL1271_TKIP_IV_SPACE;
if (info->control.hw_key) {
idx = info->control.hw_key->hw_key_idx;
bool is_wep;
u8 idx = info->control.hw_key->hw_key_idx;
u32 cipher = info->control.hw_key->cipher;
/* FIXME: do we have to do this if we're not using WEP? */
if (unlikely(wl->default_key != idx)) {
ret = wl1271_cmd_set_sta_default_wep_key(wl, idx);
is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) ||
(cipher == WLAN_CIPHER_SUITE_WEP104);
if (unlikely(is_wep && wl->default_key != idx)) {
ret = wl1271_set_default_wep_key(wl, idx);
if (ret < 0)
return ret;
wl->default_key = idx;

View File

@ -253,6 +253,19 @@ struct wl1271_if_operations {
void (*disable_irq)(struct wl1271 *wl);
};
#define MAX_NUM_KEYS 14
#define MAX_KEY_SIZE 32
struct wl1271_ap_key {
u8 id;
u8 key_type;
u8 key_size;
u8 key[MAX_KEY_SIZE];
u8 hlid;
u32 tx_seq_32;
u16 tx_seq_16;
};
struct wl1271 {
struct platform_device *plat_dev;
struct ieee80211_hw *hw;
@ -438,6 +451,9 @@ struct wl1271 {
/* map for HLIDs of associated stations - when operating in AP mode */
unsigned long ap_hlid_map[BITS_TO_LONGS(AP_MAX_STATIONS)];
/* recoreded keys for AP-mode - set here before AP startup */
struct wl1271_ap_key *recorded_ap_keys[MAX_NUM_KEYS];
};
struct wl1271_station {