diff options
Diffstat (limited to 'sys/net80211/ieee80211_input.c')
-rw-r--r-- | sys/net80211/ieee80211_input.c | 1027 |
1 files changed, 878 insertions, 149 deletions
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 24ef3ed4b6a..13afce16102 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -1,9 +1,9 @@ -/* $OpenBSD: ieee80211_input.c,v 1.106 2008/09/27 15:16:09 damien Exp $ */ +/* $OpenBSD: ieee80211_input.c,v 1.107 2009/01/26 19:09:41 damien Exp $ */ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting - * Copyright (c) 2007, 2008 Damien Bergamini + * Copyright (c) 2007-2009 Damien Bergamini * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -62,6 +62,19 @@ #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_priv.h> +#ifndef IEEE80211_NO_HT +void ieee80211_input_ba(struct ifnet *, struct mbuf *, + struct ieee80211_node *, int, struct ieee80211_rxinfo *); +void ieee80211_ba_move_window(struct ieee80211com *, + struct ieee80211_node *, u_int8_t, u_int16_t); +#endif +struct mbuf *ieee80211_align_mbuf(struct mbuf *); +void ieee80211_decap(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *, int); +#ifndef IEEE80211_NO_HT +void ieee80211_amsdu_decap(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *, int); +#endif void ieee80211_deliver_data(struct ieee80211com *, struct mbuf *, struct ieee80211_node *); int ieee80211_parse_edca_params_body(struct ieee80211com *, @@ -73,10 +86,6 @@ enum ieee80211_akm ieee80211_parse_rsn_akm(const u_int8_t[]); int ieee80211_parse_rsn_body(struct ieee80211com *, const u_int8_t *, u_int, struct ieee80211_rsnparams *); int ieee80211_save_ie(const u_int8_t *, u_int8_t **); -#ifndef IEEE80211_STA_ONLY -void ieee80211_recv_pspoll(struct ieee80211com *, struct mbuf *, - struct ieee80211_node *); -#endif void ieee80211_recv_probe_resp(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, struct ieee80211_rxinfo *, int); #ifndef IEEE80211_STA_ONLY @@ -95,11 +104,35 @@ void ieee80211_recv_deauth(struct ieee80211com *, struct mbuf *, struct ieee80211_node *); void ieee80211_recv_disassoc(struct ieee80211com *, struct mbuf *, struct ieee80211_node *); +#ifndef IEEE80211_NO_HT +void ieee80211_recv_addba_req(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *); +void ieee80211_recv_addba_resp(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *); +void ieee80211_recv_delba(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *); +#endif +void ieee80211_recv_sa_query_req(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *); +#ifndef IEEE80211_STA_ONLY +void ieee80211_recv_sa_query_resp(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *); +#endif void ieee80211_recv_action(struct ieee80211com *, struct mbuf *, struct ieee80211_node *); +#ifndef IEEE80211_STA_ONLY +void ieee80211_recv_pspoll(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *); +#endif +#ifndef IEEE80211_NO_HT +void ieee80211_recv_bar(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *); +void ieee80211_bar_tid(struct ieee80211com *, struct ieee80211_node *, + u_int8_t, u_int16_t); +#endif /* - * Retrieve the length in bytes of a 802.11 header. + * Retrieve the length in bytes of an 802.11 header. */ u_int ieee80211_get_hdrlen(const struct ieee80211_frame *wh) @@ -134,14 +167,12 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, { struct ieee80211com *ic = (void *)ifp; struct ieee80211_frame *wh; - u_int16_t *orxseq, nrxseq; + u_int16_t *orxseq, nrxseq, qos; u_int8_t dir, type, subtype, tid; - int hdrlen; + int hdrlen, hasqos; + + KASSERT(ni != NULL); -#ifdef DIAGNOSTIC - if (ni == NULL) - panic("null node"); -#endif /* in monitor mode, send everything directly to bpf */ if (ic->ic_opmode == IEEE80211_M_MONITOR) goto out; @@ -175,15 +206,19 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, goto err; } } - /* check and save sequence control field, if present */ + if ((hasqos = ieee80211_has_qos(wh))) { + qos = ieee80211_get_qos(wh); + tid = qos & IEEE80211_QOS_TID; + } + + /* duplicate detection (see 9.2.9) */ if (ieee80211_has_seq(wh) && ic->ic_state != IEEE80211_S_SCAN) { nrxseq = letoh16(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; - if (ieee80211_has_qos(wh)) { - tid = ieee80211_get_qos(wh) & IEEE80211_QOS_TID; + if (hasqos) orxseq = &ni->ni_qos_rxseqs[tid]; - } else + else orxseq = &ni->ni_rxseq; if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && nrxseq == *orxseq) { @@ -324,6 +359,26 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, 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_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))) { @@ -354,14 +409,14 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, if (ic->ic_rawbpf) bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_IN); #endif - m = ieee80211_decap(ifp, m, hdrlen); - if (m == NULL) { - DPRINTF(("decapsulation error for src %s\n", - ether_sprintf(wh->i_addr2))); - ic->ic_stats.is_rx_decap++; - goto err; - } - ieee80211_deliver_data(ic, m, ni); + +#ifndef IEEE80211_NO_HT + if ((ni->ni_flags & IEEE80211_NODE_HT) && + hasqos && (qos & IEEE80211_QOS_AMSDU)) + ieee80211_amsdu_decap(ic, m, ni, hdrlen); + else +#endif + ieee80211_decap(ic, m, ni, hdrlen); return; case IEEE80211_FC0_TYPE_MGT: @@ -467,11 +522,12 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, ieee80211_recv_pspoll(ic, m, ni); break; #endif +#ifndef IEEE80211_NO_HT case IEEE80211_FC0_SUBTYPE_BAR: - /* NYI */ + ieee80211_recv_bar(ic, m, ni); break; - case IEEE80211_FC0_SUBTYPE_BA: - /* NYI */ +#endif + default: break; } goto out; @@ -493,6 +549,120 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, } } +#ifndef IEEE80211_NO_HT +/* + * Process a received data MPDU related to a specific HT-immediate Block Ack + * agreement (see 9.10.7.6). + */ +void +ieee80211_input_ba(struct ifnet *ifp, struct mbuf *m, + struct ieee80211_node *ni, int tid, struct ieee80211_rxinfo *rxi) +{ + struct ieee80211_ba *ba = &ni->ni_ba[tid]; + struct ieee80211_frame *wh; + int idx, count; + u_int16_t sn; + + wh = mtod(m, struct ieee80211_frame *); + sn = letoh16(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; + + /* reset Block Ack inactivity timer */ + timeout_add_usec(&ba->ba_to, ba->ba_timeout_val); + + if (SEQ_LT(sn, ba->ba_winstart)) { /* SN < WinStartB */ + ifp->if_ierrors++; + m_freem(m); /* discard the MPDU */ + return; + } + if (SEQ_LT(ba->ba_winend, sn)) { /* WinEndB < SN */ + count = (sn - ba->ba_winend) & 0xfff; + if (count > ba->ba_winsize) /* no overlap */ + count = ba->ba_winsize; + while (count-- > 0) { + /* gaps may exist */ + if (ba->ba_buf[ba->ba_head].m != NULL) { + ieee80211_input(ifp, ba->ba_buf[ba->ba_head].m, + ni, &ba->ba_buf[ba->ba_head].rxi); + ba->ba_buf[ba->ba_head].m = NULL; + } + ba->ba_head = (ba->ba_head + 1) % + IEEE80211_BA_MAX_WINSZ; + } + /* move window forward */ + ba->ba_winend = sn; + ba->ba_winstart = (sn - ba->ba_winsize + 1) & 0xfff; + } + /* WinStartB <= SN <= WinEndB */ + + idx = (sn - ba->ba_winstart) & 0xfff; + idx = (ba->ba_head + idx) % IEEE80211_BA_MAX_WINSZ; + /* store the received MPDU in the buffer */ + if (ba->ba_buf[idx].m != NULL) { + ifp->if_ierrors++; + m_freem(m); + return; + } + ba->ba_buf[idx].m = m; + /* store Rx meta-data too */ + rxi->rxi_flags |= IEEE80211_RXI_AMPDU_DONE; + ba->ba_buf[idx].rxi = *rxi; + + /* pass reordered MPDUs up to the next MAC process */ + while (ba->ba_buf[ba->ba_head].m != NULL) { + ieee80211_input(ifp, ba->ba_buf[ba->ba_head].m, ni, + &ba->ba_buf[ba->ba_head].rxi); + ba->ba_buf[ba->ba_head].m = NULL; + + ba->ba_head = (ba->ba_head + 1) % IEEE80211_BA_MAX_WINSZ; + /* move window forward */ + ba->ba_winstart = (ba->ba_winstart + 1) & 0xfff; + } + ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff; +} + +/* + * Change the value of WinStartB (move window forward) upon reception of a + * Block Ack Request frame or an ADDBA Request (PBAC). + */ +void +ieee80211_ba_move_window(struct ieee80211com *ic, struct ieee80211_node *ni, + u_int8_t tid, u_int16_t ssn) +{ + struct ifnet *ifp = &ic->ic_if; + struct ieee80211_ba *ba = &ni->ni_ba[tid]; + int count; + + /* assert(WinStartB <= SSN) */ + + count = (ssn - ba->ba_winstart) & 0xfff; + if (count > ba->ba_winsize) /* no overlap */ + count = ba->ba_winsize; + while (count-- > 0) { + /* gaps may exist */ + if (ba->ba_buf[ba->ba_head].m != NULL) { + ieee80211_input(ifp, ba->ba_buf[ba->ba_head].m, ni, + &ba->ba_buf[ba->ba_head].rxi); + ba->ba_buf[ba->ba_head].m = NULL; + } + ba->ba_head = (ba->ba_head + 1) % IEEE80211_BA_MAX_WINSZ; + } + /* move window forward */ + ba->ba_winstart = ssn; + + /* pass reordered MPDUs up to the next MAC process */ + while (ba->ba_buf[ba->ba_head].m != NULL) { + ieee80211_input(ifp, ba->ba_buf[ba->ba_head].m, ni, + &ba->ba_buf[ba->ba_head].rxi); + ba->ba_buf[ba->ba_head].m = NULL; + + ba->ba_head = (ba->ba_head + 1) % IEEE80211_BA_MAX_WINSZ; + /* move window forward */ + ba->ba_winstart = (ba->ba_winstart + 1) & 0xfff; + } + ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff; +} +#endif /* !IEEE80211_NO_HT */ + void ieee80211_deliver_data(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni) @@ -570,17 +740,78 @@ ieee80211_deliver_data(struct ieee80211com *ic, struct mbuf *m, } } +/* + * Make sure protocol header (e.g. IP) is aligned on a 32-bit boundary. + * This is achieved by copying mbufs so drivers should try to map their + * buffers such that this copying is not necessary. It is however not + * always possible because 802.11 header length may vary (non-QoS+LLC + * is 32 bytes while QoS+LLC is 34 bytes). Some devices are smart and + * add 2 padding bytes after the 802.11 header in the QoS case so this + * function is there for brain-dead devices only. + */ struct mbuf * -ieee80211_decap(struct ifnet *ifp, struct mbuf *m, int hdrlen) +ieee80211_align_mbuf(struct mbuf *m) +{ + struct mbuf *n, *n0, **np; + caddr_t newdata; + int off, pktlen; + + n0 = NULL; + np = &n0; + off = 0; + pktlen = m->m_pkthdr.len; + while (pktlen > off) { + if (n0 == NULL) { + MGETHDR(n, M_DONTWAIT, MT_DATA); + if (n == NULL) { + m_freem(m); + return NULL; + } + M_DUP_PKTHDR(n, m); + n->m_len = MHLEN; + } else { + MGET(n, M_DONTWAIT, MT_DATA); + if (n == NULL) { + m_freem(m); + m_freem(n0); + return NULL; + } + n->m_len = MLEN; + } + if (pktlen - off >= MINCLSIZE) { + MCLGET(n, M_DONTWAIT); + if (n->m_flags & M_EXT) + n->m_len = n->m_ext.ext_size; + } + if (n0 == NULL) { + newdata = (caddr_t)ALIGN(n->m_data + ETHER_HDR_LEN) - + ETHER_HDR_LEN; + n->m_len -= newdata - n->m_data; + n->m_data = newdata; + } + if (n->m_len > pktlen - off) + n->m_len = pktlen - off; + m_copydata(m, off, n->m_len, mtod(n, caddr_t)); + off += n->m_len; + *np = n; + np = &n->m_next; + } + m_freem(m); + return n0; +} + +void +ieee80211_decap(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni, int hdrlen) { struct ieee80211_qosframe_addr4 wh; /* largest 802.11 header */ struct ether_header *eh; struct llc *llc; - if (m->m_len < hdrlen + LLC_SNAPFRAMELEN) { - m = m_pullup(m, hdrlen + LLC_SNAPFRAMELEN); - if (m == NULL) - return NULL; + if (m->m_len < hdrlen + LLC_SNAPFRAMELEN && + (m = m_pullup(m, hdrlen + LLC_SNAPFRAMELEN)) == NULL) { + ic->ic_stats.is_rx_decap++; + return; } memcpy(&wh, mtod(m, caddr_t), hdrlen); llc = (struct llc *)(mtod(m, caddr_t) + hdrlen); @@ -590,10 +821,10 @@ ieee80211_decap(struct ifnet *ifp, struct mbuf *m, int hdrlen) llc->llc_snap.org_code[0] == 0 && llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0) { - m_adj(m, hdrlen + LLC_SNAPFRAMELEN - sizeof(*eh)); + m_adj(m, hdrlen + LLC_SNAPFRAMELEN - ETHER_HDR_LEN); llc = NULL; } else { - m_adj(m, hdrlen - sizeof(*eh)); + m_adj(m, hdrlen - ETHER_HDR_LEN); } eh = mtod(m, struct ether_header *); switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) { @@ -614,62 +845,90 @@ ieee80211_decap(struct ifnet *ifp, struct mbuf *m, int hdrlen) IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr4); break; } - if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) { - struct mbuf *n, *n0, **np; - caddr_t newdata; - int off, pktlen; - - n0 = NULL; - np = &n0; - off = 0; - pktlen = m->m_pkthdr.len; - while (pktlen > off) { - if (n0 == NULL) { - MGETHDR(n, M_DONTWAIT, MT_DATA); - if (n == NULL) { - m_freem(m); - return NULL; - } - M_DUP_PKTHDR(n, m); - n->m_len = MHLEN; - } else { - MGET(n, M_DONTWAIT, MT_DATA); - if (n == NULL) { - m_freem(m); - m_freem(n0); - return NULL; - } - n->m_len = MLEN; - } - if (pktlen - off >= MINCLSIZE) { - MCLGET(n, M_DONTWAIT); - if (n->m_flags & M_EXT) - n->m_len = n->m_ext.ext_size; - } - if (n0 == NULL) { - newdata = - (caddr_t)ALIGN(n->m_data + sizeof(*eh)) - - sizeof(*eh); - n->m_len -= newdata - n->m_data; - n->m_data = newdata; - } - if (n->m_len > pktlen - off) - n->m_len = pktlen - off; - m_copydata(m, off, n->m_len, mtod(n, caddr_t)); - off += n->m_len; - *np = n; - np = &n->m_next; + if (!ALIGNED_POINTER(mtod(m, caddr_t) + ETHER_HDR_LEN, u_int32_t)) { + if ((m = ieee80211_align_mbuf(m)) == NULL) { + ic->ic_stats.is_rx_decap++; + return; } - m_freem(m); - m = n0; } if (llc != NULL) { eh = mtod(m, struct ether_header *); - eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh)); + eh->ether_type = htons(m->m_pkthdr.len - ETHER_HDR_LEN); } - return m; + ieee80211_deliver_data(ic, m, ni); } +#ifndef IEEE80211_NO_HT +/* + * Decapsulate an Aggregate MSDU (see 7.2.2.2). + */ +void +ieee80211_amsdu_decap(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni, int hdrlen) +{ + struct mbuf *n; + struct ether_header *eh; + struct llc *llc; + int len, pad; + + /* strip 802.11 header */ + m_adj(m, hdrlen); + + for (;;) { + /* process an A-MSDU subframe */ + if (m->m_len < ETHER_HDR_LEN + LLC_SNAPFRAMELEN) { + m = m_pullup(m, ETHER_HDR_LEN + LLC_SNAPFRAMELEN); + if (m == NULL) { + ic->ic_stats.is_rx_decap++; + break; + } + } + eh = mtod(m, struct ether_header *); + /* examine 802.3 header */ + len = ntohs(eh->ether_type); + if (len < LLC_SNAPFRAMELEN) { + DPRINTF(("A-MSDU subframe too short (%d)\n", len)); + /* stop processing A-MSDU subframes */ + ic->ic_stats.is_rx_decap++; + m_freem(m); + break; + } + llc = (struct llc *)&eh[1]; + /* examine 802.2 LLC header */ + if (llc->llc_dsap == LLC_SNAP_LSAP && + llc->llc_ssap == LLC_SNAP_LSAP && + llc->llc_control == LLC_UI && + llc->llc_snap.org_code[0] == 0 && + llc->llc_snap.org_code[1] == 0 && + llc->llc_snap.org_code[2] == 0) { + /* convert to Ethernet II header */ + eh->ether_type = llc->llc_snap.ether_type; + /* strip LLC+SNAP headers */ + ovbcopy(eh, (u_int8_t *)eh + LLC_SNAPFRAMELEN, + ETHER_HDR_LEN); + m_adj(m, LLC_SNAPFRAMELEN); + len -= LLC_SNAPFRAMELEN; + } + len += ETHER_HDR_LEN; + + /* "detach" our A-MSDU subframe from the others */ + n = m_split(m, len, M_NOWAIT); + if (n == NULL) { + /* stop processing A-MSDU subframes */ + ic->ic_stats.is_rx_decap++; + m_freem(m); + break; + } + ieee80211_deliver_data(ic, m, ni); + + m = n; + /* remove padding */ + pad = ((len + 3) & ~3) - len; + m_adj(m, pad); + } +} +#endif /* !IEEE80211_NO_HT */ + /* * Parse an EDCA Parameter Set element (see 7.3.2.27). */ @@ -758,8 +1017,8 @@ ieee80211_parse_rsn_cipher(const u_int8_t selector[4]) return IEEE80211_CIPHER_CCMP; case 5: /* WEP-104 */ return IEEE80211_CIPHER_WEP104; - case 6: /* AES-128-CMAC */ - return IEEE80211_CIPHER_AES128_CMAC; + case 6: /* BIP */ + return IEEE80211_CIPHER_BIP; } } return IEEE80211_CIPHER_NONE; /* ignore unknown ciphers */ @@ -814,8 +1073,8 @@ ieee80211_parse_rsn_body(struct ieee80211com *ic, const u_int8_t *frm, rsn->rsn_groupcipher = IEEE80211_CIPHER_CCMP; rsn->rsn_nciphers = 1; rsn->rsn_ciphers = IEEE80211_CIPHER_CCMP; - /* if Group Management Cipher Suite missing, defaut to AES-128-CMAC */ - rsn->rsn_groupmgmtcipher = IEEE80211_CIPHER_AES128_CMAC; + /* if Group Management Cipher Suite missing, defaut to BIP */ + rsn->rsn_groupmgmtcipher = IEEE80211_CIPHER_BIP; /* if AKM Suite missing, default to 802.1X */ rsn->rsn_nakms = 1; rsn->rsn_akms = IEEE80211_AKM_8021X; @@ -936,17 +1195,19 @@ ieee80211_save_ie(const u_int8_t *frm, u_int8_t **ie) /*- * Beacon/Probe response frame format: - * [8] Timestamp - * [2] Beacon interval - * [2] Capability - * [tlv] Service Set Identifier (SSID) - * [tlv] Supported rates - * [tlv*] DS Parameter Set (802.11g) - * [tlv] ERP Information (802.11g) - * [tlv] Extended Supported Rates (802.11g) - * [tlv] RSN (802.11i) - * [tlv] EDCA Parameter Set (802.11e) - * [tlv] QoS Capability (Beacon only, 802.11e) + * [8] Timestamp + * [2] Beacon interval + * [2] Capability + * [tlv] Service Set Identifier (SSID) + * [tlv] Supported rates + * [tlv] DS Parameter Set (802.11g) + * [tlv] ERP Information (802.11g) + * [tlv] Extended Supported Rates (802.11g) + * [tlv] RSN (802.11i) + * [tlv] EDCA Parameter Set (802.11e) + * [tlv] QoS Capability (Beacon only, 802.11e) + * [tlv] HT Capabilities (802.11n) + * [tlv] HT Operation (802.11n) */ void ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0, @@ -955,7 +1216,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0, const struct ieee80211_frame *wh; const u_int8_t *frm, *efrm; const u_int8_t *tstamp, *ssid, *rates, *xrates, *edcaie, *wmmie; - const u_int8_t *rsnie, *wpaie; + const u_int8_t *rsnie, *wpaie, *htcaps, *htop; u_int16_t capinfo, bintval; u_int8_t chan, bchan, erp; int is_new; @@ -996,6 +1257,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0, capinfo = LE_READ_2(frm); frm += 2; ssid = rates = xrates = edcaie = wmmie = rsnie = wpaie = NULL; + htcaps = htop = NULL; bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan); chan = bchan; erp = 0; @@ -1040,6 +1302,14 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0, break; case IEEE80211_ELEMID_QOS_CAP: break; +#ifndef IEEE80211_NO_HT + case IEEE80211_ELEMID_HTCAPS: + htcaps = frm; + break; + case IEEE80211_ELEMID_HTOP: + htop = frm; + break; +#endif case IEEE80211_ELEMID_VENDOR: if (frm[1] < 4) { ic->ic_stats.is_rx_elem_toosmall++; @@ -1260,6 +1530,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0, * [tlv] SSID * [tlv] Supported rates * [tlv] Extended Supported Rates (802.11g) + * [tlv] HT Capabilities (802.11n) */ void ieee80211_recv_probe_req(struct ieee80211com *ic, struct mbuf *m0, @@ -1267,7 +1538,7 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, struct mbuf *m0, { const struct ieee80211_frame *wh; const u_int8_t *frm, *efrm; - const u_int8_t *ssid, *rates, *xrates; + const u_int8_t *ssid, *rates, *xrates, *htcaps; u_int8_t rate; if (ic->ic_opmode == IEEE80211_M_STA || @@ -1278,7 +1549,7 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, struct mbuf *m0, frm = (const u_int8_t *)&wh[1]; efrm = mtod(m0, u_int8_t *) + m0->m_len; - ssid = rates = xrates = NULL; + ssid = rates = xrates = htcaps = NULL; while (frm + 2 <= efrm) { if (frm + 2 + frm[1] > efrm) { ic->ic_stats.is_rx_elem_toosmall++; @@ -1294,6 +1565,11 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_ELEMID_XRATES: xrates = frm; break; +#ifndef IEEE80211_NO_HT + case IEEE80211_ELEMID_HTCAPS: + htcaps = frm; + break; +#endif } frm += 2 + frm[1]; } @@ -1379,8 +1655,8 @@ ieee80211_recv_auth(struct ieee80211com *ic, struct mbuf *m0, if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX hack to workaround calling convention */ IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, - (seq+1) | (IEEE80211_STATUS_ALG<<16)); + IEEE80211_FC0_SUBTYPE_AUTH, + IEEE80211_STATUS_ALG << 16 | ((seq + 1) & 0xffff)); } #endif return; @@ -1399,6 +1675,7 @@ ieee80211_recv_auth(struct ieee80211com *ic, struct mbuf *m0, * [tlv] Extended Supported Rates (802.11g) * [tlv] RSN (802.11i) * [tlv] QoS Capability (802.11e) + * [tlv] HT Capabilities (802.11n) */ void ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0, @@ -1406,7 +1683,7 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0, { const struct ieee80211_frame *wh; const u_int8_t *frm, *efrm; - const u_int8_t *ssid, *rates, *xrates, *rsnie, *wpaie; + const u_int8_t *ssid, *rates, *xrates, *rsnie, *wpaie, *htcaps; u_int16_t capinfo, bintval; int resp, status = 0; struct ieee80211_rsnparams rsn; @@ -1433,10 +1710,13 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0, } capinfo = LE_READ_2(frm); frm += 2; bintval = LE_READ_2(frm); frm += 2; - if (reassoc) + if (reassoc) { frm += IEEE80211_ADDR_LEN; /* skip current AP address */ + resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP; + } else + resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP; - ssid = rates = xrates = rsnie = wpaie = NULL; + ssid = rates = xrates = rsnie = wpaie = htcaps = NULL; while (frm + 2 <= efrm) { if (frm + 2 + frm[1] > efrm) { ic->ic_stats.is_rx_elem_toosmall++; @@ -1457,6 +1737,11 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0, break; case IEEE80211_ELEMID_QOS_CAP: break; +#ifndef IEEE80211_NO_HT + case IEEE80211_ELEMID_HTCAPS: + htcaps = frm; + break; +#endif case IEEE80211_ELEMID_VENDOR: if (frm[1] < 4) { ic->ic_stats.is_rx_elem_toosmall++; @@ -1503,6 +1788,27 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0, return; } + if (ni->ni_state == IEEE80211_STA_ASSOC && + (ni->ni_flags & IEEE80211_NODE_MFP)) { + if (ni->ni_flags & IEEE80211_NODE_SA_QUERY_FAILED) { + /* send a protected Disassociate frame */ + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_AUTH_EXPIRE); + /* terminate the old SA */ + ieee80211_node_leave(ic, ni); + } else { + /* reject the (Re)Association Request temporarily */ + IEEE80211_SEND_MGMT(ic, ni, resp, + IEEE80211_STATUS_TRY_AGAIN_LATER); + /* start SA Query procedure if not already engaged */ + if (!(ni->ni_flags & IEEE80211_NODE_SA_QUERY)) + ieee80211_sa_query_request(ic, ni); + /* do not modify association state */ + } + return; + } + if (!(capinfo & IEEE80211_CAPINFO_ESS)) { ic->ic_stats.is_rx_assoc_capmismatch++; status = IEEE80211_STATUS_CAPINFO; @@ -1569,23 +1875,28 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0, goto end; } - if (!(rsn.rsn_caps & IEEE80211_RSNCAP_MFPC) && - (ic->ic_bss->ni_rsncaps & IEEE80211_RSNCAP_MFPR)) { - status = IEEE80211_REASON_MFP_POLICY; /* XXX */ + if ((ic->ic_bss->ni_rsncaps & IEEE80211_RSNCAP_MFPR) && + !(rsn.rsn_caps & IEEE80211_RSNCAP_MFPC)) { + status = IEEE80211_STATUS_MFP_POLICY; goto end; } if ((ic->ic_bss->ni_rsncaps & IEEE80211_RSNCAP_MFPC) && (rsn.rsn_caps & (IEEE80211_RSNCAP_MFPC | IEEE80211_RSNCAP_MFPR)) == IEEE80211_RSNCAP_MFPR) { /* STA advertises an invalid setting */ - status = IEEE80211_REASON_MFP_POLICY; /* XXX */ + status = IEEE80211_STATUS_MFP_POLICY; goto end; } + /* + * A STA that has associated with Management Frame Protection + * enabled shall not use cipher suite pairwise selector WEP40, + * WEP104, TKIP, or "Use Group cipher suite". + */ if ((rsn.rsn_caps & IEEE80211_RSNCAP_MFPC) && - rsn.rsn_groupmgmtcipher != - ic->ic_bss->ni_rsngroupmgmtcipher) { - /* XXX satus not reason?! */ - status = IEEE80211_REASON_BAD_GROUP_MGMT_CIPHER; + (rsn.rsn_ciphers != IEEE80211_CIPHER_CCMP || + rsn.rsn_groupmgmtcipher != + ic->ic_bss->ni_rsngroupmgmtcipher)) { + status = IEEE80211_STATUS_MFP_POLICY; goto end; } @@ -1641,8 +1952,6 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0, ni->ni_capinfo = capinfo; ni->ni_chan = ic->ic_bss->ni_chan; end: - resp = reassoc ? IEEE80211_FC0_SUBTYPE_REASSOC_RESP : - IEEE80211_FC0_SUBTYPE_ASSOC_RESP; if (status != 0) { IEEE80211_SEND_MGMT(ic, ni, resp, status); ieee80211_node_leave(ic, ni); @@ -1659,6 +1968,8 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0, * [tlv] Supported rates * [tlv] Extended Supported Rates (802.11g) * [tlv] EDCA Parameter Set (802.11e) + * [tlv] HT Capabilities (802.11n) + * [tlv] HT Operation (802.11n) */ void ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0, @@ -1667,7 +1978,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0, struct ifnet *ifp = &ic->ic_if; const struct ieee80211_frame *wh; const u_int8_t *frm, *efrm; - const u_int8_t *rates, *xrates, *edcaie, *wmmie; + const u_int8_t *rates, *xrates, *edcaie, *wmmie, *htcaps, *htop; u_int16_t capinfo, status, associd; u_int8_t rate; @@ -1701,7 +2012,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0, } associd = LE_READ_2(frm); frm += 2; - rates = xrates = edcaie = wmmie = NULL; + rates = xrates = edcaie = wmmie = htcaps = htop = NULL; while (frm + 2 <= efrm) { if (frm + 2 + frm[1] > efrm) { ic->ic_stats.is_rx_elem_toosmall++; @@ -1717,6 +2028,14 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_ELEMID_EDCAPARMS: edcaie = frm; break; +#ifndef IEEE80211_NO_HT + case IEEE80211_ELEMID_HTCAPS: + htcaps = frm; + break; + case IEEE80211_ELEMID_HTOP: + htop = frm; + break; +#endif case IEEE80211_ELEMID_VENDOR: if (frm[1] < 4) { ic->ic_stats.is_rx_elem_toosmall++; @@ -1883,54 +2202,374 @@ ieee80211_recv_disassoc(struct ieee80211com *ic, struct mbuf *m0, } } +#ifndef IEEE80211_NO_HT +/*- + * ADDBA Request frame format: + * [1] Category + * [1] Action + * [1] Dialog Token + * [2] Block Ack Parameter Set + * [2] Block Ack Timeout Value + * [2] Block Ack Starting Sequence Control + */ +void +ieee80211_recv_addba_req(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni) +{ + const struct ieee80211_frame *wh; + const u_int8_t *frm; + struct ieee80211_ba *ba; + u_int16_t params, ssn, bufsz, timeout, status; + u_int8_t token, tid; + + if (!(ni->ni_flags & IEEE80211_NODE_HT)) { + DPRINTF(("received ADDBA req from non-HT STA %s\n", + ether_sprintf(ni->ni_macaddr))); + return; + } + if (m->m_len < sizeof(*wh) + 9) { + DPRINTF(("frame too short\n")); + return; + } + /* MLME-ADDBA.indication */ + wh = mtod(m, struct ieee80211_frame *); + frm = (const u_int8_t *)&wh[1]; + + token = frm[2]; + params = LE_READ_2(&frm[3]); + tid = (params >> 2) & 0xf; + bufsz = (params >> 6) & 0x3ff; + timeout = LE_READ_2(&frm[5]); + ssn = LE_READ_2(&frm[7]) >> 4; + + ba = &ni->ni_ba[tid]; + /* check if we already have a BA agreement for this RA/TID */ + if (ba->ba_state == IEEE80211_BA_AGREED) { + /* XXX should we update the timeout value? */ + /* reset BA inactivity timer */ + timeout_add_usec(&ba->ba_to, ba->ba_timeout_val); + + /* check if it's a Protected Block Ack agreement */ + if (!(ni->ni_flags & IEEE80211_NODE_MFP) || + !(ni->ni_rsncaps & IEEE80211_RSNCAP_PBAC)) + return; /* not a PBAC, ignore */ + + /* PBAC: treat the ADDBA Request like a BAR */ + if (SEQ_LT(ba->ba_winstart, ssn)) + ieee80211_ba_move_window(ic, ni, tid, ssn); + return; + } + /* if PBAC required but RA does not support it, refuse request */ + if ((ic->ic_flags & IEEE80211_F_PBAR) && + (!(ni->ni_flags & IEEE80211_NODE_MFP) || + !(ni->ni_rsncaps & IEEE80211_RSNCAP_PBAC))) { + status = IEEE80211_STATUS_REFUSED; + goto resp; + } + /* + * If the TID for which the Block Ack agreement is requested is + * configured with a no-ACK policy, refuse the agreement. + */ + if (ic->ic_tid_noack & (1 << tid)) { + status = IEEE80211_STATUS_REFUSED; + goto resp; + } + /* check that we support the requested Block Ack Policy */ + if (!(ic->ic_htcaps & IEEE80211_HTCAP_DELAYEDBA) && + !(params & IEEE80211_BA_ACK_POLICY)) { + status = IEEE80211_STATUS_INVALID_PARAM; + goto resp; + } + + /* setup Block Ack agreement */ + ba->ba_state = IEEE80211_BA_INIT; + ba->ba_token = token; /* save Dialog Token for ADDBA Resp */ + ba->ba_timeout_val = timeout * IEEE80211_DUR_TU; + if (ba->ba_timeout_val < IEEE80211_BA_MIN_TIMEOUT) + 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; + timeout_set(&ba->ba_to, ieee80211_ba_timeout, ba); + ba->ba_winsize = bufsz; + if (ba->ba_winsize == 0 || ba->ba_winsize > IEEE80211_BA_MAX_WINSZ) + ba->ba_winsize = IEEE80211_BA_MAX_WINSZ; + ba->ba_winstart = ssn; + ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff; + /* allocate and setup our reordering buffer */ + ba->ba_buf = malloc(IEEE80211_BA_MAX_WINSZ * sizeof(*ba->ba_buf), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (ba->ba_buf == NULL) { + status = IEEE80211_STATUS_REFUSED; + goto resp; + } + ba->ba_head = 0; + + /* notify drivers of this new Block Ack agreement */ + if (ic->ic_htimmba_start != NULL && + ic->ic_htimmba_start(ic, ni, tid) != 0) { + /* driver failed to setup, rollback */ + free(ba->ba_buf, M_DEVBUF); + ba->ba_buf = NULL; + status = IEEE80211_STATUS_REFUSED; + goto resp; + } + ba->ba_state = IEEE80211_BA_AGREED; + /* start Block Ack inactivity timer */ + timeout_add_usec(&ba->ba_to, ba->ba_timeout_val); + status = IEEE80211_STATUS_SUCCESS; + resp: + /* MLME-ADDBA.response */ + IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_BA, + IEEE80211_ACTION_ADDBA_RESP, status << 16 | tid); +} + +/*- + * ADDBA Response frame format: + * [1] Category + * [1] Action + * [1] Dialog Token + * [2] Status Code + * [2] Block Ack Parameter Set + * [2] Block Ack Timeout Value + */ +void +ieee80211_recv_addba_resp(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni) +{ + const struct ieee80211_frame *wh; + const u_int8_t *frm; + struct ieee80211_ba *ba; + u_int16_t status, params, bufsz, timeout; + u_int8_t token, tid; + + if (m->m_len < sizeof(*wh) + 9) { + DPRINTF(("frame too short\n")); + return; + } + wh = mtod(m, struct ieee80211_frame *); + frm = (const u_int8_t *)&wh[1]; + + token = frm[2]; + status = LE_READ_2(&frm[3]); + params = LE_READ_2(&frm[5]); + tid = (params >> 2) & 0xf; + bufsz = (params >> 6) & 0x3ff; + timeout = LE_READ_2(&frm[7]); + + DPRINTF(("received ADDBA resp from %s, TID %d, status %d\n", + ether_sprintf(ni->ni_macaddr), tid, status)); + + /* + * Ignore if no ADDBA request has been sent for this RA/TID or + * if we already have a Block Ack agreement. + */ + ba = &ni->ni_ba[tid]; + if (ba->ba_state != IEEE80211_BA_REQUESTED) { + DPRINTF(("no matching ADDBA req found\n")); + return; + } + if (token != ba->ba_token) { + DPRINTF(("ignoring ADDBA resp from %s: token %x!=%x\n", + ether_sprintf(ni->ni_macaddr), token, ba->ba_token)); + return; + } + /* we got an ADDBA Response matching our request, stop timeout */ + timeout_del(&ba->ba_to); + + if (status != IEEE80211_STATUS_SUCCESS) { + /* MLME-ADDBA.confirm(Failure) */ + ba->ba_state = IEEE80211_BA_INIT; + return; + } + /* MLME-ADDBA.confirm(Success) */ + ba->ba_state = IEEE80211_BA_AGREED; + + /* notify drivers of this new Block Ack agreement */ + if (ic->ic_htimmba_start != NULL) + (void)ic->ic_htimmba_start(ic, ni, tid); + + /* start Block Ack inactivity timeout */ + timeout_add_usec(&ba->ba_to, ba->ba_timeout_val); +} + +/*- + * DELBA frame format: + * [1] Category + * [1] Action + * [2] DELBA Parameter Set + * [2] Reason Code + */ +void +ieee80211_recv_delba(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni) +{ + const struct ieee80211_frame *wh; + const u_int8_t *frm; + struct ieee80211_ba *ba; + u_int16_t params, reason; + u_int8_t tid; + int i; + + if (m->m_len < sizeof(*wh) + 6) { + DPRINTF(("frame too short\n")); + return; + } + wh = mtod(m, struct ieee80211_frame *); + frm = (const u_int8_t *)&wh[1]; + + params = LE_READ_2(&frm[2]); + reason = LE_READ_2(&frm[4]); + tid = params >> 12; + + DPRINTF(("received DELBA from %s, TID %d, reason %d\n", + ether_sprintf(ni->ni_macaddr), tid, reason)); + + ba = &ni->ni_ba[tid]; + if (ba->ba_state != IEEE80211_BA_AGREED && + ba->ba_state != IEEE80211_BA_REQUESTED) { + DPRINTF(("no matching BA agreement found\n")); + return; + } + /* MLME-DELBA.indication */ + + /* notify drivers of the end of the Block Ack agreement */ + if (ic->ic_htimmba_stop != NULL) + ic->ic_htimmba_stop(ic, ni, tid); + + ba->ba_state = IEEE80211_BA_INIT; + /* stop Block Ack inactivity timer */ + timeout_del(&ba->ba_to); + if (ba->ba_buf != NULL) { + /* free all MSDUs stored in reordering buffer */ + for (i = 0; i < IEEE80211_BA_MAX_WINSZ; i++) + if (ba->ba_buf[i].m != NULL) + m_freem(ba->ba_buf[i].m); + /* free reordering buffer */ + free(ba->ba_buf, M_DEVBUF); + ba->ba_buf = NULL; + } +} +#endif /* !IEEE80211_NO_HT */ + +/*- + * SA Query Request frame format: + * [1] Category + * [1] Action + * [16] Transaction Identifier + */ +void +ieee80211_recv_sa_query_req(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni) +{ + const struct ieee80211_frame *wh; + const u_int8_t *frm; + + if (ic->ic_opmode != IEEE80211_M_STA || + !(ni->ni_flags & IEEE80211_NODE_MFP)) { + DPRINTF(("unexpected SA Query req from %s\n", + ether_sprintf(ni->ni_macaddr))); + return; + } + if (m->m_len < sizeof(*wh) + 18) { + DPRINTF(("frame too short\n")); + return; + } + wh = mtod(m, struct ieee80211_frame *); + frm = (const u_int8_t *)&wh[1]; + + /* MLME-SAQuery.indication */ + + /* save Transaction Identifier for SA Query Response */ + memcpy(ni->ni_sa_query_trid, &frm[2], 16); + + /* MLME-SAQuery.response */ + IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_SA_QUERY, + IEEE80211_ACTION_SA_QUERY_RESP, 0); +} + +#ifndef IEEE80211_STA_ONLY +/*- + * SA Query Response frame format: + * [1] Category + * [1] Action + * [16] Transaction Identifier + */ +void +ieee80211_recv_sa_query_resp(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni) +{ + const struct ieee80211_frame *wh; + const u_int8_t *frm; + + /* ignore if we're not engaged in an SA Query with that STA */ + if (!(ni->ni_flags & IEEE80211_NODE_SA_QUERY)) { + DPRINTF(("unexpected SA Query resp from %s\n", + ether_sprintf(ni->ni_macaddr))); + return; + } + if (m->m_len < sizeof(*wh) + 18) { + DPRINTF(("frame too short\n")); + return; + } + wh = mtod(m, struct ieee80211_frame *); + frm = (const u_int8_t *)&wh[1]; + + /* check that Transaction Identifier matches */ + if (memcmp(&frm[2], ni->ni_sa_query_trid, 16) != 0) { + DPRINTF(("transaction identifier does not match\n")); + return; + } + /* MLME-SAQuery.confirm */ + timeout_del(&ni->ni_sa_query_to); + ni->ni_flags &= ~IEEE80211_NODE_SA_QUERY; +} +#endif + /*- * Action frame format: * [1] Category * [1] Action */ void -ieee80211_recv_action(struct ieee80211com *ic, struct mbuf *m0, +ieee80211_recv_action(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni) { const struct ieee80211_frame *wh; const u_int8_t *frm; - if (m0->m_len < sizeof(*wh) + 2) { + if (m->m_len < sizeof(*wh) + 2) { DPRINTF(("frame too short\n")); return; } - wh = mtod(m0, struct ieee80211_frame *); + wh = mtod(m, struct ieee80211_frame *); frm = (const u_int8_t *)&wh[1]; switch (frm[0]) { +#ifndef IEEE80211_NO_HT case IEEE80211_CATEG_BA: switch (frm[1]) { case IEEE80211_ACTION_ADDBA_REQ: - /* NYI */ + ieee80211_recv_addba_req(ic, m, ni); break; case IEEE80211_ACTION_ADDBA_RESP: - /* NYI */ + ieee80211_recv_addba_resp(ic, m, ni); break; case IEEE80211_ACTION_DELBA: - /* NYI */ + ieee80211_recv_delba(ic, m, ni); break; } break; - case IEEE80211_CATEG_HT: - switch (frm[1]) { - case IEEE80211_ACTION_NOTIFYCW: - /* NYI */ - break; - } - break; - case IEEE80211_CATEG_SALT: +#endif + case IEEE80211_CATEG_SA_QUERY: switch (frm[1]) { - case IEEE80211_ACTION_SALT_REQ: - /* NYI */ + case IEEE80211_ACTION_SA_QUERY_REQ: + ieee80211_recv_sa_query_req(ic, m, ni); break; - case IEEE80211_ACTION_SALT_RESP: - /* NYI */ +#ifndef IEEE80211_STA_ONLY + case IEEE80211_ACTION_SA_QUERY_RESP: + ieee80211_recv_sa_query_resp(ic, m, ni); break; +#endif } break; default: @@ -1940,46 +2579,46 @@ ieee80211_recv_action(struct ieee80211com *ic, struct mbuf *m0, } void -ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, +ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni, struct ieee80211_rxinfo *rxi, int subtype) { switch (subtype) { case IEEE80211_FC0_SUBTYPE_BEACON: - ieee80211_recv_probe_resp(ic, m0, ni, rxi, 0); + ieee80211_recv_probe_resp(ic, m, ni, rxi, 0); break; case IEEE80211_FC0_SUBTYPE_PROBE_RESP: - ieee80211_recv_probe_resp(ic, m0, ni, rxi, 1); + ieee80211_recv_probe_resp(ic, m, ni, rxi, 1); break; #ifndef IEEE80211_STA_ONLY case IEEE80211_FC0_SUBTYPE_PROBE_REQ: - ieee80211_recv_probe_req(ic, m0, ni, rxi); + ieee80211_recv_probe_req(ic, m, ni, rxi); break; #endif case IEEE80211_FC0_SUBTYPE_AUTH: - ieee80211_recv_auth(ic, m0, ni, rxi); + ieee80211_recv_auth(ic, m, ni, rxi); break; #ifndef IEEE80211_STA_ONLY case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: - ieee80211_recv_assoc_req(ic, m0, ni, rxi, 0); + ieee80211_recv_assoc_req(ic, m, ni, rxi, 0); break; case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: - ieee80211_recv_assoc_req(ic, m0, ni, rxi, 1); + ieee80211_recv_assoc_req(ic, m, ni, rxi, 1); break; #endif case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: - ieee80211_recv_assoc_resp(ic, m0, ni, 0); + ieee80211_recv_assoc_resp(ic, m, ni, 0); break; case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: - ieee80211_recv_assoc_resp(ic, m0, ni, 1); + ieee80211_recv_assoc_resp(ic, m, ni, 1); break; case IEEE80211_FC0_SUBTYPE_DEAUTH: - ieee80211_recv_deauth(ic, m0, ni); + ieee80211_recv_deauth(ic, m, ni); break; case IEEE80211_FC0_SUBTYPE_DISASSOC: - ieee80211_recv_disassoc(ic, m0, ni); + ieee80211_recv_disassoc(ic, m, ni); break; case IEEE80211_FC0_SUBTYPE_ACTION: - ieee80211_recv_action(ic, m0, ni); + ieee80211_recv_action(ic, m, ni); break; default: DPRINTF(("mgmt frame with subtype 0x%x not handled\n", @@ -2042,3 +2681,93 @@ ieee80211_recv_pspoll(struct ieee80211com *ic, struct mbuf *m, (*ifp->if_start)(ifp); } #endif /* IEEE80211_STA_ONLY */ + +#ifndef IEEE80211_NO_HT +/* + * Process an incoming Block Ack Request control frame (see 7.2.1.7). + */ +void +ieee80211_recv_bar(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni) +{ + const struct ieee80211_frame_min *wh; + const u_int8_t *frm; + u_int16_t ctl, ssn; + u_int8_t tid, ntids; + + if (!(ni->ni_flags & IEEE80211_NODE_HT)) { + DPRINTF(("received BAR from non-HT STA %s\n", + ether_sprintf(ni->ni_macaddr))); + return; + } + if (m->m_len < sizeof(*wh) + 4) { + DPRINTF(("frame too short\n")); + return; + } + wh = mtod(m, struct ieee80211_frame_min *); + frm = (const u_int8_t *)&wh[1]; + + /* read BAR Control field */ + ctl = LE_READ_2(&frm[0]); + tid = ctl >> 12; + + /* determine BlockAckReq frame variant */ + if (ctl & IEEE80211_BA_MULTI_TID) { + /* Multi-TID BlockAckReq variant (PSMP only) */ + ntids = tid + 1; + + if (m->m_len < sizeof(*wh) + 2 + 4 * ntids) { + DPRINTF(("MTBAR frame too short\n")); + return; + } + frm += 2; /* skip BAR Control field */ + while (ntids-- > 0) { + /* read MTBAR Information field */ + tid = LE_READ_2(&frm[0]) >> 12; + ssn = LE_READ_2(&frm[2]) >> 4; + ieee80211_bar_tid(ic, ni, tid, ssn); + frm += 4; + } + } else { + /* Basic or Compressed BlockAckReq variants */ + ssn = LE_READ_2(&frm[2]) >> 4; + ieee80211_bar_tid(ic, ni, tid, ssn); + } +} + +/* + * Process a Block Ack Request for a specific TID (see 9.10.7.6.3). + * This is the common back-end for all BlockAckReq frame variants. + */ +void +ieee80211_bar_tid(struct ieee80211com *ic, struct ieee80211_node *ni, + u_int8_t tid, u_int16_t ssn) +{ + struct ieee80211_ba *ba = &ni->ni_ba[tid]; + + /* check if we have a Block Ack agreement for RA/TID */ + if (ba->ba_state != IEEE80211_BA_AGREED) { + /* XXX not sure in PBAC case */ + /* 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); + return; + } + /* check if it is a Protected Block Ack agreement */ + if ((ni->ni_flags & IEEE80211_NODE_MFP) && + (ni->ni_rsncaps & IEEE80211_RSNCAP_PBAC)) { + /* ADDBA Requests must be used in PBAC case */ + if (SEQ_LT(ssn, ba->ba_winstart) || + SEQ_LT(ba->ba_winend, ssn)) + ic->ic_stats.is_pbac_errs++; + return; /* PBAC, do not move window */ + } + /* reset Block Ack inactivity timer */ + timeout_add_usec(&ba->ba_to, ba->ba_timeout_val); + + if (SEQ_LT(ba->ba_winstart, ssn)) + ieee80211_ba_move_window(ic, ni, tid, ssn); +} +#endif /* !IEEE80211_NO_HT */ + |