mac80211: allow vendor specific cipher suites
Allow drivers to specify their own set of cipher suites to advertise vendor-specific ciphers. The driver is then required to implement hardware crypto offload for it. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
7d64b7cc1f
commit
3ffc2a905b
|
@ -119,9 +119,10 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ieee80211_key_link(key, sdata, sta);
|
err = ieee80211_key_link(key, sdata, sta);
|
||||||
|
if (err)
|
||||||
|
ieee80211_key_free(sdata->local, key);
|
||||||
|
|
||||||
err = 0;
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
mutex_unlock(&sdata->local->sta_mtx);
|
mutex_unlock(&sdata->local->sta_mtx);
|
||||||
|
|
||||||
|
|
|
@ -662,6 +662,8 @@ struct ieee80211_local {
|
||||||
int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll;
|
int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll;
|
||||||
unsigned int filter_flags; /* FIF_* */
|
unsigned int filter_flags; /* FIF_* */
|
||||||
|
|
||||||
|
bool wiphy_ciphers_allocated;
|
||||||
|
|
||||||
/* protects the aggregated multicast list and filter calls */
|
/* protects the aggregated multicast list and filter calls */
|
||||||
spinlock_t filter_lock;
|
spinlock_t filter_lock;
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ static struct ieee80211_sta *get_sta_for_key(struct ieee80211_key *key)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
|
static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
|
||||||
{
|
{
|
||||||
struct ieee80211_sub_if_data *sdata;
|
struct ieee80211_sub_if_data *sdata;
|
||||||
struct ieee80211_sta *sta;
|
struct ieee80211_sta *sta;
|
||||||
|
@ -68,8 +68,10 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
|
||||||
|
|
||||||
might_sleep();
|
might_sleep();
|
||||||
|
|
||||||
if (!key->local->ops->set_key)
|
if (!key->local->ops->set_key) {
|
||||||
return;
|
ret = -EOPNOTSUPP;
|
||||||
|
goto out_unsupported;
|
||||||
|
}
|
||||||
|
|
||||||
assert_key_lock(key->local);
|
assert_key_lock(key->local);
|
||||||
|
|
||||||
|
@ -90,6 +92,24 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
|
||||||
wiphy_err(key->local->hw.wiphy,
|
wiphy_err(key->local->hw.wiphy,
|
||||||
"failed to set key (%d, %pM) to hardware (%d)\n",
|
"failed to set key (%d, %pM) to hardware (%d)\n",
|
||||||
key->conf.keyidx, sta ? sta->addr : bcast_addr, ret);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
|
static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
|
||||||
|
@ -329,12 +349,12 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)
|
||||||
kfree(key);
|
kfree(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ieee80211_key_link(struct ieee80211_key *key,
|
int ieee80211_key_link(struct ieee80211_key *key,
|
||||||
struct ieee80211_sub_if_data *sdata,
|
struct ieee80211_sub_if_data *sdata,
|
||||||
struct sta_info *sta)
|
struct sta_info *sta)
|
||||||
{
|
{
|
||||||
struct ieee80211_key *old_key;
|
struct ieee80211_key *old_key;
|
||||||
int idx;
|
int idx, ret;
|
||||||
|
|
||||||
BUG_ON(!sdata);
|
BUG_ON(!sdata);
|
||||||
BUG_ON(!key);
|
BUG_ON(!key);
|
||||||
|
@ -389,9 +409,11 @@ void ieee80211_key_link(struct ieee80211_key *key,
|
||||||
|
|
||||||
ieee80211_debugfs_key_add(key);
|
ieee80211_debugfs_key_add(key);
|
||||||
|
|
||||||
ieee80211_key_enable_hw_accel(key);
|
ret = ieee80211_key_enable_hw_accel(key);
|
||||||
|
|
||||||
mutex_unlock(&sdata->local->key_mtx);
|
mutex_unlock(&sdata->local->key_mtx);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __ieee80211_key_free(struct ieee80211_key *key)
|
static void __ieee80211_key_free(struct ieee80211_key *key)
|
||||||
|
|
|
@ -130,7 +130,7 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
|
||||||
* Insert a key into data structures (sdata, sta if necessary)
|
* Insert a key into data structures (sdata, sta if necessary)
|
||||||
* to make it used, free old key.
|
* to make it used, free old key.
|
||||||
*/
|
*/
|
||||||
void ieee80211_key_link(struct ieee80211_key *key,
|
int __must_check ieee80211_key_link(struct ieee80211_key *key,
|
||||||
struct ieee80211_sub_if_data *sdata,
|
struct ieee80211_sub_if_data *sdata,
|
||||||
struct sta_info *sta);
|
struct sta_info *sta);
|
||||||
void ieee80211_key_free(struct ieee80211_local *local,
|
void ieee80211_key_free(struct ieee80211_local *local,
|
||||||
|
|
|
@ -662,13 +662,40 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||||
if (local->hw.wiphy->max_scan_ie_len)
|
if (local->hw.wiphy->max_scan_ie_len)
|
||||||
local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len;
|
local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len;
|
||||||
|
|
||||||
|
/* Set up cipher suites unless driver already did */
|
||||||
|
if (!local->hw.wiphy->cipher_suites) {
|
||||||
local->hw.wiphy->cipher_suites = cipher_suites;
|
local->hw.wiphy->cipher_suites = cipher_suites;
|
||||||
local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
|
local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
|
||||||
if (!(local->hw.flags & IEEE80211_HW_MFP_CAPABLE))
|
if (!(local->hw.flags & IEEE80211_HW_MFP_CAPABLE))
|
||||||
local->hw.wiphy->n_cipher_suites--;
|
local->hw.wiphy->n_cipher_suites--;
|
||||||
|
}
|
||||||
if (IS_ERR(local->wep_tx_tfm) || IS_ERR(local->wep_rx_tfm)) {
|
if (IS_ERR(local->wep_tx_tfm) || IS_ERR(local->wep_rx_tfm)) {
|
||||||
|
if (local->hw.wiphy->cipher_suites == cipher_suites) {
|
||||||
local->hw.wiphy->cipher_suites += 2;
|
local->hw.wiphy->cipher_suites += 2;
|
||||||
local->hw.wiphy->n_cipher_suites -= 2;
|
local->hw.wiphy->n_cipher_suites -= 2;
|
||||||
|
} else {
|
||||||
|
u32 *suites;
|
||||||
|
int r, w = 0;
|
||||||
|
|
||||||
|
/* Filter out WEP */
|
||||||
|
|
||||||
|
suites = kmemdup(
|
||||||
|
local->hw.wiphy->cipher_suites,
|
||||||
|
sizeof(u32) * local->hw.wiphy->n_cipher_suites,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!suites)
|
||||||
|
return -ENOMEM;
|
||||||
|
for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {
|
||||||
|
u32 suite = local->hw.wiphy->cipher_suites[r];
|
||||||
|
if (suite == WLAN_CIPHER_SUITE_WEP40 ||
|
||||||
|
suite == WLAN_CIPHER_SUITE_WEP104)
|
||||||
|
continue;
|
||||||
|
suites[w++] = suite;
|
||||||
|
}
|
||||||
|
local->hw.wiphy->cipher_suites = suites;
|
||||||
|
local->hw.wiphy->n_cipher_suites = w;
|
||||||
|
local->wiphy_ciphers_allocated = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = wiphy_register(local->hw.wiphy);
|
result = wiphy_register(local->hw.wiphy);
|
||||||
|
@ -783,6 +810,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||||
fail_workqueue:
|
fail_workqueue:
|
||||||
wiphy_unregister(local->hw.wiphy);
|
wiphy_unregister(local->hw.wiphy);
|
||||||
fail_wiphy_register:
|
fail_wiphy_register:
|
||||||
|
if (local->wiphy_ciphers_allocated)
|
||||||
|
kfree(local->hw.wiphy->cipher_suites);
|
||||||
kfree(local->int_scan_req);
|
kfree(local->int_scan_req);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -840,6 +869,9 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)
|
||||||
mutex_destroy(&local->iflist_mtx);
|
mutex_destroy(&local->iflist_mtx);
|
||||||
mutex_destroy(&local->mtx);
|
mutex_destroy(&local->mtx);
|
||||||
|
|
||||||
|
if (local->wiphy_ciphers_allocated)
|
||||||
|
kfree(local->hw.wiphy->cipher_suites);
|
||||||
|
|
||||||
wiphy_free(local->hw.wiphy);
|
wiphy_free(local->hw.wiphy);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ieee80211_free_hw);
|
EXPORT_SYMBOL(ieee80211_free_hw);
|
||||||
|
|
|
@ -1000,6 +1000,12 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
|
||||||
case WLAN_CIPHER_SUITE_AES_CMAC:
|
case WLAN_CIPHER_SUITE_AES_CMAC:
|
||||||
result = ieee80211_crypto_aes_cmac_decrypt(rx);
|
result = ieee80211_crypto_aes_cmac_decrypt(rx);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
* We can reach here only with HW-only algorithms
|
||||||
|
* but why didn't it decrypt the frame?!
|
||||||
|
*/
|
||||||
|
return RX_DROP_UNUSABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* either the frame has been decrypted or will be dropped */
|
/* either the frame has been decrypted or will be dropped */
|
||||||
|
|
|
@ -947,6 +947,8 @@ ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
|
||||||
static ieee80211_tx_result debug_noinline
|
static ieee80211_tx_result debug_noinline
|
||||||
ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
|
ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
|
||||||
{
|
{
|
||||||
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
|
||||||
|
|
||||||
if (!tx->key)
|
if (!tx->key)
|
||||||
return TX_CONTINUE;
|
return TX_CONTINUE;
|
||||||
|
|
||||||
|
@ -960,10 +962,16 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
|
||||||
return ieee80211_crypto_ccmp_encrypt(tx);
|
return ieee80211_crypto_ccmp_encrypt(tx);
|
||||||
case WLAN_CIPHER_SUITE_AES_CMAC:
|
case WLAN_CIPHER_SUITE_AES_CMAC:
|
||||||
return ieee80211_crypto_aes_cmac_encrypt(tx);
|
return ieee80211_crypto_aes_cmac_encrypt(tx);
|
||||||
|
default:
|
||||||
|
/* handle hw-only algorithm */
|
||||||
|
if (info->control.hw_key) {
|
||||||
|
ieee80211_tx_set_protected(tx);
|
||||||
|
return TX_CONTINUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* not reached */
|
|
||||||
WARN_ON(1);
|
|
||||||
return TX_DROP;
|
return TX_DROP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue