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
> 

Reply via email to