mirror of
https://git.freebsd.org/src.git
synced 2026-01-16 23:02:24 +00:00
LinuxKPI: 802.11: hardware crypto offload improvements
Implement mac80211 functions ieee80211_iter_keys[_rcu](), and
ieee80211_get_key_rx_seq() (*).
Implement an internal function lkpi_sta_del_keys() to delete keys
when we are leaving RUN (AUTHORIZED) state.
Improve and make work (*) the net80211 vap function pointers
(*iv_key_set)(), (*iv_key_delete)() implementations.
Correct the logic in lkpi_80211_txq_tx_one() and factor most crypto
related bits out into lkpi_hw_crypto_prepare() (*).
(*) We are currently supporting CCMP only. I would hope we will
not have to do full WEP/TKIP support anymore given both are deprecated.
The entire logic remains behind the LKPI_80211_HW_CRYPTO pre-processor
condition and with this commit in addition behind a tunable.
The code is compiled in by default now but the tunable remains off until
wider testing has shown no problems.
I have seen one net80211 triggered panic on shutdown related to deleting
keys which I cannot reproduce anymore and could have been fixed by
9763fec11b83; otherwise we will have to investigate as it shows again.
The dedicated hw crypto tracing option can help in that case with debug
kernels.
Sponsored by: The FreeBSD Foundation
MFC after: 3 days
Fixes: b35f6cd066
This commit is contained in:
parent
7e26f1c210
commit
11db70b605
4 changed files with 491 additions and 90 deletions
|
|
@ -555,6 +555,9 @@ enum ieee802111_key_flag {
|
|||
};
|
||||
|
||||
struct ieee80211_key_conf {
|
||||
#if defined(__FreeBSD__)
|
||||
const struct ieee80211_key *_k; /* backpointer to net80211 */
|
||||
#endif
|
||||
atomic64_t tx_pn;
|
||||
uint32_t cipher;
|
||||
uint8_t icv_len; /* __unused nowadays? */
|
||||
|
|
@ -1154,7 +1157,7 @@ void linuxkpi_ieee80211_iterate_keys(struct ieee80211_hw *,
|
|||
struct ieee80211_vif *,
|
||||
void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *,
|
||||
struct ieee80211_sta *, struct ieee80211_key_conf *, void *),
|
||||
void *);
|
||||
void *, bool);
|
||||
void linuxkpi_ieee80211_iterate_chan_contexts(struct ieee80211_hw *,
|
||||
void(*iterfunc)(struct ieee80211_hw *,
|
||||
struct ieee80211_chanctx_conf *, void *),
|
||||
|
|
@ -1520,25 +1523,22 @@ ieee80211_iterate_interfaces(struct ieee80211_hw *hw,
|
|||
linuxkpi_ieee80211_iterate_interfaces(hw, flags, iterfunc, arg);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
static inline void
|
||||
ieee80211_iter_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *,
|
||||
struct ieee80211_sta *, struct ieee80211_key_conf *, void *),
|
||||
void *arg)
|
||||
{
|
||||
|
||||
linuxkpi_ieee80211_iterate_keys(hw, vif, iterfunc, arg);
|
||||
linuxkpi_ieee80211_iterate_keys(hw, vif, iterfunc, arg, false);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
static inline void
|
||||
ieee80211_iter_keys_rcu(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *,
|
||||
struct ieee80211_sta *, struct ieee80211_key_conf *, void *),
|
||||
void *arg)
|
||||
{
|
||||
|
||||
IMPROVE(); /* "rcu" */
|
||||
linuxkpi_ieee80211_iterate_keys(hw, vif, iterfunc, arg);
|
||||
linuxkpi_ieee80211_iterate_keys(hw, vif, iterfunc, arg, true);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
|
|
@ -2094,33 +2094,6 @@ ieee80211_sta_set_buffered(struct ieee80211_sta *sta, uint8_t tid, bool t)
|
|||
TODO();
|
||||
}
|
||||
|
||||
static __inline void
|
||||
ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, uint8_t tid,
|
||||
struct ieee80211_key_seq *seq)
|
||||
{
|
||||
|
||||
KASSERT(keyconf != NULL && seq != NULL, ("%s: keyconf %p seq %p\n",
|
||||
__func__, keyconf, seq));
|
||||
|
||||
TODO();
|
||||
switch (keyconf->cipher) {
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
case WLAN_CIPHER_SUITE_CCMP_256:
|
||||
memset(seq->ccmp.pn, 0xfa, sizeof(seq->ccmp.pn)); /* XXX TODO */
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_AES_CMAC:
|
||||
memset(seq->aes_cmac.pn, 0xfa, sizeof(seq->aes_cmac.pn)); /* XXX TODO */
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
seq->tkip.iv32 = 0xfa; /* XXX TODO */
|
||||
seq->tkip.iv16 = 0xfa; /* XXX TODO */
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s: unsupported cipher suite %d\n", __func__, keyconf->cipher);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static __inline void
|
||||
ieee80211_sched_scan_results(struct ieee80211_hw *hw)
|
||||
{
|
||||
|
|
@ -2466,6 +2439,44 @@ ieee80211_remove_key(struct ieee80211_key_conf *key)
|
|||
TODO();
|
||||
}
|
||||
|
||||
static inline void
|
||||
ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, int8_t tid,
|
||||
struct ieee80211_key_seq *seq)
|
||||
{
|
||||
const struct ieee80211_key *k;
|
||||
const uint8_t *p;
|
||||
|
||||
KASSERT(keyconf != NULL && seq != NULL, ("%s: keyconf %p seq %p\n",
|
||||
__func__, keyconf, seq));
|
||||
KASSERT(tid <= IEEE80211_NUM_TIDS, ("%s: tid out of bounds %d\n",
|
||||
__func__, tid));
|
||||
k = keyconf->_k;
|
||||
KASSERT(k != NULL, ("%s: keyconf %p ieee80211_key is NULL\n", __func__, keyconf));
|
||||
|
||||
switch (keyconf->cipher) {
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
case WLAN_CIPHER_SUITE_CCMP_256:
|
||||
if (tid < 0)
|
||||
p = (const uint8_t *)&k->wk_keyrsc[IEEE80211_NUM_TIDS]; /* IEEE80211_NONQOS_TID */
|
||||
else
|
||||
p = (const uint8_t *)&k->wk_keyrsc[tid];
|
||||
memcpy(seq->ccmp.pn, p, sizeof(seq->ccmp.pn));
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_AES_CMAC:
|
||||
TODO();
|
||||
memset(seq->aes_cmac.pn, 0xfa, sizeof(seq->aes_cmac.pn)); /* XXX TODO */
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
TODO();
|
||||
seq->tkip.iv32 = 0xfa; /* XXX TODO */
|
||||
seq->tkip.iv16 = 0xfa; /* XXX TODO */
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s: unsupported cipher suite %d\n", __func__, keyconf->cipher);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static __inline void
|
||||
ieee80211_set_key_rx_seq(struct ieee80211_key_conf *key, int tid,
|
||||
struct ieee80211_key_seq *seq)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,12 @@
|
|||
* We call the internal versions lxxx (e.g., hw -> lhw, sta -> lsta).
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* - lots :)
|
||||
* - HW_CRYPTO: we need a "keystore" and an ordered list for suspend/resume.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/kernel.h>
|
||||
|
|
@ -71,12 +77,16 @@
|
|||
#include "linux_80211.h"
|
||||
|
||||
#define LKPI_80211_WME
|
||||
/* #define LKPI_80211_HW_CRYPTO */
|
||||
/* #define LKPI_80211_VHT */
|
||||
#define LKPI_80211_HW_CRYPTO
|
||||
/* #define LKPI_80211_HT */
|
||||
/* #define LKPI_80211_VHT */
|
||||
|
||||
#if defined(LKPI_80211_VHT) && !defined(LKPI_80211_HT)
|
||||
#define LKPI_80211_HT
|
||||
#endif
|
||||
#if defined(LKPI_80211_HT) && !defined(LKPI_80211_HW_CRYPTO)
|
||||
#define LKPI_80211_HW_CRYPTO
|
||||
#endif
|
||||
|
||||
static MALLOC_DEFINE(M_LKPI80211, "lkpi80211", "LinuxKPI 80211 compat");
|
||||
|
||||
|
|
@ -92,6 +102,12 @@ SYSCTL_DECL(_compat_linuxkpi);
|
|||
SYSCTL_NODE(_compat_linuxkpi, OID_AUTO, 80211, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
|
||||
"LinuxKPI 802.11 compatibility layer");
|
||||
|
||||
#if defined(LKPI_80211_HW_CRYPTO)
|
||||
static bool lkpi_hwcrypto = false;
|
||||
SYSCTL_BOOL(_compat_linuxkpi_80211, OID_AUTO, hw_crypto, CTLFLAG_RDTUN,
|
||||
&lkpi_hwcrypto, 0, "Enable LinuxKPI 802.11 hardware crypto offload");
|
||||
#endif
|
||||
|
||||
/* Keep public for as long as header files are using it too. */
|
||||
int linuxkpi_debug_80211;
|
||||
|
||||
|
|
@ -438,7 +454,7 @@ lkpi_lsta_dump(struct lkpi_sta *lsta, struct ieee80211_node *ni,
|
|||
ieee80211_dump_node(NULL, ni);
|
||||
printf("\ttxq_task txq len %d mtx\n", mbufq_len(&lsta->txq));
|
||||
printf("\tkc %p state %d added_to_drv %d in_mgd %d\n",
|
||||
lsta->kc, lsta->state, lsta->added_to_drv, lsta->in_mgd);
|
||||
&lsta->kc[0], lsta->state, lsta->added_to_drv, lsta->in_mgd);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -904,20 +920,71 @@ linuxkpi_ieee80211_get_channel(struct wiphy *wiphy, uint32_t freq)
|
|||
|
||||
#ifdef LKPI_80211_HW_CRYPTO
|
||||
static int
|
||||
_lkpi_iv_key_set_delete(struct ieee80211vap *vap, const struct ieee80211_key *k,
|
||||
enum set_key_cmd cmd)
|
||||
lkpi_sta_del_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct lkpi_sta *lsta)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (!lkpi_hwcrypto)
|
||||
return (0);
|
||||
|
||||
lockdep_assert_wiphy(hw->wiphy);
|
||||
ieee80211_ref_node(lsta->ni);
|
||||
|
||||
error = 0;
|
||||
for (ieee80211_keyix keyix = 0; keyix < nitems(lsta->kc); keyix++) {
|
||||
struct ieee80211_key_conf *kc;
|
||||
int err;
|
||||
|
||||
if (lsta->kc[keyix] == NULL)
|
||||
continue;
|
||||
kc = lsta->kc[keyix];
|
||||
|
||||
err = lkpi_80211_mo_set_key(hw, DISABLE_KEY, vif,
|
||||
LSTA_TO_STA(lsta), kc);
|
||||
if (err != 0) {
|
||||
ic_printf(lsta->ni->ni_ic, "%s: set_key cmd %d(%s) for "
|
||||
"sta %6D failed: %d\n", __func__, DISABLE_KEY,
|
||||
"DISABLE", lsta->sta.addr, ":", err);
|
||||
error++;
|
||||
|
||||
/*
|
||||
* If we free the key here we will never be able to get it
|
||||
* removed from the driver/fw which will likely make us
|
||||
* crash (firmware).
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
#ifdef LINUXKPI_DEBUG_80211
|
||||
if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
|
||||
ic_printf(lsta->ni->ni_ic, "%s: set_key cmd %d(%s) for "
|
||||
"sta %6D succeeded: keyidx %u hw_key_idx %u flags %#10x\n",
|
||||
__func__, DISABLE_KEY, "DISABLE", lsta->sta.addr, ":",
|
||||
kc->keyidx, kc->hw_key_idx, kc->flags);
|
||||
#endif
|
||||
|
||||
lsta->kc[keyix] = NULL;
|
||||
free(kc, M_LKPI80211);
|
||||
}
|
||||
ieee80211_free_node(lsta->ni);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
_lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
|
||||
{
|
||||
struct ieee80211com *ic;
|
||||
struct lkpi_hw *lhw;
|
||||
struct ieee80211_hw *hw;
|
||||
struct lkpi_vif *lvif;
|
||||
struct lkpi_sta *lsta;
|
||||
struct ieee80211_vif *vif;
|
||||
struct ieee80211_sta *sta;
|
||||
struct ieee80211_node *ni;
|
||||
struct ieee80211_key_conf *kc;
|
||||
struct ieee80211_node_table *nt;
|
||||
int error;
|
||||
|
||||
/* XXX TODO Check (k->wk_flags & IEEE80211_KEY_SWENCRYPT) and don't upload to driver/hw? */
|
||||
bool islocked;
|
||||
|
||||
ic = vap->iv_ic;
|
||||
lhw = ic->ic_softc;
|
||||
|
|
@ -925,10 +992,152 @@ _lkpi_iv_key_set_delete(struct ieee80211vap *vap, const struct ieee80211_key *k,
|
|||
lvif = VAP_TO_LVIF(vap);
|
||||
vif = LVIF_TO_VIF(lvif);
|
||||
|
||||
memset(&kc, 0, sizeof(kc));
|
||||
kc = malloc(sizeof(*kc) + k->wk_keylen, M_LKPI80211, M_WAITOK | M_ZERO);
|
||||
kc->cipher = lkpi_net80211_to_l80211_cipher_suite(
|
||||
if (vap->iv_bss == NULL) {
|
||||
ic_printf(ic, "%s: iv_bss %p for vap %p is NULL\n",
|
||||
__func__, vap->iv_bss, vap);
|
||||
return (0);
|
||||
}
|
||||
ni = ieee80211_ref_node(vap->iv_bss);
|
||||
lsta = ni->ni_drv_data;
|
||||
if (lsta == NULL) {
|
||||
ic_printf(ic, "%s: ni %p (%6D) with lsta NULL\n",
|
||||
__func__, ni, ni->ni_bssid, ":");
|
||||
ieee80211_free_node(ni);
|
||||
return (0);
|
||||
}
|
||||
sta = LSTA_TO_STA(lsta);
|
||||
|
||||
if (lsta->kc[k->wk_keyix] == NULL) {
|
||||
#ifdef LINUXKPI_DEBUG_80211
|
||||
if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
|
||||
ic_printf(ic, "%s: sta %6D and no key information, "
|
||||
"keyidx %u wk_macaddr %6D; returning success\n",
|
||||
__func__, sta->addr, ":",
|
||||
k->wk_keyix, k->wk_macaddr, ":");
|
||||
#endif
|
||||
ieee80211_free_node(ni);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* This is inconsistent net80211 locking to be fixed one day. */
|
||||
nt = &ic->ic_sta;
|
||||
islocked = IEEE80211_NODE_IS_LOCKED(nt);
|
||||
if (islocked)
|
||||
IEEE80211_NODE_UNLOCK(nt);
|
||||
|
||||
wiphy_lock(hw->wiphy);
|
||||
kc = lsta->kc[k->wk_keyix];
|
||||
/* Re-check under lock. */
|
||||
if (kc == NULL) {
|
||||
#ifdef LINUXKPI_DEBUG_80211
|
||||
if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
|
||||
ic_printf(ic, "%s: sta %6D and key information vanished, "
|
||||
"returning success\n", __func__, sta->addr, ":");
|
||||
#endif
|
||||
error = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = lkpi_80211_mo_set_key(hw, DISABLE_KEY, vif, sta, kc);
|
||||
if (error != 0) {
|
||||
ic_printf(ic, "%s: set_key cmd %d(%s) for sta %6D failed: %d\n",
|
||||
__func__, DISABLE_KEY, "DISABLE", sta->addr, ":", error);
|
||||
error = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef LINUXKPI_DEBUG_80211
|
||||
if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
|
||||
ic_printf(ic, "%s: set_key cmd %d(%s) for sta %6D succeeded: "
|
||||
"keyidx %u hw_key_idx %u flags %#10x\n", __func__,
|
||||
DISABLE_KEY, "DISABLE", sta->addr, ":",
|
||||
kc->keyidx, kc->hw_key_idx, kc->flags);
|
||||
#endif
|
||||
lsta->kc[k->wk_keyix] = NULL;
|
||||
free(kc, M_LKPI80211);
|
||||
error = 1;
|
||||
out:
|
||||
wiphy_unlock(hw->wiphy);
|
||||
if (islocked)
|
||||
IEEE80211_NODE_LOCK(nt);
|
||||
ieee80211_free_node(ni);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
|
||||
{
|
||||
|
||||
/* XXX-BZ one day we should replace this iterating over VIFs, or node list? */
|
||||
/* See also lkpi_sta_del_keys() these days. */
|
||||
return (_lkpi_iv_key_delete(vap, k));
|
||||
}
|
||||
|
||||
static int
|
||||
_lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
|
||||
{
|
||||
struct ieee80211com *ic;
|
||||
struct lkpi_hw *lhw;
|
||||
struct ieee80211_hw *hw;
|
||||
struct lkpi_vif *lvif;
|
||||
struct lkpi_sta *lsta;
|
||||
struct ieee80211_vif *vif;
|
||||
struct ieee80211_sta *sta;
|
||||
struct ieee80211_node *ni;
|
||||
struct ieee80211_key_conf *kc;
|
||||
uint32_t lcipher;
|
||||
int error;
|
||||
|
||||
ic = vap->iv_ic;
|
||||
lhw = ic->ic_softc;
|
||||
hw = LHW_TO_HW(lhw);
|
||||
lvif = VAP_TO_LVIF(vap);
|
||||
vif = LVIF_TO_VIF(lvif);
|
||||
|
||||
if (vap->iv_bss == NULL) {
|
||||
ic_printf(ic, "%s: iv_bss %p for vap %p is NULL\n",
|
||||
__func__, vap->iv_bss, vap);
|
||||
return (0);
|
||||
}
|
||||
ni = ieee80211_ref_node(vap->iv_bss);
|
||||
lsta = ni->ni_drv_data;
|
||||
if (lsta == NULL) {
|
||||
ic_printf(ic, "%s: ni %p (%6D) with lsta NULL\n",
|
||||
__func__, ni, ni->ni_bssid, ":");
|
||||
ieee80211_free_node(ni);
|
||||
return (0);
|
||||
}
|
||||
sta = LSTA_TO_STA(lsta);
|
||||
|
||||
wiphy_lock(hw->wiphy);
|
||||
if (lsta->kc[k->wk_keyix] != NULL) {
|
||||
IMPROVE("Still in firmware? Del first. Can we assert this cannot happen?");
|
||||
ic_printf(ic, "%s: sta %6D found with key information\n",
|
||||
__func__, sta->addr, ":");
|
||||
kc = lsta->kc[k->wk_keyix];
|
||||
lsta->kc[k->wk_keyix] = NULL;
|
||||
free(kc, M_LKPI80211);
|
||||
kc = NULL; /* safeguard */
|
||||
}
|
||||
|
||||
lcipher = lkpi_net80211_to_l80211_cipher_suite(
|
||||
k->wk_cipher->ic_cipher, k->wk_keylen);
|
||||
switch (lcipher) {
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
default:
|
||||
ic_printf(ic, "%s: CIPHER SUITE %#x not supported\n", __func__, lcipher);
|
||||
IMPROVE();
|
||||
wiphy_unlock(hw->wiphy);
|
||||
ieee80211_free_node(ni);
|
||||
return (0);
|
||||
}
|
||||
|
||||
kc = malloc(sizeof(*kc) + k->wk_keylen, M_LKPI80211, M_WAITOK | M_ZERO);
|
||||
kc->_k = k; /* Save the pointer to net80211. */
|
||||
atomic64_set(&kc->tx_pn, k->wk_keytsc);
|
||||
kc->cipher = lcipher;
|
||||
kc->keyidx = k->wk_keyix;
|
||||
#if 0
|
||||
kc->hw_key_idx = /* set by hw and needs to be passed for TX */;
|
||||
|
|
@ -937,6 +1146,11 @@ _lkpi_iv_key_set_delete(struct ieee80211vap *vap, const struct ieee80211_key *k,
|
|||
kc->keylen = k->wk_keylen;
|
||||
memcpy(kc->key, k->wk_key, k->wk_keylen);
|
||||
|
||||
if (k->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
|
||||
kc->flags |= IEEE80211_KEY_FLAG_PAIRWISE;
|
||||
if (k->wk_flags & IEEE80211_KEY_GROUP)
|
||||
kc->flags &= ~IEEE80211_KEY_FLAG_PAIRWISE;
|
||||
|
||||
switch (kc->cipher) {
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
kc->iv_len = k->wk_cipher->ic_header;
|
||||
|
|
@ -944,44 +1158,41 @@ _lkpi_iv_key_set_delete(struct ieee80211vap *vap, const struct ieee80211_key *k,
|
|||
break;
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
default:
|
||||
/* currently UNREACH */
|
||||
IMPROVE();
|
||||
return (0);
|
||||
break;
|
||||
};
|
||||
lsta->kc[k->wk_keyix] = kc;
|
||||
|
||||
ni = vap->iv_bss;
|
||||
sta = ieee80211_find_sta(vif, ni->ni_bssid);
|
||||
if (sta != NULL) {
|
||||
struct lkpi_sta *lsta;
|
||||
|
||||
lsta = STA_TO_LSTA(sta);
|
||||
lsta->kc = kc;
|
||||
}
|
||||
|
||||
error = lkpi_80211_mo_set_key(hw, cmd, vif, sta, kc);
|
||||
error = lkpi_80211_mo_set_key(hw, SET_KEY, vif, sta, kc);
|
||||
if (error != 0) {
|
||||
/* XXX-BZ leaking kc currently */
|
||||
ic_printf(ic, "%s: set_key failed: %d\n", __func__, error);
|
||||
ic_printf(ic, "%s: set_key cmd %d(%s) for sta %6D failed: %d\n",
|
||||
__func__, SET_KEY, "SET", sta->addr, ":", error);
|
||||
lsta->kc[k->wk_keyix] = NULL;
|
||||
free(kc, M_LKPI80211);
|
||||
wiphy_unlock(hw->wiphy);
|
||||
ieee80211_free_node(ni);
|
||||
return (0);
|
||||
} else {
|
||||
ic_printf(ic, "%s: set_key succeeded: keyidx %u hw_key_idx %u "
|
||||
"flags %#10x\n", __func__,
|
||||
kc->keyidx, kc->hw_key_idx, kc->flags);
|
||||
return (1);
|
||||
}
|
||||
|
||||
#ifdef LINUXKPI_DEBUG_80211
|
||||
if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
|
||||
ic_printf(ic, "%s: set_key cmd %d(%s) for sta %6D succeeded: "
|
||||
"kc %p keyidx %u hw_key_idx %u flags %#010x\n", __func__,
|
||||
SET_KEY, "SET", sta->addr, ":",
|
||||
kc, kc->keyidx, kc->hw_key_idx, kc->flags);
|
||||
#endif
|
||||
|
||||
wiphy_unlock(hw->wiphy);
|
||||
ieee80211_free_node(ni);
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
|
||||
{
|
||||
|
||||
/* XXX-BZ one day we should replace this iterating over VIFs, or node list? */
|
||||
return (_lkpi_iv_key_set_delete(vap, k, DISABLE_KEY));
|
||||
}
|
||||
static int
|
||||
lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
|
||||
{
|
||||
|
||||
return (_lkpi_iv_key_set_delete(vap, k, SET_KEY));
|
||||
return (_lkpi_iv_key_set(vap, k));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -2424,6 +2635,24 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
|
|||
|
||||
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
|
||||
|
||||
#ifdef LKPI_80211_HW_CRYPTO
|
||||
if (lkpi_hwcrypto) {
|
||||
wiphy_lock(hw->wiphy);
|
||||
error = lkpi_sta_del_keys(hw, vif, lsta);
|
||||
wiphy_unlock(hw->wiphy);
|
||||
if (error != 0) {
|
||||
ic_printf(vap->iv_ic, "%s:%d: lkpi_sta_del_keys "
|
||||
"failed: %d\n", __func__, __LINE__, error);
|
||||
/*
|
||||
* Either drv/fw will crash or cleanup itself,
|
||||
* otherwise net80211 will delete the keys (at a
|
||||
* less appropriate time).
|
||||
*/
|
||||
/* goto out; */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Update sta_state (ASSOC to AUTH). */
|
||||
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
|
||||
KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not "
|
||||
|
|
@ -2562,6 +2791,24 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
|
|||
|
||||
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
|
||||
|
||||
#ifdef LKPI_80211_HW_CRYPTO
|
||||
if (lkpi_hwcrypto) {
|
||||
wiphy_lock(hw->wiphy);
|
||||
error = lkpi_sta_del_keys(hw, vif, lsta);
|
||||
wiphy_unlock(hw->wiphy);
|
||||
if (error != 0) {
|
||||
ic_printf(vap->iv_ic, "%s:%d: lkpi_sta_del_keys "
|
||||
"failed: %d\n", __func__, __LINE__, error);
|
||||
/*
|
||||
* Either drv/fw will crash or cleanup itself,
|
||||
* otherwise net80211 will delete the keys (at a
|
||||
* less appropriate time).
|
||||
*/
|
||||
/* goto out; */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Update sta_state (ASSOC to AUTH). */
|
||||
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
|
||||
KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not "
|
||||
|
|
@ -3142,13 +3389,13 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
|
|||
lvif->iv_update_bss = vap->iv_update_bss;
|
||||
vap->iv_update_bss = lkpi_iv_update_bss;
|
||||
|
||||
/* Key management. */
|
||||
if (lhw->ops->set_key != NULL) {
|
||||
#ifdef LKPI_80211_HW_CRYPTO
|
||||
/* Key management. */
|
||||
if (lkpi_hwcrypto && lhw->ops->set_key != NULL) {
|
||||
vap->iv_key_set = lkpi_iv_key_set;
|
||||
vap->iv_key_delete = lkpi_iv_key_delete;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LKPI_80211_HT
|
||||
/* Stay with the iv_ampdu_rxmax,limit / iv_ampdu_density defaults until later. */
|
||||
|
|
@ -3160,6 +3407,15 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
|
|||
ieee80211_vap_attach(vap, ieee80211_media_change,
|
||||
ieee80211_media_status, mac);
|
||||
|
||||
#ifdef LKPI_80211_HT
|
||||
/*
|
||||
* Modern chipset/fw/drv will do A-MPDU in drv/fw and fail
|
||||
* to do so if they cannot do the crypto too.
|
||||
*/
|
||||
if (!lkpi_hwcrypto && ieee80211_hw_check(hw, AMPDU_AGGREGATION))
|
||||
vap->iv_flags_ht &= ~IEEE80211_FHT_AMPDU_RX;
|
||||
#endif
|
||||
|
||||
if (hw->max_listen_interval == 0)
|
||||
hw->max_listen_interval = 7 * (ic->ic_lintval / ic->ic_bintval);
|
||||
hw->conf.listen_interval = hw->max_listen_interval;
|
||||
|
|
@ -3990,13 +4246,74 @@ lkpi_ic_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
|
|||
return (0);
|
||||
}
|
||||
|
||||
#ifdef LKPI_80211_HW_CRYPTO
|
||||
static int
|
||||
lkpi_hw_crypto_prepare(struct lkpi_sta *lsta, struct ieee80211_key *k,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_tx_info *info;
|
||||
struct ieee80211_key_conf *kc;
|
||||
struct ieee80211_hdr *hdr;
|
||||
uint32_t hlen, hdrlen;
|
||||
uint8_t *p;
|
||||
|
||||
KASSERT(lsta != NULL, ("%s: lsta is NULL", __func__));
|
||||
KASSERT(k != NULL, ("%s: key is NULL", __func__));
|
||||
KASSERT(skb != NULL, ("%s: skb is NULL", __func__));
|
||||
|
||||
kc = lsta->kc[k->wk_keyix];
|
||||
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
info->control.hw_key = kc;
|
||||
|
||||
/* MUST NOT happen. KASSERT? */
|
||||
if (kc == NULL) {
|
||||
ic_printf(lsta->ni->ni_ic, "%s: lsta %p k %p skb %p, "
|
||||
"kc is NULL on hw crypto offload\n", __func__, lsta, k, skb);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
|
||||
IMPROVE("the following should be WLAN_CIPHER_SUITE specific");
|
||||
/* We currently only support CCMP so we hardcode things here. */
|
||||
|
||||
hdr = (void *)skb->data;
|
||||
|
||||
/*
|
||||
* Check if we have anythig to do as requested by driver
|
||||
* or if we are done?
|
||||
*/
|
||||
if ((kc->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) == 0 &&
|
||||
(kc->flags & IEEE80211_KEY_FLAG_GENERATE_IV) == 0 &&
|
||||
/* MFP */
|
||||
!((kc->flags & IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) != 0 &&
|
||||
ieee80211_is_mgmt(hdr->frame_control)))
|
||||
return (0);
|
||||
|
||||
hlen = k->wk_cipher->ic_header;
|
||||
if (skb_headroom(skb) < hlen)
|
||||
return (ENOSPC);
|
||||
|
||||
hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
p = skb_push(skb, hlen);
|
||||
memmove(p, p + hlen, hdrlen);
|
||||
|
||||
/* If driver request space only we are done. */
|
||||
if ((kc->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) != 0)
|
||||
return (0);
|
||||
|
||||
p += hdrlen;
|
||||
k->wk_cipher->ic_setiv(k, p);
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
|
||||
{
|
||||
struct ieee80211_node *ni;
|
||||
#ifndef LKPI_80211_HW_CRYPTO
|
||||
struct ieee80211_frame *wh;
|
||||
#endif
|
||||
struct ieee80211_key *k;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211com *ic;
|
||||
|
|
@ -4011,6 +4328,7 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
|
|||
struct ieee80211_hdr *hdr;
|
||||
struct lkpi_txq *ltxq;
|
||||
void *buf;
|
||||
ieee80211_keyix keyix;
|
||||
uint8_t ac, tid;
|
||||
|
||||
M_ASSERTPKTHDR(m);
|
||||
|
|
@ -4021,19 +4339,29 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
|
|||
|
||||
ni = lsta->ni;
|
||||
k = NULL;
|
||||
#ifndef LKPI_80211_HW_CRYPTO
|
||||
/* Encrypt the frame if need be; XXX-BZ info->control.hw_key. */
|
||||
keyix = IEEE80211_KEYIX_NONE;
|
||||
wh = mtod(m, struct ieee80211_frame *);
|
||||
if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
|
||||
/* Retrieve key for TX && do software encryption. */
|
||||
k = ieee80211_crypto_encap(ni, m);
|
||||
if (k == NULL) {
|
||||
ieee80211_free_node(ni);
|
||||
m_freem(m);
|
||||
return;
|
||||
|
||||
#ifdef LKPI_80211_HW_CRYPTO
|
||||
if (lkpi_hwcrypto) {
|
||||
k = ieee80211_crypto_get_txkey(ni, m);
|
||||
if (k != NULL && lsta->kc[k->wk_keyix] != NULL)
|
||||
keyix = k->wk_keyix;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Encrypt the frame if need be. */
|
||||
if (keyix == IEEE80211_KEYIX_NONE) {
|
||||
/* Retrieve key for TX && do software encryption. */
|
||||
k = ieee80211_crypto_encap(ni, m);
|
||||
if (k == NULL) {
|
||||
ieee80211_free_node(ni);
|
||||
m_freem(m);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ic = ni->ni_ic;
|
||||
lhw = ic->ic_softc;
|
||||
|
|
@ -4145,7 +4473,19 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
|
|||
|
||||
sta = LSTA_TO_STA(lsta);
|
||||
#ifdef LKPI_80211_HW_CRYPTO
|
||||
info->control.hw_key = lsta->kc;
|
||||
if (lkpi_hwcrypto && keyix != IEEE80211_KEYIX_NONE) {
|
||||
int error;
|
||||
|
||||
error = lkpi_hw_crypto_prepare(lsta, k, skb);
|
||||
if (error != 0) {
|
||||
/*
|
||||
* We only have to free the skb which will free the
|
||||
* mbuf and release the reference on the ni.
|
||||
*/
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
IMPROVE();
|
||||
|
|
@ -5106,7 +5446,7 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
|
|||
|
||||
ic->ic_cryptocaps = 0;
|
||||
#ifdef LKPI_80211_HW_CRYPTO
|
||||
if (hw->wiphy->n_cipher_suites > 0) {
|
||||
if (lkpi_hwcrypto && hw->wiphy->n_cipher_suites > 0) {
|
||||
for (i = 0; i < hw->wiphy->n_cipher_suites; i++)
|
||||
ic->ic_cryptocaps |= lkpi_l80211_to_net80211_cyphers(
|
||||
hw->wiphy->cipher_suites[i]);
|
||||
|
|
@ -5345,15 +5685,62 @@ linuxkpi_ieee80211_iterate_interfaces(struct ieee80211_hw *hw,
|
|||
LKPI_80211_LHW_LVIF_UNLOCK(lhw);
|
||||
}
|
||||
|
||||
static void
|
||||
lkpi_ieee80211_iterate_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
ieee80211_keyix keyix, struct lkpi_sta *lsta,
|
||||
void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *,
|
||||
struct ieee80211_sta *, struct ieee80211_key_conf *, void *),
|
||||
void *arg)
|
||||
{
|
||||
if (!lsta->added_to_drv)
|
||||
return;
|
||||
|
||||
if (lsta->kc[keyix] == NULL)
|
||||
return;
|
||||
|
||||
iterfunc(hw, vif, LSTA_TO_STA(lsta), lsta->kc[keyix], arg);
|
||||
}
|
||||
|
||||
void
|
||||
linuxkpi_ieee80211_iterate_keys(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *,
|
||||
struct ieee80211_sta *, struct ieee80211_key_conf *, void *),
|
||||
void *arg)
|
||||
void *arg, bool rcu)
|
||||
{
|
||||
struct lkpi_sta *lsta;
|
||||
struct lkpi_vif *lvif;
|
||||
|
||||
UNIMPLEMENTED;
|
||||
lvif = VIF_TO_LVIF(vif);
|
||||
|
||||
if (rcu) {
|
||||
if (vif == NULL) {
|
||||
TODO();
|
||||
} else {
|
||||
IMPROVE("We do not actually match the RCU code");
|
||||
TAILQ_FOREACH(lsta, &lvif->lsta_head, lsta_entry) {
|
||||
for (ieee80211_keyix keyix = 0; keyix < nitems(lsta->kc);
|
||||
keyix++)
|
||||
lkpi_ieee80211_iterate_keys(hw, vif,
|
||||
keyix, lsta, iterfunc, arg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TODO("Used by suspend/resume; order of keys as installed to "
|
||||
"firmware is important; we'll need to rewrite some code for that");
|
||||
lockdep_assert_wiphy(hw->wiphy);
|
||||
|
||||
if (vif == NULL) {
|
||||
TODO();
|
||||
} else {
|
||||
TAILQ_FOREACH(lsta, &lvif->lsta_head, lsta_entry) {
|
||||
for (ieee80211_keyix keyix = 0; keyix < nitems(lsta->kc);
|
||||
keyix++)
|
||||
lkpi_ieee80211_iterate_keys(hw, vif,
|
||||
keyix, lsta, iterfunc, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@
|
|||
#define D80211_TRACEX (D80211_TRACE_TX|D80211_TRACE_RX)
|
||||
#define D80211_TRACEX_DUMP (D80211_TRACE_TX_DUMP|D80211_TRACE_RX_DUMP)
|
||||
#define D80211_TRACE_STA 0x00010000
|
||||
#define D80211_TRACE_HW_CRYPTO 0x00020000
|
||||
#define D80211_TRACE_MO 0x00100000
|
||||
#define D80211_TRACE_MODE 0x0f000000
|
||||
#define D80211_TRACE_MODE_HT 0x01000000
|
||||
|
|
@ -151,7 +152,7 @@ struct lkpi_sta {
|
|||
struct mbufq txq;
|
||||
struct mtx txq_mtx;
|
||||
|
||||
struct ieee80211_key_conf *kc;
|
||||
struct ieee80211_key_conf *kc[IEEE80211_WEP_NKID];
|
||||
enum ieee80211_sta_state state;
|
||||
bool txq_ready; /* Can we run the taskq? */
|
||||
bool added_to_drv; /* Driver knows; i.e. we called ...(). */
|
||||
|
|
|
|||
|
|
@ -683,6 +683,8 @@ lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
|||
struct lkpi_hw *lhw;
|
||||
int error;
|
||||
|
||||
lockdep_assert_wiphy(hw->wiphy);
|
||||
|
||||
lhw = HW_TO_LHW(hw);
|
||||
if (lhw->ops->set_key == NULL) {
|
||||
error = EOPNOTSUPP;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue