A-MSDU frames are aggregates which contain MSDUs (ethernet frames) as
subframes, rather than MPDUs (802.11 frames) as A-MPDUs do.
Each subframe has a header containing sender and target MAC address
and the subframe's length. For a visual illustration, see
http://www.gta.ufrj.br/ensino/eel879/trabalhos_vf_2014_2/remi/img/A-MSDU.png
The 802.11 standard requires 11n STAs to receive and send A-MSDUs.
As with A-MPDUs, we only implement receive for now.
Since the subframes are ethernet frames they go directly to the interface
input queue via ieee80211_deliver_data(), rather than to ieee80211_input().
damien@ added code for A-MSDU Rx years ago and it works mostly out of
the box. This diff adds an upper bounds check on the subframe length,
and a clean exit at the bottom of the loop for fully processed A-MSDUs
without which we'd normally terminate the loop via the error handling
path of the m_pullup() call at the top of the loop.
Tested against several APs. Only two of my APs send A-MSDUs (fritzbox
and Linux iwlwifi). With the Linux iwlwifi AP I'm seeing A-MSDU decryption
failures which I believe is caused by an intel firmware bug since the frame
sent by the AP has the wrong kind of encryption header (not CCMP).
The fritzbox AP uses a CCMP header as mandated by the standard so no
problem there.
Note that A-MPDUs may contain A-MSDUs in subframes. The iwlwifi AP
sends frames like this (unencrypted, though ;) so I could confirm
that this works as expected.
Index: net80211/ieee80211_input.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_input.c,v
retrieving revision 1.143
diff -u -p -r1.143 ieee80211_input.c
--- net80211/ieee80211_input.c 12 Dec 2015 11:25:46 -0000 1.143
+++ net80211/ieee80211_input.c 12 Dec 2015 13:04:29 -0000
@@ -1061,6 +1061,13 @@ ieee80211_amsdu_decap(struct ieee80211co
len -= LLC_SNAPFRAMELEN;
}
len += ETHER_HDR_LEN;
+ if (len > m->m_pkthdr.len) {
+ /* stop processing A-MSDU subframes */
+ DPRINTF(("A-MSDU subframe too long (%d)\n", len));
+ ic->ic_stats.is_rx_decap++;
+ m_freem(m);
+ break;
+ }
/* "detach" our A-MSDU subframe from the others */
n = m_split(m, len, M_NOWAIT);
@@ -1072,6 +1079,10 @@ ieee80211_amsdu_decap(struct ieee80211co
}
ieee80211_deliver_data(ic, m, ni);
+ if (n->m_len == 0) {
+ m_freem(n);
+ break;
+ }
m = n;
/* remove padding */
pad = ((len + 3) & ~3) - len;