From 48f95a360ed15bd208c90b54c5e3711a917f11d8 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Thu, 12 Oct 2017 21:56:58 +0000 Subject: [PATCH] [net80211] begin handling multiple hardware decap'ed A-MSDU in the RX path. The duplicate detection code currently expects A-MSDU frames to be encaped - they're decap'ed /after/ duplicate detection. However for ath10k (and iwm hardware later on) the firmware supports doing A-MSDU decap in hardware - which shows up as multiple frames with the same sequence number and IV. This is the first part of decap handling - if we see a stretch of A-MSDU frames from the driver with the MORE bit set, then don't treat them as duplicates. This isn't 100% complete as crypto sequence number handling and "A-MSDU in A-MPDU" needs handling, but it's a start. This should be a glorified no-op for everyone. Please tell me if it isn't. --- sys/net80211/ieee80211_input.h | 47 +++++++++++++++++++++++++++++++++- sys/net80211/ieee80211_ioctl.h | 6 ++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/sys/net80211/ieee80211_input.h b/sys/net80211/ieee80211_input.h index cff07c68e9ca..192138fa28db 100644 --- a/sys/net80211/ieee80211_input.h +++ b/sys/net80211/ieee80211_input.h @@ -131,6 +131,38 @@ ishtinfooui(const uint8_t *frm) return frm[1] > 3 && le32dec(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI); } +static __inline int +ieee80211_check_rxseq_amsdu(const struct ieee80211_rx_stats *rxs) +{ + + return (!! (rxs->c_pktflags & IEEE80211_RX_F_AMSDU)); +} + +/* + * Return 1 if the rxseq check should increment the sequence + * number. Return 0 if it's part of an AMSDU batch and it isn't + * the final frame in the decap'ed burst. + */ +static __inline int +ieee80211_check_rxseq_amsdu_more(const struct ieee80211_rx_stats *rxs) +{ + /* No state? ok */ + if (rxs == NULL) + return (1); + + /* State but no AMSDU set? ok */ + if ((rxs->c_pktflags & IEEE80211_RX_F_AMSDU) == 0) + return (1); + + /* State, AMSDU set, then _MORE means "don't inc yet" */ + if (rxs->c_pktflags & IEEE80211_RX_F_AMSDU_MORE) { + return (0); + } + + /* Both are set, so return ok */ + return (1); +} + /* * Check the current frame sequence number against the current TID * state and return whether it's in sequence or should be dropped. @@ -238,7 +270,20 @@ ieee80211_check_rxseq(struct ieee80211_node *ni, struct ieee80211_frame *wh, goto fail; ok: - ni->ni_rxseqs[tid] = rxseq; + /* + * Only bump the sequence number if it's the last frame + * in a batch. That way frames in the rest of the batch + * get included, and the last frame in the batch kicks + * it next. + */ + if (ieee80211_check_rxseq_amsdu_more(rxs)) { + ni->ni_rxseqs[tid] = rxseq; + if (ieee80211_check_rxseq_amsdu(rxs)) + IEEE80211_NODE_STAT(ni, rx_amsdu_more_end); + } else { + /* .. still waiting */ + IEEE80211_NODE_STAT(ni, rx_amsdu_more); + } return 1; diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h index 7d472bc64c8d..a565593e780d 100644 --- a/sys/net80211/ieee80211_ioctl.h +++ b/sys/net80211/ieee80211_ioctl.h @@ -84,7 +84,11 @@ struct ieee80211_nodestats { uint32_t ns_tx_deauth_code; /* last deauth reason */ uint32_t ns_tx_disassoc; /* disassociations */ uint32_t ns_tx_disassoc_code; /* last disassociation reason */ - uint32_t ns_spare[8]; + + /* Hardware A-MSDU decode */ + uint32_t ns_rx_amsdu_more; /* RX decap A-MSDU, more coming from A-MSDU */ + uint32_t ns_rx_amsdu_more_end; /* RX decap A-MSDU (or any other frame), no more coming */ + uint32_t ns_spare[6]; }; /*