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:
parent
488fc54047
commit
7f179b4689
|
@ -291,7 +291,6 @@ struct wl1271_cmd_ps_params {
|
||||||
|
|
||||||
/* HW encryption keys */
|
/* HW encryption keys */
|
||||||
#define NUM_ACCESS_CATEGORIES_COPY 4
|
#define NUM_ACCESS_CATEGORIES_COPY 4
|
||||||
#define MAX_KEY_SIZE 32
|
|
||||||
|
|
||||||
enum wl1271_cmd_key_action {
|
enum wl1271_cmd_key_action {
|
||||||
KEY_ADD_OR_REPLACE = 1,
|
KEY_ADD_OR_REPLACE = 1,
|
||||||
|
|
|
@ -296,6 +296,7 @@ static struct conf_drv_settings default_conf = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static void __wl1271_op_remove_interface(struct wl1271 *wl);
|
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)
|
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->flags = 0;
|
||||||
wl->vif = NULL;
|
wl->vif = NULL;
|
||||||
wl->filters = 0;
|
wl->filters = 0;
|
||||||
|
wl1271_free_ap_keys(wl);
|
||||||
memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
|
memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
|
||||||
|
|
||||||
for (i = 0; i < NUM_TX_QUEUES; i++)
|
for (i = 0; i < NUM_TX_QUEUES; i++)
|
||||||
|
@ -1627,38 +1629,192 @@ out:
|
||||||
kfree(fp);
|
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,
|
static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
||||||
struct ieee80211_vif *vif,
|
struct ieee80211_vif *vif,
|
||||||
struct ieee80211_sta *sta,
|
struct ieee80211_sta *sta,
|
||||||
struct ieee80211_key_conf *key_conf)
|
struct ieee80211_key_conf *key_conf)
|
||||||
{
|
{
|
||||||
struct wl1271 *wl = hw->priv;
|
struct wl1271 *wl = hw->priv;
|
||||||
const u8 *addr;
|
|
||||||
int ret;
|
int ret;
|
||||||
u32 tx_seq_32 = 0;
|
u32 tx_seq_32 = 0;
|
||||||
u16 tx_seq_16 = 0;
|
u16 tx_seq_16 = 0;
|
||||||
u8 key_type;
|
u8 key_type;
|
||||||
|
|
||||||
static const u8 bcast_addr[ETH_ALEN] =
|
|
||||||
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
||||||
|
|
||||||
wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
|
wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
|
||||||
|
|
||||||
addr = sta ? sta->addr : bcast_addr;
|
wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta);
|
||||||
|
|
||||||
wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd);
|
|
||||||
wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN);
|
|
||||||
wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
|
wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
|
||||||
key_conf->cipher, key_conf->keyidx,
|
key_conf->cipher, key_conf->keyidx,
|
||||||
key_conf->keylen, key_conf->flags);
|
key_conf->keylen, key_conf->flags);
|
||||||
wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
|
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);
|
mutex_lock(&wl->mutex);
|
||||||
|
|
||||||
if (unlikely(wl->state == WL1271_STATE_OFF)) {
|
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) {
|
switch (cmd) {
|
||||||
case SET_KEY:
|
case SET_KEY:
|
||||||
ret = wl1271_cmd_set_sta_key(wl, KEY_ADD_OR_REPLACE,
|
ret = wl1271_set_key(wl, KEY_ADD_OR_REPLACE,
|
||||||
key_conf->keyidx, key_type,
|
key_conf->keyidx, key_type,
|
||||||
key_conf->keylen, key_conf->key,
|
key_conf->keylen, key_conf->key,
|
||||||
addr, tx_seq_32, tx_seq_16);
|
tx_seq_32, tx_seq_16, sta);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
wl1271_error("Could not add or replace key");
|
wl1271_error("Could not add or replace key");
|
||||||
goto out_sleep;
|
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;
|
break;
|
||||||
|
|
||||||
case DISABLE_KEY:
|
case DISABLE_KEY:
|
||||||
/* The wl1271 does not allow to remove unicast keys - they
|
ret = wl1271_set_key(wl, KEY_REMOVE,
|
||||||
will be cleared automatically on next CMD_JOIN. Ignore the
|
key_conf->keyidx, key_type,
|
||||||
request silently, as we dont want the mac80211 to emit
|
key_conf->keylen, key_conf->key,
|
||||||
an error message. */
|
0, 0, sta);
|
||||||
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);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
wl1271_error("Could not remove key");
|
wl1271_error("Could not remove key");
|
||||||
goto out_sleep;
|
goto out_sleep;
|
||||||
|
@ -1753,7 +1894,6 @@ out_sleep:
|
||||||
out_unlock:
|
out_unlock:
|
||||||
mutex_unlock(&wl->mutex);
|
mutex_unlock(&wl->mutex);
|
||||||
|
|
||||||
out:
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2019,6 +2159,10 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
|
||||||
|
|
||||||
set_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
|
set_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
|
||||||
wl1271_debug(DEBUG_AP, "started AP");
|
wl1271_debug(DEBUG_AP, "started AP");
|
||||||
|
|
||||||
|
ret = wl1271_ap_init_hwenc(wl);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
|
if (test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
|
||||||
|
|
|
@ -31,6 +31,23 @@
|
||||||
#include "ps.h"
|
#include "ps.h"
|
||||||
#include "tx.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)
|
static int wl1271_alloc_tx_id(struct wl1271 *wl, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
int id;
|
int id;
|
||||||
|
@ -190,7 +207,6 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
|
||||||
struct ieee80211_tx_info *info;
|
struct ieee80211_tx_info *info;
|
||||||
u32 extra = 0;
|
u32 extra = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
u8 idx;
|
|
||||||
u32 total_len;
|
u32 total_len;
|
||||||
|
|
||||||
if (!skb)
|
if (!skb)
|
||||||
|
@ -203,11 +219,15 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
|
||||||
extra = WL1271_TKIP_IV_SPACE;
|
extra = WL1271_TKIP_IV_SPACE;
|
||||||
|
|
||||||
if (info->control.hw_key) {
|
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? */
|
is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) ||
|
||||||
if (unlikely(wl->default_key != idx)) {
|
(cipher == WLAN_CIPHER_SUITE_WEP104);
|
||||||
ret = wl1271_cmd_set_sta_default_wep_key(wl, idx);
|
|
||||||
|
if (unlikely(is_wep && wl->default_key != idx)) {
|
||||||
|
ret = wl1271_set_default_wep_key(wl, idx);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
wl->default_key = idx;
|
wl->default_key = idx;
|
||||||
|
|
|
@ -253,6 +253,19 @@ struct wl1271_if_operations {
|
||||||
void (*disable_irq)(struct wl1271 *wl);
|
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 wl1271 {
|
||||||
struct platform_device *plat_dev;
|
struct platform_device *plat_dev;
|
||||||
struct ieee80211_hw *hw;
|
struct ieee80211_hw *hw;
|
||||||
|
@ -438,6 +451,9 @@ struct wl1271 {
|
||||||
|
|
||||||
/* map for HLIDs of associated stations - when operating in AP mode */
|
/* map for HLIDs of associated stations - when operating in AP mode */
|
||||||
unsigned long ap_hlid_map[BITS_TO_LONGS(AP_MAX_STATIONS)];
|
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 {
|
struct wl1271_station {
|
||||||
|
|
Loading…
Reference in New Issue