Hi Stefan, > On 2/08/2019, at 11:13 PM, Stefan Sperling <s...@stsp.name> wrote: > > This diff enables HW offload for WPA2 CCMP (AES) encrypted unicast > frames in iwm(4). This is in preparation for Tx aggregation support.
Lightly tested on: iwm0 at pci1 dev 0 function 0 "Intel Dual Band Wireless AC 7265" rev 0x69, msi iwm0: hw rev 0x210, fw ver 16.242414.0, address xx:xx:xx:xx:xx:xx pkg_add -u; download a few big files; ifconfig down; ifconfig up; change network. comments inline. supposing the async set keys command is a non-issue then, for what it’s worth from this 802.11 novice, ok procter@ thanks! Richard. > WEP and WPA1/TKIP ciphers are still handled in software, which mirrors > what the older iwn(4) driver is doing. We don't enable 11n at all with > those ciphers (see ieee80211_ht_negotiate()), and we won't aggregate > non-encrypted frames (see ieee80211_can_use_ampdu()). > > Based on an initial diff by procter@ and some code from iwn(4). > > Tested on 7260, 7265, 8260, and 8265 in bsd.rd upgrade and with pkg_add -u. > > ok? > > diff refs/heads/master refs/heads/iwm-hwcrypt > blob - 7add1e9e682ef5e22169ec1e89a182cda1af7e2a > blob + 839c0a0f8b3a62115ba6d5e15adfa63158475c86 > --- sys/dev/pci/if_iwm.c > +++ sys/dev/pci/if_iwm.c > @@ -367,6 +367,8 @@ int iwm_get_signal_strength(struct iwm_softc *, > struct > void iwm_rx_rx_phy_cmd(struct iwm_softc *, struct iwm_rx_packet *, > struct iwm_rx_data *); > int iwm_get_noise(const struct iwm_statistics_rx_non_phy *); > +int iwm_ccmp_decap(struct iwm_softc *, struct mbuf *, > + struct ieee80211_node *); > void iwm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *, > struct iwm_rx_data *); > void iwm_enable_ht_cck_fallback(struct iwm_softc *, struct iwm_node *); > @@ -448,6 +450,10 @@ int iwm_disassoc(struct iwm_softc *); > int iwm_run(struct iwm_softc *); > int iwm_run_stop(struct iwm_softc *); > struct ieee80211_node *iwm_node_alloc(struct ieee80211com *); > +int iwm_set_key(struct ieee80211com *, struct ieee80211_node *, > + struct ieee80211_key *); > +void iwm_delete_key(struct ieee80211com *, > + struct ieee80211_node *, struct ieee80211_key *); > void iwm_calib_timeout(void *); > int iwm_media_change(struct ifnet *); > void iwm_newstate_task(void *); > @@ -3429,11 +3435,91 @@ iwm_get_noise(const struct iwm_statistics_rx_non_phy * > return (nbant == 0) ? -127 : (total / nbant) - 107; > } > > +int > +iwm_ccmp_decap(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node > *ni) > +{ > + struct ieee80211com *ic = &sc->sc_ic; > + struct ieee80211_key *k = &ni->ni_pairwise_key; > + struct ieee80211_frame *wh; > + struct ieee80211_rx_ba *ba; > + uint64_t pn, *prsc; > + uint8_t *ivp; > + uint8_t tid; > + int hdrlen, hasqos; > + > + wh = mtod(m, struct ieee80211_frame *); > + hdrlen = ieee80211_get_hdrlen(wh); > + ivp = (uint8_t *)wh + hdrlen; > + > + /* Check that ExtIV bit is be set. */ > + if (!(ivp[3] & IEEE80211_WEP_EXTIV)) { > + DPRINTF(("CCMP decap ExtIV not set\n")); > + 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[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) { > + 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_input() as part of A-MPDU reordering. > + */ > + } else if (ieee80211_has_seq(wh)) { > + /* > + * Not necessarily a replayed frame since we did not > + * check the sequence number of the 802.11 header yet. > + */ > + int nrxseq, orxseq; > + > + nrxseq = letoh16(*(u_int16_t *)wh->i_seq) >> > + IEEE80211_SEQ_SEQ_SHIFT; > + if (hasqos) > + orxseq = ni->ni_qos_rxseqs[tid]; > + else > + orxseq = ni->ni_rxseq; > + if (nrxseq < orxseq) { > + DPRINTF(("CCMP replayed (n=%d < o=%d)\n", > + nrxseq, orxseq)); > + ic->ic_stats.is_ccmp_replays++; > + return 1; > + } > + } else { > + DPRINTF(("CCMP replayed\n")); > + 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; > +} > + > void > iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_packet *pkt, > struct iwm_rx_data *data) > { > struct ieee80211com *ic = &sc->sc_ic; > + struct ifnet *ifp = IC2IFP(ic); > struct ieee80211_frame *wh; > struct ieee80211_node *ni; > struct ieee80211_rxinfo rxi; > @@ -3509,6 +3595,38 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac > rxi.rxi_rssi = rssi; > rxi.rxi_tstamp = device_timestamp; > > + /* Handle hardware decryption. */ > + if (((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) > + && (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && > + !IEEE80211_IS_MULTICAST(wh->i_addr1) && > + (ni->ni_flags & IEEE80211_NODE_RXPROT) && > + ni->ni_pairwise_key.k_cipher == IEEE80211_CIPHER_CCMP) { > + if ((rx_pkt_status & IWM_RX_MPDU_RES_STATUS_SEC_ENC_MSK) != > + IWM_RX_MPDU_RES_STATUS_SEC_CCM_ENC) { > + ic->ic_stats.is_ccmp_dec_errs++; > + ifp->if_ierrors++; > + m_freem(m); > + goto done; > + } > + /* Check whether decryption was successful or not. */ > + if ((rx_pkt_status & > + (IWM_RX_MPDU_RES_STATUS_DEC_DONE | > + IWM_RX_MPDU_RES_STATUS_MIC_OK)) != > + (IWM_RX_MPDU_RES_STATUS_DEC_DONE | > + IWM_RX_MPDU_RES_STATUS_MIC_OK)) { > + ic->ic_stats.is_ccmp_dec_errs++; > + ifp->if_ierrors++; > + m_freem(m); > + goto done; > + } > + if (iwm_ccmp_decap(sc, m, ni) != 0) { > + ifp->if_ierrors++; > + m_freem(m); > + goto done; > + } > + rxi.rxi_flags |= IEEE80211_RXI_HWDEC; > + } > + > #if NBPFILTER > 0 > if (sc->sc_drvbpf != NULL) { > struct mbuf mb; > @@ -3566,6 +3684,7 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac > } > #endif > ieee80211_input(IC2IFP(ic), m, ni, &rxi); > +done: > /* > * ieee80211_input() might have changed our BSS. > * Restore ic_bss's channel if we are still in the same BSS. > @@ -4218,6 +4337,7 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ie > uint8_t tid, type; > int i, totlen, err, pad; > int hdrlen2, rtsthres = ic->ic_rtsthreshold; > + uint8_t *ivp; nit: shift declare up under const struct iwm_rate? (declare order by size) > > wh = mtod(m, struct ieee80211_frame *); > hdrlen = ieee80211_get_hdrlen(wh); > @@ -4278,16 +4398,48 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ie > bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT); > } > #endif > + totlen = m->m_pkthdr.len; > > 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; > - /* 802.11 header may have moved. */ > - wh = mtod(m, struct ieee80211_frame *); > + k = ieee80211_get_txkey(ic, wh, ni); > + if (k->k_cipher != IEEE80211_CIPHER_CCMP) { > + if ((m = ieee80211_encrypt(ic, m, k)) == NULL) > + return ENOBUFS; > + > + /* 802.11 header may have moved. */ > + wh = mtod(m, struct ieee80211_frame *); > + totlen = m->m_pkthdr.len; > + } else { > + /* HW appends CCMP MIC */ > + totlen += IEEE80211_CCMP_HDRLEN; > + } > } > - totlen = m->m_pkthdr.len; > > + /* Copy 802.11 header in TX command. */ > + memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen); > + > + if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) { > + /* Trim 802.11 header and prepend CCMP IV. */ > + m_adj(m, hdrlen - IEEE80211_CCMP_HDRLEN); > + ivp = mtod(m, u_int8_t *); > + k->k_tsc++; /* increment the 48-bit PN */ > + ivp[0] = k->k_tsc; /* PN0 */ > + ivp[1] = k->k_tsc >> 8; /* PN1 */ > + ivp[2] = 0; /* Rsvd */ > + ivp[3] = k->k_id << 6 | IEEE80211_WEP_EXTIV; > + ivp[4] = k->k_tsc >> 16; /* PN2 */ > + ivp[5] = k->k_tsc >> 24; /* PN3 */ > + ivp[6] = k->k_tsc >> 32; /* PN4 */ > + ivp[7] = k->k_tsc >> 40; /* PN5 */ > + > + tx->sec_ctl = IWM_TX_CMD_SEC_CCM; > + memcpy(tx->key, k->k_key, k->k_len); The ring warrants an explicit_bzero() on teardown to clear these keys I think. The other drivers don’t do this that I can see; perhaps best left for another diff. > + } else { > + /* Trim 802.11 header. */ > + m_adj(m, hdrlen); > + tx->sec_ctl = 0; > + } > + > flags = 0; > if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { > flags |= IWM_TX_CMD_FLG_ACK; > @@ -4339,17 +4491,10 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ie > tx->dram_lsb_ptr = htole32(data->scratch_paddr); > tx->dram_msb_ptr = iwm_get_dma_hi_addr(data->scratch_paddr); > > - /* Copy 802.11 header in TX command. */ > - memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen); > - > flags |= IWM_TX_CMD_FLG_BT_DIS | IWM_TX_CMD_FLG_SEQ_CTL; > > - tx->sec_ctl = 0; > tx->tx_flags |= htole32(flags); > > - /* Trim 802.11 header. */ > - m_adj(m, hdrlen); > - > err = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m, > BUS_DMA_NOWAIT | BUS_DMA_WRITE); > if (err && err != EFBIG) { > @@ -5939,7 +6084,60 @@ iwm_node_alloc(struct ieee80211com *ic) > return malloc(sizeof (struct iwm_node), M_DEVBUF, M_NOWAIT | M_ZERO); > } > > +int > +iwm_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, > + struct ieee80211_key *k) > +{ > + struct iwm_softc *sc = ic->ic_softc; > + struct iwm_add_sta_key_cmd cmd = { 0 }; > + > + if ((k->k_flags & IEEE80211_KEY_GROUP) || > + k->k_cipher != IEEE80211_CIPHER_CCMP) { > + /* Fallback to software crypto for other ciphers. */ > + return (ieee80211_set_key(ic, ni, k)); nit: bad indent > + } > + > + cmd.key_flags = htole16(IWM_STA_KEY_FLG_CCM | > + IWM_STA_KEY_FLG_WEP_KEY_MAP | > + ((k->k_id << IWM_STA_KEY_FLG_KEYID_POS) & > + IWM_STA_KEY_FLG_KEYID_MSK)); > + if (k->k_flags & IEEE80211_KEY_GROUP) > + cmd.key_flags |= htole16(IWM_STA_KEY_MULTICAST); > + > + memcpy(cmd.key, k->k_key, k->k_len); > + cmd.key_offset = 0; > + cmd.sta_id = IWM_STATION_ID; > + > + return iwm_send_cmd_pdu(sc, IWM_ADD_STA_KEY, IWM_CMD_ASYNC, > + sizeof(cmd), &cmd); Might the async command open a race between loading keys and decrypting received packets? > +} > + > void > +iwm_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, > + struct ieee80211_key *k) > +{ > + struct iwm_softc *sc = ic->ic_softc; > + struct iwm_add_sta_key_cmd cmd = { 0 }; > + > + if ((k->k_flags & IEEE80211_KEY_GROUP) || > + (k->k_cipher != IEEE80211_CIPHER_CCMP)) { > + /* Fallback to software crypto for other ciphers. */ > + ieee80211_delete_key(ic, ni, k); > + return; > + } > + > + cmd.key_flags = htole16(IWM_STA_KEY_NOT_VALID | > + IWM_STA_KEY_FLG_NO_ENC | IWM_STA_KEY_FLG_WEP_KEY_MAP | > + ((k->k_id << IWM_STA_KEY_FLG_KEYID_POS) & > + IWM_STA_KEY_FLG_KEYID_MSK)); > + memcpy(cmd.key, k->k_key, k->k_len); > + cmd.key_offset = 0; > + cmd.sta_id = IWM_STATION_ID; > + > + iwm_send_cmd_pdu(sc, IWM_ADD_STA_KEY, IWM_CMD_ASYNC, sizeof(cmd), &cmd); > +} > + > +void > iwm_calib_timeout(void *arg) > { > struct iwm_softc *sc = arg; > @@ -7132,6 +7330,7 @@ iwm_notif_intr(struct iwm_softc *sc) > case IWM_DTS_MEASUREMENT_NOTIFICATION: > break; > > + case IWM_ADD_STA_KEY: > case IWM_PHY_CONFIGURATION_CMD: > case IWM_TX_ANT_CONFIGURATION_CMD: > case IWM_ADD_STA: > @@ -7814,6 +8013,8 @@ iwm_attach(struct device *parent, struct device *self, > > ic->ic_node_alloc = iwm_node_alloc; > ic->ic_bgscan_start = iwm_bgscan; > + ic->ic_set_key = iwm_set_key; > + ic->ic_delete_key = iwm_delete_key; > > /* Override 802.11 state transition machine. */ > sc->sc_newstate = ic->ic_newstate; > blob - da3d4bd1b26d2f88f469180075a00691739be6a5 > blob + def6715b6dc57708417d5dc3fb9d346b053e0197 > --- sys/dev/pci/if_iwmreg.h > +++ sys/dev/pci/if_iwmreg.h > @@ -641,11 +641,11 @@ > #define IWM_UCODE_TLV_API_LQ_SS_PARAMS (1 << 18) > #define IWM_UCODE_TLV_API_EXT_SCAN_PRIORITY (1 << 24) > #define IWM_UCODE_TLV_API_TX_POWER_CHAIN (1 << 27) > - > +#define IWM_UCODE_TLV_API_TKIP_MIC_KEYS (1 << 29) > #define IWM_NUM_UCODE_TLV_API = 32 > > #define IWM_UCODE_TLV_API_BITS \ > - > "\020\10FRAGMENTED_SCAN\11WIFI_MCC_UPDATE\16WIDE_CMD_HDR\22LQ_SS_PARAMS\30EXT_SCAN_PRIO\33TX_POWER_CHAIN" > + > "\020\10FRAGMENTED_SCAN\11WIFI_MCC_UPDATE\16WIDE_CMD_HDR\22LQ_SS_PARAMS\30EXT_SCAN_PRIO\33TX_POWER_CHAIN\35TKIP_MIC_KEYS" > > /** > * uCode capabilities note: I copied this last hunk over from the linux driver; it’s unused by your diff.