This enables use of hardware crypto for CCMP in urtwn(4). As with other drivers, this reduces cpu usage significantly when moving lots of data. I've tested this on an assortment of hardware (RTL8188CUS, RTL8188EU, RTL8192EU) with no problems, and this is one of the few things that remains constant across a lot of Realtek wifi chips, but some wider testing couldn't hurt. Since this touches the code shared with rtwn(4), I've also tested that that still works.
Index: ic/r92creg.h =================================================================== RCS file: /cvs/src/sys/dev/ic/r92creg.h,v retrieving revision 1.24 diff -u -p -r1.24 r92creg.h --- ic/r92creg.h 11 Mar 2019 06:19:33 -0000 1.24 +++ ic/r92creg.h 5 Jun 2020 11:52:21 -0000 @@ -688,6 +688,16 @@ #define R92C_CAMCMD_CLR 0x40000000 #define R92C_CAMCMD_POLLING 0x80000000 +/* Bits for R92C_SECCFG. */ +#define R92C_SECCFG_TXUCKEY_DEF 0x0001 +#define R92C_SECCFG_RXUCKEY_DEF 0x0002 +#define R92C_SECCFG_TXENC_ENA 0x0004 +#define R92C_SECCFG_RXENC_ENA 0x0008 +#define R92C_SECCFG_CMP_A2 0x0010 +#define R92C_SECCFG_MC_SRCH_DIS 0x0020 +#define R92C_SECCFG_TXBCKEY_DEF 0x0040 +#define R92C_SECCFG_RXBCKEY_DEF 0x0080 + /* IMR */ /*Beacon DMA interrupt 6 */ Index: ic/rtwn.c =================================================================== RCS file: /cvs/src/sys/dev/ic/rtwn.c,v retrieving revision 1.49 diff -u -p -r1.49 rtwn.c --- ic/rtwn.c 9 Jan 2020 14:35:19 -0000 1.49 +++ ic/rtwn.c 5 Jun 2020 11:52:22 -0000 @@ -3154,6 +3154,14 @@ rtwn_init(struct ifnet *ifp) /* Clear per-station keys table. */ rtwn_cam_init(sc); + /* Enable decryption / encryption. */ + if (sc->chip & RTWN_CHIP_USB) { + rtwn_write_2(sc, R92C_SECCFG, + R92C_SECCFG_TXUCKEY_DEF | R92C_SECCFG_RXUCKEY_DEF | + R92C_SECCFG_TXENC_ENA | R92C_SECCFG_RXENC_ENA | + R92C_SECCFG_TXBCKEY_DEF | R92C_SECCFG_RXBCKEY_DEF); + } + /* Enable hardware sequence numbering. */ rtwn_write_1(sc, R92C_HWSEQ_CTRL, 0xff); @@ -3204,14 +3212,14 @@ rtwn_init(struct ifnet *ifp) ifq_clr_oactive(&ifp->if_snd); ifp->if_flags |= IFF_RUNNING; -#ifdef notyet - if (ic->ic_flags & IEEE80211_F_WEPON) { + if ((ic->ic_flags & IEEE80211_F_WEPON) && + (sc->chip & RTWN_CHIP_USB)) { /* Install WEP keys. */ for (i = 0; i < IEEE80211_WEP_NKID; i++) ic->ic_set_key(ic, NULL, &ic->ic_nw_keys[i]); sc->sc_ops.wait_async(sc->sc_ops.cookie); } -#endif + if (ic->ic_opmode == IEEE80211_M_MONITOR) ieee80211_new_state(ic, IEEE80211_S_RUN, -1); else Index: usb/if_urtwn.c =================================================================== RCS file: /cvs/src/sys/dev/usb/if_urtwn.c,v retrieving revision 1.89 diff -u -p -r1.89 if_urtwn.c --- usb/if_urtwn.c 26 May 2020 06:04:30 -0000 1.89 +++ usb/if_urtwn.c 5 Jun 2020 11:52:22 -0000 @@ -490,10 +490,8 @@ urtwn_attach(struct device *parent, stru ic->ic_updateslot = urtwn_updateslot; ic->ic_updateedca = urtwn_updateedca; -#ifdef notyet ic->ic_set_key = urtwn_set_key; ic->ic_delete_key = urtwn_delete_key; -#endif /* Override state transition machine. */ ic->ic_newstate = urtwn_newstate; @@ -1035,6 +1033,10 @@ urtwn_set_key(struct ieee80211com *ic, s struct urtwn_softc *sc = (struct urtwn_softc *)self; struct urtwn_cmd_key cmd; + /* Only handle keys for CCMP */ + if (k->k_cipher != IEEE80211_CIPHER_CCMP) + return ieee80211_set_key(ic, ni, k); + /* Defer setting of WEP keys until interface is brought up. */ if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) @@ -1065,6 +1067,12 @@ urtwn_delete_key(struct ieee80211com *ic struct urtwn_softc *sc = (struct urtwn_softc *)self; struct urtwn_cmd_key cmd; + /* Only handle keys for CCMP */ + if (k->k_cipher != IEEE80211_CIPHER_CCMP) { + ieee80211_delete_key(ic, ni, k); + return; + } + if (!(ic->ic_if.if_flags & IFF_RUNNING) || ic->ic_state != IEEE80211_S_RUN) return; /* Nothing to do. */ @@ -1084,6 +1092,52 @@ urtwn_delete_key_cb(struct urtwn_softc * rtwn_delete_key(ic, cmd->ni, &cmd->key); } +int +urtwn_ccmp_decap(struct urtwn_softc *sc, struct mbuf *m, + struct ieee80211_node *ni) +{ + struct ieee80211com *ic = &sc->sc_sc.sc_ic; + struct ieee80211_key *k; + struct ieee80211_frame *wh; + uint64_t pn, *prsc; + uint8_t *ivp; + uint8_t tid; + int hdrlen, hasqos; + + k = ieee80211_get_rxkey(ic, m, ni); + if (k == NULL) + return 1; + + wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_get_hdrlen(wh); + ivp = (uint8_t *)wh + hdrlen; + + /* Check that ExtIV bit is set. */ + if (!(ivp[3] & IEEE80211_WEP_EXTIV)) + return 1; + + hasqos = ieee80211_has_qos(wh); + tid = hasqos ? ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0; + prsc = &k->k_rsc[tid]; + + /* 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) { + ic->ic_stats.is_ccmp_replays++; + return 1; + } + /* Last seen packet number is updated in ieee80211_inputm(). */ + + /* Strip MIC. IV will be stripped by ieee80211_inputm(). */ + m_adj(m, -IEEE80211_CCMP_MICLEN); + return 0; +} + void urtwn_rx_frame(struct urtwn_softc *sc, uint8_t *buf, int pktlen, struct mbuf_list *ml) @@ -1197,6 +1251,21 @@ urtwn_rx_frame(struct urtwn_softc *sc, u rxi.rxi_flags = 0; rxi.rxi_rssi = rssi; rxi.rxi_tstamp = 0; /* Unused. */ + + /* Handle hardware decryption. */ + if (((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) + && (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && + (ni->ni_flags & IEEE80211_NODE_RXPROT) && + ni->ni_pairwise_key.k_cipher == IEEE80211_CIPHER_CCMP) { + if (urtwn_ccmp_decap(sc, m, ni) != 0) { + ifp->if_ierrors++; + m_freem(m); + ieee80211_release_node(ic, ni); + return; + } + rxi.rxi_flags |= IEEE80211_RXI_HWDEC; + } + ieee80211_inputm(ifp, m, ni, &rxi, ml); /* Node is no longer needed. */ ieee80211_release_node(ic, ni); @@ -1360,37 +1429,28 @@ urtwn_tx_fill_desc(struct urtwn_softc *s struct r92c_tx_desc_usb *txd; struct ieee80211com *ic = &sc->sc_sc.sc_ic; uint8_t raid, type; + uint32_t pktlen; txd = (struct r92c_tx_desc_usb *)*txdp; (*txdp) += sizeof(*txd); memset(txd, 0, sizeof(*txd)); + pktlen = m->m_pkthdr.len; + if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) { + txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER, + R92C_TXDW1_CIPHER_AES)); + pktlen += IEEE80211_CCMP_HDRLEN; + } + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; txd->txdw0 |= htole32( - SM(R92C_TXDW0_PKTLEN, m->m_pkthdr.len) | + SM(R92C_TXDW0_PKTLEN, pktlen) | SM(R92C_TXDW0_OFFSET, sizeof(*txd)) | R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) txd->txdw0 |= htole32(R92C_TXDW0_BMCAST); -#ifdef notyet - if (k != NULL) { - switch (k->k_cipher) { - case IEEE80211_CIPHER_WEP40: - case IEEE80211_CIPHER_WEP104: - case IEEE80211_CIPHER_TKIP: - cipher = R92C_TXDW1_CIPHER_RC4; - break; - case IEEE80211_CIPHER_CCMP: - cipher = R92C_TXDW1_CIPHER_AES; - break; - default: - cipher = R92C_TXDW1_CIPHER_NONE; - } - txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER, cipher)); - } -#endif if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && type == IEEE80211_FC0_TYPE_DATA) { if (ic->ic_curmode == IEEE80211_MODE_11B || @@ -1413,7 +1473,7 @@ urtwn_tx_fill_desc(struct urtwn_softc *s SM(R92C_TXDW1_RAID, raid) | R92C_TXDW1_AGGBK); } - if (m->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { + if (pktlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { txd->txdw4 |= htole32(R92C_TXDW4_RTSEN | R92C_TXDW4_HWRTSEN); } else if (ic->ic_flags & IEEE80211_F_USEPROT) { @@ -1468,6 +1528,7 @@ urtwn_tx_fill_desc_gen2(struct urtwn_sof struct r92e_tx_desc_usb *txd; struct ieee80211com *ic = &sc->sc_sc.sc_ic; uint8_t raid, type; + uint32_t pktlen; txd = (struct r92e_tx_desc_usb *)*txdp; (*txdp) += sizeof(*txd); @@ -1475,17 +1536,20 @@ urtwn_tx_fill_desc_gen2(struct urtwn_sof type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + pktlen = m->m_pkthdr.len; + if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) { + txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER, + R92C_TXDW1_CIPHER_AES)); + pktlen += IEEE80211_CCMP_HDRLEN; + } + txd->txdw0 |= htole32( - SM(R92C_TXDW0_PKTLEN, m->m_pkthdr.len) | + SM(R92C_TXDW0_PKTLEN, pktlen) | SM(R92C_TXDW0_OFFSET, sizeof(*txd)) | R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) txd->txdw0 |= htole32(R92C_TXDW0_BMCAST); -#ifdef notyet - /* cipher */ -#endif - if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && type == IEEE80211_FC0_TYPE_DATA) { if (ic->ic_curmode == IEEE80211_MODE_11B || @@ -1500,7 +1564,7 @@ urtwn_tx_fill_desc_gen2(struct urtwn_sof /* Request TX status report for AMRR */ txd->txdw2 |= htole32(R92C_TXDW2_CCX_RPT | R88E_TXDW2_AGGBK); - if (m->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { + if (pktlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { txd->txdw4 |= htole32(R92C_TXDW4_RTSEN | R92C_TXDW4_HWRTSEN); } else if (ic->ic_flags & IEEE80211_F_USEPROT) { @@ -1549,16 +1613,18 @@ urtwn_tx(void *cookie, struct mbuf *m, s struct usbd_pipe *pipe; uint16_t qos, sum; uint8_t tid, qid; - int i, xferlen, error; + int i, xferlen, error, headerlen; uint8_t *txdp; 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); - wh = mtod(m, struct ieee80211_frame *); + if (k->k_cipher != IEEE80211_CIPHER_CCMP) { + if ((m = ieee80211_encrypt(ic, m, k)) == NULL) + return (ENOBUFS); + wh = mtod(m, struct ieee80211_frame *); + } } if (ieee80211_has_qos(wh)) { @@ -1611,9 +1677,31 @@ urtwn_tx(void *cookie, struct mbuf *m, s } #endif - xferlen = (txdp - data->buf) + m->m_pkthdr.len; - m_copydata(m, 0, m->m_pkthdr.len, txdp); - m_freem(m); + if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) { + xferlen = (txdp - data->buf) + m->m_pkthdr.len + + IEEE80211_CCMP_HDRLEN; + headerlen = ieee80211_get_hdrlen(wh); + + m_copydata(m, 0, headerlen, txdp); + txdp += headerlen; + + k->k_tsc++; + txdp[0] = k->k_tsc; + txdp[1] = k->k_tsc >> 8; + txdp[2] = 0; + txdp[3] = k->k_id | IEEE80211_WEP_EXTIV; + txdp[4] = k->k_tsc >> 16; + txdp[5] = k->k_tsc >> 24; + txdp[6] = k->k_tsc >> 32; + txdp[7] = k->k_tsc >> 40; + txdp += IEEE80211_CCMP_HDRLEN; + + m_copydata(m, headerlen, m->m_pkthdr.len - headerlen, txdp); + } else { + xferlen = (txdp - data->buf) + m->m_pkthdr.len; + m_copydata(m, 0, m->m_pkthdr.len, txdp); + m_freem(m); + } data->pipe = pipe; usbd_setup_xfer(data->xfer, pipe, data, data->buf, xferlen,