iwn currently flags A-MPDU subframes which arrived out of sequence as CCMP
replays and drops them. We can't rely on the LAST_RX_AMPDU flag from the
last RX PHY notification, it seems. Check for a valid BA agreement instead.

Updating the last seen packet number with every out-of-order packet isn't
quite right, either.

Also, align the sequence number replay check with what ieee80211_input() does.

Stops netstat -W iwn0 showing tons of replayed (and hence, dropped) frames
when using 11n mode on a WPA network.

Index: if_iwn.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwn.c,v
retrieving revision 1.160
diff -u -p -r1.160 if_iwn.c
--- if_iwn.c    4 Feb 2016 21:32:01 -0000       1.160
+++ if_iwn.c    5 Feb 2016 14:40:03 -0000
@@ -1844,6 +1844,7 @@ iwn_ccmp_decap(struct iwn_softc *sc, str
        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;
@@ -1860,6 +1861,7 @@ iwn_ccmp_decap(struct iwn_softc *sc, str
        }
        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];
 
        /* Extract the 48-bit PN from the CCMP header. */
@@ -1870,7 +1872,7 @@ iwn_ccmp_decap(struct iwn_softc *sc, str
             (uint64_t)ivp[6] << 32 |
             (uint64_t)ivp[7] << 40;
        if (pn <= *prsc) {
-               if (hasqos && (sc->last_rx_valid & IWN_LAST_RX_AMPDU)) {
+               if (hasqos && ba->ba_state == IEEE80211_BA_AGREED) {
                        /*
                         * This is an A-MPDU subframe.
                         * Such frames may be received out of order due to
@@ -1891,20 +1893,24 @@ iwn_ccmp_decap(struct iwn_softc *sc, str
                                orxseq = ni->ni_qos_rxseqs[tid];
                        else
                                orxseq = ni->ni_rxseq;
-                       if (nrxseq < orxseq) {
-                               DPRINTF(("CCMP replayed (n=%d < o=%d)\n",
+                       if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+                           nrxseq == orxseq) {
+                               DPRINTF(("CCMP replayed (n=%d == o=%d)\n",
                                    nrxseq, orxseq));
                                ic->ic_stats.is_ccmp_replays++;
                                return 1;
                        }
+                       /* Update last seen packet number. */
+                       *prsc = pn;
                } else {
                        DPRINTF(("CCMP replayed\n"));
                        ic->ic_stats.is_ccmp_replays++;
                        return 1;
                }
+       } else {
+               /* Update last seen packet number. */
+               *prsc = pn;
        }
-       /* Update last seen packet number. */
-       *prsc = pn;
 
        /* Clear Protected bit and strip IV. */
        wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;

Reply via email to