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;