Commit 7f179b46 authored by Arik Nemtsov's avatar Arik Nemtsov Committed by Luciano Coelho

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: default avatarArik Nemtsov <arik@wizery.com>
Reviewed-by: default avatarLuciano Coelho <coelho@ti.com>
Signed-off-by: default avatarLuciano Coelho <coelho@ti.com>
parent 488fc540
...@@ -291,7 +291,6 @@ struct wl1271_cmd_ps_params { ...@@ -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 = { ...@@ -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) ...@@ -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 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw, ...@@ -1627,38 +1629,192 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
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, ...@@ -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 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ...@@ -1753,7 +1894,6 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
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, ...@@ -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 @@ ...@@ -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, ...@@ -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, ...@@ -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;
is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) ||
(cipher == WLAN_CIPHER_SUITE_WEP104);
/* FIXME: do we have to do this if we're not using WEP? */ if (unlikely(is_wep && wl->default_key != idx)) {
if (unlikely(wl->default_key != idx)) { ret = wl1271_set_default_wep_key(wl, idx);
ret = wl1271_cmd_set_sta_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 { ...@@ -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 { ...@@ -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 {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment