On Fri, May 08, 2020 at 10:53:30AM +0200, Stefan Sperling wrote:
> So the diff below contains just the reordering fix for drivers using
> hardware acceleration for WPA.

Is there anybody who wants to OK this?

To recap:

Currently, CCMP replay checking is skipped for aggregated 11n frames on
drivers which use CCMP hardware acceleration: athn(4) and iwn(4).

This diff makes reply checking in that case possible without false positives,
by updating the last-seen packet number after the aggregated subframes, which
may be received out of order, have been put back into their intended order by
ieee80211_input_ba(). The old code only works in the single-frame case for
11a/b/g modes where no such legitimate re-transmissions can occur.

> diff refs/heads/master refs/heads/pn
> 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 - 6b6331d167a881a526b9d0a30f7f452882fee19a
> blob + 620cbd4c138516398a4683d83c1b6bf8aac57c82
> --- sys/dev/pci/if_iwn.c
> +++ sys/dev/pci/if_iwn.c
> @@ -1936,47 +1936,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