diff options
Diffstat (limited to 'sys/net80211/ieee80211_input.c')
-rw-r--r-- | sys/net80211/ieee80211_input.c | 967 |
1 files changed, 705 insertions, 262 deletions
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 721966898180..a5b53410b359 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -67,7 +67,7 @@ doprint(struct ieee80211com *ic, int subtype) return 1; } -static const u_int8_t *ieee80211_getbssid(struct ieee80211com *, +static const uint8_t *ieee80211_getbssid(struct ieee80211com *, const struct ieee80211_frame *); #endif /* IEEE80211_DEBUG */ @@ -75,10 +75,9 @@ static struct mbuf *ieee80211_defrag(struct ieee80211com *, struct ieee80211_node *, struct mbuf *, int); static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *, int); static void ieee80211_send_error(struct ieee80211com *, struct ieee80211_node *, - const u_int8_t *mac, int subtype, int arg); -static void ieee80211_deliver_data(struct ieee80211com *, + const uint8_t *mac, int subtype, int arg); +static struct mbuf *ieee80211_decap_fastframe(struct ieee80211com *, struct ieee80211_node *, struct mbuf *); -static void ieee80211_node_pwrsave(struct ieee80211_node *, int enable); static void ieee80211_recv_pspoll(struct ieee80211com *, struct ieee80211_node *, struct mbuf *); @@ -94,7 +93,7 @@ static void ieee80211_recv_pspoll(struct ieee80211com *, */ int ieee80211_input(struct ieee80211com *ic, struct mbuf *m, - struct ieee80211_node *ni, int rssi, u_int32_t rstamp) + struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp) { #define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) #define HAS_SEQ(type) ((type & 0x4) == 0) @@ -102,19 +101,33 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_frame *wh; struct ieee80211_key *key; struct ether_header *eh; - int hdrspace; - u_int8_t dir, type, subtype; - u_int8_t *bssid; - u_int16_t rxseq; + int hdrspace, need_tap; + uint8_t dir, type, subtype, qos; + uint8_t *bssid; + uint16_t rxseq; + + if (m->m_flags & M_AMPDU) { + /* + * Fastpath for A-MPDU reorder q resubmission. Frames + * w/ M_AMPDU marked have already passed through here + * but were received out of order and been held on the + * reorder queue. When resubmitted they are marked + * with the M_AMPDU flag and we can bypass most of the + * normal processing. + */ + wh = mtod(m, struct ieee80211_frame *); + type = IEEE80211_FC0_TYPE_DATA; + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + subtype = IEEE80211_FC0_SUBTYPE_QOS; + hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ + need_tap = 0; + goto resubmit_ampdu; + } KASSERT(ni != NULL, ("null node")); ni->ni_inact = ni->ni_inact_reload; - /* trim CRC here so WEP can find its own CRC at the end of packet. */ - if (m->m_flags & M_HASFCS) { - m_adj(m, -IEEE80211_CRC_LEN); - m->m_flags &= ~M_HASFCS; - } + need_tap = 1; /* mbuf need to be tapped. */ type = -1; /* undefined */ /* * In monitor mode, send everything directly to bpf. @@ -215,9 +228,10 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, goto out; } ni->ni_rssi = rssi; + ni->ni_noise = noise; ni->ni_rstamp = rstamp; if (HAS_SEQ(type)) { - u_int8_t tid; + uint8_t tid; if (IEEE80211_QOS_HAS_SEQ(wh)) { tid = ((struct ieee80211_qosframe *)wh)-> i_qos[0] & IEEE80211_QOS_TID; @@ -226,8 +240,9 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, tid++; } else tid = IEEE80211_NONQOS_TID; - rxseq = le16toh(*(u_int16_t *)wh->i_seq); - if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && + rxseq = le16toh(*(uint16_t *)wh->i_seq); + if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 && + (wh->i_fc[1] & IEEE80211_FC1_RETRY) && SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) { /* duplicate, discard */ IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, @@ -263,7 +278,7 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, case IEEE80211_M_STA: if (dir != IEEE80211_FC1_DIR_FROMDS) { IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "%s", "unknown dir 0x%x", dir); + wh, "data", "unknown dir 0x%x", dir); ic->ic_stats.is_rx_wrongdir++; goto out; } @@ -286,7 +301,7 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, case IEEE80211_M_AHDEMO: if (dir != IEEE80211_FC1_DIR_NODS) { IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "%s", "unknown dir 0x%x", dir); + wh, "data", "unknown dir 0x%x", dir); ic->ic_stats.is_rx_wrongdir++; goto out; } @@ -295,7 +310,7 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, case IEEE80211_M_HOSTAP: if (dir != IEEE80211_FC1_DIR_TODS) { IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "%s", "unknown dir 0x%x", dir); + wh, "data", "unknown dir 0x%x", dir); ic->ic_stats.is_rx_wrongdir++; goto out; } @@ -321,6 +336,7 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, /* * Check for power save state change. + * XXX out-of-order A-MPDU frames? */ if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^ (ni->ni_flags & IEEE80211_NODE_PWR_MGT))) @@ -333,6 +349,23 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, } /* + * Handle A-MPDU re-ordering. The station must be + * associated and negotiated HT. The frame must be + * a QoS frame (not QoS null data) and not previously + * processed for A-MPDU re-ordering. If the frame is + * to be processed directly then ieee80211_ampdu_reorder + * will return 0; otherwise it has consumed the mbuf + * and we should do nothing more with it. + */ + if ((ni->ni_flags & IEEE80211_NODE_HT) && + subtype == IEEE80211_FC0_SUBTYPE_QOS && + ieee80211_ampdu_reorder(ni, m) != 0) { + m = NULL; + goto out; + } + resubmit_ampdu: + + /* * Handle privacy requirements. Note that we * must not be preempted from here until after * we (potentially) call ieee80211_crypto_demic; @@ -364,6 +397,16 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, } /* + * Save QoS bits for use below--before we strip the header. + */ + if (subtype == IEEE80211_FC0_SUBTYPE_QOS) { + qos = (dir == IEEE80211_FC1_DIR_DSTODS) ? + ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] : + ((struct ieee80211_qosframe *)wh)->i_qos[0]; + } else + qos = 0; + + /* * Next up, any fragmentation. */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { @@ -389,6 +432,7 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, /* copy to listener after decrypt */ if (bpf_peers_present(ic->ic_rawbpf)) bpf_mtap(ic->ic_rawbpf, m); + need_tap = 0; /* * Finally, strip the 802.11 header. @@ -396,7 +440,8 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, m = ieee80211_decap(ic, m, hdrspace); if (m == NULL) { /* don't count Null data frames as errors */ - if (subtype == IEEE80211_FC0_SUBTYPE_NODATA) + if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || + subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) goto out; IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, ni->ni_macaddr, "data", "%s", "decap error"); @@ -439,6 +484,37 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, goto out; } } + /* XXX require HT? */ + if (qos & IEEE80211_QOS_AMSDU) { + m = ieee80211_decap_amsdu(ni, m); + if (m == NULL) + return IEEE80211_FC0_TYPE_DATA; + } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) && +#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc)) + m->m_pkthdr.len >= 3*FF_LLC_SIZE) { + struct llc *llc; + + /* + * Check for fast-frame tunnel encapsulation. + */ + if (m->m_len < FF_LLC_SIZE && + (m = m_pullup(m, FF_LLC_SIZE)) == NULL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, "fast-frame", + "%s", "m_pullup(llc) failed"); + ic->ic_stats.is_rx_tooshort++; + return IEEE80211_FC0_TYPE_DATA; + } + llc = (struct llc *)(mtod(m, uint8_t *) + + sizeof(struct ether_header)); + if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) { + m_adj(m, FF_LLC_SIZE); + m = ieee80211_decap_fastframe(ic, ni, m); + if (m == NULL) + return IEEE80211_FC0_TYPE_DATA; + } + } +#undef FF_LLC_SIZE ieee80211_deliver_data(ic, ni, m); return IEEE80211_FC0_TYPE_DATA; @@ -447,7 +523,7 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, IEEE80211_NODE_STAT(ni, rx_mgmt); if (dir != IEEE80211_FC1_DIR_NODS) { IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "%s", "unknown dir 0x%x", dir); + wh, "data", "unknown dir 0x%x", dir); ic->ic_stats.is_rx_wrongdir++; goto err; } @@ -500,9 +576,9 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, } if (bpf_peers_present(ic->ic_rawbpf)) bpf_mtap(ic->ic_rawbpf, m); - (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp); + (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, noise, rstamp); m_freem(m); - return type; + return IEEE80211_FC0_TYPE_MGT; case IEEE80211_FC0_TYPE_CTL: ic->ic_stats.is_rx_ctl++; @@ -512,6 +588,9 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, case IEEE80211_FC0_SUBTYPE_PS_POLL: ieee80211_recv_pspoll(ic, ni, m); break; + case IEEE80211_FC0_SUBTYPE_BAR: + ieee80211_recv_bar(ni, m); + break; } } goto out; @@ -525,7 +604,7 @@ err: ifp->if_ierrors++; out: if (m != NULL) { - if (bpf_peers_present(ic->ic_rawbpf)) + if (bpf_peers_present(ic->ic_rawbpf) && need_tap) bpf_mtap(ic->ic_rawbpf, m); m_freem(m); } @@ -542,14 +621,14 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, { struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); struct ieee80211_frame *lwh; - u_int16_t rxseq; - u_int8_t fragno; - u_int8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG; + uint16_t rxseq; + uint8_t fragno; + uint8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG; struct mbuf *mfrag; KASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?")); - rxseq = le16toh(*(u_int16_t *)wh->i_seq); + rxseq = le16toh(*(uint16_t *)wh->i_seq); fragno = rxseq & IEEE80211_SEQ_FRAG_MASK; /* Quick way out, if there's nothing to defragment */ @@ -582,10 +661,10 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, * related to the previous ones. */ if (mfrag != NULL) { - u_int16_t last_rxseq; + uint16_t last_rxseq; lwh = mtod(mfrag, struct ieee80211_frame *); - last_rxseq = le16toh(*(u_int16_t *)lwh->i_seq); + last_rxseq = le16toh(*(uint16_t *)lwh->i_seq); /* NB: check seq # and frag together */ if (rxseq != last_rxseq+1 || !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) || @@ -614,7 +693,7 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, mfrag->m_pkthdr.len += m->m_pkthdr.len; /* track last seqnum and fragno */ lwh = mtod(mfrag, struct ieee80211_frame *); - *(u_int16_t *) lwh->i_seq = *(u_int16_t *) wh->i_seq; + *(uint16_t *) lwh->i_seq = *(uint16_t *) wh->i_seq; } if (more_frag) { /* more to come, save */ ni->ni_rxfragstamp = ticks; @@ -624,7 +703,7 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, return mfrag; } -static void +void ieee80211_deliver_data(struct ieee80211com *ic, struct ieee80211_node *ni, struct mbuf *m) { @@ -735,7 +814,7 @@ ieee80211_decap(struct ieee80211com *ic, struct mbuf *m, int hdrlen) break; } #ifdef ALIGNED_POINTER - if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) { + if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) { struct mbuf *n, *n0, **np; caddr_t newdata; int off, pktlen; @@ -793,11 +872,109 @@ ieee80211_decap(struct ieee80211com *ic, struct mbuf *m, int hdrlen) } /* + * Decap a frame encapsulated in a fast-frame/A-MSDU. + */ +struct mbuf * +ieee80211_decap1(struct mbuf *m, int *framelen) +{ +#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc)) + struct ether_header *eh; + struct llc *llc; + + /* + * The frame has an 802.3 header followed by an 802.2 + * LLC header. The encapsulated frame length is in the + * first header type field; save that and overwrite it + * with the true type field found in the second. Then + * copy the 802.3 header up to where it belongs and + * adjust the mbuf contents to remove the void. + */ + if (m->m_len < FF_LLC_SIZE && (m = m_pullup(m, FF_LLC_SIZE)) == NULL) + return NULL; + eh = mtod(m, struct ether_header *); /* 802.3 header is first */ + llc = (struct llc *)&eh[1]; /* 802.2 header follows */ + *framelen = ntohs(eh->ether_type) /* encap'd frame size */ + + sizeof(struct ether_header) - sizeof(struct llc); + eh->ether_type = llc->llc_un.type_snap.ether_type; + ovbcopy(eh, mtod(m, uint8_t *) + sizeof(struct llc), + sizeof(struct ether_header)); + m_adj(m, sizeof(struct llc)); + return m; +#undef FF_LLC_SIZE +} + +/* + * Decap the encapsulated frame pair and dispatch the first + * for delivery. The second frame is returned for delivery + * via the normal path. + */ +static struct mbuf * +ieee80211_decap_fastframe(struct ieee80211com *ic, + struct ieee80211_node *ni, struct mbuf *m) +{ +#define MS(x,f) (((x) & f) >> f##_S) + uint32_t ath; + struct mbuf *n; + int framelen; + + m_copydata(m, 0, sizeof(uint32_t), (caddr_t) &ath); + if (MS(ath, ATH_FF_PROTO) != ATH_FF_PROTO_L2TUNNEL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, "fast-frame", + "unsupport tunnel protocol, header 0x%x", ath); + ic->ic_stats.is_ff_badhdr++; + m_freem(m); + return NULL; + } + /* NB: skip header and alignment padding */ + m_adj(m, roundup(sizeof(uint32_t) - 2, 4) + 2); + + ic->ic_stats.is_ff_decap++; + + /* + * Decap the first frame, bust it apart from the + * second and deliver; then decap the second frame + * and return it to the caller for normal delivery. + */ + m = ieee80211_decap1(m, &framelen); + if (m == NULL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, "fast-frame", "%s", "first decap failed"); + ic->ic_stats.is_ff_tooshort++; + return NULL; + } + n = m_split(m, framelen, M_NOWAIT); + if (n == NULL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, "fast-frame", + "%s", "unable to split encapsulated frames"); + ic->ic_stats.is_ff_split++; + m_freem(m); /* NB: must reclaim */ + return NULL; + } + ieee80211_deliver_data(ic, ni, m); /* 1st of pair */ + + /* + * Decap second frame. + */ + m_adj(n, roundup2(framelen, 4) - framelen); /* padding */ + n = ieee80211_decap1(n, &framelen); + if (n == NULL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, "fast-frame", "%s", "second decap failed"); + ic->ic_stats.is_ff_tooshort++; + } + /* XXX verify framelen against mbuf contents */ + return n; /* 2nd delivered by caller */ +#undef MS +} + +/* * Install received rate set information in the node's state block. */ int ieee80211_setup_rates(struct ieee80211_node *ni, - const u_int8_t *rates, const u_int8_t *xrates, int flags) + const uint8_t *rates, const uint8_t *xrates, int flags) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211_rateset *rs = &ni->ni_rates; @@ -806,7 +983,7 @@ ieee80211_setup_rates(struct ieee80211_node *ni, rs->rs_nrates = rates[1]; memcpy(rs->rs_rates, rates + 2, rs->rs_nrates); if (xrates != NULL) { - u_int8_t nxrates; + uint8_t nxrates; /* * Tack on 11g extended supported rate element. */ @@ -827,8 +1004,8 @@ ieee80211_setup_rates(struct ieee80211_node *ni, static void ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, - struct ieee80211_node *ni, int rssi, u_int32_t rstamp, u_int16_t seq, - u_int16_t status) + struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp, + uint16_t seq, uint16_t status) { if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { @@ -843,7 +1020,7 @@ ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, * open auth is attempted. */ if (ni->ni_challenge != NULL) { - FREE(ni->ni_challenge, M_DEVBUF); + FREE(ni->ni_challenge, M_80211_NODE); ni->ni_challenge = NULL; } /* XXX hack to workaround calling convention */ @@ -857,6 +1034,7 @@ ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: case IEEE80211_M_MONITOR: + case IEEE80211_M_WDS: /* should not come here */ IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, ni->ni_macaddr, "open auth", @@ -911,10 +1089,10 @@ ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, if (ni != ic->ic_bss) ni->ni_fails++; ic->ic_stats.is_rx_auth_fail++; - ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + ieee80211_new_state(ic, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_STATUS); } else - ieee80211_new_state(ic, IEEE80211_S_ASSOC, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0); break; } } @@ -928,7 +1106,7 @@ ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, */ static void ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni, - const u_int8_t *mac, int subtype, int arg) + const uint8_t *mac, int subtype, int arg) { int istmp; @@ -950,8 +1128,8 @@ static int alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni) { if (ni->ni_challenge == NULL) - MALLOC(ni->ni_challenge, u_int32_t*, IEEE80211_CHALLENGE_LEN, - M_DEVBUF, M_NOWAIT); + MALLOC(ni->ni_challenge, uint32_t*, IEEE80211_CHALLENGE_LEN, + M_80211_NODE, M_NOWAIT); if (ni->ni_challenge == NULL) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, "[%s] shared key challenge alloc failed\n", @@ -964,10 +1142,10 @@ alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni) /* XXX TODO: add statistics */ static void ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, - u_int8_t *frm, u_int8_t *efrm, struct ieee80211_node *ni, int rssi, - u_int32_t rstamp, u_int16_t seq, u_int16_t status) + uint8_t *frm, uint8_t *efrm, struct ieee80211_node *ni, + int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status) { - u_int8_t *challenge; + uint8_t *challenge; int allocbs, estatus; /* @@ -1041,6 +1219,7 @@ ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, case IEEE80211_M_MONITOR: case IEEE80211_M_AHDEMO: case IEEE80211_M_IBSS: + case IEEE80211_M_WDS: IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, ni->ni_macaddr, "shared key auth", "bad operating mode %u", ic->ic_opmode); @@ -1074,6 +1253,7 @@ ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, */ ni->ni_flags |= IEEE80211_NODE_AREF; ni->ni_rssi = rssi; + ni->ni_noise = noise; ni->ni_rstamp = rstamp; if (!alloc_challenge(ic, ni)) { /* NB: don't return error so they rexmit */ @@ -1136,7 +1316,7 @@ ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, switch (seq) { case IEEE80211_AUTH_SHARED_PASS: if (ni->ni_challenge != NULL) { - FREE(ni->ni_challenge, M_DEVBUF); + FREE(ni->ni_challenge, M_80211_NODE); ni->ni_challenge = NULL; } if (status != 0) { @@ -1151,8 +1331,7 @@ ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, ic->ic_stats.is_rx_auth_fail++; return; } - ieee80211_new_state(ic, IEEE80211_S_ASSOC, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0); break; case IEEE80211_AUTH_SHARED_CHALLENGE: if (!alloc_challenge(ic, ni)) @@ -1187,7 +1366,8 @@ bad: * state transition. */ if (ic->ic_state == IEEE80211_S_AUTH) - ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + ieee80211_new_state(ic, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_STATUS); } } @@ -1211,7 +1391,7 @@ bad: } \ } while (0) -#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \ +#define IEEE80211_VERIFY_LENGTH(_len, _minlen, _action) do { \ if ((_len) < (_minlen)) { \ IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ wh, ieee80211_mgt_subtype_name[subtype >> \ @@ -1219,14 +1399,14 @@ bad: "ie too short, got %d, expected %d", \ (_len), (_minlen)); \ ic->ic_stats.is_rx_elem_toosmall++; \ - return; \ + _action; \ } \ } while (0) #ifdef IEEE80211_DEBUG static void ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag, - u_int8_t mac[IEEE80211_ADDR_LEN], u_int8_t *ssid) + uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid) { printf("[%s] discard %s frame, ssid mismatch: ", ether_sprintf(mac), tag); @@ -1260,58 +1440,70 @@ ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag, /* unalligned little endian access */ #define LE_READ_2(p) \ - ((u_int16_t) \ - ((((const u_int8_t *)(p))[0] ) | \ - (((const u_int8_t *)(p))[1] << 8))) + ((uint16_t) \ + ((((const uint8_t *)(p))[0] ) | \ + (((const uint8_t *)(p))[1] << 8))) #define LE_READ_4(p) \ - ((u_int32_t) \ - ((((const u_int8_t *)(p))[0] ) | \ - (((const u_int8_t *)(p))[1] << 8) | \ - (((const u_int8_t *)(p))[2] << 16) | \ - (((const u_int8_t *)(p))[3] << 24))) + ((uint32_t) \ + ((((const uint8_t *)(p))[0] ) | \ + (((const uint8_t *)(p))[1] << 8) | \ + (((const uint8_t *)(p))[2] << 16) | \ + (((const uint8_t *)(p))[3] << 24))) static __inline int -iswpaoui(const u_int8_t *frm) +iswpaoui(const uint8_t *frm) { return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); } static __inline int -iswmeoui(const u_int8_t *frm) +iswmeoui(const uint8_t *frm) { return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI); } static __inline int -iswmeparam(const u_int8_t *frm) +iswmeparam(const uint8_t *frm) { return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && frm[6] == WME_PARAM_OUI_SUBTYPE; } static __inline int -iswmeinfo(const u_int8_t *frm) +iswmeinfo(const uint8_t *frm) { return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && frm[6] == WME_INFO_OUI_SUBTYPE; } static __inline int -isatherosoui(const u_int8_t *frm) +isatherosoui(const uint8_t *frm) { return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); } +static __inline int +ishtcapoui(const uint8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI); +} + +static __inline int +ishtinfooui(const uint8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI); +} + /* * Convert a WPA cipher selector OUI to an internal * cipher algorithm. Where appropriate we also * record any key length. */ static int -wpa_cipher(u_int8_t *sel, u_int8_t *keylen) +wpa_cipher(uint8_t *sel, uint8_t *keylen) { #define WPA_SEL(x) (((x)<<24)|WPA_OUI) - u_int32_t w = LE_READ_4(sel); + uint32_t w = LE_READ_4(sel); switch (w) { case WPA_SEL(WPA_CSE_NULL): @@ -1338,10 +1530,10 @@ wpa_cipher(u_int8_t *sel, u_int8_t *keylen) * to an internal code. */ static int -wpa_keymgmt(u_int8_t *sel) +wpa_keymgmt(uint8_t *sel) { #define WPA_SEL(x) (((x)<<24)|WPA_OUI) - u_int32_t w = LE_READ_4(sel); + uint32_t w = LE_READ_4(sel); switch (w) { case WPA_SEL(WPA_ASE_8021X_UNSPEC): @@ -1361,11 +1553,11 @@ wpa_keymgmt(u_int8_t *sel) * configured for the system. */ static int -ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm, +ieee80211_parse_wpa(struct ieee80211com *ic, uint8_t *frm, struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) { - u_int8_t len = frm[1]; - u_int32_t w; + uint8_t len = frm[1]; + uint32_t w; int n; /* @@ -1473,10 +1665,10 @@ ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm, * record any key length. */ static int -rsn_cipher(u_int8_t *sel, u_int8_t *keylen) +rsn_cipher(uint8_t *sel, uint8_t *keylen) { #define RSN_SEL(x) (((x)<<24)|RSN_OUI) - u_int32_t w = LE_READ_4(sel); + uint32_t w = LE_READ_4(sel); switch (w) { case RSN_SEL(RSN_CSE_NULL): @@ -1505,10 +1697,10 @@ rsn_cipher(u_int8_t *sel, u_int8_t *keylen) * to an internal code. */ static int -rsn_keymgmt(u_int8_t *sel) +rsn_keymgmt(uint8_t *sel) { #define RSN_SEL(x) (((x)<<24)|RSN_OUI) - u_int32_t w = LE_READ_4(sel); + uint32_t w = LE_READ_4(sel); switch (w) { case RSN_SEL(RSN_ASE_8021X_UNSPEC): @@ -1528,11 +1720,11 @@ rsn_keymgmt(u_int8_t *sel) * configured for the system. */ static int -ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm, +ieee80211_parse_rsn(struct ieee80211com *ic, uint8_t *frm, struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) { - u_int8_t len = frm[1]; - u_int32_t w; + uint8_t len = frm[1]; + uint32_t w; int n; /* @@ -1636,7 +1828,7 @@ ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm, } static int -ieee80211_parse_wmeparams(struct ieee80211com *ic, u_int8_t *frm, +ieee80211_parse_wmeparams(struct ieee80211com *ic, uint8_t *frm, const struct ieee80211_frame *wh) { #define MS(_v, _f) (((_v) & _f) >> _f##_S) @@ -1672,8 +1864,65 @@ ieee80211_parse_wmeparams(struct ieee80211com *ic, u_int8_t *frm, #undef MS } +static int +ieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm, + const struct ieee80211_frame *wh) +{ + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_ath_ie *ath; + u_int len = frm[1]; + int capschanged; + uint16_t defkeyix; + + if (len < sizeof(struct ieee80211_ath_ie)-2) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG, + wh, "Atheros", "too short, len %u", len); + return -1; + } + ath = (const struct ieee80211_ath_ie *)frm; + capschanged = (ni->ni_ath_flags != ath->ath_capability); + defkeyix = LE_READ_2(ath->ath_defkeyix); + if (capschanged || defkeyix != ni->ni_ath_defkeyix) { + ni->ni_ath_flags = ath->ath_capability; + ni->ni_ath_defkeyix = defkeyix; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + "[%s] ath ie change: new caps 0x%x defkeyix 0x%x\n", + ether_sprintf(ni->ni_macaddr), + ni->ni_ath_flags, ni->ni_ath_defkeyix); + } + if (IEEE80211_ATH_CAP(ic, ni, ATHEROS_CAP_TURBO_PRIME)) { + uint16_t curflags, newflags; + + /* + * Check for turbo mode switch. Calculate flags + * for the new mode and effect the switch. + */ + newflags = curflags = ic->ic_bsschan->ic_flags; + /* NB: BOOST is not in ic_flags, so get it from the ie */ + if (ath->ath_capability & ATHEROS_CAP_BOOST) + newflags |= IEEE80211_CHAN_TURBO; + else + newflags &= ~IEEE80211_CHAN_TURBO; + if (newflags != curflags) + ieee80211_dturbo_switch(ic, newflags); + } + return capschanged; +} + void -ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie) +ieee80211_saveath(struct ieee80211_node *ni, uint8_t *ie) +{ + const struct ieee80211_ath_ie *ath = + (const struct ieee80211_ath_ie *) ie; + + ni->ni_ath_flags = ath->ath_capability; + ni->ni_ath_defkeyix = LE_READ_2(&ath->ath_defkeyix); + ieee80211_saveie(&ni->ni_ath_ie, ie); +} + +void +ieee80211_saveie(uint8_t **iep, const uint8_t *ie) { u_int ielen = ie[1]+2; /* @@ -1681,8 +1930,8 @@ ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie) */ if (*iep == NULL || (*iep)[1] != ie[1]) { if (*iep != NULL) - FREE(*iep, M_DEVBUF); - MALLOC(*iep, void*, ielen, M_DEVBUF, M_NOWAIT); + FREE(*iep, M_80211_NODE); + MALLOC(*iep, void*, ielen, M_80211_NODE, M_NOWAIT); } if (*iep != NULL) memcpy(*iep, ie, ielen); @@ -1692,10 +1941,10 @@ ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie) /* XXX find a better place for definition */ struct l2_update_frame { struct ether_header eh; - u_int8_t dsap; - u_int8_t ssap; - u_int8_t control; - u_int8_t xid[3]; + uint8_t dsap; + uint8_t ssap; + uint8_t control; + uint8_t xid[3]; } __packed; /* @@ -1738,6 +1987,22 @@ ieee80211_deliver_l2uf(struct ieee80211_node *ni) ieee80211_deliver_data(ic, ni, m); } +static __inline int +contbgscan(struct ieee80211com *ic) +{ + return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) && + time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle)); +} + +static __inline int +startbgscan(struct ieee80211com *ic) +{ + return ((ic->ic_flags & IEEE80211_F_BGSCAN) && + !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) && + time_after(ticks, ic->ic_lastscan + ic->ic_bgscanintvl) && + time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle)); +} + static void ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, int reassoc, int resp, const char *tag, int rate) @@ -1745,7 +2010,7 @@ ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, struct ieee80211com *ic = ni->ni_ic; IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "[%s] deny %s request, %srate set mismatch, rate 0x%x\n", + "[%s] deny %s request, %s rate set mismatch, rate 0x%x\n", ether_sprintf(wh->i_addr2), reassoc ? "reassoc" : "assoc", tag, rate); IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE); @@ -1771,19 +2036,19 @@ capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, void ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, struct ieee80211_node *ni, - int subtype, int rssi, u_int32_t rstamp) + int subtype, int rssi, int noise, uint32_t rstamp) { #define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) #define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP) struct ieee80211_frame *wh; - u_int8_t *frm, *efrm; - u_int8_t *ssid, *rates, *xrates, *wpa, *wme; + uint8_t *frm, *efrm; + uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap; int reassoc, resp, allocbs; - u_int8_t rate; + uint8_t rate; wh = mtod(m0, struct ieee80211_frame *); - frm = (u_int8_t *)&wh[1]; - efrm = mtod(m0, u_int8_t *) + m0->m_len; + frm = (uint8_t *)&wh[1]; + efrm = mtod(m0, uint8_t *) + m0->m_len; switch (subtype) { case IEEE80211_FC0_SUBTYPE_PROBE_RESP: case IEEE80211_FC0_SUBTYPE_BEACON: { @@ -1816,17 +2081,20 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [tlv] extended supported rates * [tlv] WME * [tlv] WPA or RSN + * [tlv] HT capabilities + * [tlv] HT information + * [tlv] Atheros capabilities */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 12); + IEEE80211_VERIFY_LENGTH(efrm - frm, 12, return); memset(&scan, 0, sizeof(scan)); scan.tstamp = frm; frm += 8; - scan.bintval = le16toh(*(u_int16_t *)frm); frm += 2; - scan.capinfo = le16toh(*(u_int16_t *)frm); frm += 2; + scan.bintval = le16toh(*(uint16_t *)frm); frm += 2; + scan.capinfo = le16toh(*(uint16_t *)frm); frm += 2; scan.bchan = ieee80211_chan2ieee(ic, ic->ic_curchan); scan.chan = scan.bchan; while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2); + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); switch (*frm) { case IEEE80211_ELEMID_SSID: scan.ssid = frm; @@ -1855,7 +2123,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_ELEMID_TIM: /* XXX ATIM? */ scan.tim = frm; - scan.timoff = frm - mtod(m0, u_int8_t *); + scan.timoff = frm - mtod(m0, uint8_t *); break; case IEEE80211_ELEMID_IBSSPARMS: break; @@ -1872,15 +2140,35 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } scan.erp = frm[2]; break; + case IEEE80211_ELEMID_HTCAP: + scan.htcap = frm; + break; case IEEE80211_ELEMID_RSN: - scan.wpa = frm; + scan.rsn = frm; + break; + case IEEE80211_ELEMID_HTINFO: + scan.htinfo = frm; break; case IEEE80211_ELEMID_VENDOR: if (iswpaoui(frm)) scan.wpa = frm; else if (iswmeparam(frm) || iswmeinfo(frm)) scan.wme = frm; - /* XXX Atheros OUI support */ + else if (isatherosoui(frm)) + scan.ath = frm; + else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) { + /* + * Accept pre-draft HT ie's if the + * standard ones have not been seen. + */ + if (ishtcapoui(frm)) { + if (scan.htcap == NULL) + scan.htcap = frm; + } else if (ishtinfooui(frm)) { + if (scan.htinfo == NULL) + scan.htcap = frm; + } + } break; default: IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID, @@ -1892,20 +2180,20 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(scan.rates, IEEE80211_RATE_MAXSIZE); + if (scan.xrates != NULL) + IEEE80211_VERIFY_ELEMENT(scan.xrates, + IEEE80211_RATE_MAXSIZE - scan.rates[1]); IEEE80211_VERIFY_ELEMENT(scan.ssid, IEEE80211_NWID_LEN); - if ( #if IEEE80211_CHAN_MAX < 255 - scan.chan > IEEE80211_CHAN_MAX || -#endif - isclr(ic->ic_chan_active, scan.chan)) { - IEEE80211_DISCARD(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, + if (scan.chan > IEEE80211_CHAN_MAX) { + IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, wh, ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], "invalid channel %u", scan.chan); ic->ic_stats.is_rx_badchan++; return; } +#endif if (scan.chan != scan.bchan && ic->ic_phytype != IEEE80211_T_FH) { /* @@ -1936,6 +2224,25 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ic->ic_stats.is_rx_badbintval++; return; } + /* + * Process HT ie's. This is complicated by our + * accepting both the standard ie's and the pre-draft + * vendor OUI ie's that some vendors still use/require. + */ + if (scan.htcap != NULL) { + IEEE80211_VERIFY_LENGTH(scan.htcap[1], + scan.htcap[0] == IEEE80211_ELEMID_VENDOR ? + 4 + sizeof(struct ieee80211_ie_htcap)-2 : + sizeof(struct ieee80211_ie_htcap)-2, + scan.htcap = NULL); + } + if (scan.htinfo != NULL) { + IEEE80211_VERIFY_LENGTH(scan.htinfo[1], + scan.htinfo[0] == IEEE80211_ELEMID_VENDOR ? + 4 + sizeof(struct ieee80211_ie_htinfo)-2 : + sizeof(struct ieee80211_ie_htinfo)-2, + scan.htinfo = NULL); + } /* * Count frame now that we know it's to be processed. @@ -1966,7 +2273,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, "[%s] erp change: was 0x%x, now 0x%x\n", ether_sprintf(wh->i_addr2), ni->ni_erp, scan.erp); - if (ic->ic_curmode == IEEE80211_MODE_11G && + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) ic->ic_flags |= IEEE80211_F_USEPROT; else @@ -1985,25 +2292,70 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * change dynamically */ ieee80211_set_shortslottime(ic, - ic->ic_curmode == IEEE80211_MODE_11A || + IEEE80211_IS_CHAN_A(ic->ic_bsschan) || (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); - ni->ni_capinfo = scan.capinfo; + ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME) + | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME); /* XXX statistic */ } if (scan.wme != NULL && (ni->ni_flags & IEEE80211_NODE_QOS) && ieee80211_parse_wmeparams(ic, scan.wme, wh) > 0) ieee80211_wme_updateparams(ic); + if (scan.ath != NULL) + ieee80211_parse_athparams(ni, scan.ath, wh); + if (scan.htcap != NULL) + ieee80211_parse_htcap(ni, scan.htcap); + if (scan.htinfo != NULL) + ieee80211_parse_htinfo(ni, scan.htinfo); if (scan.tim != NULL) { - struct ieee80211_tim_ie *ie = + struct ieee80211_tim_ie *tim = (struct ieee80211_tim_ie *) scan.tim; - - ni->ni_dtim_count = ie->tim_count; - ni->ni_dtim_period = ie->tim_period; +#if 0 + int aid = IEEE80211_AID(ni->ni_associd); + int ix = aid / NBBY; + int min = tim->tim_bitctl &~ 1; + int max = tim->tim_len + min - 4; + if ((tim->tim_bitctl&1) || + (min <= ix && ix <= max && + isset(tim->tim_bitmap - min, aid))) { + /* + * XXX Do not let bg scan kick off + * we are expecting data. + */ + ic->ic_lastdata = ticks; + ieee80211_sta_pwrsave(ic, 0); + } +#endif + ni->ni_dtim_count = tim->tim_count; + ni->ni_dtim_period = tim->tim_period; } - if (ic->ic_flags & IEEE80211_F_SCAN) + /* + * If scanning, pass the info to the scan module. + * Otherwise, check if it's the right time to do + * a background scan. Background scanning must + * be enabled and we must not be operating in the + * turbo phase of dynamic turbo mode. Then, + * it's been a while since the last background + * scan and if no data frames have come through + * recently, kick off a scan. Note that this + * is the mechanism by which a background scan + * is started _and_ continued each time we + * return on-channel to receive a beacon from + * our ap. + */ + if (ic->ic_flags & IEEE80211_F_SCAN) { ieee80211_add_scan(ic, &scan, wh, - subtype, rssi, rstamp); + subtype, rssi, noise, rstamp); + } else if (contbgscan(ic)) { + ieee80211_bg_scan(ic); + } else if (startbgscan(ic)) { +#if 0 + /* wakeup if we are sleeing */ + ieee80211_set_pwrsave(ic, 0); +#endif + ieee80211_bg_scan(ic); + } return; } /* @@ -2023,7 +2375,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; } ieee80211_add_scan(ic, &scan, wh, - subtype, rssi, rstamp); + subtype, rssi, noise, rstamp); return; } if (scan.capinfo & IEEE80211_CAPINFO_IBSS) { @@ -2047,6 +2399,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } if (ni != NULL) { ni->ni_rssi = rssi; + ni->ni_noise = noise; ni->ni_rstamp = rstamp; } } @@ -2070,10 +2423,11 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates + * [tlv] Atheros capabilities */ - ssid = rates = xrates = NULL; + ssid = rates = xrates = ath = NULL; while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2); + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; @@ -2084,10 +2438,17 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_ELEMID_XRATES: xrates = frm; break; + case IEEE80211_ELEMID_VENDOR: + if (isatherosoui(frm)) + ath = frm; + break; } frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + if (xrates != NULL) + IEEE80211_VERIFY_ELEMENT(xrates, + IEEE80211_RATE_MAXSIZE - rates[1]); IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); IEEE80211_VERIFY_SSID(ic->ic_bss, ssid); if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) { @@ -2139,11 +2500,12 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * response, reclaim immediately. */ ieee80211_free_node(ni); - } + } else if (ath != NULL) + ieee80211_saveath(ni, ath); break; case IEEE80211_FC0_SUBTYPE_AUTH: { - u_int16_t algo, seq, status; + uint16_t algo, seq, status; /* * auth frame format * [2] algorithm @@ -2151,10 +2513,10 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [2] status * [tlv*] challenge */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 6); - algo = le16toh(*(u_int16_t *)frm); - seq = le16toh(*(u_int16_t *)(frm + 2)); - status = le16toh(*(u_int16_t *)(frm + 4)); + IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); + algo = le16toh(*(uint16_t *)frm); + seq = le16toh(*(uint16_t *)(frm + 2)); + status = le16toh(*(uint16_t *)(frm + 4)); IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, "[%s] recv auth frame with algorithm %d seq %d\n", ether_sprintf(wh->i_addr2), algo, seq); @@ -2187,10 +2549,10 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } if (algo == IEEE80211_AUTH_ALG_SHARED) ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi, - rstamp, seq, status); + noise, rstamp, seq, status); else if (algo == IEEE80211_AUTH_ALG_OPEN) - ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq, - status); + ieee80211_auth_open(ic, wh, ni, rssi, noise, rstamp, + seq, status); else { IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, wh, "auth", "unsupported alg %d", algo); @@ -2208,9 +2570,10 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { - u_int16_t capinfo, lintval; - struct ieee80211_rsnparms rsn; - u_int8_t reason; + uint16_t capinfo, lintval; + struct ieee80211_rsnparms rsnparms; + uint8_t reason; + int badwparsn; if (ic->ic_opmode != IEEE80211_M_HOSTAP || ic->ic_state != IEEE80211_S_RUN) { @@ -2234,8 +2597,10 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [tlv] supported rates * [tlv] extended supported rates * [tlv] WPA or RSN + * [tlv] HT capabilities + * [tlv] Atheros capabilities */ - IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4)); + IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return); if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) { IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, wh, ieee80211_mgt_subtype_name[subtype >> @@ -2244,13 +2609,13 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ic->ic_stats.is_rx_assoc_bss++; return; } - capinfo = le16toh(*(u_int16_t *)frm); frm += 2; - lintval = le16toh(*(u_int16_t *)frm); frm += 2; + capinfo = le16toh(*(uint16_t *)frm); frm += 2; + lintval = le16toh(*(uint16_t *)frm); frm += 2; if (reassoc) frm += 6; /* ignore current AP info */ - ssid = rates = xrates = wpa = wme = NULL; + ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL; while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2); + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; @@ -2263,21 +2628,39 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, break; /* XXX verify only one of RSN and WPA ie's? */ case IEEE80211_ELEMID_RSN: - wpa = frm; + rsn = frm; + break; + case IEEE80211_ELEMID_HTCAP: + htcap = frm; break; case IEEE80211_ELEMID_VENDOR: if (iswpaoui(frm)) wpa = frm; else if (iswmeinfo(frm)) wme = frm; - /* XXX Atheros OUI support */ + else if (isatherosoui(frm)) + ath = frm; + else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) { + if (ishtcapoui(frm) && htcap == NULL) + htcap = frm; + } break; } frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + if (xrates != NULL) + IEEE80211_VERIFY_ELEMENT(xrates, + IEEE80211_RATE_MAXSIZE - rates[1]); IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); IEEE80211_VERIFY_SSID(ic->ic_bss, ssid); + if (htcap != NULL) { + IEEE80211_VERIFY_LENGTH(htcap[1], + htcap[0] == IEEE80211_ELEMID_VENDOR ? + 4 + sizeof(struct ieee80211_ie_htcap)-2 : + sizeof(struct ieee80211_ie_htcap)-2, + return); /* XXX just NULL out? */ + } if (ni == ic->ic_bss) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, @@ -2290,8 +2673,23 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ic->ic_stats.is_rx_assoc_notauth++; return; } - /* assert right associstion security credentials */ - if (wpa == NULL && (ic->ic_flags & IEEE80211_F_WPA)) { + /* assert right association security credentials */ + badwparsn = 0; + switch (ic->ic_flags & IEEE80211_F_WPA) { + case IEEE80211_F_WPA1: + if (wpa == NULL) + badwparsn = 1; + break; + case IEEE80211_F_WPA2: + if (rsn == NULL) + badwparsn = 1; + break; + case IEEE80211_F_WPA1|IEEE80211_F_WPA2: + if (wpa == NULL && rsn == NULL) + badwparsn = 1; + break; + } + if (badwparsn) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, "[%s] no WPA/RSN IE in association request\n", @@ -2300,23 +2698,22 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_RSN_REQUIRED); ieee80211_node_leave(ic, ni); - /* XXX distinguish WPA/RSN? */ ic->ic_stats.is_rx_assoc_badwpaie++; - return; + return; } - if (wpa != NULL) { + if (wpa != NULL || rsn != NULL) { /* - * Parse WPA information element. Note that + * Parse WPA/RSN information element. Note that * we initialize the param block from the node * state so that information in the IE overrides * our defaults. The resulting parameters are * installed below after the association is assured. */ - rsn = ni->ni_rsn; - if (wpa[0] != IEEE80211_ELEMID_RSN) - reason = ieee80211_parse_wpa(ic, wpa, &rsn, wh); + rsnparms = ni->ni_rsn; + if (wpa != NULL) + reason = ieee80211_parse_wpa(ic, wpa, &rsnparms, wh); else - reason = ieee80211_parse_rsn(ic, wpa, &rsn, wh); + reason = ieee80211_parse_rsn(ic, rsn, &rsnparms, wh); if (reason != 0) { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, reason); @@ -2329,14 +2726,14 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, "[%s] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n", ether_sprintf(wh->i_addr2), - wpa[0] != IEEE80211_ELEMID_RSN ? "WPA" : "RSN", - rsn.rsn_mcastcipher, rsn.rsn_mcastkeylen, - rsn.rsn_ucastcipher, rsn.rsn_ucastkeylen, - rsn.rsn_keymgmt, rsn.rsn_caps); + wpa != NULL ? "WPA" : "RSN", + rsnparms.rsn_mcastcipher, rsnparms.rsn_mcastkeylen, + rsnparms.rsn_ucastcipher, rsnparms.rsn_ucastkeylen, + rsnparms.rsn_keymgmt, rsnparms.rsn_caps); } /* discard challenge after association */ if (ni->ni_challenge != NULL) { - FREE(ni->ni_challenge, M_DEVBUF); + FREE(ni->ni_challenge, M_80211_NODE); ni->ni_challenge = NULL; } /* NB: 802.11 spec says to ignore station's privacy bit */ @@ -2349,7 +2746,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * Disallow re-associate w/ invalid slot time setting. */ if (ni->ni_associd != 0 && - ic->ic_curmode == IEEE80211_MODE_11G && + IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) { capinfomismatch(ni, wh, reassoc, resp, "slot time", capinfo); @@ -2372,28 +2769,59 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ratesetmismatch(ni, wh, reassoc, resp, "11g", rate); return; } + /* XXX enforce PUREN */ + /* 802.11n-specific rateset handling */ + if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) && htcap != NULL) { + rate = ieee80211_setup_htrates(ni, htcap, + IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | + IEEE80211_F_DOBRS); + if (rate & IEEE80211_RATE_BASIC) { + /* XXX 11n-specific stat */ + ratesetmismatch(ni, wh, reassoc, resp, + "HT", rate); + return; + } + ieee80211_ht_node_init(ni, htcap); + } else if (ni->ni_flags & IEEE80211_NODE_HT) + ieee80211_ht_node_cleanup(ni); ni->ni_rssi = rssi; + ni->ni_noise = noise; ni->ni_rstamp = rstamp; ni->ni_intval = lintval; ni->ni_capinfo = capinfo; - ni->ni_chan = ic->ic_bss->ni_chan; + ni->ni_chan = ic->ic_bsschan; ni->ni_fhdwell = ic->ic_bss->ni_fhdwell; ni->ni_fhindex = ic->ic_bss->ni_fhindex; if (wpa != NULL) { /* - * Record WPA/RSN parameters for station, mark + * Record WPA parameters for station, mark * node as using WPA and record information element * for applications that require it. */ - ni->ni_rsn = rsn; + ni->ni_rsn = rsnparms; ieee80211_saveie(&ni->ni_wpa_ie, wpa); } else if (ni->ni_wpa_ie != NULL) { /* * Flush any state from a previous association. */ - FREE(ni->ni_wpa_ie, M_DEVBUF); + FREE(ni->ni_wpa_ie, M_80211_NODE); ni->ni_wpa_ie = NULL; } + if (rsn != NULL) { + /* + * Record RSN parameters for station, mark + * node as using WPA and record information element + * for applications that require it. + */ + ni->ni_rsn = rsnparms; + ieee80211_saveie(&ni->ni_rsn_ie, rsn); + } else if (ni->ni_rsn_ie != NULL) { + /* + * Flush any state from a previous association. + */ + FREE(ni->ni_rsn_ie, M_80211_NODE); + ni->ni_rsn_ie = NULL; + } if (wme != NULL) { /* * Record WME parameters for station, mark node @@ -2406,19 +2834,35 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, /* * Flush any state from a previous association. */ - FREE(ni->ni_wme_ie, M_DEVBUF); + FREE(ni->ni_wme_ie, M_80211_NODE); ni->ni_wme_ie = NULL; ni->ni_flags &= ~IEEE80211_NODE_QOS; } - ieee80211_deliver_l2uf(ni); + if (ath != NULL) { + /* + * Record ATH parameters for station, mark + * node with appropriate capabilities, and + * record the information element for + * applications that require it. + */ + ieee80211_saveath(ni, ath); + } else if (ni->ni_ath_ie != NULL) { + /* + * Flush any state from a previous association. + */ + FREE(ni->ni_ath_ie, M_80211_NODE); + ni->ni_ath_ie = NULL; + ni->ni_ath_flags = 0; + } ieee80211_node_join(ic, ni, resp); + ieee80211_deliver_l2uf(ni); break; } case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { - u_int16_t capinfo, associd; - u_int16_t status; + uint16_t capinfo, associd; + uint16_t status; if (ic->ic_opmode != IEEE80211_M_STA || ic->ic_state != IEEE80211_S_ASSOC) { @@ -2434,12 +2878,13 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [tlv] supported rates * [tlv] extended supported rates * [tlv] WME + * [tlv] HT capabilities */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 6); + IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); ni = ic->ic_bss; - capinfo = le16toh(*(u_int16_t *)frm); + capinfo = le16toh(*(uint16_t *)frm); frm += 2; - status = le16toh(*(u_int16_t *)frm); + status = le16toh(*(uint16_t *)frm); frm += 2; if (status != 0) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, @@ -2451,12 +2896,12 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ic->ic_stats.is_rx_auth_fail++; /* XXX */ return; } - associd = le16toh(*(u_int16_t *)frm); + associd = le16toh(*(uint16_t *)frm); frm += 2; - rates = xrates = wpa = wme = NULL; + rates = xrates = wme = htcap = NULL; while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2); + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); switch (*frm) { case IEEE80211_ELEMID_RATES: rates = frm; @@ -2464,6 +2909,9 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_ELEMID_XRATES: xrates = frm; break; + case IEEE80211_ELEMID_HTCAP: + htcap = frm; + break; case IEEE80211_ELEMID_VENDOR: if (iswmeoui(frm)) wme = frm; @@ -2474,6 +2922,9 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + if (xrates != NULL) + IEEE80211_VERIFY_ELEMENT(xrates, + IEEE80211_RATE_MAXSIZE - rates[1]); rate = ieee80211_setup_rates(ni, rates, xrates, IEEE80211_F_JOIN | IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | @@ -2486,7 +2937,8 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, if (ni != ic->ic_bss) /* XXX never true? */ ni->ni_fails++; ic->ic_stats.is_rx_assoc_norate++; - ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + ieee80211_new_state(ic, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_STATUS); return; } @@ -2503,7 +2955,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * * XXX may need different/additional driver callbacks? */ - if (ic->ic_curmode == IEEE80211_MODE_11A || + if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { ic->ic_flags |= IEEE80211_F_SHPREAMBLE; ic->ic_flags &= ~IEEE80211_F_USEBARKER; @@ -2512,34 +2964,37 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ic->ic_flags |= IEEE80211_F_USEBARKER; } ieee80211_set_shortslottime(ic, - ic->ic_curmode == IEEE80211_MODE_11A || + IEEE80211_IS_CHAN_A(ic->ic_curchan) || (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); /* * Honor ERP protection. * * NB: ni_erp should zero for non-11g operation. - * XXX check ic_curmode anyway? */ - if (ic->ic_curmode == IEEE80211_MODE_11G && + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) ic->ic_flags |= IEEE80211_F_USEPROT; else ic->ic_flags &= ~IEEE80211_F_USEPROT; IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "[%s] %sassoc success: %s preamble, %s slot time%s%s\n", + "[%s] %sassoc success: %s preamble, %s slot time%s%s%s%s\n", ether_sprintf(wh->i_addr2), ISREASSOC(subtype) ? "re" : "", ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "", - ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "" + ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "", + IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ? + ", fast-frames" : "", + IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ? + ", turbo" : "" ); ieee80211_new_state(ic, IEEE80211_S_RUN, subtype); break; } case IEEE80211_FC0_SUBTYPE_DEAUTH: { - u_int16_t reason; + uint16_t reason; if (ic->ic_state == IEEE80211_S_SCAN) { ic->ic_stats.is_rx_mgtdiscard++; @@ -2549,8 +3004,8 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * deauth frame format * [2] reason */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 2); - reason = le16toh(*(u_int16_t *)frm); + IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); + reason = le16toh(*(uint16_t *)frm); ic->ic_stats.is_rx_deauth++; IEEE80211_NODE_STAT(ni, rx_deauth); @@ -2565,7 +3020,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, switch (ic->ic_opmode) { case IEEE80211_M_STA: ieee80211_new_state(ic, IEEE80211_S_AUTH, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH); break; case IEEE80211_M_HOSTAP: if (ni != ic->ic_bss) @@ -2579,7 +3034,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } case IEEE80211_FC0_SUBTYPE_DISASSOC: { - u_int16_t reason; + uint16_t reason; if (ic->ic_state != IEEE80211_S_RUN && ic->ic_state != IEEE80211_S_ASSOC && @@ -2591,8 +3046,8 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * disassoc frame format * [2] reason */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 2); - reason = le16toh(*(u_int16_t *)frm); + IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); + reason = le16toh(*(uint16_t *)frm); ic->ic_stats.is_rx_disassoc++; IEEE80211_NODE_STAT(ni, rx_disassoc); @@ -2606,8 +3061,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ether_sprintf(ni->ni_macaddr), reason); switch (ic->ic_opmode) { case IEEE80211_M_STA: - ieee80211_new_state(ic, IEEE80211_S_ASSOC, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0); break; case IEEE80211_M_HOSTAP: if (ni != ic->ic_bss) @@ -2619,6 +3073,65 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } break; } + + case IEEE80211_FC0_SUBTYPE_ACTION: { + const struct ieee80211_action *ia; + + if (ic->ic_state != IEEE80211_S_RUN && + ic->ic_state != IEEE80211_S_ASSOC && + ic->ic_state != IEEE80211_S_AUTH) { + ic->ic_stats.is_rx_mgtdiscard++; + return; + } + /* + * action frame format: + * [1] category + * [1] action + * [tlv] parameters + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action), return); + ia = (const struct ieee80211_action *) frm; + + ic->ic_stats.is_rx_action++; + IEEE80211_NODE_STAT(ni, rx_action); + + /* verify frame payloads but defer processing */ + /* XXX maybe push this to method */ + switch (ia->ia_category) { + case IEEE80211_ACTION_CAT_BA: + switch (ia->ia_action) { + case IEEE80211_ACTION_BA_ADDBA_REQUEST: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ba_addbarequest), + return); + break; + case IEEE80211_ACTION_BA_ADDBA_RESPONSE: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ba_addbaresponse), + return); + break; + case IEEE80211_ACTION_BA_DELBA: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ba_delba), + return); + break; + } + break; + case IEEE80211_ACTION_CAT_HT: + switch (ia->ia_action) { + case IEEE80211_ACTION_HT_TXCHWIDTH: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ht_txchwidth), + return); + break; + } + break; + } + ic->ic_recv_action(ni, frm, efrm); + break; + } + default: IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, wh, "mgt", "subtype 0x%x not handled", subtype); @@ -2632,76 +3145,6 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, #undef IEEE80211_VERIFY_ELEMENT /* - * Handle station power-save state change. - */ -static void -ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable) -{ - struct ieee80211com *ic = ni->ni_ic; - struct mbuf *m, *mhead, *mtail; - int mcount; - - if (enable) { - if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) - ic->ic_ps_sta++; - ni->ni_flags |= IEEE80211_NODE_PWR_MGT; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, - "[%s] power save mode on, %u sta's in ps mode\n", - ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta); - return; - } - - if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) - ic->ic_ps_sta--; - ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, - "[%s] power save mode off, %u sta's in ps mode\n", - ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta); - /* XXX if no stations in ps mode, flush mc frames */ - - /* - * Flush queued unicast frames. - */ - if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) { - if (ic->ic_set_tim != NULL) - ic->ic_set_tim(ni, 0); /* just in case */ - return; - } - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, - "[%s] flush ps queue, %u packets queued\n", - ether_sprintf(ni->ni_macaddr), IEEE80211_NODE_SAVEQ_QLEN(ni)); - /* - * Unload the frames from the ps q but don't send them - * to the driver yet. We do this in two stages to minimize - * locking but also because there's no easy way to preserve - * ordering given the existing ifnet access mechanisms. - * XXX could be optimized - */ - IEEE80211_NODE_SAVEQ_LOCK(ni); - mcount = IEEE80211_NODE_SAVEQ_QLEN(ni); - mhead = mtail = NULL; - for (;;) { - _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); - if (m == NULL) - break; - if (mhead == NULL) { - mhead = m; - m->m_nextpkt = NULL; - } else - mtail->m_nextpkt = m; - mtail = m; - } - IEEE80211_NODE_SAVEQ_UNLOCK(ni); - if (mhead != NULL) { - /* XXX need different driver interface */ - /* XXX bypasses q max */ - IF_PREPEND_LIST(&ic->ic_ifp->if_snd, mhead, mtail, mcount); - } - if (ic->ic_set_tim != NULL) - ic->ic_set_tim(ni, 0); -} - -/* * Process a received ps-poll frame. */ static void @@ -2710,7 +3153,7 @@ ieee80211_recv_pspoll(struct ieee80211com *ic, { struct ieee80211_frame_min *wh; struct mbuf *m; - u_int16_t aid; + uint16_t aid; int qlen; wh = mtod(m0, struct ieee80211_frame_min *); @@ -2724,7 +3167,7 @@ ieee80211_recv_pspoll(struct ieee80211com *ic, return; } - aid = le16toh(*(u_int16_t *)wh->i_dur); + aid = le16toh(*(uint16_t *)wh->i_dur); if (aid != ni->ni_associd) { IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, (struct ieee80211_frame *) wh, "ps-poll", @@ -2777,7 +3220,7 @@ ieee80211_recv_pspoll(struct ieee80211com *ic, /* * Return the bssid of a frame. */ -static const u_int8_t * +static const uint8_t * ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh) { if (ic->ic_opmode == IEEE80211_M_STA) @@ -2819,7 +3262,7 @@ ieee80211_note_frame(struct ieee80211com *ic, void ieee80211_note_mac(struct ieee80211com *ic, - const u_int8_t mac[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...) { char buf[128]; /* XXX */ @@ -2871,7 +3314,7 @@ ieee80211_discard_ie(struct ieee80211com *ic, void ieee80211_discard_mac(struct ieee80211com *ic, - const u_int8_t mac[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN], const char *type, const char *fmt, ...) { va_list ap; |