This diff needs testing in particular on: athn(4), iwn(4), wpi(4)
I have tested on iwn(4) and athn(4) so far.
Testing with other drivers would be good, too, to ensure that no
regressions are introduced for the software crypto case.

Drivers which offload CCMP decryption to hardware contain a check for
replayed frames. This check is simple: The CCMP header contains a nonce,
which is incremented by the transmitter whenever it encrypts a new frame.
This nonce is known as the "Packet Number" in CCMP (WPA2) and as the
"Transmit Sequence Counter" in TKIP (WPA1). If an encrypted frame is
received which uses a nonce lower than the most recently received nonce,
then the frame can be assumed to be a replay and is discarded.
This is important to defend against replay attacks on WPA.

The way our drivers implement this check doesn't work with block ack.
This is because a driver-side check must assume that frames arrive in order.
With block ack, frames may arrive out of order, and they are put back into
the proper order in ieee80211_inputm() by checking their sequence number
(a separate number from the nonce discussed above).

The 802.11 standard says that the Packet Number should be checked *after*
re-ordering frames back into their proper sequence.
In our drivers, this check can only happen before re-ordering and hence
legitimate out-of-order frame transmissions would be discarded.
For this reason, we currently skip the replay check entirely if block ack
and hardware decryption are used together. Which makes things work but is
obviously not good for security.

This problem only affects hardware crypto because software decryption
happens after re-ordering. I would like to fix this problem before adding
CCMP hardware offload support to more drivers.

The following diff moves the Packet Number check out of affected drivers
into ieee80211_inputm() so the check can be performed after frames have
been re-ordered.
Drivers no longer update the expected replay counter value, but they can
still read its current value to discard replayed frames early.
This simplifies CCMP input handling in affected drivers significantly.

Drivers which only do WEP in hardware don't need any change.

While here:

 - Implement ieee80211_get_rxkey(); a prototype already existed but now
   we actually need an implementation of this to avoid code duplication
   within net80211 and some drivers.

 - Fix a wrong assumption made about reply counters and TIDs. We only
   announce support for one replay counter per key, and never initialize
   counters other than k_rsc[0]. Some code was accessing k_rsc[tid] based
   on the TID value in the frame header. Based on the fact that counters
   at offsets other than zero are not initialized, and on my understanding
   of the 802.11-2012 spec, this is wrong. Multiple counters are only used
   if support for them is announced in an RSN capability field, and we do
   not announce this. We also use TIDs only to correlate frames with an
   existing block ack agreement. The multi-replay counter case assumes
   instead that TIDs are used for purposes of real OoS, which we do not
   implement. Real QoS could lead to out-of-order transmissions based
   on the priority assigned to the data contained in a frame, so it makes
   sense to maintain one replay counter per priority with real QoS.

diff refs/heads/master refs/heads/pn
blob - 9193ae3c7b5101a86b85b1b3ba48489d75f5150c
blob + 5c17a7d065c622cdde56ccc81baa1859df93404f
--- sys/dev/ic/ar5008.c
+++ sys/dev/ic/ar5008.c
@@ -792,12 +792,9 @@ ar5008_ccmp_decap(struct athn_softc *sc, struct mbuf *
        struct ieee80211com *ic = &sc->sc_ic;
        struct ieee80211_key *k;
        struct ieee80211_frame *wh;
-       struct ieee80211_rx_ba *ba;
        uint64_t pn, *prsc;
-       u_int8_t *ivp, *mmie;
-       uint8_t tid;
-       uint16_t kid;
-       int hdrlen, hasqos;
+       u_int8_t *ivp;
+       int hdrlen;
        uintptr_t entry;
 
        wh = mtod(m, struct ieee80211_frame *);
@@ -805,35 +802,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. */
@@ -850,9 +820,6 @@ ar5008_ccmp_decap(struct athn_softc *sc, struct mbuf *
        if (!(ivp[3] & IEEE80211_WEP_EXTIV))
                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[0];
 
        /* Extract the 48-bit PN from the CCMP header. */
@@ -863,30 +830,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 - b0fb1ec2174ed550eb96627676c66c4fa9f99eff
blob + 7c5e1ce5cfd415d6ddaf906f390360afc32c3ada
--- sys/dev/pci/if_iwn.c
+++ sys/dev/pci/if_iwn.c
@@ -1908,11 +1908,9 @@ iwn_ccmp_decap(struct iwn_softc *sc, struct mbuf *m, s
        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;
+       int hdrlen;
 
        wh = mtod(m, struct ieee80211_frame *);
        hdrlen = ieee80211_get_hdrlen(wh);
@@ -1923,11 +1921,9 @@ iwn_ccmp_decap(struct iwn_softc *sc, struct mbuf *m, s
                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];
 
+       prsc = &k->k_rsc[0];
+
        /* Extract the 48-bit PN from the CCMP header. */
        pn = (uint64_t)ivp[0]       |
             (uint64_t)ivp[1] <<  8 |
@@ -1936,47 +1932,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 + f8f71710461ccd444ebc9dab108d742ceb51f1c4
--- sys/dev/pci/if_wpi.c
+++ sys/dev/pci/if_wpi.c
@@ -1132,10 +1132,10 @@ 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;
-       uint8_t tid;
        int hdrlen;
 
        wh = mtod(m, struct ieee80211_frame *);
@@ -1147,10 +1147,9 @@ wpi_ccmp_decap(struct wpi_softc *sc, struct mbuf *m, s
                DPRINTF(("CCMP decap ExtIV not set\n"));
                return 1;
        }
-       tid = ieee80211_has_qos(wh) ?
-           ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
-       prsc = &k->k_rsc[tid];
 
+       prsc = &k->k_rsc[0];
+
        /* Extract the 48-bit PN from the CCMP header. */
        pn = (uint64_t)ivp[0]       |
             (uint64_t)ivp[1] <<  8 |
@@ -1159,21 +1158,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 + e15bfc51b0255a979cfc54c69a87991a9b11b46b
--- sys/net80211/ieee80211_crypto.h
+++ sys/net80211/ieee80211_crypto.h
@@ -81,7 +81,7 @@ struct ieee80211_key {
 #define IEEE80211_KEY_SWCRYPTO 0x00000080      /* loaded for software crypto */
 
        u_int                   k_len;
-       u_int64_t               k_rsc[IEEE80211_NUM_TID];
+       u_int64_t               k_rsc[1];       /* just one replay counter */
        u_int64_t               k_mgmt_rsc;
        u_int64_t               k_tsc;
        u_int8_t                k_key[32];
@@ -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 + 2692fa1ab214d67c4ce96fb58e453830bbf9f785
--- sys/net80211/ieee80211_crypto_ccmp.c
+++ sys/net80211/ieee80211_crypto_ccmp.c
@@ -300,6 +300,49 @@ 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) {
+               /*
+                * IEEE 802.11 supports up to 4 replay counters, one per
+                * frame priority. We only support one replay counter and
+                * leave IEEE80211_RSNCAP_PTKSA_RCNT in our RSN capabilities
+                * set to zero (meaning one replay counter is supported).
+                */
+               *prsc = &k->k_rsc[0];
+       } 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 +350,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 +361,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 + dbf5cbb3b76b6e11de6dcfccea5ead6f1c8b404d
--- sys/net80211/ieee80211_crypto_tkip.c
+++ sys/net80211/ieee80211_crypto_tkip.c
@@ -313,6 +313,45 @@ 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;
+       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.
+        * IEEE 802.11 supports up to 4 replay counters, one per
+        * frame priority. We only support one replay counter and
+        * leave IEEE80211_RSNCAP_PTKSA_RCNT in our RSN capabilities
+        * set to zero (meaning one replay counter is supported).
+        */
+       *prsc = &k->k_rsc[0];
+
+       /* 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,38 +363,27 @@ 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;
 
        wh = mtod(m0, struct ieee80211_frame *);
        hdrlen = ieee80211_get_hdrlen(wh);
-
        if (m0->m_pkthdr.len < hdrlen + IEEE80211_TKIP_OVHD) {
                m_freem(m0);
                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.
+        * This also performs a minimum required frame length check.
+        */
+       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 + 5cc2d961c5be0a2bdfdce6d0a06a046c78cd0669
--- 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,88 @@ 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 check the VI and strip it 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) {
+                       /* Driver should have dropped this frame. */
+                       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) {
+                       /* Driver should have dropped this frame. */
+                       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 +538,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