summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Sperling <stsp@cvs.openbsd.org>2016-02-08 00:54:58 +0000
committerStefan Sperling <stsp@cvs.openbsd.org>2016-02-08 00:54:58 +0000
commit51f5a28d96c8f257f540c20eb47506c1695e50e6 (patch)
tree87715a0cc5e3ffae6627d4f0b670f3a43baae779
parent7ace03cb3d02ebe8632a047a253dcbdc0bd1aaad (diff)
Work around buggy APs which occasionally emit sequence numbers much higher
than the current 11n BlockAck window. The previous code would be fooled into moving the window forward and then drop packets until their sequence numbers catch up with the new window, which can take several minutes. Fixes traffic stalls observed with Broadcom APs. ok krw@ tb@
-rw-r--r--sys/net80211/ieee80211_input.c33
-rw-r--r--sys/net80211/ieee80211_node.h8
2 files changed, 39 insertions, 2 deletions
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
index a2be77453dd..752774837b2 100644
--- a/sys/net80211/ieee80211_input.c
+++ b/sys/net80211/ieee80211_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_input.c,v 1.159 2016/02/07 23:36:43 stsp Exp $ */
+/* $OpenBSD: ieee80211_input.c,v 1.160 2016/02/08 00:54:57 stsp Exp $ */
/*-
* Copyright (c) 2001 Atsushi Onoe
@@ -712,6 +712,37 @@ ieee80211_input_ba(struct ieee80211com *ic, struct mbuf *m,
return;
}
if (SEQ_LT(ba->ba_winend, sn)) { /* WinEndB < SN */
+ /*
+ * If this frame would move the window outside the range of
+ * winend + winsize, drop it. This is likely a fluke and the
+ * next frame will fit into the window again. Allowing the
+ * window to be moved too far ahead makes us drop frames
+ * until their sequence numbers catch up with the new window.
+ *
+ * However, if the window really did move arbitrarily, we must
+ * allow it to move forward. We try to detect this condition
+ * by counting missed consecutive frames.
+ *
+ * Works around buggy behaviour observed with Broadcom-based
+ * APs, which emit "sequence" numbers such as 1888, 1889, 2501,
+ * 1890, 1891, ... all for the same TID.
+ */
+ if (((sn - ba->ba_winend) & 0xfff) > IEEE80211_BA_MAX_WINSZ) {
+ if (ba->ba_winmiss < IEEE80211_BA_MAX_WINMISS) {
+ if (ba->ba_missedsn == sn - 1)
+ ba->ba_winmiss++;
+ else
+ ba->ba_winmiss = 0;
+ ba->ba_missedsn = sn;
+ ifp->if_ierrors++;
+ m_freem(m); /* discard the MPDU */
+ return;
+ }
+
+ /* It appears the window has moved for real. */
+ ba->ba_winmiss = 0;
+ ba->ba_missedsn = 0;
+ }
count = (sn - ba->ba_winend) & 0xfff;
if (count > ba->ba_winsize) /* no overlap */
count = ba->ba_winsize;
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index c6ffeccb8f9..ee2ae728cfb 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_node.h,v 1.56 2016/02/05 16:07:57 stsp Exp $ */
+/* $OpenBSD: ieee80211_node.h,v 1.57 2016/02/08 00:54:57 stsp Exp $ */
/* $NetBSD: ieee80211_node.h,v 1.9 2004/04/30 22:57:32 dyoung Exp $ */
/*-
@@ -150,6 +150,12 @@ struct ieee80211_rx_ba {
u_int16_t ba_head;
struct timeout ba_gap_to;
#define IEEE80211_BA_GAP_TIMEOUT 500 /* msec */
+ /* Counter for consecutive frames which missed the BA window. */
+ int ba_winmiss;
+ /* Sequence number of previous frame which missed the BA window. */
+ uint16_t ba_missedsn;
+ /* Window moves forward after this many frames have missed it. */
+#define IEEE80211_BA_MAX_WINMISS 8
};
/*