The branch main has been updated by bz:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=11db70b6057e41b259dc2245cd893d5b19179fcc

commit 11db70b6057e41b259dc2245cd893d5b19179fcc
Author:     Bjoern A. Zeeb <b...@freebsd.org>
AuthorDate: 2024-01-27 22:39:48 +0000
Commit:     Bjoern A. Zeeb <b...@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;

Reply via email to