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;

Reply via email to