On Fri, Apr 24, 2020 at 03:31:12PM +0200, Stefan Sperling wrote:
> On Wed, Apr 22, 2020 at 07:37:10PM +0200, Stefan Sperling wrote:
> > This makes athn(4) offload CCMP encryption and decryption to hardware.
> > CCMP is used with WPA2, so this reduces CPU load on WPA2 networks only.
> > 
> > The WPA1 (TKIP) and WEP ciphers remain in software because this simplifies
> > the driver. TKIP in particular is a annoying to deal with on this hardware.
> > Old ciphers should not be used anymore anyway and they don't work in 11n 
> > mode.
> > 
> > I have successfully tested the following on this device:
> > athn0 at pci1 dev 0 function 0 "Atheros AR9281" rev 0x01: apic 2 int 16
> > athn0: AR9280 rev 2 (2T2R), ROM rev 22, address xx:xx:xx:xx:xx:xx
> > 
> > Hostap mode with multiple clients in parallel: wpa2, wpa1, wep, plaintext
> > Client mode: wpa2, plaintext
> > 
> > Could anyone else test this?
> 
> This patch panics on athn(4) USB devices (reported to me by several people).
> I am working on a fix for USB.

New diff which also works on USB devices (tested on AR9271 and AR9280+AR7010).

diff c50b0f0215d7dd5773e0ee8623d8aa2c7d2107c6 
b593e0ca32b1c816510ec76bdd498bc9101dea15
blob - 9dd0499c9704c87fbe039b65f02e907284fea120
blob + 0f78d27c9e99239d6ec08410dd42e65c5781245d
--- sys/dev/ic/ar5008.c
+++ sys/dev/ic/ar5008.c
@@ -77,11 +77,14 @@ void        ar5008_rx_free(struct athn_softc *);
 void   ar5008_rx_enable(struct athn_softc *);
 void   ar5008_rx_radiotap(struct athn_softc *, struct mbuf *,
            struct ar_rx_desc *);
+int    ar5008_ccmp_decap(struct athn_softc *, struct mbuf *,
+           struct ieee80211_node *);
 void   ar5008_rx_intr(struct athn_softc *);
 int    ar5008_tx_process(struct athn_softc *, int);
 void   ar5008_tx_intr(struct athn_softc *);
 int    ar5008_swba_intr(struct athn_softc *);
 int    ar5008_intr(struct athn_softc *);
+int    ar5008_ccmp_encap(struct mbuf *, u_int, struct ieee80211_key *);
 int    ar5008_tx(struct athn_softc *, struct mbuf *, struct ieee80211_node *,
            int);
 void   ar5008_set_rf_mode(struct athn_softc *, struct ieee80211_channel *);
@@ -254,6 +257,8 @@ ar5008_attach(struct athn_softc *sc)
        kc_entries_log = MS(base->deviceCap, AR_EEP_DEVCAP_KC_ENTRIES);
        sc->kc_entries = (kc_entries_log != 0) ?
            1 << kc_entries_log : AR_KEYTABLE_SIZE;
+       if (sc->kc_entries > AR_KEYTABLE_SIZE)
+               sc->kc_entries = AR_KEYTABLE_SIZE;
 
        sc->txchainmask = base->txMask;
        if (sc->mac_ver == AR_SREV_VERSION_5416_PCI &&
@@ -781,6 +786,111 @@ ar5008_rx_radiotap(struct athn_softc *sc, struct mbuf 
 }
 #endif
 
+int
+ar5008_ccmp_decap(struct athn_softc *sc, struct mbuf *m, struct ieee80211_node 
*ni)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ieee80211_key *k;
+       struct ieee80211_frame *wh;
+       struct ieee80211_rx_ba *ba;
+       uint64_t pn, *prsc;
+       u_int8_t *ivp, *mmie;
+       uint8_t tid;
+       uint16_t kid;
+       int hdrlen, hasqos;
+       uintptr_t entry;
+
+       wh = mtod(m, struct ieee80211_frame *);
+       hdrlen = ieee80211_get_hdrlen(wh);
+       ivp = mtod(m, u_int8_t *) + hdrlen;
+
+       /* find key for decryption */
+       if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+               k = &ni->ni_pairwise_key;
+       } else if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
+           IEEE80211_FC0_TYPE_MGT) {
+               /* retrieve group data key id from IV field */
+               /* check that IV field is present */
+               if (m->m_len < hdrlen + 4)
+                       return 1;
+               kid = ivp[3] >> 6;
+               k = &ic->ic_nw_keys[kid];
+       } else {
+               /* retrieve integrity group key id from MMIE */
+               if (m->m_len < sizeof(*wh) + IEEE80211_MMIE_LEN) {
+                       return 1;
+               }
+               /* it is assumed management frames are contiguous */
+               mmie = (u_int8_t *)wh + m->m_len - IEEE80211_MMIE_LEN;
+               /* check that MMIE is valid */
+               if (mmie[0] != IEEE80211_ELEMID_MMIE || mmie[1] != 16) {
+                       return 1;
+               }
+               kid = LE_READ_2(&mmie[2]);
+               if (kid != 4 && kid != 5) {
+                       return 1;
+               }
+               k = &ic->ic_nw_keys[kid];
+       }
+
+       if (k->k_cipher != IEEE80211_CIPHER_CCMP)
+               return 1;
+
+       /* Sanity checks to ensure this is really a key we installed. */
+       entry = (uintptr_t)k->k_priv;
+       if (k->k_flags & IEEE80211_KEY_GROUP) {
+               if (k->k_id > IEEE80211_WEP_NKID ||
+                   entry != k->k_id)
+                       return 1;
+       } else if (entry != IEEE80211_WEP_NKID +
+           IEEE80211_AID(ni->ni_associd))
+               return 1;
+
+       /* Check that ExtIV bit is be set. */
+       if (!(ivp[3] & IEEE80211_WEP_EXTIV))
+               return 1;
+
+       hasqos = ieee80211_has_qos(wh);
+       tid = hasqos ? ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
+       ba = hasqos ? &ni->ni_rx_ba[tid] : NULL;
+       prsc = &k->k_rsc[0];
+
+       /* Extract the 48-bit PN from the CCMP header. */
+       pn = (uint64_t)ivp[0]       |
+            (uint64_t)ivp[1] <<  8 |
+            (uint64_t)ivp[4] << 16 |
+            (uint64_t)ivp[5] << 24 |
+            (uint64_t)ivp[6] << 32 |
+            (uint64_t)ivp[7] << 40;
+       if (pn <= *prsc) {
+               if (hasqos && ba->ba_state == IEEE80211_BA_AGREED) {
+                       /*
+                        * This is an A-MPDU subframe.
+                        * Such frames may be received out of order due to
+                        * legitimate retransmissions of failed subframes
+                        * in previous A-MPDUs. Duplicates will be handled
+                        * in ieee80211_inputm() as part of A-MPDU reordering.
+                        *
+                        * XXX TODO We can probably do better than this! Store
+                        * re-ordered PN in BA agreement state and check it?
+                        */
+               } else {
+                       ic->ic_stats.is_ccmp_replays++;
+                       return 1;
+               }
+       }
+       /* Update last seen packet number. */
+       *prsc = pn;
+
+       /* Clear Protected bit and strip IV. */
+       wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+       memmove(mtod(m, caddr_t) + IEEE80211_CCMP_HDRLEN, wh, hdrlen);
+       m_adj(m, IEEE80211_CCMP_HDRLEN);
+       /* Strip MIC. */
+       m_adj(m, -IEEE80211_CCMP_MICLEN);
+       return 0;
+}
+
 static __inline int
 ar5008_rx_process(struct athn_softc *sc, struct mbuf_list *ml)
 {
@@ -837,9 +947,12 @@ ar5008_rx_process(struct athn_softc *sc, struct mbuf_l
                else if (ds->ds_status8 & AR_RXS8_PHY_ERR)
                        DPRINTFN(6, ("PHY error=0x%x\n",
                            MS(ds->ds_status8, AR_RXS8_PHY_ERR_CODE)));
-               else if (ds->ds_status8 & AR_RXS8_DECRYPT_CRC_ERR)
+               else if ((ds->ds_status8 & AR_RXS8_DECRYPT_CRC_ERR) ||
+                   (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+                   (ds->ds_status8 & AR_RXS8_KEY_MISS))) {
                        DPRINTFN(6, ("Decryption CRC error\n"));
-               else if (ds->ds_status8 & AR_RXS8_MICHAEL_ERR) {
+                       ic->ic_stats.is_ccmp_dec_errs++;
+               } else if (ds->ds_status8 & AR_RXS8_MICHAEL_ERR) {
                        DPRINTFN(2, ("Michael MIC failure\n"));
                        /* Report Michael MIC failures to net80211. */
                        ic->ic_stats.is_rx_locmicfail++;
@@ -848,7 +961,8 @@ ar5008_rx_process(struct athn_softc *sc, struct mbuf_l
                         * XXX Check that it is not a control frame
                         * (invalid MIC failures on valid ctl frames).
                         */
-               }
+               } else if (ds->ds_status8 & AR_RXS8_DECRYPT_BUSY_ERR)
+                       ic->ic_stats.is_ccmp_dec_errs++;
                ifp->if_ierrors++;
                goto skip;
        }
@@ -911,6 +1025,7 @@ ar5008_rx_process(struct athn_softc *sc, struct mbuf_l
                        memmove((caddr_t)wh + 2, wh, hdrlen);
                        m_adj(m, 2);
                }
+               wh = mtod(m, struct ieee80211_frame *);
        }
 #if NBPFILTER > 0
        if (__predict_false(sc->sc_drvbpf != NULL))
@@ -924,6 +1039,21 @@ ar5008_rx_process(struct athn_softc *sc, struct mbuf_l
        rxi.rxi_rssi = MS(ds->ds_status4, AR_RXS4_RSSI_COMBINED);
        rxi.rxi_rssi += AR_DEFAULT_NOISE_FLOOR;
        rxi.rxi_tstamp = ds->ds_status2;
+       if (!(wh->i_fc[0] & IEEE80211_FC0_TYPE_CTL) &&
+           (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
+           (ic->ic_flags & IEEE80211_F_RSNON) &&
+           (ni->ni_flags & IEEE80211_NODE_RXPROT) &&
+           (ni->ni_rsncipher == IEEE80211_CIPHER_CCMP ||
+           (IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+           ic->ic_rsngroupcipher == IEEE80211_CIPHER_CCMP))) {
+               if (ar5008_ccmp_decap(sc, m, ni) != 0) {
+                       ifp->if_ierrors++;
+                       ieee80211_release_node(ic, ni);
+                       m_freem(m);
+                       goto skip;
+               }
+               rxi.rxi_flags |= IEEE80211_RXI_HWDEC;
+       }
        ieee80211_inputm(ifp, m, ni, &rxi, ml);
 
        /* Node is no longer needed. */
@@ -1288,6 +1418,33 @@ ar5008_intr(struct athn_softc *sc)
 }
 
 int
+ar5008_ccmp_encap(struct mbuf *m, u_int hdrlen, struct ieee80211_key *k)
+{
+       struct mbuf *n;
+       uint8_t *ivp;
+       int off;
+
+       /* Insert IV for CCMP hardware encryption. */
+       n = m_makespace(m, hdrlen, IEEE80211_CCMP_HDRLEN, &off);
+       if (n == NULL) {
+               m_freem(m);
+               return (ENOBUFS);
+       }
+       ivp = mtod(n, uint8_t *) + off;
+       k->k_tsc++;
+       ivp[0] = k->k_tsc;
+       ivp[1] = k->k_tsc >> 8;
+       ivp[2] = 0;
+       ivp[3] = k->k_id << 6 | IEEE80211_WEP_EXTIV;
+       ivp[4] = k->k_tsc >> 16;
+       ivp[5] = k->k_tsc >> 24;
+       ivp[6] = k->k_tsc >> 32;
+       ivp[7] = k->k_tsc >> 40;
+
+       return 0;
+}
+
+int
 ar5008_tx(struct athn_softc *sc, struct mbuf *m, struct ieee80211_node *ni,
     int txflags)
 {
@@ -1330,8 +1487,15 @@ ar5008_tx(struct athn_softc *sc, struct mbuf *m, struc
 
        if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
                k = ieee80211_get_txkey(ic, wh, ni);
-               if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
-                       return (ENOBUFS);
+               if (k->k_cipher == IEEE80211_CIPHER_CCMP) {
+                       u_int hdrlen = ieee80211_get_hdrlen(wh);
+                       if (ar5008_ccmp_encap(m, hdrlen, k) != 0)
+                               return (ENOBUFS);
+               } else {
+                       if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
+                               return (ENOBUFS);
+                       k = NULL; /* skip hardware crypto further below */
+               }
                wh = mtod(m, struct ieee80211_frame *);
        }
 
@@ -1461,28 +1625,13 @@ ar5008_tx(struct athn_softc *sc, struct mbuf *m, struc
             IEEE80211_QOS_ACK_POLICY_NOACK))
                ds->ds_ctl1 |= AR_TXC1_NO_ACK;
 
-       if (0 && k != NULL) {
-               /*
-                * Map 802.11 cipher to hardware encryption type and
-                * compute MIC+ICV overhead.
-                */
-               switch (k->k_cipher) {
-               case IEEE80211_CIPHER_WEP40:
-               case IEEE80211_CIPHER_WEP104:
-                       encrtype = AR_ENCR_TYPE_WEP;
-                       totlen += 4;
-                       break;
-               case IEEE80211_CIPHER_TKIP:
-                       encrtype = AR_ENCR_TYPE_TKIP;
-                       totlen += 12;
-                       break;
-               case IEEE80211_CIPHER_CCMP:
+       if (k != NULL) {
+               /* Map 802.11 cipher to hardware encryption type. */
+               if (k->k_cipher == IEEE80211_CIPHER_CCMP) {
                        encrtype = AR_ENCR_TYPE_AES;
-                       totlen += 8;
-                       break;
-               default:
+                       totlen += IEEE80211_CCMP_MICLEN;
+               } else
                        panic("unsupported cipher");
-               }
                /*
                 * NB: The key cache entry index is stored in the key
                 * private field when the key is installed.
blob - a45fe825940b6bb6810a7de7830ccd87d6bcb6fc
blob + eed13c59a4d1991bfa8a2827f3d190b6ae5e6a42
--- sys/dev/ic/ar5008reg.h
+++ sys/dev/ic/ar5008reg.h
@@ -927,6 +927,7 @@ struct ar_rx_desc {
 #define AR_RXS8_KEY_IDX_S              9
 #define AR_RXS8_POST_DELIM_CRC_ERR     0x00040000
 #define AR_RXS8_DECRYPT_BUSY_ERR       0x40000000
+#define AR_RXS8_KEY_MISS               0x80000000
 
 #define AR_MAX_PWR_RANGE_IN_HALF_DB    64
 #define AR9285_PD_GAIN_BOUNDARY_DEFAULT        58
blob - b3532eb8263f678c80a44257fe4f55820458a18c
blob + f4c865e299f3302dd9a98aefb15bbfff76296bc0
--- sys/dev/ic/athn.c
+++ sys/dev/ic/athn.c
@@ -244,9 +244,10 @@ athn_attach(struct athn_softc *sc)
        /*
         * In HostAP mode, the number of STAs that we can handle is
         * limited by the number of entries in the HW key cache.
-        * TKIP keys consume 2 entries in the cache.
+        * TKIP keys would consume 2 entries in this cache but we
+        * only use the hardware crypto engine for CCMP.
         */
-       ic->ic_max_nnodes = (sc->kc_entries / 2) - IEEE80211_WEP_NKID;
+       ic->ic_max_nnodes = sc->kc_entries - IEEE80211_WEP_NKID;
        if (ic->ic_max_nnodes > IEEE80211_CACHE_SIZE)
                ic->ic_max_nnodes = IEEE80211_CACHE_SIZE;
 
@@ -373,10 +374,8 @@ athn_attach(struct athn_softc *sc)
        ic->ic_newassoc = athn_newassoc;
        ic->ic_updateslot = athn_updateslot;
        ic->ic_updateedca = athn_updateedca;
-#ifdef notyet
        ic->ic_set_key = athn_set_key;
        ic->ic_delete_key = athn_delete_key;
-#endif
 
        /* Override 802.11 state transition machine. */
        sc->sc_newstate = ic->ic_newstate;
@@ -990,6 +989,12 @@ athn_reset_key(struct athn_softc *sc, int entry)
         * NB: Key cache registers access special memory area that requires
         * two 32-bit writes to actually update the values in the internal
         * memory.  Consequently, writes must be grouped by pair.
+        *
+        * All writes to registers with an offset of 0x0 or 0x8 write to a
+        * temporary register. A write to a register with an offset of 0x4
+        * or 0xc writes concatenates the written value with the value in
+        * the temporary register and writes the result to key cache memory.
+        * The actual written memory area is 50 bits wide.
         */
        AR_WRITE(sc, AR_KEYTABLE_KEY0(entry), 0);
        AR_WRITE(sc, AR_KEYTABLE_KEY1(entry), 0);
@@ -1011,58 +1016,29 @@ athn_set_key(struct ieee80211com *ic, struct ieee80211
     struct ieee80211_key *k)
 {
        struct athn_softc *sc = ic->ic_softc;
-       const uint8_t *txmic, *rxmic, *key, *addr;
-       uintptr_t entry, micentry;
-       uint32_t type, lo, hi;
+       const uint8_t *key, *addr;
+       uintptr_t entry;
+       uint32_t lo, hi, unicast;
 
-       switch (k->k_cipher) {
-       case IEEE80211_CIPHER_WEP40:
-               type = AR_KEYTABLE_TYPE_40;
-               break;
-       case IEEE80211_CIPHER_WEP104:
-               type = AR_KEYTABLE_TYPE_104;
-               break;
-       case IEEE80211_CIPHER_TKIP:
-               type = AR_KEYTABLE_TYPE_TKIP;
-               break;
-       case IEEE80211_CIPHER_CCMP:
-               type = AR_KEYTABLE_TYPE_CCM;
-               break;
-       default:
-               /* Fallback to software crypto for other ciphers. */
-               return (ieee80211_set_key(ic, ni, k));
+       if (k->k_cipher != IEEE80211_CIPHER_CCMP) {
+               /* Use software crypto for ciphers other than CCMP. */
+               return ieee80211_set_key(ic, ni, k);
        }
 
-       if (!(k->k_flags & IEEE80211_KEY_GROUP))
+       if (!(k->k_flags & IEEE80211_KEY_GROUP)) {
                entry = IEEE80211_WEP_NKID + IEEE80211_AID(ni->ni_associd);
-       else
+               if (entry >= sc->kc_entries - IEEE80211_WEP_NKID)
+                       return ENOSPC;
+       } else {
                entry = k->k_id;
+               if (entry > IEEE80211_WEP_NKID)
+                       return ENOSPC;
+       }
        k->k_priv = (void *)entry;
 
        /* NB: See note about key cache registers access above. */
        key = k->k_key;
-       if (type == AR_KEYTABLE_TYPE_TKIP) {
-#ifndef IEEE80211_STA_ONLY
-               if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-                       txmic = &key[16];
-                       rxmic = &key[24];
-               } else
-#endif
-               {
-                       rxmic = &key[16];
-                       txmic = &key[24];
-               }
-               /* Tx+Rx MIC key is at entry + 64. */
-               micentry = entry + 64;
-               AR_WRITE(sc, AR_KEYTABLE_KEY0(micentry), LE_READ_4(&rxmic[0]));
-               AR_WRITE(sc, AR_KEYTABLE_KEY1(micentry), LE_READ_2(&txmic[2]));
 
-               AR_WRITE(sc, AR_KEYTABLE_KEY2(micentry), LE_READ_4(&rxmic[4]));
-               AR_WRITE(sc, AR_KEYTABLE_KEY3(micentry), LE_READ_2(&txmic[0]));
-
-               AR_WRITE(sc, AR_KEYTABLE_KEY4(micentry), LE_READ_4(&txmic[4]));
-               AR_WRITE(sc, AR_KEYTABLE_TYPE(micentry), AR_KEYTABLE_TYPE_CLR);
-       }
        AR_WRITE(sc, AR_KEYTABLE_KEY0(entry), LE_READ_4(&key[ 0]));
        AR_WRITE(sc, AR_KEYTABLE_KEY1(entry), LE_READ_2(&key[ 4]));
 
@@ -1070,19 +1046,46 @@ athn_set_key(struct ieee80211com *ic, struct ieee80211
        AR_WRITE(sc, AR_KEYTABLE_KEY3(entry), LE_READ_2(&key[10]));
 
        AR_WRITE(sc, AR_KEYTABLE_KEY4(entry), LE_READ_4(&key[12]));
-       AR_WRITE(sc, AR_KEYTABLE_TYPE(entry), type);
+       AR_WRITE(sc, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CCM);
 
+       unicast = AR_KEYTABLE_VALID;
        if (!(k->k_flags & IEEE80211_KEY_GROUP)) {
                addr = ni->ni_macaddr;
                lo = LE_READ_4(&addr[0]);
                hi = LE_READ_2(&addr[4]);
                lo = lo >> 1 | hi << 31;
                hi = hi >> 1;
-       } else
-               lo = hi = 0;
+       } else {
+#ifndef IEEE80211_STA_ONLY
+               if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+                       uint8_t groupaddr[ETHER_ADDR_LEN];
+                       IEEE80211_ADDR_COPY(groupaddr, ic->ic_myaddr);
+                       groupaddr[0] |= 0x01;
+                       lo = LE_READ_4(&groupaddr[0]);
+                       hi = LE_READ_2(&groupaddr[4]);
+                       lo = lo >> 1 | hi << 31;
+                       hi = hi >> 1;
+                       /*
+                        * KEYTABLE_VALID indicates that the address
+                        * is a unicast address which must match the
+                        * transmitter address when decrypting frames.
+                        * Not setting KEYTABLE_VALID allows hardware to
+                        * use this key for multicast frame decryption.
+                        */
+                       unicast = 0;
+               } else
+#endif
+                       lo = hi = 0;
+       }
        AR_WRITE(sc, AR_KEYTABLE_MAC0(entry), lo);
-       AR_WRITE(sc, AR_KEYTABLE_MAC1(entry), hi | AR_KEYTABLE_VALID);
+       AR_WRITE(sc, AR_KEYTABLE_MAC1(entry), hi | unicast);
+
        AR_WRITE_BARRIER(sc);
+
+       /* Enable HW crypto. */
+       AR_CLRBITS(sc, AR_DIAG_SW, AR_DIAG_ENCRYPT_DIS | AR_DIAG_DECRYPT_DIS);
+
+       AR_WRITE_BARRIER(sc);
        return (0);
 }
 
@@ -1093,22 +1096,12 @@ athn_delete_key(struct ieee80211com *ic, struct ieee80
        struct athn_softc *sc = ic->ic_softc;
        uintptr_t entry;
 
-       switch (k->k_cipher) {
-       case IEEE80211_CIPHER_WEP40:
-       case IEEE80211_CIPHER_WEP104:
-       case IEEE80211_CIPHER_CCMP:
+       if (k->k_cipher == IEEE80211_CIPHER_CCMP) {
                entry = (uintptr_t)k->k_priv;
                athn_reset_key(sc, entry);
-               break;
-       case IEEE80211_CIPHER_TKIP:
-               entry = (uintptr_t)k->k_priv;
-               athn_reset_key(sc, entry);
-               athn_reset_key(sc, entry + 64);
-               break;
-       default:
-               /* Fallback to software crypto for other ciphers. */
+               explicit_bzero(k, sizeof(*k));
+       } else
                ieee80211_delete_key(ic, ni, k);
-       }
 }
 
 void
blob - 7cd0644df368944fcb0d5a1ecb3a75f3f5d16841
blob + a3549193408fbebd9e2bd5314e2ef8389249583b
--- sys/dev/usb/if_athn_usb.c
+++ sys/dev/usb/if_athn_usb.c
@@ -53,6 +53,7 @@
 #include <net80211/ieee80211_radiotap.h>
 
 #include <dev/ic/athnreg.h>
+#include <dev/ic/ar5008reg.h>
 #include <dev/ic/athnvar.h>
 
 #include <dev/usb/usb.h>
@@ -190,6 +191,9 @@ int         athn_usb_ioctl(struct ifnet *, u_long, caddr_t);
 int            athn_usb_init(struct ifnet *);
 void           athn_usb_stop(struct ifnet *);
 void           ar9271_load_ani(struct athn_softc *);
+int            ar5008_ccmp_decap(struct athn_softc *, struct mbuf *,
+                   struct ieee80211_node *);
+int            ar5008_ccmp_encap(struct mbuf *, u_int, struct ieee80211_key *);
 
 /* Shortcut. */
 #define athn_usb_wmi_cmd(sc, cmd_id)   \
@@ -341,9 +345,9 @@ athn_usb_attachhook(struct device *self)
 #endif
        ic->ic_updateslot = athn_usb_updateslot;
        ic->ic_updateedca = athn_usb_updateedca;
-#ifdef notyet
        ic->ic_set_key = athn_usb_set_key;
        ic->ic_delete_key = athn_usb_delete_key;
+#ifdef notyet
        ic->ic_ampdu_tx_start = athn_usb_ampdu_tx_start;
        ic->ic_ampdu_tx_stop = athn_usb_ampdu_tx_stop;
 #endif
@@ -1653,6 +1657,7 @@ athn_usb_set_key_cb(struct athn_usb_softc *usc, void *
        int s;
 
        s = splnet();
+       athn_usb_write_barrier(&usc->sc_sc);
        athn_set_key(ic, cmd->ni, cmd->key);
        if (cmd->ni != NULL)
                ieee80211_release_node(ic, cmd->ni);
@@ -2040,6 +2045,12 @@ athn_usb_rx_frame(struct athn_usb_softc *usc, struct m
        if (__predict_false(datalen < sizeof(*wh) + IEEE80211_CRC_LEN))
                goto skip;
 
+       if (rs->rs_status != 0) {
+               if (rs->rs_status & AR_RXS_RXERR_DECRYPT)
+                       ic->ic_stats.is_ccmp_dec_errs++;
+               ifp->if_ierrors++;
+               goto skip;
+       }
        m_adj(m, sizeof(*rs));  /* Strip Rx status. */
 
        s = splnet();
@@ -2055,6 +2066,7 @@ athn_usb_rx_frame(struct athn_usb_softc *usc, struct m
                        memmove((caddr_t)wh + 2, wh, hdrlen);
                        m_adj(m, 2);
                }
+               wh = mtod(m, struct ieee80211_frame *);
        }
 #if NBPFILTER > 0
        if (__predict_false(sc->sc_drvbpf != NULL))
@@ -2067,6 +2079,21 @@ athn_usb_rx_frame(struct athn_usb_softc *usc, struct m
        rxi.rxi_flags = 0;
        rxi.rxi_rssi = rs->rs_rssi + AR_USB_DEFAULT_NF;
        rxi.rxi_tstamp = betoh64(rs->rs_tstamp);
+       if (!(wh->i_fc[0] & IEEE80211_FC0_TYPE_CTL) &&
+           (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
+           (ic->ic_flags & IEEE80211_F_RSNON) &&
+           (ni->ni_flags & IEEE80211_NODE_RXPROT) &&
+           (ni->ni_rsncipher == IEEE80211_CIPHER_CCMP ||
+           (IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+           ic->ic_rsngroupcipher == IEEE80211_CIPHER_CCMP))) {
+               if (ar5008_ccmp_decap(sc, m, ni) != 0) {
+                       ifp->if_ierrors++;
+                       ieee80211_release_node(ic, ni);
+                       splx(s);
+                       goto skip;
+               }
+               rxi.rxi_flags |= IEEE80211_RXI_HWDEC;
+       }
        ieee80211_inputm(ifp, m, ni, &rxi, ml);
 
        /* Node is no longer needed. */
@@ -2247,8 +2274,15 @@ athn_usb_tx(struct athn_softc *sc, struct mbuf *m, str
        wh = mtod(m, struct ieee80211_frame *);
        if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
                k = ieee80211_get_txkey(ic, wh, ni);
-               if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
-                       return (ENOBUFS);
+               if (k->k_cipher == IEEE80211_CIPHER_CCMP) {
+                       u_int hdrlen = ieee80211_get_hdrlen(wh);
+                       if (ar5008_ccmp_encap(m, hdrlen, k) != 0)
+                               return (ENOBUFS);
+               } else {
+                       if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
+                               return (ENOBUFS);
+                       k = NULL; /* skip hardware crypto further below */
+               }
                wh = mtod(m, struct ieee80211_frame *);
        }
        if ((hasqos = ieee80211_has_qos(wh))) {
@@ -2305,7 +2339,21 @@ athn_usb_tx(struct athn_softc *sc, struct mbuf *m, str
                        else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
                                txf->flags |= htobe32(AR_HTC_TX_RTSCTS);
                }
-               txf->key_idx = 0xff;
+
+               if (k != NULL) {
+                       /* Map 802.11 cipher to hardware encryption type. */
+                       if (k->k_cipher == IEEE80211_CIPHER_CCMP) {
+                               txf->key_type = AR_ENCR_TYPE_AES;
+                       } else
+                               panic("unsupported cipher");
+                       /*
+                        * NB: The key cache entry index is stored in the key
+                        * private field when the key is installed.
+                        */
+                       txf->key_idx = (uintptr_t)k->k_priv;
+               } else
+                       txf->key_idx = 0xff;
+
                txf->cookie = an->sta_index;
                frm = (uint8_t *)&txf[1];
        } else {
blob - c70004ed99d4fa005b265bad80456a7f4b464363
blob + 0c2865315410d92096330b46435b166fc70d4932
--- sys/dev/usb/if_athn_usb.h
+++ sys/dev/usb/if_athn_usb.h
@@ -338,6 +338,11 @@ struct ar_rx_status {
        uint64_t        rs_tstamp;
        uint16_t        rs_datalen;
        uint8_t         rs_status;
+#define AR_RXS_RXERR_CRC               0x01
+#define AR_RXS_RXERR_PHY               0x02
+#define AR_RXS_RXERR_FIFO              0x04
+#define AR_RXS_RXERR_DECRYPT           0x08
+#define AR_RXS_RXERR_MIC               0x10
        uint8_t         rs_phyerr;
        int8_t          rs_rssi;
        int8_t          rs_rssi_ctl[AR_MAX_CHAINS];

Reply via email to