diff options
author | Stefan Sperling <stsp@cvs.openbsd.org> | 2020-04-27 08:21:36 +0000 |
---|---|---|
committer | Stefan Sperling <stsp@cvs.openbsd.org> | 2020-04-27 08:21:36 +0000 |
commit | adade575d7e0f5d803170fc3b45b1cffa53f0df6 (patch) | |
tree | 54a3e7991c61bbb46c1322d2bb8c43f3d36d142f /sys/dev | |
parent | d3823ffd9dffe0ca84d69069a8cc8228332dbd3e (diff) |
Offload CCMP (WPA2) encryption and decryption to athn(4) hardware.
This reduces CPU load during traffic bursts, which is especially noticeable
on boards with relatively slow CPUs such as Alix and APU. For reference,
my rcc-ve boards (Intel Atom C2558) now forward ~2000pps between Ethernet
and wifi with <= 1% CPU interrupt time according to systat. Beforehand, these
boards became noticeably less responsive with fairly high interrupt and spin %.
CCMP offload is used on both PCI and USB devices (thanks kevlo@ for catching
my omission of USB devices in the first iteration of this change!)
Tested by (at least) Kevin Chadwick, tracey@, kevlo@, kili@, Ted Patterson,
David Dahlberg, and Scott Bennett.
ok tracey@ kevlo@
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ic/ar5008.c | 201 | ||||
-rw-r--r-- | sys/dev/ic/ar5008reg.h | 3 | ||||
-rw-r--r-- | sys/dev/ic/athn.c | 121 | ||||
-rw-r--r-- | sys/dev/usb/if_athn_usb.c | 58 | ||||
-rw-r--r-- | sys/dev/usb/if_athn_usb.h | 7 |
5 files changed, 293 insertions, 97 deletions
diff --git a/sys/dev/ic/ar5008.c b/sys/dev/ic/ar5008.c index 9dd0499c970..3d1235de376 100644 --- a/sys/dev/ic/ar5008.c +++ b/sys/dev/ic/ar5008.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ar5008.c,v 1.55 2020/02/17 14:37:36 claudio Exp $ */ +/* $OpenBSD: ar5008.c,v 1.56 2020/04/27 08:21:34 stsp Exp $ */ /*- * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr> @@ -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 *m, } #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_list *ml) 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_list *ml) * 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_list *ml) 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_list *ml) 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, struct ieee80211_node *ni, 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, struct ieee80211_node *ni, 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. diff --git a/sys/dev/ic/ar5008reg.h b/sys/dev/ic/ar5008reg.h index a45fe825940..817e90df478 100644 --- a/sys/dev/ic/ar5008reg.h +++ b/sys/dev/ic/ar5008reg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ar5008reg.h,v 1.6 2019/07/24 07:53:57 stsp Exp $ */ +/* $OpenBSD: ar5008reg.h,v 1.7 2020/04/27 08:21:34 stsp Exp $ */ /*- * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr> @@ -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 diff --git a/sys/dev/ic/athn.c b/sys/dev/ic/athn.c index 88784561c13..81539232169 100644 --- a/sys/dev/ic/athn.c +++ b/sys/dev/ic/athn.c @@ -1,4 +1,4 @@ -/* $OpenBSD: athn.c,v 1.104 2019/05/17 01:05:20 kevlo Exp $ */ +/* $OpenBSD: athn.c,v 1.105 2020/04/27 08:21:34 stsp Exp $ */ /*- * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr> @@ -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_node *ni, 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,18 +1046,45 @@ athn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, 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 ieee80211_node *ni, 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: - entry = (uintptr_t)k->k_priv; - athn_reset_key(sc, entry); - break; - case IEEE80211_CIPHER_TKIP: + if (k->k_cipher == IEEE80211_CIPHER_CCMP) { 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 diff --git a/sys/dev/usb/if_athn_usb.c b/sys/dev/usb/if_athn_usb.c index 7cd0644df36..abdefe08dd0 100644 --- a/sys/dev/usb/if_athn_usb.c +++ b/sys/dev/usb/if_athn_usb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_athn_usb.c,v 1.55 2019/11/25 11:32:17 mpi Exp $ */ +/* $OpenBSD: if_athn_usb.c,v 1.56 2020/04/27 08:21:35 stsp Exp $ */ /*- * Copyright (c) 2011 Damien Bergamini <damien.bergamini@free.fr> @@ -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 *arg) 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 mbuf *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 mbuf *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 mbuf *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, struct ieee80211_node *ni) 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, struct ieee80211_node *ni) 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 { diff --git a/sys/dev/usb/if_athn_usb.h b/sys/dev/usb/if_athn_usb.h index c70004ed99d..0f5525eff24 100644 --- a/sys/dev/usb/if_athn_usb.h +++ b/sys/dev/usb/if_athn_usb.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_athn_usb.h,v 1.10 2018/02/05 09:52:03 stsp Exp $ */ +/* $OpenBSD: if_athn_usb.h,v 1.11 2020/04/27 08:21:35 stsp Exp $ */ /*- * Copyright (c) 2011 Damien Bergamini <damien.bergamini@free.fr> @@ -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]; |