Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 18 Feb 2025 02:55:36 GMT
From:      "Bjoern A. Zeeb" <bz@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 11db70b6057e - main - LinuxKPI: 802.11: hardware crypto offload improvements
Message-ID:  <202502180255.51I2tau8049355@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202502180255.51I2tau8049355>