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 */
>
>