This patch implements WPA2 (CCMP) crypto offload for iwx(4). The patch will only work on top of an up-to-date tree. Note that I just committed another patch (Tx rate selection offload) to CVS which is required in order to test this CCMP offload patch.
ok? diff 928c82450e6e5ea105cab6ca7a7766c4b84f1e21 730c65a14bf291ecd58baa8b3cc72e18ca34903d blob - 113362f4c0f4a672dc8ef719bd95964a53817db5 blob + d3fa66b174be1e0200e95254640f95c60c0e4508 --- sys/dev/pci/if_iwx.c +++ sys/dev/pci/if_iwx.c @@ -349,8 +349,10 @@ int iwx_rxmq_get_signal_strength(struct iwx_softc *, s void iwx_rx_rx_phy_cmd(struct iwx_softc *, struct iwx_rx_packet *, struct iwx_rx_data *); int iwx_get_noise(const struct iwx_statistics_rx_non_phy *); -void iwx_rx_frame(struct iwx_softc *, struct mbuf *, int, int, int, uint32_t, - struct ieee80211_rxinfo *, struct mbuf_list *); +int iwx_ccmp_decap(struct iwx_softc *, struct mbuf *, + struct ieee80211_node *); +void iwx_rx_frame(struct iwx_softc *, struct mbuf *, int, uint32_t, int, int, + uint32_t, struct ieee80211_rxinfo *, struct mbuf_list *); void iwx_enable_ht_cck_fallback(struct iwx_softc *, struct iwx_node *); void iwx_rx_tx_cmd_single(struct iwx_softc *, struct iwx_rx_packet *, struct iwx_node *, int, int); @@ -420,6 +422,10 @@ int iwx_disassoc(struct iwx_softc *); int iwx_run(struct iwx_softc *); int iwx_run_stop(struct iwx_softc *); struct ieee80211_node *iwx_node_alloc(struct ieee80211com *); +int iwx_set_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); +void iwx_delete_key(struct ieee80211com *, + struct ieee80211_node *, struct ieee80211_key *); void iwx_calib_timeout(void *); int iwx_media_change(struct ifnet *); void iwx_newstate_task(void *); @@ -3538,16 +3544,65 @@ iwx_get_noise(const struct iwx_statistics_rx_non_phy * return (nbant == 0) ? -127 : (total / nbant) - 107; } +int +iwx_ccmp_decap(struct iwx_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; + 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)) + 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(). */ + + /* + * Some firmware versions strip the MIC, and some don't. It is not + * clear which of the capability flags could tell us what to expect. + * For now, keep things simple and just leave the MIC in place if + * it is present. + * + * The IV will be stripped by ieee80211_inputm(). + */ + return 0; +} + void iwx_rx_frame(struct iwx_softc *sc, struct mbuf *m, int chanidx, - int is_shortpre, int rate_n_flags, uint32_t device_timestamp, - struct ieee80211_rxinfo *rxi, struct mbuf_list *ml) + uint32_t rx_pkt_status, int is_shortpre, int rate_n_flags, + uint32_t device_timestamp, struct ieee80211_rxinfo *rxi, + struct mbuf_list *ml) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct ieee80211_channel *bss_chan; uint8_t saved_bssid[IEEE80211_ADDR_LEN] = { 0 }; + struct ifnet *ifp = IC2IFP(ic); if (chanidx < 0 || chanidx >= nitems(ic->ic_channels)) chanidx = ieee80211_chan2ieee(ic, ic->ic_ibss_chan); @@ -3564,6 +3619,37 @@ iwx_rx_frame(struct iwx_softc *sc, struct mbuf *m, int } ni->ni_chan = &ic->ic_channels[chanidx]; + /* 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 ((rx_pkt_status & IWX_RX_MPDU_RES_STATUS_SEC_ENC_MSK) != + IWX_RX_MPDU_RES_STATUS_SEC_CCM_ENC) { + ic->ic_stats.is_ccmp_dec_errs++; + ifp->if_ierrors++; + m_freem(m); + return; + } + /* Check whether decryption was successful or not. */ + if ((rx_pkt_status & + (IWX_RX_MPDU_RES_STATUS_DEC_DONE | + IWX_RX_MPDU_RES_STATUS_MIC_OK)) != + (IWX_RX_MPDU_RES_STATUS_DEC_DONE | + IWX_RX_MPDU_RES_STATUS_MIC_OK)) { + ic->ic_stats.is_ccmp_dec_errs++; + ifp->if_ierrors++; + m_freem(m); + return; + } + if (iwx_ccmp_decap(sc, m, ni) != 0) { + ifp->if_ierrors++; + m_freem(m); + return; + } + rxi->rxi_flags |= IEEE80211_RXI_HWDEC; + } + #if NBPFILTER > 0 if (sc->sc_drvbpf != NULL) { struct iwx_rx_radiotap_header *tap = &sc->sc_rxtap; @@ -3685,6 +3771,14 @@ iwx_rx_mpdu_mq(struct iwx_softc *sc, struct mbuf *m, v } } else hdrlen = ieee80211_get_hdrlen(wh); + + if ((le16toh(desc->status) & + IWX_RX_MPDU_RES_STATUS_SEC_ENC_MSK) == + IWX_RX_MPDU_RES_STATUS_SEC_CCM_ENC) { + /* Padding is inserted after the IV. */ + hdrlen += IEEE80211_CCMP_HDRLEN; + } + memmove(m->m_data + 2, m->m_data, hdrlen); m_adj(m, 2); } @@ -3702,7 +3796,7 @@ iwx_rx_mpdu_mq(struct iwx_softc *sc, struct mbuf *m, v rxi.rxi_rssi = rssi; rxi.rxi_tstamp = le64toh(desc->v1.tsf_on_air_rise); - iwx_rx_frame(sc, m, chanidx, + iwx_rx_frame(sc, m, chanidx, le16toh(desc->status), (phy_info & IWX_RX_MPDU_PHY_SHORT_PREAMBLE), rate_n_flags, device_timestamp, &rxi, ml); } @@ -4316,7 +4410,6 @@ iwx_tx_fill_cmd(struct iwx_softc *sc, struct iwx_node ridx = min_ridx; } - flags |= IWX_TX_FLAGS_ENCRYPT_DIS; if ((ic->ic_flags & IEEE80211_F_RSNON) && ni->ni_rsn_supp_state == RSNA_SUPP_PTKNEGOTIATING) flags |= IWX_TX_FLAGS_HIGH_PRI; @@ -4454,11 +4547,19 @@ iwx_tx(struct iwx_softc *sc, struct mbuf *m, struct ie 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 *); - } + 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 *); + tx->flags |= htole32(IWX_TX_FLAGS_ENCRYPT_DIS); + } else { + k->k_tsc++; + /* Hardware increments PN internally and adds IV. */ + } + } else + tx->flags |= htole32(IWX_TX_FLAGS_ENCRYPT_DIS); + totlen = m->m_pkthdr.len; if (hdrlen & 3) { @@ -5746,7 +5847,7 @@ iwx_enable_data_tx_queues(struct iwx_softc *sc) * Regular data frames use the "MGMT" TID and queue. * Other TIDs and queues are reserved for frame aggregation. */ - err = iwx_enable_txq(sc, IWX_STATION_ID, qid, IWX_MGMT_TID, + err = iwx_enable_txq(sc, IWX_STATION_ID, qid, IWX_TID_NON_QOS, IWX_TX_RING_COUNT); if (err) { printf("%s: could not enable Tx queue %d (error %d)\n", @@ -6225,7 +6326,69 @@ iwx_node_alloc(struct ieee80211com *ic) return malloc(sizeof (struct iwx_node), M_DEVBUF, M_NOWAIT | M_ZERO); } +int +iwx_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct iwx_softc *sc = ic->ic_softc; + struct iwx_add_sta_key_cmd cmd; + + if (k->k_cipher != IEEE80211_CIPHER_CCMP) { + /* Fallback to software crypto for other ciphers. */ + return (ieee80211_set_key(ic, ni, k)); + } + + memset(&cmd, 0, sizeof(cmd)); + + cmd.common.key_flags = htole16(IWX_STA_KEY_FLG_CCM | + IWX_STA_KEY_FLG_WEP_KEY_MAP | + ((k->k_id << IWX_STA_KEY_FLG_KEYID_POS) & + IWX_STA_KEY_FLG_KEYID_MSK)); + if (k->k_flags & IEEE80211_KEY_GROUP) { + cmd.common.key_offset = 1; + cmd.common.key_flags |= htole16(IWX_STA_KEY_MULTICAST); + } else + cmd.common.key_offset = 0; + + memcpy(cmd.common.key, k->k_key, MIN(sizeof(cmd.common.key), k->k_len)); + cmd.common.sta_id = IWX_STATION_ID; + + cmd.transmit_seq_cnt = htole64(k->k_tsc); + + return iwx_send_cmd_pdu(sc, IWX_ADD_STA_KEY, IWX_CMD_ASYNC, + sizeof(cmd), &cmd); +} + void +iwx_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct iwx_softc *sc = ic->ic_softc; + struct iwx_add_sta_key_cmd cmd; + + if (k->k_cipher != IEEE80211_CIPHER_CCMP) { + /* Fallback to software crypto for other ciphers. */ + ieee80211_delete_key(ic, ni, k); + return; + } + + memset(&cmd, 0, sizeof(cmd)); + + cmd.common.key_flags = htole16(IWX_STA_KEY_NOT_VALID | + IWX_STA_KEY_FLG_NO_ENC | IWX_STA_KEY_FLG_WEP_KEY_MAP | + ((k->k_id << IWX_STA_KEY_FLG_KEYID_POS) & + IWX_STA_KEY_FLG_KEYID_MSK)); + memcpy(cmd.common.key, k->k_key, MIN(sizeof(cmd.common.key), k->k_len)); + if (k->k_flags & IEEE80211_KEY_GROUP) + cmd.common.key_offset = 1; + else + cmd.common.key_offset = 0; + cmd.common.sta_id = IWX_STATION_ID; + + iwx_send_cmd_pdu(sc, IWX_ADD_STA_KEY, IWX_CMD_ASYNC, sizeof(cmd), &cmd); +} + +void iwx_calib_timeout(void *arg) { struct iwx_softc *sc = arg; @@ -7461,6 +7624,7 @@ iwx_rx_pkt(struct iwx_softc *sc, struct iwx_rx_data *d case IWX_WIDE_ID(IWX_REGULATORY_AND_NVM_GROUP, IWX_NVM_GET_INFO): + case IWX_ADD_STA_KEY: case IWX_PHY_CONFIGURATION_CMD: case IWX_TX_ANT_CONFIGURATION_CMD: case IWX_ADD_STA: @@ -8237,6 +8401,8 @@ iwx_attach(struct device *parent, struct device *self, /* TODO: background scans trigger firmware errors */ ic->ic_bgscan_start = iwx_bgscan; #endif + ic->ic_set_key = iwx_set_key; + ic->ic_delete_key = iwx_delete_key; /* Override 802.11 state transition machine. */ sc->sc_newstate = ic->ic_newstate; blob - c49690425cac441d8e3e7068a8159076ffb0b1eb blob + edbee21ab26f94dca9b78ef92b9d92afd0128ebe --- sys/dev/pci/if_iwxreg.h +++ sys/dev/pci/if_iwxreg.h @@ -6023,7 +6023,7 @@ struct iwx_umac_scan_iter_complete_notif { * @IWX_STA_KEY_FLG_KEYID_MSK: the index of the key * @IWX_STA_KEY_NOT_VALID: key is invalid * @IWX_STA_KEY_FLG_WEP_13BYTES: set for 13 bytes WEP key - * @IWX_STA_KEY_MULTICAST: set for multical key + * @IWX_STA_KEY_MULTICAST: set for multicast key * @IWX_STA_KEY_MFP: key is used for Management Frame Protection */ #define IWX_STA_KEY_FLG_NO_ENC (0 << 0) @@ -6217,30 +6217,51 @@ struct iwx_add_sta_cmd { #define IWX_STA_AUX_ACTIVITY 4 /** - * struct iwx_add_sta_key_cmd - add/modify sta key - * ( IWX_REPLY_ADD_STA_KEY = 0x17 ) + * struct iwx_add_sta_key_common - add/modify sta key common part + * ( REPLY_ADD_STA_KEY = 0x17 ) * @sta_id: index of station in uCode's station table * @key_offset: key offset in key storage - * @key_flags: type %iwx_sta_key_flag + * @key_flags: IWX_STA_KEY_FLG_* * @key: key material data - * @key2: key material data * @rx_secur_seq_cnt: RX security sequence counter for the key - * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection - * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx */ -struct iwx_add_sta_key_cmd { +struct iwx_add_sta_key_common { uint8_t sta_id; uint8_t key_offset; uint16_t key_flags; - uint8_t key[16]; - uint8_t key2[16]; + uint8_t key[32]; uint8_t rx_secur_seq_cnt[16]; +} __packed; + +/** + * struct iwx_add_sta_key_cmd_v1 - add/modify sta key + * @common: see &struct iwx_add_sta_key_common + * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection + * @reserved: reserved + * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx + */ +struct iwx_add_sta_key_cmd_v1 { + struct iwx_add_sta_key_common common; uint8_t tkip_rx_tsc_byte2; uint8_t reserved; uint16_t tkip_rx_ttak[5]; -} __packed; /* IWX_ADD_MODIFY_STA_KEY_API_S_VER_1 */ +} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_1 */ /** + * struct iwx_add_sta_key_cmd - add/modify sta key + * @common: see &struct iwx_add_sta_key_common + * @rx_mic_key: TKIP RX unicast or multicast key + * @tx_mic_key: TKIP TX key + * @transmit_seq_cnt: TSC, transmit packet number + */ +struct iwx_add_sta_key_cmd { + struct iwx_add_sta_key_common common; + uint64_t rx_mic_key; + uint64_t tx_mic_key; + uint64_t transmit_seq_cnt; +} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_2 */ + +/** * status in the response to ADD_STA command * @IWX_ADD_STA_SUCCESS: operation was executed successfully * @IWX_ADD_STA_STATIONS_OVERLOAD: no room left in the fw's station table @@ -6579,6 +6600,7 @@ struct iwx_rx_packet { #define IWX_FH_RSCSR_FRAME_INVALID 0x55550000 #define IWX_FH_RSCSR_FRAME_ALIGN 0x40 #define IWX_FH_RSCSR_RPA_EN (1 << 25) +#define IWX_FH_RSCSR_RADA_EN (1 << 26) #define IWX_FH_RSCSR_RXQ_POS 16 #define IWX_FH_RSCSR_RXQ_MASK 0x3F0000