On Fri, May 15, 2020 at 05:02:28PM +0200, Stefan Sperling wrote:
> This has been attempted before, but had to backed out because in some
> cases firmware was failing to decrypt a subset of received frames, which
> resulted in packet loss.
> 
> That was before we updated all iwm(4) devices to newer firmware in the
> previous release cycle. I hope we won't see such problems again now.
> 
> Please help out with getting this tested on the many iwm(4) variants:
> 7260, 7265, 3160, 3165, 3168, 8260, 8265, 9260, and 9560.

Turns out that my initial testing was insufficient. Sorry about that.

My previous patch, which was based on code code written against older
firmware, did not work on some devices. I missed that some newer firmware
versions we use nowadays have different hardware encryption APIs.

This new version has been tested by me on 7265, 8260, and 9260.
It seems to work fine on those devices.

Could someone test on any of the other devices?

ok?

diff refs/heads/master refs/heads/iwm-ccmp
blob - 9193ae3c7b5101a86b85b1b3ba48489d75f5150c
blob + 8bc189dc146562f7ab0af2f1690fa02676aecfcd
--- sys/dev/ic/ar5008.c
+++ sys/dev/ic/ar5008.c
@@ -794,9 +794,8 @@ ar5008_ccmp_decap(struct athn_softc *sc, struct mbuf *
        struct ieee80211_frame *wh;
        struct ieee80211_rx_ba *ba;
        uint64_t pn, *prsc;
-       u_int8_t *ivp, *mmie;
+       u_int8_t *ivp;
        uint8_t tid;
-       uint16_t kid;
        int hdrlen, hasqos;
        uintptr_t entry;
 
@@ -805,35 +804,8 @@ ar5008_ccmp_decap(struct athn_softc *sc, struct mbuf *
        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)
+       k = ieee80211_get_rxkey(ic, m, ni);
+       if (k == NULL || k->k_cipher != IEEE80211_CIPHER_CCMP)
                return 1;
 
        /* Sanity checks to ensure this is really a key we installed. */
@@ -853,7 +825,7 @@ ar5008_ccmp_decap(struct athn_softc *sc, struct mbuf *
        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];
+       prsc = &k->k_rsc[tid];
 
        /* Extract the 48-bit PN from the CCMP header. */
        pn = (uint64_t)ivp[0]       |
@@ -863,30 +835,12 @@ ar5008_ccmp_decap(struct athn_softc *sc, struct mbuf *
             (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;
-               }
+               ic->ic_stats.is_ccmp_replays++;
+               return 1;
        }
-       /* Update last seen packet number. */
-       *prsc = pn;
+       /* Last seen packet number is updated in ieee80211_inputm(). */
 
-       /* 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. */
+       /* Strip MIC. IV will be stripped by ieee80211_inputm(). */
        m_adj(m, -IEEE80211_CCMP_MICLEN);
        return 0;
 }
blob - 906e910b02a49203bca9a703455a37fcb716b5da
blob + 9b4477cbba3795b6665b1ff0d33082ce205b9c9b
--- sys/dev/pci/if_iwm.c
+++ sys/dev/pci/if_iwm.c
@@ -372,8 +372,10 @@ int        iwm_rxmq_get_signal_strength(struct iwm_softc 
*, s
 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 *);
-void   iwm_rx_frame(struct iwm_softc *, struct mbuf *, int, int, int, uint32_t,
-           struct ieee80211_rxinfo *, struct mbuf_list *);
+int    iwm_ccmp_decap(struct iwm_softc *, struct mbuf *,
+           struct ieee80211_node *);
+void   iwm_rx_frame(struct iwm_softc *, struct mbuf *, int, uint32_t, int, int,
+           uint32_t, struct ieee80211_rxinfo *, struct mbuf_list *);
 void   iwm_rx_tx_cmd_single(struct iwm_softc *, struct iwm_rx_packet *,
            struct iwm_node *, int, int);
 void   iwm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *,
@@ -452,6 +454,14 @@ 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_v1(struct ieee80211com *, struct ieee80211_node *,
+           struct ieee80211_key *);
+int    iwm_set_key(struct ieee80211com *, struct ieee80211_node *,
+           struct ieee80211_key *);
+void   iwm_delete_key_v1(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 *);
 void   iwm_setrates(struct iwm_node *, int);
 int    iwm_media_change(struct ifnet *);
@@ -3896,16 +3906,65 @@ 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;
+       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
 iwm_rx_frame(struct iwm_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);
@@ -3922,6 +3981,38 @@ iwm_rx_frame(struct iwm_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) &&
+           !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);
+                       return;
+               }
+               /* 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);
+                       return;
+               }
+               if (iwm_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 iwm_rx_radiotap_header *tap = &sc->sc_rxtap;
@@ -4046,7 +4137,7 @@ iwm_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, void
        rxi.rxi_rssi = rssi;
        rxi.rxi_tstamp = device_timestamp;
 
-       iwm_rx_frame(sc, m, chanidx,
+       iwm_rx_frame(sc, m, chanidx, rx_pkt_status,
            (phy_flags & IWM_PHY_INFO_FLAG_SHPREAMBLE),
            rate_n_flags, device_timestamp, &rxi, ml);
 }
@@ -4113,6 +4204,14 @@ iwm_rx_mpdu_mq(struct iwm_softc *sc, struct mbuf *m, v
                        }
                } else
                        hdrlen = ieee80211_get_hdrlen(wh);
+
+               if ((le16toh(desc->status) &
+                   IWM_RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
+                   IWM_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);
        }
@@ -4130,7 +4229,7 @@ iwm_rx_mpdu_mq(struct iwm_softc *sc, struct mbuf *m, v
        rxi.rxi_rssi = rssi;
        rxi.rxi_tstamp = le64toh(desc->v1.tsf_on_air_rise);
 
-       iwm_rx_frame(sc, m, chanidx,
+       iwm_rx_frame(sc, m, chanidx, le16toh(desc->status),
            (phy_info & IWM_RX_MPDU_PHY_SHORT_PREAMBLE),
            rate_n_flags, device_timestamp, &rxi, ml);
 }
@@ -4791,6 +4890,7 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ie
        struct ieee80211_frame *wh;
        struct ieee80211_key *k = NULL;
        const struct iwm_rate *rinfo;
+       uint8_t *ivp;
        uint32_t flags;
        u_int hdrlen;
        bus_dma_segment_t *seg;
@@ -4865,15 +4965,23 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ie
                    m, 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_flags & IEEE80211_KEY_GROUP) ||
+                   (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;
+                       k = NULL; /* skip hardware crypto below */
+               } else {
+                       /* HW appends CCMP MIC */
+                       totlen += IEEE80211_CCMP_HDRLEN;
+               }
        }
-       totlen = m->m_pkthdr.len;
 
        flags = 0;
        if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
@@ -4925,14 +5033,32 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ie
        /* 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, MIN(sizeof(tx->key), k->k_len));
+       } else {
+               /* Trim 802.11 header. */
+               m_adj(m, hdrlen);
+               tx->sec_ctl = 0;
+       }
+
        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) {
@@ -6784,7 +6910,116 @@ iwm_node_alloc(struct ieee80211com *ic)
        return malloc(sizeof (struct iwm_node), M_DEVBUF, M_NOWAIT | M_ZERO);
 }
 
+int
+iwm_set_key_v1(struct ieee80211com *ic, struct ieee80211_node *ni,
+    struct ieee80211_key *k)
+{
+       struct iwm_softc *sc = ic->ic_softc;
+       struct iwm_add_sta_key_cmd_v1 cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+
+       cmd.common.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.common.key_flags |= htole16(IWM_STA_KEY_MULTICAST);
+
+       memcpy(cmd.common.key, k->k_key, MIN(sizeof(cmd.common.key), k->k_len));
+       cmd.common.key_offset = 0;
+       cmd.common.sta_id = IWM_STATION_ID;
+
+       return iwm_send_cmd_pdu(sc, IWM_ADD_STA_KEY, IWM_CMD_ASYNC,
+           sizeof(cmd), &cmd);
+}
+
+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;
+
+       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));
+       }
+
+       if (!isset(sc->sc_ucode_api, IWM_UCODE_TLV_API_TKIP_MIC_KEYS))
+               return iwm_set_key_v1(ic, ni, k);
+
+       memset(&cmd, 0, sizeof(cmd));
+
+       cmd.common.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.common.key_flags |= htole16(IWM_STA_KEY_MULTICAST);
+
+       memcpy(cmd.common.key, k->k_key, MIN(sizeof(cmd.common.key), k->k_len));
+       cmd.common.key_offset = 0;
+       cmd.common.sta_id = IWM_STATION_ID;
+
+       cmd.transmit_seq_cnt = htole64(k->k_tsc);
+
+       return iwm_send_cmd_pdu(sc, IWM_ADD_STA_KEY, IWM_CMD_ASYNC,
+           sizeof(cmd), &cmd);
+}
+
 void
+iwm_delete_key_v1(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;
+
+       memset(&cmd, 0, sizeof(cmd));
+
+       cmd.common.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.common.key, k->k_key, MIN(sizeof(cmd.common.key), k->k_len));
+       cmd.common.key_offset = 0;
+       cmd.common.sta_id = IWM_STATION_ID;
+
+       iwm_send_cmd_pdu(sc, IWM_ADD_STA_KEY, IWM_CMD_ASYNC, sizeof(cmd), &cmd);
+}
+
+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;
+
+       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;
+       }
+
+       if (!isset(sc->sc_ucode_api, IWM_UCODE_TLV_API_TKIP_MIC_KEYS))
+               return iwm_delete_key_v1(ic, ni, k);
+
+       memset(&cmd, 0, sizeof(cmd));
+
+       cmd.common.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.common.key, k->k_key, MIN(sizeof(cmd.common.key), k->k_len));
+       cmd.common.key_offset = 0;
+       cmd.common.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;
@@ -8433,6 +8668,7 @@ iwm_rx_pkt(struct iwm_softc *sc, struct iwm_rx_data *d
                                 IWM_DTS_MEASUREMENT_NOTIF_WIDE):
                        break;
 
+               case IWM_ADD_STA_KEY:
                case IWM_PHY_CONFIGURATION_CMD:
                case IWM_TX_ANT_CONFIGURATION_CMD:
                case IWM_ADD_STA:
@@ -9283,6 +9519,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 - 194c7aa8544726254f09d6d321677da914162df6
blob + 49c1efdd11b09fbbe5579a5b8916e0591599536e
--- sys/dev/pci/if_iwmreg.h
+++ sys/dev/pci/if_iwmreg.h
@@ -5869,7 +5869,7 @@ struct iwm_umac_scan_iter_complete_notif {
  * @IWM_STA_KEY_FLG_KEYID_MSK: the index of the key
  * @IWM_STA_KEY_NOT_VALID: key is invalid
  * @IWM_STA_KEY_FLG_WEP_13BYTES: set for 13 bytes WEP key
- * @IWM_STA_KEY_MULTICAST: set for multical key
+ * @IWM_STA_KEY_MULTICAST: set for multicast key
  * @IWM_STA_KEY_MFP: key is used for Management Frame Protection
  */
 #define IWM_STA_KEY_FLG_NO_ENC         (0 << 0)
@@ -6122,30 +6122,51 @@ struct iwm_add_sta_cmd {
 #define IWM_STA_AUX_ACTIVITY   4
 
 /**
- * struct iwm_add_sta_key_cmd - add/modify sta key
- * ( IWM_REPLY_ADD_STA_KEY = 0x17 )
+ * struct iwm_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 %iwm_sta_key_flag
+ * @key_flags: type &enum iwl_sta_key_flag
  * @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 iwm_add_sta_key_cmd {
+struct iwm_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 iwm_add_sta_key_cmd_v1 - add/modify sta key
+ * @common: see &struct iwm_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 iwm_add_sta_key_cmd_v1 {
+       struct iwm_add_sta_key_common common;
        uint8_t tkip_rx_tsc_byte2;
        uint8_t reserved;
        uint16_t tkip_rx_ttak[5];
-} __packed; /* IWM_ADD_MODIFY_STA_KEY_API_S_VER_1 */
+} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_1 */
 
 /**
+ * struct iwm_add_sta_key_cmd - add/modify sta key
+ * @common: see &struct iwm_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 iwm_add_sta_key_cmd {
+       struct iwm_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
  * @IWM_ADD_STA_SUCCESS: operation was executed successfully
  * @IWM_ADD_STA_STATIONS_OVERLOAD: no room left in the fw's station table
@@ -6489,6 +6510,7 @@ struct iwm_rx_packet {
 #define        IWM_FH_RSCSR_FRAME_INVALID      0x55550000
 #define        IWM_FH_RSCSR_FRAME_ALIGN        0x40
 #define        IWM_FH_RSCSR_RPA_EN             (1 << 25)
+#define        IWM_FH_RSCSR_RADA_EN            (1 << 26)
 #define        IWM_FH_RSCSR_RXQ_POS            16
 #define        IWM_FH_RSCSR_RXQ_MASK           0x3F0000
 
blob - dc4b5863f7d0b7c979fcdbf6c4ac2fdab5a25a0e
blob + ea7cf2a8181f62ac65821eca9abeecc0d9e7961d
--- sys/dev/pci/if_iwn.c
+++ sys/dev/pci/if_iwn.c
@@ -1938,47 +1938,13 @@ iwn_ccmp_decap(struct iwn_softc *sc, struct mbuf *m, s
             (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.
-                        */
-               } 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;
-               }
+               DPRINTF(("CCMP replayed\n"));
+               ic->ic_stats.is_ccmp_replays++;
+               return 1;
        }
-       /* Update last seen packet number. */
-       *prsc = pn;
+       /* Last seen packet number is updated in ieee80211_inputm(). */
 
-       /* 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. */
+       /* Strip MIC. IV will be stripped by ieee80211_inputm(). */
        m_adj(m, -IEEE80211_CCMP_MICLEN);
        return 0;
 }
blob - 23514161af1c3576b1f594a1068f95a359b4ea1b
blob + 87bbeeabbb06f66d1ecde98b147c18df98143ae9
--- sys/dev/pci/if_wpi.c
+++ sys/dev/pci/if_wpi.c
@@ -1132,6 +1132,7 @@ wpi_calib_timeout(void *arg)
 int
 wpi_ccmp_decap(struct wpi_softc *sc, struct mbuf *m, struct ieee80211_key *k)
 {
+       struct ieee80211com *ic = &sc->sc_ic;
        struct ieee80211_frame *wh;
        uint64_t pn, *prsc;
        uint8_t *ivp;
@@ -1159,21 +1160,13 @@ wpi_ccmp_decap(struct wpi_softc *sc, struct mbuf *m, s
             (uint64_t)ivp[6] << 32 |
             (uint64_t)ivp[7] << 40;
        if (pn <= *prsc) {
-               /*
-                * Not necessarily a replayed frame since we did not check
-                * the sequence number of the 802.11 header yet.
-                */
                DPRINTF(("CCMP replayed\n"));
+               ic->ic_stats.is_ccmp_replays++;
                return 1;
        }
-       /* Update last seen packet number. */
-       *prsc = pn;
+       /* Last seen packet number is updated in ieee80211_inputm(). */
 
-       /* 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. */
+       /* Strip MIC. IV will be stripped by ieee80211_inputm(). */
        m_adj(m, -IEEE80211_CCMP_MICLEN);
        return 0;
 }
blob - f7683fc8f15fb5bdca674900571d0c9d0ca36079
blob + bf287803d05a86f4f6113b44206382a26b188b05
--- sys/net80211/ieee80211_crypto.c
+++ sys/net80211/ieee80211_crypto.c
@@ -209,6 +209,50 @@ ieee80211_get_txkey(struct ieee80211com *ic, const str
        return &ic->ic_nw_keys[kid];
 }
 
+struct ieee80211_key *
+ieee80211_get_rxkey(struct ieee80211com *ic, struct mbuf *m,
+    struct ieee80211_node *ni)
+{
+       struct ieee80211_key *k = NULL;
+       struct ieee80211_frame *wh;
+       u_int16_t kid;
+       u_int8_t *ivp, *mmie;
+       int hdrlen;
+
+       wh = mtod(m, struct ieee80211_frame *);
+       if ((ic->ic_flags & IEEE80211_F_RSNON) &&
+           !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+           ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP) {
+               k = &ni->ni_pairwise_key;
+       } else if (!IEEE80211_IS_MULTICAST(wh->i_addr1) ||
+           (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
+           IEEE80211_FC0_TYPE_MGT) {
+               /* retrieve group data key id from IV field */
+               hdrlen = ieee80211_get_hdrlen(wh);
+               /* check that IV field is present */
+               if (m->m_len < hdrlen + 4)
+                       return NULL;
+               ivp = (u_int8_t *)wh + hdrlen;
+               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 NULL;
+               /* 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 NULL;
+               kid = LE_READ_2(&mmie[2]);
+               if (kid != 4 && kid != 5)
+                       return NULL;
+               k = &ic->ic_nw_keys[kid];
+       }
+
+       return k;
+}
+
 struct mbuf *
 ieee80211_encrypt(struct ieee80211com *ic, struct mbuf *m0,
     struct ieee80211_key *k)
@@ -241,54 +285,11 @@ struct mbuf *
 ieee80211_decrypt(struct ieee80211com *ic, struct mbuf *m0,
     struct ieee80211_node *ni)
 {
-       struct ieee80211_frame *wh;
        struct ieee80211_key *k;
-       u_int8_t *ivp, *mmie;
-       u_int16_t kid;
-       int hdrlen;
 
        /* find key for decryption */
-       wh = mtod(m0, struct ieee80211_frame *);
-       if ((ic->ic_flags & IEEE80211_F_RSNON) &&
-           !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
-           ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP) {
-               k = &ni->ni_pairwise_key;
-
-       } else if (!IEEE80211_IS_MULTICAST(wh->i_addr1) ||
-           (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
-           IEEE80211_FC0_TYPE_MGT) {
-               /* retrieve group data key id from IV field */
-               hdrlen = ieee80211_get_hdrlen(wh);
-               /* check that IV field is present */
-               if (m0->m_len < hdrlen + 4) {
-                       m_freem(m0);
-                       return NULL;
-               }
-               ivp = (u_int8_t *)wh + hdrlen;
-               kid = ivp[3] >> 6;
-               k = &ic->ic_nw_keys[kid];
-       } else {
-               /* retrieve integrity group key id from MMIE */
-               if (m0->m_len < sizeof(*wh) + IEEE80211_MMIE_LEN) {
-                       m_freem(m0);
-                       return NULL;
-               }
-               /* it is assumed management frames are contiguous */
-               mmie = (u_int8_t *)wh + m0->m_len - IEEE80211_MMIE_LEN;
-               /* check that MMIE is valid */
-               if (mmie[0] != IEEE80211_ELEMID_MMIE || mmie[1] != 16) {
-                       m_freem(m0);
-                       return NULL;
-               }
-               kid = LE_READ_2(&mmie[2]);
-               if (kid != 4 && kid != 5) {
-                       m_freem(m0);
-                       return NULL;
-               }
-               k = &ic->ic_nw_keys[kid];
-       }
-
-       if ((k->k_flags & IEEE80211_KEY_SWCRYPTO) == 0) {
+       k = ieee80211_get_rxkey(ic, m0, ni);
+       if (k == NULL || (k->k_flags & IEEE80211_KEY_SWCRYPTO) == 0) {
                m_free(m0);
                return NULL;
        }
blob - a3bc45c699af9a53dbdad4a39e984c00999b105a
blob + f54fff2981e70909d96a4c8ed66d0c1ea664d6ac
--- sys/net80211/ieee80211_crypto.h
+++ sys/net80211/ieee80211_crypto.h
@@ -160,6 +160,8 @@ void        ieee80211_tkip_delete_key(struct ieee80211com *,
            struct ieee80211_key *);
 struct mbuf *ieee80211_tkip_encrypt(struct ieee80211com *,
            struct mbuf *, struct ieee80211_key *);
+int    ieee80211_tkip_get_tsc(uint64_t *, uint64_t **, struct mbuf *,
+           struct ieee80211_key *);
 struct mbuf *ieee80211_tkip_decrypt(struct ieee80211com *,
            struct mbuf *, struct ieee80211_key *);
 void   ieee80211_tkip_mic(struct mbuf *, int, const u_int8_t *,
@@ -173,6 +175,8 @@ int ieee80211_ccmp_set_key(struct ieee80211com *, stru
 void   ieee80211_ccmp_delete_key(struct ieee80211com *,
            struct ieee80211_key *);
 struct mbuf *ieee80211_ccmp_encrypt(struct ieee80211com *, struct mbuf *,
+           struct ieee80211_key *);
+int    ieee80211_ccmp_get_pn(uint64_t *, uint64_t **, struct mbuf *,
            struct ieee80211_key *);
 struct mbuf *ieee80211_ccmp_decrypt(struct ieee80211com *, struct mbuf *,
            struct ieee80211_key *);
blob - f4c8748602f910f75ab4f4eea042df80e398d244
blob + 5885e174818b83fc20e24e5fa31b721df6275c4a
--- sys/net80211/ieee80211_crypto_ccmp.c
+++ sys/net80211/ieee80211_crypto_ccmp.c
@@ -300,6 +300,45 @@ ieee80211_ccmp_encrypt(struct ieee80211com *ic, struct
        return NULL;
 }
 
+int
+ieee80211_ccmp_get_pn(uint64_t *pn, uint64_t **prsc, struct mbuf *m,
+    struct ieee80211_key *k)
+{
+       struct ieee80211_frame *wh;
+       int hdrlen;
+       const u_int8_t *ivp;
+
+       wh = mtod(m, struct ieee80211_frame *);
+       hdrlen = ieee80211_get_hdrlen(wh);
+       if (m->m_pkthdr.len < hdrlen + IEEE80211_CCMP_HDRLEN)
+               return EINVAL;
+
+       ivp = (u_int8_t *)wh + hdrlen;
+
+       /* check that ExtIV bit is set */
+       if (!(ivp[3] & IEEE80211_WEP_EXTIV))
+               return EINVAL;
+
+       /* retrieve last seen packet number for this frame type/priority */
+       if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+           IEEE80211_FC0_TYPE_DATA) {
+               u_int8_t tid = ieee80211_has_qos(wh) ?
+                   ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
+               *prsc = &k->k_rsc[tid];
+       } else  /* 11w: management frames have their own counters */
+               *prsc = &k->k_mgmt_rsc;
+
+       /* extract the 48-bit PN from the CCMP header */
+       *pn = (u_int64_t)ivp[0]      |
+            (u_int64_t)ivp[1] <<  8 |
+            (u_int64_t)ivp[4] << 16 |
+            (u_int64_t)ivp[5] << 24 |
+            (u_int64_t)ivp[6] << 32 |
+            (u_int64_t)ivp[7] << 40;
+
+       return 0;
+}
+
 struct mbuf *
 ieee80211_ccmp_decrypt(struct ieee80211com *ic, struct mbuf *m0,
     struct ieee80211_key *k)
@@ -307,7 +346,7 @@ ieee80211_ccmp_decrypt(struct ieee80211com *ic, struct
        struct ieee80211_ccmp_ctx *ctx = k->k_priv;
        struct ieee80211_frame *wh;
        u_int64_t pn, *prsc;
-       const u_int8_t *ivp, *src;
+       const u_int8_t *src;
        u_int8_t *dst;
        u_int8_t mic0[IEEE80211_CCMP_MICLEN];
        u_int8_t a[16], b[16], s0[16], s[16];
@@ -318,35 +357,20 @@ ieee80211_ccmp_decrypt(struct ieee80211com *ic, struct
 
        wh = mtod(m0, struct ieee80211_frame *);
        hdrlen = ieee80211_get_hdrlen(wh);
-       ivp = (u_int8_t *)wh + hdrlen;
-
        if (m0->m_pkthdr.len < hdrlen + IEEE80211_CCMP_HDRLEN +
            IEEE80211_CCMP_MICLEN) {
                m_freem(m0);
                return NULL;
        }
-       /* check that ExtIV bit is set */
-       if (!(ivp[3] & IEEE80211_WEP_EXTIV)) {
+
+       /*
+        * Get the frame's Packet Number (PN) and a pointer to our last-seen
+        * Receive Sequence Counter (RSC) which we can use to detect replays.
+        */
+       if (ieee80211_ccmp_get_pn(&pn, &prsc, m0, k) != 0) {
                m_freem(m0);
                return NULL;
        }
-
-       /* retrieve last seen packet number for this frame type/priority */
-       if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
-           IEEE80211_FC0_TYPE_DATA) {
-               u_int8_t tid = ieee80211_has_qos(wh) ?
-                   ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
-               prsc = &k->k_rsc[tid];
-       } else  /* 11w: management frames have their own counters */
-               prsc = &k->k_mgmt_rsc;
-
-       /* extract the 48-bit PN from the CCMP header */
-       pn = (u_int64_t)ivp[0]       |
-            (u_int64_t)ivp[1] <<  8 |
-            (u_int64_t)ivp[4] << 16 |
-            (u_int64_t)ivp[5] << 24 |
-            (u_int64_t)ivp[6] << 32 |
-            (u_int64_t)ivp[7] << 40;
        if (pn <= *prsc) {
                /* replayed frame, discard */
                ic->ic_stats.is_ccmp_replays++;
blob - 70fbe7c9769f9dbd5e9f3cc2fb1961d1282c9e2b
blob + 3473de8eb99d01dce20a0862275a3339e5031a10
--- sys/net80211/ieee80211_crypto_tkip.c
+++ sys/net80211/ieee80211_crypto_tkip.c
@@ -313,6 +313,42 @@ ieee80211_tkip_encrypt(struct ieee80211com *ic, struct
        return NULL;
 }
 
+int
+ieee80211_tkip_get_tsc(uint64_t *tsc, uint64_t **prsc, struct mbuf *m,
+    struct ieee80211_key *k)
+{
+       struct ieee80211_frame *wh;
+       int hdrlen;
+       u_int8_t tid;
+       const u_int8_t *ivp;
+
+       wh = mtod(m, struct ieee80211_frame *);
+       hdrlen = ieee80211_get_hdrlen(wh);
+
+       if (m->m_pkthdr.len < hdrlen + IEEE80211_TKIP_HDRLEN)
+               return EINVAL;
+
+       ivp = (u_int8_t *)wh + hdrlen;
+       /* check that ExtIV bit is set */
+       if (!(ivp[3] & IEEE80211_WEP_EXTIV))
+               return EINVAL;
+
+       /* Retrieve last seen packet number for this frame priority. */
+       tid = ieee80211_has_qos(wh) ?
+           ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
+       *prsc = &k->k_rsc[tid];
+
+       /* extract the 48-bit TSC from the TKIP header */
+       *tsc = (u_int64_t)ivp[2]      |
+             (u_int64_t)ivp[0] <<  8 |
+             (u_int64_t)ivp[4] << 16 |
+             (u_int64_t)ivp[5] << 24 |
+             (u_int64_t)ivp[6] << 32 |
+             (u_int64_t)ivp[7] << 40;
+
+       return 0;
+}
+
 struct mbuf *
 ieee80211_tkip_decrypt(struct ieee80211com *ic, struct mbuf *m0,
     struct ieee80211_key *k)
@@ -324,8 +360,7 @@ ieee80211_tkip_decrypt(struct ieee80211com *ic, struct
        u_int8_t mic[IEEE80211_TKIP_MICLEN];
        u_int64_t tsc, *prsc;
        u_int32_t crc, crc0;
-       u_int8_t *ivp, *mic0;
-       u_int8_t tid;
+       u_int8_t *mic0;
        struct mbuf *n0, *m, *n;
        int hdrlen, left, moff, noff, len;
 
@@ -337,25 +372,15 @@ ieee80211_tkip_decrypt(struct ieee80211com *ic, struct
                return NULL;
        }
 
-       ivp = (u_int8_t *)wh + hdrlen;
-       /* check that ExtIV bit is set */
-       if (!(ivp[3] & IEEE80211_WEP_EXTIV)) {
+       /*
+        * Get the frame's Tansmit Sequence Counter (TSC), and a pointer to
+        * our last-seen Receive Sequence Counter (RSC) with which we can
+        * detect replays.
+        */
+       if (ieee80211_tkip_get_tsc(&tsc, &prsc, m0, k) != 0) {
                m_freem(m0);
                return NULL;
        }
-
-       /* retrieve last seen packet number for this frame priority */
-       tid = ieee80211_has_qos(wh) ?
-           ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
-       prsc = &k->k_rsc[tid];
-
-       /* extract the 48-bit TSC from the TKIP header */
-       tsc = (u_int64_t)ivp[2]       |
-             (u_int64_t)ivp[0] <<  8 |
-             (u_int64_t)ivp[4] << 16 |
-             (u_int64_t)ivp[5] << 24 |
-             (u_int64_t)ivp[6] << 32 |
-             (u_int64_t)ivp[7] << 40;
        if (tsc <= *prsc) {
                /* replayed frame, discard */
                ic->ic_stats.is_tkip_replays++;
blob - d802a15ad6ca1eaca39e681fd5e59297fc2ef464
blob + 2e4714aeaa9a7853b2ba65bacb7d45e6e338d8f8
--- sys/net80211/ieee80211_input.c
+++ sys/net80211/ieee80211_input.c
@@ -58,6 +58,8 @@
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_priv.h>
 
+struct mbuf *ieee80211_input_hwdecrypt(struct ieee80211com *,
+           struct ieee80211_node *, struct mbuf *);
 struct mbuf *ieee80211_defrag(struct ieee80211com *, struct mbuf *, int);
 void   ieee80211_defrag_timeout(void *);
 void   ieee80211_input_ba(struct ieee80211com *, struct mbuf *,
@@ -147,6 +149,86 @@ ieee80211_get_hdrlen(const struct ieee80211_frame *wh)
        return size;
 }
 
+/* Post-processing for drivers which perform decryption in hardware. */
+struct mbuf *
+ieee80211_input_hwdecrypt(struct ieee80211com *ic, struct ieee80211_node *ni,
+    struct mbuf *m)
+{
+       struct ieee80211_key *k;
+       struct ieee80211_frame *wh;
+       uint64_t pn, *prsc;
+       int hdrlen;
+
+       k = ieee80211_get_rxkey(ic, m, ni);
+       if (k == NULL)
+               return NULL;
+       
+       wh = mtod(m, struct ieee80211_frame *);
+       hdrlen = ieee80211_get_hdrlen(wh);
+
+       /*
+        * Update the last-seen packet number (PN) for drivers using hardware
+        * crypto offloading. This cannot be done by drivers because A-MPDU
+        * reordering needs to occur before a valid lower bound can be
+        * determined for the PN. Drivers will read the PN we write here and
+        * are expected to discard replayed frames based on it.
+        * Drivers are expected to leave the IV of decrypted frames intact
+        * so we can update the last-seen PN and strip the IV here.
+        */
+       switch (k->k_cipher) {
+       case IEEE80211_CIPHER_CCMP:
+               if (!(wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) {
+                       /* drop unencrypted */
+                       ic->ic_stats.is_rx_unencrypted++;
+                       return NULL;
+               }
+               if (ieee80211_ccmp_get_pn(&pn, &prsc, m, k) != 0)
+                       return NULL;
+               if (pn <= *prsc) {
+                       ic->ic_stats.is_ccmp_replays++;
+                       return NULL;
+               }
+
+               /* 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);
+               /* Drivers are expected to strip the MIC. */
+               break;
+        case IEEE80211_CIPHER_TKIP:
+               if (!(wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) {
+                       /* drop unencrypted */
+                       ic->ic_stats.is_rx_unencrypted++;
+                       return NULL;
+               }
+               if (ieee80211_tkip_get_tsc(&pn, &prsc, m, k) != 0)
+                       return NULL;
+
+               if (pn <= *prsc) {
+                       ic->ic_stats.is_tkip_replays++;
+                       return NULL;
+               }
+
+               /* Update last-seen packet number. */
+               *prsc = pn;
+
+               /* Clear Protected bit and strip IV. */
+               wh = mtod(m, struct ieee80211_frame *);
+               wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+               memmove(mtod(m, caddr_t) + IEEE80211_TKIP_HDRLEN, wh, hdrlen);
+               m_adj(m, IEEE80211_TKIP_HDRLEN);
+               /* Drivers are expected to strip the MIC. */
+               break;
+       default:
+               break;
+       }
+
+       return m;
+}
+
 /*
  * Process a received frame.  The node associated with the sender
  * should be supplied.  If nothing was found in the node table then
@@ -454,8 +536,12 @@ ieee80211_inputm(struct ifnet *ifp, struct mbuf *m, st
                                        ic->ic_stats.is_rx_wepfail++;
                                        goto err;
                                }
-                               wh = mtod(m, struct ieee80211_frame *);
+                       } else {
+                               m = ieee80211_input_hwdecrypt(ic, ni, m);
+                               if (m == NULL)
+                                       goto err;
                        }
+                       wh = mtod(m, struct ieee80211_frame *);
                } else if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) ||
                    (rxi->rxi_flags & IEEE80211_RXI_HWDEC)) {
                        /* frame encrypted but protection off for Rx */

Reply via email to