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