diff options
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]; |