On 12/12/15(Sat) 10:59, Stefan Sperling wrote: > This patch completes net80211 support for receiving A-MPDUs. > > This is a required feature of 11n. Technically, sending A-MPDUS > is required, too, but since we're not trying to get certified by > the Wifi Alliance we can postpone this until later. Devices we > interoperate with won't care if we don't send them. > > A-MPDUs are aggregate frames containing several MPDUs (i.e. several > full 802.11 frames with MAC headers). Each subframe is separated by > a delimiter which contains the length and CRC of the subframe, and a > trailer for padding. If you'd like to see a visual illustration, > search the web yourself, or look at this one: > http://m.eet.com/media/1110804/802fig6.gif > > These frames are used to avoid overhead of separate ACKs for > individual 802.11 frames. From the point of view of 11a/b/g STAs > the medium is reserved by the AP for the entire period during > which an A-MPDU is sent by the AP or another 11n STA. > > Multicast frames are never sent in an A-MPDU. Before A-MPDUs > can be sent a Block Ack agreement must be established between > 2 peers. A Block Ack agreement is identified by an arbitrarily > chosen traffic identifier value (TID) and the peer's MAC address. > Once an agreement is set up, the hardware/firmware on each side > keeps track of successfully received and failed subframes of > an A-MPDU and signals success or failure of subframes to the > peer via a bitmap contained in a BlockAck response frame. > This BlockAck response is invisible to the driver and net80211 layers. > > Subframes received successfully are passed up to the driver and > net80211 layer, where they appear like regular 802.11 frames. > Subframes are matched to an existing BlockAck agreement with the > peer (ieee80211_node) based on the TID in their MAC header. > Because subframes may be received out of sequence, the net80211 layer > has to buffer subframes which cannot yet be passed to the upper > layers of the network stack since their sequence number is too new. > The net80211 layer is aware of the current sequence number window > and slides it forward as needed after either passing subframes > up the stack or discarding the entire set of buffered frames in > case the window has moved forward too far. > > Note that there is an injection attack against A-MPDUs which we can do > nothing about at the driver level: https://github.com/rpp0/aggr-inject > The problem is rooted in poorly written wireless device firmware code > responsible for splitting A-MPDU frames into subframes. > The attack is possible on unencrypted wifi only. > Once OpenBSD starts sending A-MPDUs we can prevent this attack being > performed through OpenBSD by not sending A-MPDUs on unencrypted networks. > > Most of the implementation was written by damien@ years ago and > was already committed back then. This diff adds the missing pieces > and fixes a few bugs. The changes are: > > - In ieee80211_input(), do A-MPDU processing before duplicate > detection based on sequence number. The previous order was > wrong since subframes are passed into ieee80211_input() twice, > once before going through the buffer for reordering and once > after being sent back into ieee80211_input() from the buffer. > > - Don't forget to set ba->ba_ni in ieee80211_recv_addba_req() > so we don't crash in ieee80211_rx_ba_timeout(). > > - In ieee80211_recv_addba_req(), tweak the logic to deny BlockAck > requests if the driver has no callback for doing so. This shouldn't > happen in production but helps during development while adding 11n > support to a driver. > > - Implement ieee80211_ba_del() which cleans up BlockAck state kept > in the ieee80211_node structure. This is called when the node > resets all of its state or when the node is freed. > > - Increase the minimum and maximum lifetime for BlockAck agrements. > There seem to be no values given in the spec, and neither Linux > nor FreeBSD seem to enforce any limits. The existing minimum is > too short for BlockAck to be effective because the agreement may > already be cancelled before the first A-MPDU arrives. I chose the > new values arbitrarily. These values may be tuned in the future if > necessary. > > Tested against several APs. > > OK?
ok mpi@ > Index: net80211/ieee80211_input.c > =================================================================== > RCS file: /cvs/src/sys/net80211/ieee80211_input.c,v > retrieving revision 1.142 > diff -u -p -r1.142 ieee80211_input.c > --- net80211/ieee80211_input.c 15 Nov 2015 11:14:17 -0000 1.142 > +++ net80211/ieee80211_input.c 12 Dec 2015 08:49:12 -0000 > @@ -280,6 +280,43 @@ ieee80211_input(struct ifnet *ifp, struc > tid = 0; > } > > +#ifndef IEEE80211_NO_HT > + if (type == IEEE80211_FC0_TYPE_DATA && hasqos && > + !(rxi->rxi_flags & IEEE80211_RXI_AMPDU_DONE)) { > + int ba_state = ni->ni_rx_ba[tid].ba_state; > + > + /* > + * If Block Ack was explicitly requested, check > + * if we have a BA agreement for this RA/TID. > + */ > + if ((qos & IEEE80211_QOS_ACK_POLICY_MASK) == > + IEEE80211_QOS_ACK_POLICY_BA && > + ba_state != IEEE80211_BA_AGREED) { > + DPRINTF(("no BA agreement for %s, TID %d\n", > + ether_sprintf(ni->ni_macaddr), tid)); > + /* send a DELBA with reason code UNKNOWN-BA */ > + IEEE80211_SEND_ACTION(ic, ni, > + IEEE80211_CATEG_BA, IEEE80211_ACTION_DELBA, > + IEEE80211_REASON_SETUP_REQUIRED << 16 | tid); > + goto err; > + } > + > + /* > + * Check if we have an explicit or implicit > + * Block Ack Request for a valid BA agreement. > + */ > + if (ba_state == IEEE80211_BA_AGREED && > + ((qos & IEEE80211_QOS_ACK_POLICY_MASK) == > + IEEE80211_QOS_ACK_POLICY_BA || > + (qos & IEEE80211_QOS_ACK_POLICY_MASK) == > + IEEE80211_QOS_ACK_POLICY_NORMAL)) { > + /* go through A-MPDU reordering */ > + ieee80211_input_ba(ifp, m, ni, tid, rxi); > + return; /* don't free m! */ > + } > + } > +#endif > + > /* duplicate detection (see 9.2.9) */ > if (ieee80211_has_seq(wh) && > ic->ic_state != IEEE80211_S_SCAN) { > @@ -430,27 +467,6 @@ ieee80211_input(struct ifnet *ifp, struc > goto out; > } > > -#ifndef IEEE80211_NO_HT > - if (!(rxi->rxi_flags & IEEE80211_RXI_AMPDU_DONE) && > - hasqos && (qos & IEEE80211_QOS_ACK_POLICY_MASK) == > - IEEE80211_QOS_ACK_POLICY_BA) { > - /* check if we have a BA agreement for this RA/TID */ > - if (ni->ni_rx_ba[tid].ba_state != > - IEEE80211_BA_AGREED) { > - DPRINTF(("no BA agreement for %s, TID %d\n", > - ether_sprintf(ni->ni_macaddr), tid)); > - /* send a DELBA with reason code UNKNOWN-BA */ > - IEEE80211_SEND_ACTION(ic, ni, > - IEEE80211_CATEG_BA, IEEE80211_ACTION_DELBA, > - IEEE80211_REASON_SETUP_REQUIRED << 16 | > - tid); > - goto err; > - } > - /* go through A-MPDU reordering */ > - ieee80211_input_ba(ifp, m, ni, tid, rxi); > - return; /* don't free m! */ > - } > -#endif > if ((ic->ic_flags & IEEE80211_F_WEPON) || > ((ic->ic_flags & IEEE80211_F_RSNON) && > (ni->ni_flags & IEEE80211_NODE_RXPROT))) { > @@ -2449,6 +2465,7 @@ ieee80211_recv_addba_req(struct ieee8021 > ba->ba_timeout_val = IEEE80211_BA_MIN_TIMEOUT; > else if (ba->ba_timeout_val > IEEE80211_BA_MAX_TIMEOUT) > ba->ba_timeout_val = IEEE80211_BA_MAX_TIMEOUT; > + ba->ba_ni = ni; > timeout_set(&ba->ba_to, ieee80211_rx_ba_timeout, ba); > ba->ba_winsize = bufsz; > if (ba->ba_winsize == 0 || ba->ba_winsize > IEEE80211_BA_MAX_WINSZ) > @@ -2465,7 +2482,7 @@ ieee80211_recv_addba_req(struct ieee8021 > ba->ba_head = 0; > > /* notify drivers of this new Block Ack agreement */ > - if (ic->ic_ampdu_rx_start != NULL && > + if (ic->ic_ampdu_rx_start == NULL || > ic->ic_ampdu_rx_start(ic, ni, tid) != 0) { > /* driver failed to setup, rollback */ > free(ba->ba_buf, M_DEVBUF, 0); > Index: net80211/ieee80211_node.c > =================================================================== > RCS file: /cvs/src/sys/net80211/ieee80211_node.c,v > retrieving revision 1.92 > diff -u -p -r1.92 ieee80211_node.c > --- net80211/ieee80211_node.c 24 Nov 2015 13:45:06 -0000 1.92 > +++ net80211/ieee80211_node.c 12 Dec 2015 08:49:12 -0000 > @@ -68,6 +68,9 @@ u_int8_t ieee80211_node_getrssi(struct i > void ieee80211_setup_node(struct ieee80211com *, struct ieee80211_node *, > const u_int8_t *); > void ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *); > +#ifndef IEEE80211_NO_HT > +void ieee80211_ba_del(struct ieee80211_node *); > +#endif > struct ieee80211_node *ieee80211_alloc_node_helper(struct ieee80211com *); > void ieee80211_node_cleanup(struct ieee80211com *, struct ieee80211_node *); > void ieee80211_needs_auth(struct ieee80211com *, struct ieee80211_node *); > @@ -757,6 +760,9 @@ ieee80211_node_cleanup(struct ieee80211c > free(ni->ni_rsnie, M_DEVBUF, 0); > ni->ni_rsnie = NULL; > } > +#ifndef IEEE80211_NO_HT > + ieee80211_ba_del(ni); > +#endif > } > > void > @@ -1065,6 +1071,32 @@ ieee80211_find_node_for_beacon(struct ie > return (keep); > } > > +#ifndef IEEE80211_NO_HT > +void > +ieee80211_ba_del(struct ieee80211_node *ni) > +{ > + int tid; > + > + for (tid = 0; tid < nitems(ni->ni_rx_ba); tid++) { > + struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid]; > + if (ba->ba_state == IEEE80211_BA_AGREED) { > + if (timeout_pending(&ba->ba_to)) > + timeout_del(&ba->ba_to); > + ba->ba_state = IEEE80211_BA_INIT; > + } > + } > + > + for (tid = 0; tid < nitems(ni->ni_tx_ba); tid++) { > + struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; > + if (ba->ba_state == IEEE80211_BA_AGREED) { > + if (timeout_pending(&ba->ba_to)) > + timeout_del(&ba->ba_to); > + ba->ba_state = IEEE80211_BA_INIT; > + } > + } > +} > +#endif > + > void > ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) > { > @@ -1078,6 +1110,9 @@ ieee80211_free_node(struct ieee80211com > timeout_del(&ni->ni_eapol_to); > timeout_del(&ni->ni_sa_query_to); > IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); > +#endif > +#ifndef IEEE80211_NO_HT > + ieee80211_ba_del(ni); > #endif > RB_REMOVE(ieee80211_tree, &ic->ic_tree, ni); > ic->ic_nnodes--; > Index: net80211/ieee80211_node.h > =================================================================== > RCS file: /cvs/src/sys/net80211/ieee80211_node.h,v > retrieving revision 1.49 > diff -u -p -r1.49 ieee80211_node.h > --- net80211/ieee80211_node.h 15 Nov 2015 12:34:07 -0000 1.49 > +++ net80211/ieee80211_node.h 12 Dec 2015 08:49:12 -0000 > @@ -112,8 +112,8 @@ struct ieee80211_tx_ba { > struct ieee80211_node *ba_ni; /* backpointer for callbacks */ > struct timeout ba_to; > int ba_timeout_val; > -#define IEEE80211_BA_MIN_TIMEOUT (10 * 1000) /* 10msec */ > -#define IEEE80211_BA_MAX_TIMEOUT (10 * 1000 * 1000) /* 10sec */ > +#define IEEE80211_BA_MIN_TIMEOUT (10 * 1000 * 1000) /* 10 sec */ > +#define IEEE80211_BA_MAX_TIMEOUT (60 * 1000 * 1000) /* 60 sec */ > > int ba_state; > #define IEEE80211_BA_INIT 0 >