Commit 3ffc2a90 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

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: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJuuso Oikarinen <juuso.oikarinen@nokia.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 7d64b7cc
...@@ -119,9 +119,10 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
} }
......
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