git: 11db70b6057e - main - LinuxKPI: 802.11: hardware crypto offload improvements
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 18 Feb 2025 02:55:36 UTC
The branch main has been updated by bz:
URL: https://cgit.FreeBSD.org/src/commit/?id=11db70b6057e41b259dc2245cd893d5b19179fcc
commit 11db70b6057e41b259dc2245cd893d5b19179fcc
Author: Bjoern A. Zeeb <bz@FreeBSD.org>
AuthorDate: 2024-01-27 22:39:48 +0000
Commit: Bjoern A. Zeeb <bz@FreeBSD.org>
CommitDate: 2025-02-18 02:54:33 +0000
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: b35f6cd06612
---
sys/compat/linuxkpi/common/include/net/mac80211.h | 81 ++--
sys/compat/linuxkpi/common/src/linux_80211.c | 491 ++++++++++++++++++---
sys/compat/linuxkpi/common/src/linux_80211.h | 3 +-
.../linuxkpi/common/src/linux_80211_macops.c | 2 +
4 files changed, 489 insertions(+), 88 deletions(-)
diff --git a/sys/compat/linuxkpi/common/include/net/mac80211.h b/sys/compat/linuxkpi/common/include/net/mac80211.h
index 8d6d690b2f57..69956c90042c 100644
--- a/sys/compat/linuxkpi/common/include/net/mac80211.h
+++ b/sys/compat/linuxkpi/common/include/net/mac80211.h
@@ -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)
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index 9ba0e2da99d6..36c6f92123ff 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -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,173 @@ 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;
+ bool islocked;
- /* XXX TODO Check (k->wk_flags & IEEE80211_KEY_SWENCRYPT) and don't upload to driver/hw? */
+ 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);
+
+ 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;
@@ -925,10 +1094,50 @@ _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);
+
+ 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);
}
-}
-static int
-lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
-{
+#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
- /* XXX-BZ one day we should replace this iterating over VIFs, or node list? */
- return (_lkpi_iv_key_set_delete(vap, k, DISABLE_KEY));
+ wiphy_unlock(hw->wiphy);
+ ieee80211_free_node(ni);
+ return (1);
}
+
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,20 +4339,30 @@ 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;
+ }
+ }
+ }
+
ic = ni->ni_ic;
lhw = ic->ic_softc;
hw = LHW_TO_HW(lhw);
@@ -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
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h
index bb1ecb45636f..40b11ebfef13 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.h
+++ b/sys/compat/linuxkpi/common/src/linux_80211.h
@@ -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 ...(). */
diff --git a/sys/compat/linuxkpi/common/src/linux_80211_macops.c b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
index 68b5bbefe938..fde23d02af5e 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211_macops.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
@@ -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;