diff options
author | sam <sam@FreeBSD.org> | 2008-04-20 20:35:46 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2008-04-20 20:35:46 +0000 |
commit | 3569e353ca63336d80ab0143dd9669b0b9e6b123 (patch) | |
tree | bc7985c57e7ecfa1ac03e48c406a25430dba634b /sys/net80211/ieee80211_input.c | |
parent | 682b4ae9be70192e298129ada878af3486683aaf (diff) | |
download | FreeBSD-src-3569e353ca63336d80ab0143dd9669b0b9e6b123.zip FreeBSD-src-3569e353ca63336d80ab0143dd9669b0b9e6b123.tar.gz |
Multi-bss (aka vap) support for 802.11 devices.
Note this includes changes to all drivers and moves some device firmware
loading to use firmware(9) and a separate module (e.g. ral). Also there
no longer are separate wlan_scan* modules; this functionality is now
bundled into the wlan module.
Supported by: Hobnob and Marvell
Reviewed by: many
Obtained from: Atheros (some bits)
Diffstat (limited to 'sys/net80211/ieee80211_input.c')
-rw-r--r-- | sys/net80211/ieee80211_input.c | 3250 |
1 files changed, 376 insertions, 2874 deletions
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 9238bdf..9ae653f 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,6 +27,8 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_wlan.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/mbuf.h> @@ -35,591 +37,75 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/socket.h> - -#include <net/if.h> -#include <net/if_media.h> + #include <net/ethernet.h> +#include <net/if.h> #include <net/if_llc.h> +#include <net/if_media.h> #include <net/if_vlan_var.h> #include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_input.h> #include <net/bpf.h> -#ifdef IEEE80211_DEBUG -#include <machine/stdarg.h> - -/* - * Decide if a received management frame should be - * printed when debugging is enabled. This filters some - * of the less interesting frames that come frequently - * (e.g. beacons). - */ -static __inline int -doprint(struct ieee80211com *ic, int subtype) -{ - switch (subtype) { - case IEEE80211_FC0_SUBTYPE_BEACON: - return (ic->ic_flags & IEEE80211_F_SCAN); - case IEEE80211_FC0_SUBTYPE_PROBE_REQ: - return (ic->ic_opmode == IEEE80211_M_IBSS); - } - return 1; -} - -static const uint8_t *ieee80211_getbssid(struct ieee80211com *, - const struct ieee80211_frame *); -#endif /* IEEE80211_DEBUG */ - -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 uint8_t *mac, int subtype, int arg); -static struct mbuf *ieee80211_decap_fastframe(struct ieee80211com *, - struct ieee80211_node *, struct mbuf *); -static void ieee80211_recv_pspoll(struct ieee80211com *, - struct ieee80211_node *, struct mbuf *); +#ifdef INET +#include <netinet/in.h> +#include <net/ethernet.h> +#endif -/* - * Process a received frame. The node associated with the sender - * should be supplied. If nothing was found in the node table then - * the caller is assumed to supply a reference to ic_bss instead. - * The RSSI and a timestamp are also supplied. The RSSI data is used - * during AP scanning to select a AP to associate with; it can have - * any units so long as values have consistent units and higher values - * mean ``better signal''. The receive timestamp is currently not used - * by the 802.11 layer. - */ int -ieee80211_input(struct ieee80211com *ic, struct mbuf *m, - struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp) +ieee80211_input_all(struct ieee80211com *ic, + struct mbuf *m, int rssi, int noise, u_int32_t rstamp) { -#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) -#define HAS_SEQ(type) ((type & 0x4) == 0) - struct ifnet *ifp = ic->ic_ifp; - struct ieee80211_frame *wh; - struct ieee80211_key *key; - struct ether_header *eh; - 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; - - need_tap = 1; /* mbuf need to be tapped. */ - type = -1; /* undefined */ - /* - * In monitor mode, send everything directly to bpf. - * XXX may want to include the CRC - */ - if (ic->ic_opmode == IEEE80211_M_MONITOR) - goto out; - - if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, - ni->ni_macaddr, NULL, - "too short (1): len %u", m->m_pkthdr.len); - ic->ic_stats.is_rx_tooshort++; - goto out; - } - /* - * Bit of a cheat here, we use a pointer for a 3-address - * frame format but don't reference fields past outside - * ieee80211_frame_min w/o first validating the data is - * present. - */ - wh = mtod(m, struct ieee80211_frame *); - - if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != - IEEE80211_FC0_VERSION_0) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, - ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]); - ic->ic_stats.is_rx_badversion++; - goto err; - } - - dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; - type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; - subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; - if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - bssid = wh->i_addr2; - if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) { - /* not interested in */ - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, - bssid, NULL, "%s", "not to bss"); - ic->ic_stats.is_rx_wrongbss++; - goto out; - } - break; - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: - case IEEE80211_M_HOSTAP: - if (dir != IEEE80211_FC1_DIR_NODS) - bssid = wh->i_addr1; - else if (type == IEEE80211_FC0_TYPE_CTL) - bssid = wh->i_addr1; - else { - if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { - IEEE80211_DISCARD_MAC(ic, - IEEE80211_MSG_ANY, ni->ni_macaddr, - NULL, "too short (2): len %u", - m->m_pkthdr.len); - ic->ic_stats.is_rx_tooshort++; - goto out; - } - bssid = wh->i_addr3; - } - if (type != IEEE80211_FC0_TYPE_DATA) - break; - /* - * Data frame, validate the bssid. - */ - if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) && - !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) { - /* not interested in */ - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, - bssid, NULL, "%s", "not to bss"); - ic->ic_stats.is_rx_wrongbss++; - goto out; - } - /* - * For adhoc mode we cons up a node when it doesn't - * exist. This should probably done after an ACL check. - */ - if (ni == ic->ic_bss && - ic->ic_opmode != IEEE80211_M_HOSTAP && - !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { - /* - * Fake up a node for this newly - * discovered member of the IBSS. - */ - ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta, - wh->i_addr2); - if (ni == NULL) { - /* NB: stat kept for alloc failure */ - goto err; - } - } - break; - default: - goto out; - } - ni->ni_rssi = rssi; - ni->ni_noise = noise; - ni->ni_rstamp = rstamp; - if (HAS_SEQ(type)) { - uint8_t tid; - if (IEEE80211_QOS_HAS_SEQ(wh)) { - tid = ((struct ieee80211_qosframe *)wh)-> - i_qos[0] & IEEE80211_QOS_TID; - if (TID_TO_WME_AC(tid) >= WME_AC_VI) - ic->ic_wme.wme_hipri_traffic++; - tid++; - } else - tid = IEEE80211_NONQOS_TID; - 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, - bssid, "duplicate", - "seqno <%u,%u> fragno <%u,%u> tid %u", - rxseq >> IEEE80211_SEQ_SEQ_SHIFT, - ni->ni_rxseqs[tid] >> - IEEE80211_SEQ_SEQ_SHIFT, - rxseq & IEEE80211_SEQ_FRAG_MASK, - ni->ni_rxseqs[tid] & - IEEE80211_SEQ_FRAG_MASK, - tid); - ic->ic_stats.is_rx_dup++; - IEEE80211_NODE_STAT(ni, rx_dup); - goto out; - } - ni->ni_rxseqs[tid] = rxseq; - } - } - - switch (type) { - case IEEE80211_FC0_TYPE_DATA: - hdrspace = ieee80211_hdrspace(ic, wh); - if (m->m_len < hdrspace && - (m = m_pullup(m, hdrspace)) == NULL) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, - ni->ni_macaddr, NULL, - "data too short: expecting %u", hdrspace); - ic->ic_stats.is_rx_tooshort++; - goto out; /* XXX */ - } - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - if (dir != IEEE80211_FC1_DIR_FROMDS) { - IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "unknown dir 0x%x", dir); - ic->ic_stats.is_rx_wrongdir++; - goto out; - } - if ((ifp->if_flags & IFF_SIMPLEX) && - IEEE80211_IS_MULTICAST(wh->i_addr1) && - IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) { - /* - * In IEEE802.11 network, multicast packet - * sent from me is broadcasted from AP. - * It should be silently discarded for - * SIMPLEX interface. - */ - IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, NULL, "%s", "multicast echo"); - ic->ic_stats.is_rx_mcastecho++; - goto out; - } - break; - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: - if (dir != IEEE80211_FC1_DIR_NODS) { - IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "unknown dir 0x%x", dir); - ic->ic_stats.is_rx_wrongdir++; - goto out; - } - /* XXX no power-save support */ - break; - case IEEE80211_M_HOSTAP: - if (dir != IEEE80211_FC1_DIR_TODS) { - IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "unknown dir 0x%x", dir); - ic->ic_stats.is_rx_wrongdir++; - goto out; - } - /* check if source STA is associated */ - if (ni == ic->ic_bss) { - IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "%s", "unknown src"); - ieee80211_send_error(ic, ni, wh->i_addr2, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_NOT_AUTHED); - ic->ic_stats.is_rx_notassoc++; - goto err; - } - if (ni->ni_associd == 0) { - IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "%s", "unassoc src"); - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DISASSOC, - IEEE80211_REASON_NOT_ASSOCED); - ic->ic_stats.is_rx_notassoc++; - goto err; - } - - /* - * 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))) - ieee80211_node_pwrsave(ni, - wh->i_fc[1] & IEEE80211_FC1_PWR_MGT); - break; - default: - /* XXX here to keep compiler happy */ - goto out; - } - - /* - * 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; - * otherwise we may violate assumptions in the - * crypto cipher modules used to do delayed update - * of replay sequence numbers. - */ - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { - /* - * Discard encrypted frames when privacy is off. - */ - IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "WEP", "%s", "PRIVACY off"); - ic->ic_stats.is_rx_noprivacy++; - IEEE80211_NODE_STAT(ni, rx_noprivacy); - goto out; - } - key = ieee80211_crypto_decap(ic, ni, m, hdrspace); - if (key == NULL) { - /* NB: stats+msgs handled in crypto_decap */ - IEEE80211_NODE_STAT(ni, rx_wepfail); - goto out; - } - wh = mtod(m, struct ieee80211_frame *); - wh->i_fc[1] &= ~IEEE80211_FC1_WEP; - } else { - /* XXX M_WEP and IEEE80211_F_PRIVACY */ - key = NULL; - } - - /* - * 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; + struct ieee80211vap *vap; + int type = -1; - /* - * Next up, any fragmentation. - */ - if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { - m = ieee80211_defrag(ic, ni, m, hdrspace); - if (m == NULL) { - /* Fragment dropped or frame not complete yet */ - goto out; - } - } - wh = NULL; /* no longer valid, catch any uses */ + /* XXX locking */ + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + struct ieee80211_node *ni; + struct mbuf *mcopy; /* - * Next strip any MSDU crypto bits. + * WDS vap's only receive directed traffic from the + * station at the ``far end''. That traffic should + * be passed through the AP vap the station is associated + * to--so don't spam them with mcast frames. */ - if (key != NULL && !ieee80211_crypto_demic(ic, key, m, 0)) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, - ni->ni_macaddr, "data", "%s", "demic error"); - ic->ic_stats.is_rx_demicfail++; - IEEE80211_NODE_STAT(ni, rx_demicfail); - goto out; - } - - /* 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. - */ - m = ieee80211_decap(ic, m, hdrspace); - if (m == NULL) { - /* don't count Null data frames as errors */ - 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"); - ic->ic_stats.is_rx_decap++; - IEEE80211_NODE_STAT(ni, rx_decap); - goto err; - } - eh = mtod(m, struct ether_header *); - if (!ieee80211_node_is_authorized(ni)) { + if (vap->iv_opmode == IEEE80211_M_WDS) + continue; + if (TAILQ_NEXT(vap, iv_next) != NULL) { /* - * Deny any non-PAE frames received prior to - * authorization. For open/shared-key - * authentication the port is mark authorized - * after authentication completes. For 802.1x - * the port is not marked authorized by the - * authenticator until the handshake has completed. + * Packet contents are changed by ieee80211_decap + * so do a deep copy of the packet. */ - if (eh->ether_type != htons(ETHERTYPE_PAE)) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, - eh->ether_shost, "data", - "unauthorized port: ether type 0x%x len %u", - eh->ether_type, m->m_pkthdr.len); - ic->ic_stats.is_rx_unauth++; - IEEE80211_NODE_STAT(ni, rx_unauth); - goto err; + mcopy = m_dup(m, M_DONTWAIT); + if (mcopy == NULL) { + /* XXX stat+msg */ + continue; } } else { - /* - * When denying unencrypted frames, discard - * any non-PAE frames received without encryption. - */ - if ((ic->ic_flags & IEEE80211_F_DROPUNENC) && - (key == NULL && (m->m_flags & M_WEP) == 0) && - eh->ether_type != htons(ETHERTYPE_PAE)) { - /* - * Drop unencrypted frames. - */ - ic->ic_stats.is_rx_unencrypted++; - IEEE80211_NODE_STAT(ni, rx_unencrypted); - 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; - - case IEEE80211_FC0_TYPE_MGT: - ic->ic_stats.is_rx_mgmt++; - IEEE80211_NODE_STAT(ni, rx_mgmt); - if (dir != IEEE80211_FC1_DIR_NODS) { - IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "unknown dir 0x%x", dir); - ic->ic_stats.is_rx_wrongdir++; - goto err; - } - if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, - ni->ni_macaddr, "mgt", "too short: len %u", - m->m_pkthdr.len); - ic->ic_stats.is_rx_tooshort++; - goto out; - } -#ifdef IEEE80211_DEBUG - if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) || - ieee80211_msg_dumppkts(ic)) { - if_printf(ic->ic_ifp, "received %s from %s rssi %d\n", - ieee80211_mgt_subtype_name[subtype >> - IEEE80211_FC0_SUBTYPE_SHIFT], - ether_sprintf(wh->i_addr2), rssi); - } -#endif - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) { - /* - * Only shared key auth frames with a challenge - * should be encrypted, discard all others. - */ - IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, ieee80211_mgt_subtype_name[subtype >> - IEEE80211_FC0_SUBTYPE_SHIFT], - "%s", "WEP set but not permitted"); - ic->ic_stats.is_rx_mgtdiscard++; /* XXX */ - goto out; - } - if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { - /* - * Discard encrypted frames when privacy is off. - */ - IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "mgt", "%s", "WEP set but PRIVACY off"); - ic->ic_stats.is_rx_noprivacy++; - goto out; - } - hdrspace = ieee80211_hdrspace(ic, wh); - key = ieee80211_crypto_decap(ic, ni, m, hdrspace); - if (key == NULL) { - /* NB: stats+msgs handled in crypto_decap */ - goto out; - } - wh = mtod(m, struct ieee80211_frame *); - wh->i_fc[1] &= ~IEEE80211_FC1_WEP; - } - if (bpf_peers_present(ic->ic_rawbpf)) - bpf_mtap(ic->ic_rawbpf, m); - (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, noise, rstamp); - m_freem(m); - return IEEE80211_FC0_TYPE_MGT; - - case IEEE80211_FC0_TYPE_CTL: - ic->ic_stats.is_rx_ctl++; - IEEE80211_NODE_STAT(ni, rx_ctrl); - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - switch (subtype) { - case IEEE80211_FC0_SUBTYPE_PS_POLL: - ieee80211_recv_pspoll(ic, ni, m); - break; - case IEEE80211_FC0_SUBTYPE_BAR: - ieee80211_recv_bar(ni, m); - break; - } + mcopy = m; + m = NULL; } - goto out; - default: - IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, - wh, NULL, "bad frame type 0x%x", type); - /* should not come here */ - break; + ni = ieee80211_ref_node(vap->iv_bss); + type = ieee80211_input(ni, mcopy, rssi, noise, rstamp); + ieee80211_free_node(ni); } -err: - ifp->if_ierrors++; -out: - if (m != NULL) { - if (bpf_peers_present(ic->ic_rawbpf) && need_tap) - bpf_mtap(ic->ic_rawbpf, m); + if (m != NULL) /* no vaps, reclaim mbuf */ m_freem(m); - } return type; -#undef SEQ_LEQ } /* * This function reassemble fragments. + * + * XXX should handle 3 concurrent reassemblies per-spec. */ -static struct mbuf * -ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, - struct mbuf *m, int hdrspace) +struct mbuf * +ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); struct ieee80211_frame *lwh; uint16_t rxseq; @@ -681,7 +167,7 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, if (mfrag == NULL) { if (fragno != 0) { /* !first fragment, discard */ - ic->ic_stats.is_rx_defrag++; + vap->iv_stats.is_rx_defrag++; IEEE80211_NODE_STAT(ni, rx_defrag); m_freem(m); return NULL; @@ -705,12 +191,14 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, } void -ieee80211_deliver_data(struct ieee80211com *ic, +ieee80211_deliver_data(struct ieee80211vap *vap, struct ieee80211_node *ni, struct mbuf *m) { struct ether_header *eh = mtod(m, struct ether_header *); - struct ifnet *ifp = ic->ic_ifp; + struct ifnet *ifp = vap->iv_ifp; + /* NB: see hostap_deliver_data, this path doesn't handle hostap */ + KASSERT(vap->iv_opmode != IEEE80211_M_HOSTAP, ("gack, hostap")); /* * Do accounting. */ @@ -722,66 +210,21 @@ ieee80211_deliver_data(struct ieee80211com *ic, IEEE80211_NODE_STAT(ni, rx_mcast); } else IEEE80211_NODE_STAT(ni, rx_ucast); + m->m_pkthdr.rcvif = ifp; /* clear driver/net80211 flags before passing up */ m->m_flags &= ~M_80211_RX; - /* perform as a bridge within the AP */ - if (ic->ic_opmode == IEEE80211_M_HOSTAP && - (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) { - struct mbuf *m1 = NULL; - - if (m->m_flags & M_MCAST) { - m1 = m_dup(m, M_DONTWAIT); - if (m1 == NULL) - ifp->if_oerrors++; - else - m1->m_flags |= M_MCAST; - } else { - /* - * Check if the destination is known; if so - * and the port is authorized dispatch directly. - */ - struct ieee80211_node *sta = - ieee80211_find_node(&ic->ic_sta, eh->ether_dhost); - if (sta != NULL) { - if (ieee80211_node_is_authorized(sta)) { - /* - * Beware of sending to ourself; this - * needs to happen via the normal - * input path. - */ - if (sta != ic->ic_bss) { - m1 = m; - m = NULL; - } - } else { - ic->ic_stats.is_rx_unauth++; - IEEE80211_NODE_STAT(sta, rx_unauth); - } - ieee80211_free_node(sta); - } - } - if (m1 != NULL) { - int error; - - /* XXX does not work well with WME */ - IFQ_HANDOFF(ifp, m1, error); - } - } - if (m != NULL) { - m->m_pkthdr.rcvif = ifp; - if (ni->ni_vlan != 0) { - /* attach vlan tag */ - m->m_pkthdr.ether_vtag = ni->ni_vlan; - m->m_flags |= M_VLANTAG; - } - (*ifp->if_input)(ifp, m); + if (ni->ni_vlan != 0) { + /* attach vlan tag */ + m->m_pkthdr.ether_vtag = ni->ni_vlan; + m->m_flags |= M_VLANTAG; } + ifp->if_input(ifp, m); } -static struct mbuf * -ieee80211_decap(struct ieee80211com *ic, struct mbuf *m, int hdrlen) +struct mbuf * +ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen) { struct ieee80211_qosframe_addr4 wh; /* Max size address frames */ struct ether_header *eh; @@ -916,28 +359,28 @@ ieee80211_decap1(struct mbuf *m, int *framelen) * 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) +struct mbuf * +ieee80211_decap_fastframe(struct ieee80211_node *ni, struct mbuf *m) { #define MS(x,f) (((x) & f) >> f##_S) + struct ieee80211vap *vap = ni->ni_vap; 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, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "fast-frame", "unsupport tunnel protocol, header 0x%x", ath); - ic->ic_stats.is_ff_badhdr++; + vap->iv_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++; + vap->iv_stats.is_ff_decap++; /* * Decap the first frame, bust it apart from the @@ -946,21 +389,22 @@ ieee80211_decap_fastframe(struct ieee80211com *ic, */ m = ieee80211_decap1(m, &framelen); if (m == NULL) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "fast-frame", "%s", "first decap failed"); - ic->ic_stats.is_ff_tooshort++; + vap->iv_stats.is_ff_tooshort++; return NULL; } n = m_split(m, framelen, M_NOWAIT); if (n == NULL) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "fast-frame", "%s", "unable to split encapsulated frames"); - ic->ic_stats.is_ff_split++; + vap->iv_stats.is_ff_split++; m_freem(m); /* NB: must reclaim */ return NULL; } - ieee80211_deliver_data(ic, ni, m); /* 1st of pair */ + /* XXX not right for WDS */ + vap->iv_deliver_data(vap, ni, m); /* 1st of pair */ /* * Decap second frame. @@ -968,9 +412,9 @@ ieee80211_decap_fastframe(struct ieee80211com *ic, m_adj(n, roundup2(framelen, 4) - framelen); /* padding */ n = ieee80211_decap1(n, &framelen); if (n == NULL) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "fast-frame", "%s", "second decap failed"); - ic->ic_stats.is_ff_tooshort++; + vap->iv_stats.is_ff_tooshort++; } /* XXX verify framelen against mbuf contents */ return n; /* 2nd delivered by caller */ @@ -984,7 +428,7 @@ int ieee80211_setup_rates(struct ieee80211_node *ni, const uint8_t *rates, const uint8_t *xrates, int flags) { - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_rateset *rs = &ni->ni_rates; memset(rs, 0, sizeof(*rs)); @@ -998,11 +442,10 @@ ieee80211_setup_rates(struct ieee80211_node *ni, nxrates = xrates[1]; if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) { nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE, - "[%s] extended rate set too large;" - " only using %u of %u rates\n", - ether_sprintf(ni->ni_macaddr), nxrates, xrates[1]); - ic->ic_stats.is_rx_rstoobig++; + IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE, ni, + "extended rate set too large; only using " + "%u of %u rates", nxrates, xrates[1]); + vap->iv_stats.is_rx_rstoobig++; } memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates); rs->rs_nrates += nxrates; @@ -1010,101 +453,6 @@ ieee80211_setup_rates(struct ieee80211_node *ni, return ieee80211_fix_rate(ni, rs, flags); } -static void -ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, - struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp, - uint16_t seq, uint16_t status) -{ - - if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, - ni->ni_macaddr, "open auth", - "bad sta auth mode %u", ni->ni_authmode); - ic->ic_stats.is_rx_bad_auth++; /* XXX */ - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - /* - * Clear any challenge text that may be there if - * a previous shared key auth failed and then an - * open auth is attempted. - */ - if (ni->ni_challenge != NULL) { - FREE(ni->ni_challenge, M_80211_NODE); - ni->ni_challenge = NULL; - } - /* XXX hack to workaround calling convention */ - ieee80211_send_error(ic, ni, wh->i_addr2, - IEEE80211_FC0_SUBTYPE_AUTH, - (seq + 1) | (IEEE80211_STATUS_ALG<<16)); - } - return; - } - switch (ic->ic_opmode) { - 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", - "bad operating mode %u", ic->ic_opmode); - break; - - case IEEE80211_M_HOSTAP: - if (ic->ic_state != IEEE80211_S_RUN || - seq != IEEE80211_AUTH_OPEN_REQUEST) { - ic->ic_stats.is_rx_bad_auth++; - return; - } - /* always accept open authentication requests */ - if (ni == ic->ic_bss) { - ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2); - if (ni == NULL) - return; - } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) - (void) ieee80211_ref_node(ni); - /* - * Mark the node as referenced to reflect that it's - * reference count has been bumped to insure it remains - * after the transaction completes. - */ - ni->ni_flags |= IEEE80211_NODE_AREF; - - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, - "[%s] station authenticated (open)\n", - ether_sprintf(ni->ni_macaddr)); - /* - * When 802.1x is not in use mark the port - * authorized at this point so traffic can flow. - */ - if (ni->ni_authmode != IEEE80211_AUTH_8021X) - ieee80211_node_authorize(ni); - break; - - case IEEE80211_M_STA: - if (ic->ic_state != IEEE80211_S_AUTH || - seq != IEEE80211_AUTH_OPEN_RESPONSE) { - ic->ic_stats.is_rx_bad_auth++; - return; - } - if (status != 0) { - IEEE80211_DPRINTF(ic, - IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, - "[%s] open auth failed (reason %d)\n", - ether_sprintf(ni->ni_macaddr), status); - /* XXX can this happen? */ - if (ni != ic->ic_bss) - ni->ni_fails++; - ic->ic_stats.is_rx_auth_fail++; - ieee80211_new_state(ic, IEEE80211_S_SCAN, - IEEE80211_SCAN_FAIL_STATUS); - } else - ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0); - break; - } -} - /* * Send a management frame error response to the specified * station. If ni is associated with the station then use @@ -1112,14 +460,26 @@ ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, * transmitting the frame and then free the reference so * it will go away as soon as the frame has been transmitted. */ -static void -ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni, - const uint8_t *mac, int subtype, int arg) +void +ieee80211_send_error(struct ieee80211_node *ni, + const uint8_t mac[IEEE80211_ADDR_LEN], int subtype, int arg) { + struct ieee80211vap *vap = ni->ni_vap; int istmp; - if (ni == ic->ic_bss) { - ni = ieee80211_tmp_node(ic, mac); + if (ni == vap->iv_bss) { + if (vap->iv_state != IEEE80211_S_RUN) { + /* + * XXX hack until we get rid of this routine. + * We can be called prior to the vap reaching + * run state under certain conditions in which + * case iv_bss->ni_chan will not be setup. + * Check for this explicitly and and just ignore + * the request. + */ + return; + } + ni = ieee80211_tmp_node(vap, mac); if (ni == NULL) { /* XXX msg */ return; @@ -1127,2204 +487,342 @@ ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni, istmp = 1; } else istmp = 0; - IEEE80211_SEND_MGMT(ic, ni, subtype, arg); + IEEE80211_SEND_MGMT(ni, subtype, arg); if (istmp) ieee80211_free_node(ni); } -static int -alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni) +int +ieee80211_alloc_challenge(struct ieee80211_node *ni) { if (ni->ni_challenge == NULL) 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", - ether_sprintf(ni->ni_macaddr)); + IEEE80211_NOTE(ni->ni_vap, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni, + "%s", "shared key challenge alloc failed"); /* XXX statistic */ } return (ni->ni_challenge != NULL); } -/* XXX TODO: add statistics */ -static void -ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, - uint8_t *frm, uint8_t *efrm, struct ieee80211_node *ni, - int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status) -{ - uint8_t *challenge; - int allocbs, estatus; - - /* - * NB: this can happen as we allow pre-shared key - * authentication to be enabled w/o wep being turned - * on so that configuration of these can be done - * in any order. It may be better to enforce the - * ordering in which case this check would just be - * for sanity/consistency. - */ - if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, - ni->ni_macaddr, "shared key auth", - "%s", " PRIVACY is disabled"); - estatus = IEEE80211_STATUS_ALG; - goto bad; - } - /* - * Pre-shared key authentication is evil; accept - * it only if explicitly configured (it is supported - * mainly for compatibility with clients like OS X). - */ - if (ni->ni_authmode != IEEE80211_AUTH_AUTO && - ni->ni_authmode != IEEE80211_AUTH_SHARED) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, - ni->ni_macaddr, "shared key auth", - "bad sta auth mode %u", ni->ni_authmode); - ic->ic_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */ - estatus = IEEE80211_STATUS_ALG; - goto bad; - } - - challenge = NULL; - if (frm + 1 < efrm) { - if ((frm[1] + 2) > (efrm - frm)) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, - ni->ni_macaddr, "shared key auth", - "ie %d/%d too long", - frm[0], (frm[1] + 2) - (efrm - frm)); - ic->ic_stats.is_rx_bad_auth++; - estatus = IEEE80211_STATUS_CHALLENGE; - goto bad; - } - if (*frm == IEEE80211_ELEMID_CHALLENGE) - challenge = frm; - frm += frm[1] + 2; - } - switch (seq) { - case IEEE80211_AUTH_SHARED_CHALLENGE: - case IEEE80211_AUTH_SHARED_RESPONSE: - if (challenge == NULL) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, - ni->ni_macaddr, "shared key auth", - "%s", "no challenge"); - ic->ic_stats.is_rx_bad_auth++; - estatus = IEEE80211_STATUS_CHALLENGE; - goto bad; - } - if (challenge[1] != IEEE80211_CHALLENGE_LEN) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, - ni->ni_macaddr, "shared key auth", - "bad challenge len %d", challenge[1]); - ic->ic_stats.is_rx_bad_auth++; - estatus = IEEE80211_STATUS_CHALLENGE; - goto bad; - } - default: - break; - } - switch (ic->ic_opmode) { - 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); - return; - case IEEE80211_M_HOSTAP: - if (ic->ic_state != IEEE80211_S_RUN) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, - ni->ni_macaddr, "shared key auth", - "bad state %u", ic->ic_state); - estatus = IEEE80211_STATUS_ALG; /* XXX */ - goto bad; - } - switch (seq) { - case IEEE80211_AUTH_SHARED_REQUEST: - if (ni == ic->ic_bss) { - ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2); - if (ni == NULL) { - /* NB: no way to return an error */ - return; - } - allocbs = 1; - } else { - if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) - (void) ieee80211_ref_node(ni); - allocbs = 0; - } - /* - * Mark the node as referenced to reflect that it's - * reference count has been bumped to insure it remains - * after the transaction completes. - */ - 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 */ - return; - } - get_random_bytes(ni->ni_challenge, - IEEE80211_CHALLENGE_LEN); - IEEE80211_DPRINTF(ic, - IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, - "[%s] shared key %sauth request\n", - ether_sprintf(ni->ni_macaddr), - allocbs ? "" : "re"); - break; - case IEEE80211_AUTH_SHARED_RESPONSE: - if (ni == ic->ic_bss) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, - ni->ni_macaddr, "shared key response", - "%s", "unknown station"); - /* NB: don't send a response */ - return; - } - if (ni->ni_challenge == NULL) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, - ni->ni_macaddr, "shared key response", - "%s", "no challenge recorded"); - ic->ic_stats.is_rx_bad_auth++; - estatus = IEEE80211_STATUS_CHALLENGE; - goto bad; - } - if (memcmp(ni->ni_challenge, &challenge[2], - challenge[1]) != 0) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, - ni->ni_macaddr, "shared key response", - "%s", "challenge mismatch"); - ic->ic_stats.is_rx_auth_fail++; - estatus = IEEE80211_STATUS_CHALLENGE; - goto bad; - } - IEEE80211_DPRINTF(ic, - IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, - "[%s] station authenticated (shared key)\n", - ether_sprintf(ni->ni_macaddr)); - ieee80211_node_authorize(ni); - break; - default: - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, - ni->ni_macaddr, "shared key auth", - "bad seq %d", seq); - ic->ic_stats.is_rx_bad_auth++; - estatus = IEEE80211_STATUS_SEQUENCE; - goto bad; - } - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); - break; - - case IEEE80211_M_STA: - if (ic->ic_state != IEEE80211_S_AUTH) - return; - switch (seq) { - case IEEE80211_AUTH_SHARED_PASS: - if (ni->ni_challenge != NULL) { - FREE(ni->ni_challenge, M_80211_NODE); - ni->ni_challenge = NULL; - } - if (status != 0) { - IEEE80211_DPRINTF(ic, - IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, - "[%s] shared key auth failed (reason %d)\n", - ether_sprintf(ieee80211_getbssid(ic, wh)), - status); - /* XXX can this happen? */ - if (ni != ic->ic_bss) - ni->ni_fails++; - ic->ic_stats.is_rx_auth_fail++; - return; - } - ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0); - break; - case IEEE80211_AUTH_SHARED_CHALLENGE: - if (!alloc_challenge(ic, ni)) - return; - /* XXX could optimize by passing recvd challenge */ - memcpy(ni->ni_challenge, &challenge[2], challenge[1]); - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); - break; - default: - IEEE80211_DISCARD(ic, IEEE80211_MSG_AUTH, - wh, "shared key auth", "bad seq %d", seq); - ic->ic_stats.is_rx_bad_auth++; - return; - } - break; - } - return; -bad: - /* - * Send an error response; but only when operating as an AP. - */ - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - /* XXX hack to workaround calling convention */ - ieee80211_send_error(ic, ni, wh->i_addr2, - IEEE80211_FC0_SUBTYPE_AUTH, - (seq + 1) | (estatus<<16)); - } else if (ic->ic_opmode == IEEE80211_M_STA) { - /* - * Kick the state machine. This short-circuits - * using the mgt frame timeout to trigger the - * state transition. - */ - if (ic->ic_state == IEEE80211_S_AUTH) - ieee80211_new_state(ic, IEEE80211_S_SCAN, - IEEE80211_SCAN_FAIL_STATUS); - } -} - -/* Verify the existence and length of __elem or get out. */ -#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \ - if ((__elem) == NULL) { \ - IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ - wh, ieee80211_mgt_subtype_name[subtype >> \ - IEEE80211_FC0_SUBTYPE_SHIFT], \ - "%s", "no " #__elem ); \ - ic->ic_stats.is_rx_elem_missing++; \ - return; \ - } \ - if ((__elem)[1] > (__maxlen)) { \ - IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ - wh, ieee80211_mgt_subtype_name[subtype >> \ - IEEE80211_FC0_SUBTYPE_SHIFT], \ - "bad " #__elem " len %d", (__elem)[1]); \ - ic->ic_stats.is_rx_elem_toobig++; \ - return; \ - } \ -} while (0) - -#define IEEE80211_VERIFY_LENGTH(_len, _minlen, _action) do { \ - if ((_len) < (_minlen)) { \ - IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ - wh, ieee80211_mgt_subtype_name[subtype >> \ - IEEE80211_FC0_SUBTYPE_SHIFT], \ - "ie too short, got %d, expected %d", \ - (_len), (_minlen)); \ - ic->ic_stats.is_rx_elem_toosmall++; \ - _action; \ - } \ -} while (0) - -#ifdef IEEE80211_DEBUG -static void -ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag, - uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid) -{ - printf("[%s] discard %s frame, ssid mismatch: ", - ether_sprintf(mac), tag); - ieee80211_print_essid(ssid + 2, ssid[1]); - printf("\n"); -} - -#define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \ - if ((_ssid)[1] != 0 && \ - ((_ssid)[1] != (_ni)->ni_esslen || \ - memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \ - if (ieee80211_msg_input(ic)) \ - ieee80211_ssid_mismatch(ic, \ - ieee80211_mgt_subtype_name[subtype >> \ - IEEE80211_FC0_SUBTYPE_SHIFT], \ - wh->i_addr2, _ssid); \ - ic->ic_stats.is_rx_ssidmismatch++; \ - return; \ - } \ -} while (0) -#else /* !IEEE80211_DEBUG */ -#define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \ - if ((_ssid)[1] != 0 && \ - ((_ssid)[1] != (_ni)->ni_esslen || \ - memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \ - ic->ic_stats.is_rx_ssidmismatch++; \ - return; \ - } \ -} while (0) -#endif /* !IEEE80211_DEBUG */ - -/* unalligned little endian access */ -#define LE_READ_2(p) \ - ((uint16_t) \ - ((((const uint8_t *)(p))[0] ) | \ - (((const uint8_t *)(p))[1] << 8))) -#define LE_READ_4(p) \ - ((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 uint8_t *frm) -{ - return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); -} - -static __inline int -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 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 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 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(uint8_t *sel, uint8_t *keylen) -{ -#define WPA_SEL(x) (((x)<<24)|WPA_OUI) - uint32_t w = LE_READ_4(sel); - - switch (w) { - case WPA_SEL(WPA_CSE_NULL): - return IEEE80211_CIPHER_NONE; - case WPA_SEL(WPA_CSE_WEP40): - if (keylen) - *keylen = 40 / NBBY; - return IEEE80211_CIPHER_WEP; - case WPA_SEL(WPA_CSE_WEP104): - if (keylen) - *keylen = 104 / NBBY; - return IEEE80211_CIPHER_WEP; - case WPA_SEL(WPA_CSE_TKIP): - return IEEE80211_CIPHER_TKIP; - case WPA_SEL(WPA_CSE_CCMP): - return IEEE80211_CIPHER_AES_CCM; - } - return 32; /* NB: so 1<< is discarded */ -#undef WPA_SEL -} - -/* - * Convert a WPA key management/authentication algorithm - * to an internal code. - */ -static int -wpa_keymgmt(uint8_t *sel) -{ -#define WPA_SEL(x) (((x)<<24)|WPA_OUI) - uint32_t w = LE_READ_4(sel); - - switch (w) { - case WPA_SEL(WPA_ASE_8021X_UNSPEC): - return WPA_ASE_8021X_UNSPEC; - case WPA_SEL(WPA_ASE_8021X_PSK): - return WPA_ASE_8021X_PSK; - case WPA_SEL(WPA_ASE_NONE): - return WPA_ASE_NONE; - } - return 0; /* NB: so is discarded */ -#undef WPA_SEL -} - -/* - * Parse a WPA information element to collect parameters - * and validate the parameters against what has been - * configured for the system. - */ -static int -ieee80211_parse_wpa(struct ieee80211com *ic, uint8_t *frm, - struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) -{ - uint8_t len = frm[1]; - uint32_t w; - int n; - - /* - * Check the length once for fixed parts: OUI, type, - * version, mcast cipher, and 2 selector counts. - * Other, variable-length data, must be checked separately. - */ - if ((ic->ic_flags & IEEE80211_F_WPA1) == 0) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "WPA", "not WPA, flags 0x%x", ic->ic_flags); - return IEEE80211_REASON_IE_INVALID; - } - if (len < 14) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "WPA", "too short, len %u", len); - return IEEE80211_REASON_IE_INVALID; - } - frm += 6, len -= 4; /* NB: len is payload only */ - /* NB: iswapoui already validated the OUI and type */ - w = LE_READ_2(frm); - if (w != WPA_VERSION) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "WPA", "bad version %u", w); - return IEEE80211_REASON_IE_INVALID; - } - frm += 2, len -= 2; - - /* multicast/group cipher */ - w = wpa_cipher(frm, &rsn->rsn_mcastkeylen); - if (w != rsn->rsn_mcastcipher) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "WPA", "mcast cipher mismatch; got %u, expected %u", - w, rsn->rsn_mcastcipher); - return IEEE80211_REASON_IE_INVALID; - } - frm += 4, len -= 4; - - /* unicast ciphers */ - n = LE_READ_2(frm); - frm += 2, len -= 2; - if (len < n*4+2) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "WPA", "ucast cipher data too short; len %u, n %u", - len, n); - return IEEE80211_REASON_IE_INVALID; - } - w = 0; - for (; n > 0; n--) { - w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen); - frm += 4, len -= 4; - } - w &= rsn->rsn_ucastcipherset; - if (w == 0) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "WPA", "%s", "ucast cipher set empty"); - return IEEE80211_REASON_IE_INVALID; - } - if (w & (1<<IEEE80211_CIPHER_TKIP)) - rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP; - else - rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; - - /* key management algorithms */ - n = LE_READ_2(frm); - frm += 2, len -= 2; - if (len < n*4) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "WPA", "key mgmt alg data too short; len %u, n %u", - len, n); - return IEEE80211_REASON_IE_INVALID; - } - w = 0; - for (; n > 0; n--) { - w |= wpa_keymgmt(frm); - frm += 4, len -= 4; - } - w &= rsn->rsn_keymgmtset; - if (w == 0) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "WPA", "%s", "no acceptable key mgmt alg"); - return IEEE80211_REASON_IE_INVALID; - } - if (w & WPA_ASE_8021X_UNSPEC) - rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC; - else - rsn->rsn_keymgmt = WPA_ASE_8021X_PSK; - - if (len > 2) /* optional capabilities */ - rsn->rsn_caps = LE_READ_2(frm); - - return 0; -} - -/* - * Convert an RSN cipher selector OUI to an internal - * cipher algorithm. Where appropriate we also - * record any key length. - */ -static int -rsn_cipher(uint8_t *sel, uint8_t *keylen) -{ -#define RSN_SEL(x) (((x)<<24)|RSN_OUI) - uint32_t w = LE_READ_4(sel); - - switch (w) { - case RSN_SEL(RSN_CSE_NULL): - return IEEE80211_CIPHER_NONE; - case RSN_SEL(RSN_CSE_WEP40): - if (keylen) - *keylen = 40 / NBBY; - return IEEE80211_CIPHER_WEP; - case RSN_SEL(RSN_CSE_WEP104): - if (keylen) - *keylen = 104 / NBBY; - return IEEE80211_CIPHER_WEP; - case RSN_SEL(RSN_CSE_TKIP): - return IEEE80211_CIPHER_TKIP; - case RSN_SEL(RSN_CSE_CCMP): - return IEEE80211_CIPHER_AES_CCM; - case RSN_SEL(RSN_CSE_WRAP): - return IEEE80211_CIPHER_AES_OCB; - } - return 32; /* NB: so 1<< is discarded */ -#undef WPA_SEL -} - -/* - * Convert an RSN key management/authentication algorithm - * to an internal code. - */ -static int -rsn_keymgmt(uint8_t *sel) -{ -#define RSN_SEL(x) (((x)<<24)|RSN_OUI) - uint32_t w = LE_READ_4(sel); - - switch (w) { - case RSN_SEL(RSN_ASE_8021X_UNSPEC): - return RSN_ASE_8021X_UNSPEC; - case RSN_SEL(RSN_ASE_8021X_PSK): - return RSN_ASE_8021X_PSK; - case RSN_SEL(RSN_ASE_NONE): - return RSN_ASE_NONE; - } - return 0; /* NB: so is discarded */ -#undef RSN_SEL -} - -/* - * Parse a WPA/RSN information element to collect parameters - * and validate the parameters against what has been - * configured for the system. - */ -static int -ieee80211_parse_rsn(struct ieee80211com *ic, uint8_t *frm, - struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) -{ - uint8_t len = frm[1]; - uint32_t w; - int n; - - /* - * Check the length once for fixed parts: - * version, mcast cipher, and 2 selector counts. - * Other, variable-length data, must be checked separately. - */ - if ((ic->ic_flags & IEEE80211_F_WPA2) == 0) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "WPA", "not RSN, flags 0x%x", ic->ic_flags); - return IEEE80211_REASON_IE_INVALID; - } - if (len < 10) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "RSN", "too short, len %u", len); - return IEEE80211_REASON_IE_INVALID; - } - frm += 2; - w = LE_READ_2(frm); - if (w != RSN_VERSION) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "RSN", "bad version %u", w); - return IEEE80211_REASON_IE_INVALID; - } - frm += 2, len -= 2; - - /* multicast/group cipher */ - w = rsn_cipher(frm, &rsn->rsn_mcastkeylen); - if (w != rsn->rsn_mcastcipher) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "RSN", "mcast cipher mismatch; got %u, expected %u", - w, rsn->rsn_mcastcipher); - return IEEE80211_REASON_IE_INVALID; - } - frm += 4, len -= 4; - - /* unicast ciphers */ - n = LE_READ_2(frm); - frm += 2, len -= 2; - if (len < n*4+2) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "RSN", "ucast cipher data too short; len %u, n %u", - len, n); - return IEEE80211_REASON_IE_INVALID; - } - w = 0; - for (; n > 0; n--) { - w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen); - frm += 4, len -= 4; - } - w &= rsn->rsn_ucastcipherset; - if (w == 0) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "RSN", "%s", "ucast cipher set empty"); - return IEEE80211_REASON_IE_INVALID; - } - if (w & (1<<IEEE80211_CIPHER_TKIP)) - rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP; - else - rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; - - /* key management algorithms */ - n = LE_READ_2(frm); - frm += 2, len -= 2; - if (len < n*4) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "RSN", "key mgmt alg data too short; len %u, n %u", - len, n); - return IEEE80211_REASON_IE_INVALID; - } - w = 0; - for (; n > 0; n--) { - w |= rsn_keymgmt(frm); - frm += 4, len -= 4; - } - w &= rsn->rsn_keymgmtset; - if (w == 0) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, - wh, "RSN", "%s", "no acceptable key mgmt alg"); - return IEEE80211_REASON_IE_INVALID; - } - if (w & RSN_ASE_8021X_UNSPEC) - rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC; - else - rsn->rsn_keymgmt = RSN_ASE_8021X_PSK; - - /* optional RSN capabilities */ - if (len > 2) - rsn->rsn_caps = LE_READ_2(frm); - /* XXXPMKID */ - - return 0; -} - -static int -ieee80211_parse_wmeparams(struct ieee80211com *ic, uint8_t *frm, - const struct ieee80211_frame *wh) -{ -#define MS(_v, _f) (((_v) & _f) >> _f##_S) - struct ieee80211_wme_state *wme = &ic->ic_wme; - u_int len = frm[1], qosinfo; - int i; - - if (len < sizeof(struct ieee80211_wme_param)-2) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME, - wh, "WME", "too short, len %u", len); - return -1; - } - qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)]; - qosinfo &= WME_QOSINFO_COUNT; - /* XXX do proper check for wraparound */ - if (qosinfo == wme->wme_wmeChanParams.cap_info) - return 0; - frm += __offsetof(struct ieee80211_wme_param, params_acParams); - for (i = 0; i < WME_NUM_AC; i++) { - struct wmeParams *wmep = - &wme->wme_wmeChanParams.cap_wmeParams[i]; - /* NB: ACI not used */ - wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM); - wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN); - wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN); - wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX); - wmep->wmep_txopLimit = LE_READ_2(frm+2); - frm += 4; - } - wme->wme_wmeChanParams.cap_info = qosinfo; - return 1; -#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_saveath(struct ieee80211_node *ni, uint8_t *ie) +ieee80211_parse_ath(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; - /* - * Record information element for later use. - */ - if (*iep == NULL || (*iep)[1] != ie[1]) { - if (*iep != NULL) - FREE(*iep, M_80211_NODE); - MALLOC(*iep, void*, ielen, M_80211_NODE, M_NOWAIT); - } - if (*iep != NULL) - memcpy(*iep, ie, ielen); - /* XXX note failure */ } -/* XXX find a better place for definition */ -struct l2_update_frame { - struct ether_header eh; - uint8_t dsap; - uint8_t ssap; - uint8_t control; - uint8_t xid[3]; -} __packed; - /* - * Deliver a TGf L2UF frame on behalf of a station. - * This primes any bridge when the station is roaming - * between ap's on the same wired network. + * Parse a Beacon or ProbeResponse frame and return the + * useful information in an ieee80211_scanparams structure. + * Status is set to 0 if no problems were found; otherwise + * a bitmask of IEEE80211_BPARSE_* items is returned that + * describes the problems detected. */ -static void -ieee80211_deliver_l2uf(struct ieee80211_node *ni) -{ - struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = ic->ic_ifp; - struct mbuf *m; - struct l2_update_frame *l2uf; - struct ether_header *eh; - - m = m_gethdr(M_NOWAIT, MT_DATA); - if (m == NULL) { - IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni, - "%s", "no mbuf for l2uf frame"); - ic->ic_stats.is_rx_nobuf++; /* XXX not right */ - return; - } - l2uf = mtod(m, struct l2_update_frame *); - eh = &l2uf->eh; - /* dst: Broadcast address */ - IEEE80211_ADDR_COPY(eh->ether_dhost, ifp->if_broadcastaddr); - /* src: associated STA */ - IEEE80211_ADDR_COPY(eh->ether_shost, ni->ni_macaddr); - eh->ether_type = htons(sizeof(*l2uf) - sizeof(*eh)); - - l2uf->dsap = 0; - l2uf->ssap = 0; - l2uf->control = 0xf5; - l2uf->xid[0] = 0x81; - l2uf->xid[1] = 0x80; - l2uf->xid[2] = 0x00; - - m->m_pkthdr.len = m->m_len = sizeof(*l2uf); - 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) -{ - struct ieee80211com *ic = ni->ni_ic; - - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "[%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); - ieee80211_node_leave(ic, ni); - ic->ic_stats.is_rx_assoc_norate++; -} - -static void -capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, - int reassoc, int resp, const char *tag, int capinfo) -{ - struct ieee80211com *ic = ni->ni_ic; - - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "[%s] deny %s request, %s mismatch 0x%x\n", - ether_sprintf(wh->i_addr2), - reassoc ? "reassoc" : "assoc", tag, capinfo); - IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO); - ieee80211_node_leave(ic, ni); - ic->ic_stats.is_rx_assoc_capmismatch++; -} - -static void -htcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, - int reassoc, int resp) +int +ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m, + struct ieee80211_scanparams *scan) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; - - IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_ANY, wh->i_addr2, - "deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc"); - /* XXX no better code */ - IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_OTHER); - ieee80211_node_leave(ic, ni); -} - -void -ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, - struct ieee80211_node *ni, - 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; uint8_t *frm, *efrm; - uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap, *htinfo; - int reassoc, resp, allocbs; - uint8_t rate; - wh = mtod(m0, struct ieee80211_frame *); + wh = mtod(m, struct ieee80211_frame *); 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: { - struct ieee80211_scanparams scan; - - /* - * We process beacon/probe response frames: - * o when scanning, or - * o station mode when associated (to collect state - * updates such as 802.11g slot time), or - * o adhoc mode (to discover neighbors) - * Frames otherwise received are discarded. - */ - if (!((ic->ic_flags & IEEE80211_F_SCAN) || - (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd) || - ic->ic_opmode == IEEE80211_M_IBSS)) { - ic->ic_stats.is_rx_mgtdiscard++; - return; - } - /* - * beacon/probe response frame format - * [8] time stamp - * [2] beacon interval - * [2] capability information - * [tlv] ssid - * [tlv] supported rates - * [tlv] country information - * [tlv] parameter set (FH/DS) - * [tlv] erp information - * [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, return); - memset(&scan, 0, sizeof(scan)); - scan.tstamp = frm; frm += 8; - scan.bintval = le16toh(*(uint16_t *)frm); frm += 2; - scan.capinfo = le16toh(*(uint16_t *)frm); frm += 2; - scan.bchan = IEEE80211_CHAN2IEEE(ic->ic_curchan); - scan.curchan = ic->ic_curchan; - - while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); - switch (*frm) { - case IEEE80211_ELEMID_SSID: - scan.ssid = frm; - break; - case IEEE80211_ELEMID_RATES: - scan.rates = frm; - break; - case IEEE80211_ELEMID_COUNTRY: - scan.country = frm; - break; - case IEEE80211_ELEMID_FHPARMS: - if (ic->ic_phytype == IEEE80211_T_FH) { - scan.fhdwell = LE_READ_2(&frm[2]); - scan.bchan = IEEE80211_FH_CHAN(frm[4], frm[5]); - scan.fhindex = frm[6]; - } - break; - case IEEE80211_ELEMID_DSPARMS: - /* - * XXX hack this since depending on phytype - * is problematic for multi-mode devices. - */ - if (ic->ic_phytype != IEEE80211_T_FH) - scan.bchan = frm[2]; - break; - case IEEE80211_ELEMID_TIM: - /* XXX ATIM? */ - scan.tim = frm; - scan.timoff = frm - mtod(m0, uint8_t *); - break; - case IEEE80211_ELEMID_IBSSPARMS: - break; - case IEEE80211_ELEMID_XRATES: - scan.xrates = frm; - break; - case IEEE80211_ELEMID_ERP: - if (frm[1] != 1) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID, wh, "ERP", - "bad len %u", frm[1]); - ic->ic_stats.is_rx_elem_toobig++; - break; - } - scan.erp = frm[2]; - break; - case IEEE80211_ELEMID_HTCAP: - scan.htcap = frm; - break; - case IEEE80211_ELEMID_RSN: - 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; - 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, - wh, "unhandled", - "id %u, len %u", *frm, frm[1]); - ic->ic_stats.is_rx_elem_unknown++; - break; - } - 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 IEEE80211_CHAN_MAX < 255 - 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 (IEEE80211_CHAN2IEEE(scan.curchan) != scan.bchan && - ic->ic_phytype != IEEE80211_T_FH) { - /* - * Frame was received on a channel different from the - * one indicated in the DS params element id; - * silently discard it. - * - * NB: this can happen due to signal leakage. - * But we should take it for FH phy because - * the rssi value should be correct even for - * different hop pattern in FH. - */ - IEEE80211_DISCARD(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, - wh, ieee80211_mgt_subtype_name[subtype >> - IEEE80211_FC0_SUBTYPE_SHIFT], - "for off-channel %u", - IEEE80211_CHAN2IEEE(scan.curchan)); - ic->ic_stats.is_rx_chanmismatch++; - return; - } - if (!(IEEE80211_BINTVAL_MIN <= scan.bintval && - scan.bintval <= IEEE80211_BINTVAL_MAX)) { - IEEE80211_DISCARD(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, - wh, ieee80211_mgt_subtype_name[subtype >> - IEEE80211_FC0_SUBTYPE_SHIFT], - "bogus beacon interval", scan.bintval); - 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. - */ - if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { - ic->ic_stats.is_rx_beacon++; /* XXX remove */ - IEEE80211_NODE_STAT(ni, rx_beacons); - } else - IEEE80211_NODE_STAT(ni, rx_proberesp); - - /* - * When operating in station mode, check for state updates. - * Be careful to ignore beacons received while doing a - * background scan. We consider only 11g/WMM stuff right now. - */ - if (ic->ic_opmode == IEEE80211_M_STA && - ni->ni_associd != 0 && - ((ic->ic_flags & IEEE80211_F_SCAN) == 0 || - IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) { - /* record tsf of last beacon */ - memcpy(ni->ni_tstamp.data, scan.tstamp, - sizeof(ni->ni_tstamp)); - /* count beacon frame for s/w bmiss handling */ - ic->ic_swbmiss_count++; - ic->ic_bmiss_count = 0; - if (ni->ni_erp != scan.erp) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "[%s] erp change: was 0x%x, now 0x%x\n", - ether_sprintf(wh->i_addr2), - ni->ni_erp, scan.erp); - 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; - ni->ni_erp = scan.erp; - /* XXX statistic */ - } - if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "[%s] capabilities change: before 0x%x," - " now 0x%x\n", - ether_sprintf(wh->i_addr2), - ni->ni_capinfo, scan.capinfo); - /* - * NB: we assume short preamble doesn't - * change dynamically - */ - ieee80211_set_shortslottime(ic, - IEEE80211_IS_CHAN_A(ic->ic_bsschan) || - (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); - 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 (ni->ni_chan != ic->ic_bsschan) { - /* - * Channel has been adjusted based on - * negotiated HT parameters; force the - * channel state to follow. - */ - ieee80211_setbsschan(ic, ni->ni_chan); - } - } - if (scan.tim != NULL) { - struct ieee80211_tim_ie *tim = - (struct ieee80211_tim_ie *) scan.tim; -#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; + efrm = mtod(m, uint8_t *) + m->m_len; + scan->status = 0; + /* + * beacon/probe response frame format + * [8] time stamp + * [2] beacon interval + * [2] capability information + * [tlv] ssid + * [tlv] supported rates + * [tlv] country information + * [tlv] parameter set (FH/DS) + * [tlv] erp information + * [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, + return (scan->status = IEEE80211_BPARSE_BADIELEN)); + memset(scan, 0, sizeof(*scan)); + scan->tstamp = frm; frm += 8; + 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; + scan->ies = frm; + scan->ies_len = efrm - frm; + + while (efrm - frm > 1) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, + return (scan->status = IEEE80211_BPARSE_BADIELEN)); + switch (*frm) { + case IEEE80211_ELEMID_SSID: + scan->ssid = frm; + break; + case IEEE80211_ELEMID_RATES: + scan->rates = frm; + break; + case IEEE80211_ELEMID_COUNTRY: + scan->country = frm; + break; + case IEEE80211_ELEMID_FHPARMS: + if (ic->ic_phytype == IEEE80211_T_FH) { + scan->fhdwell = LE_READ_2(&frm[2]); + scan->chan = IEEE80211_FH_CHAN(frm[4], frm[5]); + scan->fhindex = frm[6]; } + break; + case IEEE80211_ELEMID_DSPARMS: /* - * 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. + * XXX hack this since depending on phytype + * is problematic for multi-mode devices. */ - if (ic->ic_flags & IEEE80211_F_SCAN) { - ieee80211_add_scan(ic, &scan, wh, - 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; - } - /* - * If scanning, just pass information to the scan module. - */ - if (ic->ic_flags & IEEE80211_F_SCAN) { - if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) { - /* - * Actively scanning a channel marked passive; - * send a probe request now that we know there - * is 802.11 traffic present. - * - * XXX check if the beacon we recv'd gives - * us what we need and suppress the probe req - */ - ieee80211_probe_curchan(ic, 1); - ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; - } - ieee80211_add_scan(ic, &scan, wh, - subtype, rssi, noise, rstamp); - return; - } - if (scan.capinfo & IEEE80211_CAPINFO_IBSS) { - if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { - /* - * Create a new entry in the neighbor table. - */ - ni = ieee80211_add_neighbor(ic, wh, &scan); - } else if (ni->ni_capinfo == 0) { - /* - * Update faked node created on transmit. - * Note this also updates the tsf. - */ - ieee80211_init_neighbor(ni, wh, &scan); - } else { - /* - * Record tsf for potential resync. - */ - memcpy(ni->ni_tstamp.data, scan.tstamp, - sizeof(ni->ni_tstamp)); - } - if (ni != NULL) { - ni->ni_rssi = rssi; - ni->ni_noise = noise; - ni->ni_rstamp = rstamp; - } - } - break; - } - - case IEEE80211_FC0_SUBTYPE_PROBE_REQ: - if (ic->ic_opmode == IEEE80211_M_STA || - ic->ic_state != IEEE80211_S_RUN) { - ic->ic_stats.is_rx_mgtdiscard++; - return; - } - if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { - /* frame must be directed */ - ic->ic_stats.is_rx_mgtdiscard++; /* XXX stat */ - return; - } - - /* - * prreq frame format - * [tlv] ssid - * [tlv] supported rates - * [tlv] extended supported rates - * [tlv] Atheros capabilities - */ - ssid = rates = xrates = ath = NULL; - while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); - switch (*frm) { - case IEEE80211_ELEMID_SSID: - ssid = frm; - break; - case IEEE80211_ELEMID_RATES: - rates = frm; - break; - case IEEE80211_ELEMID_XRATES: - xrates = frm; - break; - case IEEE80211_ELEMID_VENDOR: - if (isatherosoui(frm)) - ath = frm; + if (ic->ic_phytype != IEEE80211_T_FH) + scan->chan = frm[2]; + break; + case IEEE80211_ELEMID_TIM: + /* XXX ATIM? */ + scan->tim = frm; + scan->timoff = frm - mtod(m, uint8_t *); + break; + case IEEE80211_ELEMID_IBSSPARMS: + case IEEE80211_ELEMID_CFPARMS: + /* NB: avoid debugging complaints */ + break; + case IEEE80211_ELEMID_XRATES: + scan->xrates = frm; + break; + case IEEE80211_ELEMID_ERP: + if (frm[1] != 1) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID, wh, "ERP", + "bad len %u", frm[1]); + vap->iv_stats.is_rx_elem_toobig++; 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) { - IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, ieee80211_mgt_subtype_name[subtype >> - IEEE80211_FC0_SUBTYPE_SHIFT], - "%s", "no ssid with ssid suppression enabled"); - ic->ic_stats.is_rx_ssidmismatch++; /*XXX*/ - return; - } - - allocbs = 0; - if (ni == ic->ic_bss) { - if (ic->ic_opmode != IEEE80211_M_IBSS) { - ni = ieee80211_tmp_node(ic, wh->i_addr2); - allocbs = 1; - } else if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { + scan->erp = frm[2] | 0x100; + break; + case IEEE80211_ELEMID_HTCAP: + scan->htcap = frm; + break; + case IEEE80211_ELEMID_RSN: + 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; + else if (isatherosoui(frm)) + scan->ath = frm; + else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) { /* - * XXX Cannot tell if the sender is operating - * in ibss mode. But we need a new node to - * send the response so blindly add them to the - * neighbor table. + * Accept pre-draft HT ie's if the + * standard ones have not been seen. */ - ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta, - wh->i_addr2); - } - if (ni == NULL) - return; - } - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "[%s] recv probe req\n", ether_sprintf(wh->i_addr2)); - ni->ni_rssi = rssi; - ni->ni_rstamp = rstamp; - rate = ieee80211_setup_rates(ni, rates, xrates, - IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE - | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (rate & IEEE80211_RATE_BASIC) { - IEEE80211_DISCARD(ic, IEEE80211_MSG_XRATE, - wh, ieee80211_mgt_subtype_name[subtype >> - IEEE80211_FC0_SUBTYPE_SHIFT], - "%s", "recv'd rate set invalid"); - } else { - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0); - } - if (allocbs) { - /* - * Temporary node created just to send a - * response, reclaim immediately. - */ - ieee80211_free_node(ni); - } else if (ath != NULL) - ieee80211_saveath(ni, ath); - break; - - case IEEE80211_FC0_SUBTYPE_AUTH: { - uint16_t algo, seq, status; - /* - * auth frame format - * [2] algorithm - * [2] sequence - * [2] status - * [tlv*] challenge - */ - 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); - /* - * Consult the ACL policy module if setup. - */ - if (ic->ic_acl != NULL && - !ic->ic_acl->iac_check(ic, wh->i_addr2)) { - IEEE80211_DISCARD(ic, IEEE80211_MSG_ACL, - wh, "auth", "%s", "disallowed by ACL"); - ic->ic_stats.is_rx_acl++; - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, - (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16)); - } - return; - } - if (ic->ic_flags & IEEE80211_F_COUNTERM) { - IEEE80211_DISCARD(ic, - IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO, - wh, "auth", "%s", "TKIP countermeasures enabled"); - ic->ic_stats.is_rx_auth_countermeasures++; - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, - IEEE80211_REASON_MIC_FAILURE); - } - return; - } - if (algo == IEEE80211_AUTH_ALG_SHARED) - ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi, - noise, rstamp, seq, status); - else if (algo == IEEE80211_AUTH_ALG_OPEN) - ieee80211_auth_open(ic, wh, ni, rssi, noise, rstamp, - seq, status); - else { - IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, - wh, "auth", "unsupported alg %d", algo); - ic->ic_stats.is_rx_auth_unsupported++; - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - /* XXX not right */ - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, - (seq+1) | (IEEE80211_STATUS_ALG<<16)); - } - return; - } - break; - } - - case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: - case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { - 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) { - ic->ic_stats.is_rx_mgtdiscard++; - return; - } - - if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { - reassoc = 1; - resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP; - } else { - reassoc = 0; - resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP; - } - /* - * asreq frame format - * [2] capability information - * [2] listen interval - * [6*] current AP address (reassoc only) - * [tlv] ssid - * [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), 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 >> - IEEE80211_FC0_SUBTYPE_SHIFT], - "%s", "wrong bssid"); - ic->ic_stats.is_rx_assoc_bss++; - return; - } - 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 = rsn = wme = ath = htcap = NULL; - while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); - switch (*frm) { - case IEEE80211_ELEMID_SSID: - ssid = frm; - break; - case IEEE80211_ELEMID_RATES: - rates = frm; - break; - case IEEE80211_ELEMID_XRATES: - xrates = frm; - break; - /* XXX verify only one of RSN and WPA ie's? */ - case IEEE80211_ELEMID_RSN: - 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; - else if (isatherosoui(frm)) - ath = frm; - else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) { - if (ishtcapoui(frm) && htcap == NULL) - htcap = frm; + if (ishtcapoui(frm)) { + if (scan->htcap == NULL) + scan->htcap = frm; + } else if (ishtinfooui(frm)) { + if (scan->htinfo == NULL) + scan->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, - "[%s] deny %s request, sta not authenticated\n", - ether_sprintf(wh->i_addr2), - reassoc ? "reassoc" : "assoc"); - ieee80211_send_error(ic, ni, wh->i_addr2, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_ASSOC_NOT_AUTHED); - ic->ic_stats.is_rx_assoc_notauth++; - return; - } - /* 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; + default: + IEEE80211_DISCARD_IE(vap, IEEE80211_MSG_ELEMID, + wh, "unhandled", + "id %u, len %u", *frm, frm[1]); + vap->iv_stats.is_rx_elem_unknown++; break; } - if (badwparsn) { - IEEE80211_DPRINTF(ic, - IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, - "[%s] no WPA/RSN IE in association request\n", - ether_sprintf(wh->i_addr2)); - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_IE_INVALID); - ieee80211_node_leave(ic, ni); - ic->ic_stats.is_rx_assoc_badwpaie++; - return; - } - if (wpa != NULL || rsn != NULL) { - /* - * 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. - */ - rsnparms = ni->ni_rsn; - if (wpa != NULL) - reason = ieee80211_parse_wpa(ic, wpa, &rsnparms, wh); - else - reason = ieee80211_parse_rsn(ic, rsn, &rsnparms, wh); - if (reason != 0) { - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, reason); - ieee80211_node_leave(ic, ni); - /* XXX distinguish WPA/RSN? */ - ic->ic_stats.is_rx_assoc_badwpaie++; - return; - } - IEEE80211_DPRINTF(ic, - 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 != 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_80211_NODE); - ni->ni_challenge = NULL; - } - /* NB: 802.11 spec says to ignore station's privacy bit */ - if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) { - capinfomismatch(ni, wh, reassoc, resp, - "capability", capinfo); - return; - } - /* - * Disallow re-associate w/ invalid slot time setting. - */ - if (ni->ni_associd != 0 && - IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && - ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) { - capinfomismatch(ni, wh, reassoc, resp, - "slot time", capinfo); - return; - } - rate = ieee80211_setup_rates(ni, rates, xrates, - IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | - IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (rate & IEEE80211_RATE_BASIC) { - ratesetmismatch(ni, wh, reassoc, resp, "basic", rate); - return; - } - /* - * If constrained to 11g-only stations reject an - * 11b-only station. We cheat a bit here by looking - * at the max negotiated xmit rate and assuming anyone - * with a best rate <24Mb/s is an 11b station. - */ - if ((ic->ic_flags & IEEE80211_F_PUREG) && rate < 48) { - 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); - /* - * Allow AMPDU operation only with unencrypted traffic - * or AES-CCM; the 11n spec only specifies these ciphers - * so permitting any others is undefined and can lead - * to interoperability problems. - * - * NB: We check for AES by looking at the GTK cipher - * since the WPA/11i specs say the PTK cipher has - * to be "as good or better". - */ - if ((ni->ni_flags & IEEE80211_NODE_HT) && - (((ic->ic_flags & IEEE80211_F_WPA) && - rsnparms.rsn_mcastcipher != IEEE80211_CIPHER_AES_CCM) || - (ic->ic_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) { - IEEE80211_NOTE(ic, - IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, - "disallow HT use because WEP or TKIP requested, " - "capinfo 0x%x mcastcipher %d", capinfo, - rsnparms.rsn_mcastcipher); - ieee80211_ht_node_cleanup(ni); - ic->ic_stats.is_ht_assoc_downgrade++; - } - /* - * If constrained to 11n-only stations reject legacy stations. - */ - if ((ic->ic_flags_ext & IEEE80211_FEXT_PUREN) && - (ni->ni_flags & IEEE80211_NODE_HT) == 0) { - htcapmismatch(ni, wh, reassoc, resp); - ic->ic_stats.is_ht_assoc_nohtcap++; - return; - } - 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_bsschan; - ni->ni_fhdwell = ic->ic_bss->ni_fhdwell; - ni->ni_fhindex = ic->ic_bss->ni_fhindex; - if (wpa != NULL) { - /* - * Record WPA 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_wpa_ie, wpa); - } else if (ni->ni_wpa_ie != NULL) { - /* - * Flush any state from a previous association. - */ - 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 - * as capable of QoS and record information - * element for applications that require it. - */ - ieee80211_saveie(&ni->ni_wme_ie, wme); - ni->ni_flags |= IEEE80211_NODE_QOS; - } else if (ni->ni_wme_ie != NULL) { - /* - * Flush any state from a previous association. - */ - FREE(ni->ni_wme_ie, M_80211_NODE); - ni->ni_wme_ie = NULL; - ni->ni_flags &= ~IEEE80211_NODE_QOS; - } - 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; + frm += frm[1] + 2; } - - case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: - case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { - uint16_t capinfo, associd; - uint16_t status; - - if (ic->ic_opmode != IEEE80211_M_STA || - ic->ic_state != IEEE80211_S_ASSOC) { - ic->ic_stats.is_rx_mgtdiscard++; - return; - } - - /* - * asresp frame format - * [2] capability information - * [2] status - * [2] association ID - * [tlv] supported rates - * [tlv] extended supported rates - * [tlv] WME - * [tlv] HT capabilities - * [tlv] HT info - */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); - ni = ic->ic_bss; - capinfo = le16toh(*(uint16_t *)frm); - frm += 2; - status = le16toh(*(uint16_t *)frm); - frm += 2; - if (status != 0) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "[%s] %sassoc failed (reason %d)\n", - ether_sprintf(wh->i_addr2), - ISREASSOC(subtype) ? "re" : "", status); - if (ni != ic->ic_bss) /* XXX never true? */ - ni->ni_fails++; - ic->ic_stats.is_rx_auth_fail++; /* XXX */ - return; - } - associd = le16toh(*(uint16_t *)frm); - frm += 2; - - rates = xrates = wme = htcap = htinfo = NULL; - while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); - switch (*frm) { - case IEEE80211_ELEMID_RATES: - rates = frm; - break; - case IEEE80211_ELEMID_XRATES: - xrates = frm; - break; - case IEEE80211_ELEMID_HTCAP: - htcap = frm; - break; - case IEEE80211_ELEMID_HTINFO: - htinfo = frm; - break; - case IEEE80211_ELEMID_VENDOR: - if (iswmeoui(frm)) - wme = 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 (htcap == NULL) - htcap = frm; - } else if (ishtinfooui(frm)) { - if (htinfo == NULL) - htcap = frm; - } - } - /* XXX Atheros OUI support */ - break; - } - frm += frm[1] + 2; - } - - 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 | - IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (rate & IEEE80211_RATE_BASIC) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "[%s] %sassoc failed (rate set mismatch)\n", - ether_sprintf(wh->i_addr2), - ISREASSOC(subtype) ? "re" : ""); - 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, - IEEE80211_SCAN_FAIL_STATUS); - return; - } - - ni->ni_capinfo = capinfo; - ni->ni_associd = associd; - if (wme != NULL && - ieee80211_parse_wmeparams(ic, wme, wh) >= 0) { - ni->ni_flags |= IEEE80211_NODE_QOS; - ieee80211_wme_updateparams(ic); - } else - ni->ni_flags &= ~IEEE80211_NODE_QOS; + IEEE80211_VERIFY_ELEMENT(scan->rates, IEEE80211_RATE_MAXSIZE, + scan->status |= IEEE80211_BPARSE_RATES_INVALID); + if (scan->rates != NULL && scan->xrates != NULL) { /* - * Setup HT state according to the negotiation. + * NB: don't process XRATES if RATES is missing. This + * avoids a potential null ptr deref and should be ok + * as the return code will already note RATES is missing + * (so callers shouldn't otherwise process the frame). */ - if ((ic->ic_htcaps & IEEE80211_HTC_HT) && - htcap != NULL && htinfo != NULL) { - ieee80211_ht_node_init(ni, htcap); - ieee80211_parse_htinfo(ni, htinfo); - ieee80211_setup_htrates(ni, - htcap, IEEE80211_F_JOIN | IEEE80211_F_DOBRS); - ieee80211_setup_basic_htrates(ni, htinfo); - if (ni->ni_chan != ic->ic_bsschan) { - /* - * Channel has been adjusted based on - * negotiated HT parameters; force the - * channel state to follow. - */ - ieee80211_setbsschan(ic, ni->ni_chan); - } - } - /* - * Configure state now that we are associated. - * - * XXX may need different/additional driver callbacks? - */ - 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; - } else { - ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; - ic->ic_flags |= IEEE80211_F_USEBARKER; - } - ieee80211_set_shortslottime(ic, - IEEE80211_IS_CHAN_A(ic->ic_curchan) || - (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); + IEEE80211_VERIFY_ELEMENT(scan->xrates, + IEEE80211_RATE_MAXSIZE - scan->rates[1], + scan->status |= IEEE80211_BPARSE_XRATES_INVALID); + } + IEEE80211_VERIFY_ELEMENT(scan->ssid, IEEE80211_NWID_LEN, + scan->status |= IEEE80211_BPARSE_SSID_INVALID); +#if IEEE80211_CHAN_MAX < 255 + if (scan->chan > IEEE80211_CHAN_MAX) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, + wh, NULL, "invalid channel %u", scan->chan); + vap->iv_stats.is_rx_badchan++; + scan->status |= IEEE80211_BPARSE_CHAN_INVALID; + } +#endif + if (scan->chan != scan->bchan && ic->ic_phytype != IEEE80211_T_FH) { /* - * Honor ERP protection. + * Frame was received on a channel different from the + * one indicated in the DS params element id; + * silently discard it. * - * NB: ni_erp should zero for non-11g operation. - */ - 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%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_HT ? - (ni->ni_chw == 20 ? ", HT20" : ", HT40") : "", - ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "", - 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; + * NB: this can happen due to signal leakage. + * But we should take it for FH phy because + * the rssi value should be correct even for + * different hop pattern in FH. + */ + IEEE80211_DISCARD(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, + wh, NULL, "for off-channel %u", scan->chan); + vap->iv_stats.is_rx_chanmismatch++; + scan->status |= IEEE80211_BPARSE_OFFCHAN; + } + if (!(IEEE80211_BINTVAL_MIN <= scan->bintval && + scan->bintval <= IEEE80211_BINTVAL_MAX)) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, + wh, NULL, "bogus beacon interval", scan->bintval); + vap->iv_stats.is_rx_badbintval++; + scan->status |= IEEE80211_BPARSE_BINTVAL_INVALID; + } + if (scan->country != NULL) { + /* + * Validate we have at least enough data to extract + * the country code. Not sure if we should return an + * error instead of discarding the IE; consider this + * being lenient as we don't depend on the data for + * correct operation. + */ + IEEE80211_VERIFY_LENGTH(scan->country[1], 3 * sizeof(uint8_t), + scan->country = NULL); } - - case IEEE80211_FC0_SUBTYPE_DEAUTH: { - uint16_t reason; - - if (ic->ic_state == IEEE80211_S_SCAN) { - ic->ic_stats.is_rx_mgtdiscard++; - return; - } - /* - * deauth frame format - * [2] reason - */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); - reason = le16toh(*(uint16_t *)frm); - ic->ic_stats.is_rx_deauth++; - IEEE80211_NODE_STAT(ni, rx_deauth); - - if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) { - /* NB: can happen when in promiscuous mode */ - ic->ic_stats.is_rx_mgtdiscard++; - break; - } - IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, - "[%s] recv deauthenticate (reason %d)\n", - ether_sprintf(ni->ni_macaddr), reason); - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - ieee80211_new_state(ic, IEEE80211_S_AUTH, - (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH); - break; - case IEEE80211_M_HOSTAP: - if (ni != ic->ic_bss) - ieee80211_node_leave(ic, ni); - break; - default: - ic->ic_stats.is_rx_mgtdiscard++; - break; - } - break; + /* + * 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); + } + return scan->status; +} - case IEEE80211_FC0_SUBTYPE_DISASSOC: { - uint16_t reason; - - 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; - } - /* - * disassoc frame format - * [2] reason - */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); - reason = le16toh(*(uint16_t *)frm); - ic->ic_stats.is_rx_disassoc++; - IEEE80211_NODE_STAT(ni, rx_disassoc); +/* + * Parse an Action frame. Return 0 on success, non-zero on failure. + */ +int +ieee80211_parse_action(struct ieee80211_node *ni, struct mbuf *m) +{ + struct ieee80211vap *vap = ni->ni_vap; + const struct ieee80211_action *ia; + struct ieee80211_frame *wh; + uint8_t *frm, *efrm; - if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) { - /* NB: can happen when in promiscuous mode */ - ic->ic_stats.is_rx_mgtdiscard++; - break; - } - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "[%s] recv disassociate (reason %d)\n", - ether_sprintf(ni->ni_macaddr), reason); - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0); + /* + * action frame format: + * [1] category + * [1] action + * [tlv] parameters + */ + wh = mtod(m, struct ieee80211_frame *); + frm = (u_int8_t *)&wh[1]; + efrm = mtod(m, u_int8_t *) + m->m_len; + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action), return EINVAL); + ia = (const struct ieee80211_action *) frm; + + vap->iv_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 EINVAL); break; - case IEEE80211_M_HOSTAP: - if (ni != ic->ic_bss) - ieee80211_node_leave(ic, ni); + case IEEE80211_ACTION_BA_ADDBA_RESPONSE: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ba_addbaresponse), + return EINVAL); break; - default: - ic->ic_stats.is_rx_mgtdiscard++; + case IEEE80211_ACTION_BA_DELBA: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ba_delba), + return EINVAL); break; } 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; - } + 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 EINVAL); 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; - } + case IEEE80211_ACTION_HT_MIMOPWRSAVE: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ht_mimopowersave), + return EINVAL); break; } - ic->ic_recv_action(ni, frm, efrm); break; } - - default: - IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, - wh, "mgt", "subtype 0x%x not handled", subtype); - ic->ic_stats.is_rx_badsubtype++; - break; - } -#undef ISREASSOC -#undef ISPROBE -} -#undef IEEE80211_VERIFY_LENGTH -#undef IEEE80211_VERIFY_ELEMENT - -/* - * Process a received ps-poll frame. - */ -static void -ieee80211_recv_pspoll(struct ieee80211com *ic, - struct ieee80211_node *ni, struct mbuf *m0) -{ - struct ieee80211_frame_min *wh; - struct mbuf *m; - uint16_t aid; - int qlen; - - wh = mtod(m0, struct ieee80211_frame_min *); - if (ni->ni_associd == 0) { - IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, - (struct ieee80211_frame *) wh, "ps-poll", - "%s", "unassociated station"); - ic->ic_stats.is_ps_unassoc++; - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_NOT_ASSOCED); - return; - } - - 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", - "aid mismatch: sta aid 0x%x poll aid 0x%x", - ni->ni_associd, aid); - ic->ic_stats.is_ps_badaid++; - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_NOT_ASSOCED); - return; - } - - /* Okay, take the first queued packet and put it out... */ - IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen); - if (m == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, - "[%s] recv ps-poll, but queue empty\n", - ether_sprintf(wh->i_addr2)); - ieee80211_send_nulldata(ieee80211_ref_node(ni)); - ic->ic_stats.is_ps_qempty++; /* XXX node stat */ - if (ic->ic_set_tim != NULL) - ic->ic_set_tim(ni, 0); /* just in case */ - return; - } - /* - * If there are more packets, set the more packets bit - * in the packet dispatched to the station; otherwise - * turn off the TIM bit. - */ - if (qlen != 0) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, - "[%s] recv ps-poll, send packet, %u still queued\n", - ether_sprintf(ni->ni_macaddr), qlen); - m->m_flags |= M_MORE_DATA; - } else { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, - "[%s] recv ps-poll, send packet, queue empty\n", - ether_sprintf(ni->ni_macaddr)); - if (ic->ic_set_tim != NULL) - ic->ic_set_tim(ni, 0); - } - m->m_flags |= M_PWR_SAV; /* bypass PS handling */ - IF_ENQUEUE(&ic->ic_ifp->if_snd, m); + return 0; } #ifdef IEEE80211_DEBUG /* * Debugging support. */ +void +ieee80211_ssid_mismatch(struct ieee80211vap *vap, const char *tag, + uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid) +{ + printf("[%s] discard %s frame, ssid mismatch: ", + ether_sprintf(mac), tag); + ieee80211_print_essid(ssid + 2, ssid[1]); + printf("\n"); +} /* * Return the bssid of a frame. */ static const uint8_t * -ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh) +ieee80211_getbssid(struct ieee80211vap *vap, const struct ieee80211_frame *wh) { - if (ic->ic_opmode == IEEE80211_M_STA) + if (vap->iv_opmode == IEEE80211_M_STA) return wh->i_addr2; if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS) return wh->i_addr1; @@ -3333,8 +831,10 @@ ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh) return wh->i_addr3; } +#include <machine/stdarg.h> + void -ieee80211_note(struct ieee80211com *ic, const char *fmt, ...) +ieee80211_note(struct ieee80211vap *vap, const char *fmt, ...) { char buf[128]; /* XXX */ va_list ap; @@ -3343,11 +843,11 @@ ieee80211_note(struct ieee80211com *ic, const char *fmt, ...) vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); - if_printf(ic->ic_ifp, "%s", buf); /* NB: no \n */ + if_printf(vap->iv_ifp, "%s", buf); /* NB: no \n */ } void -ieee80211_note_frame(struct ieee80211com *ic, +ieee80211_note_frame(struct ieee80211vap *vap, const struct ieee80211_frame *wh, const char *fmt, ...) { @@ -3357,12 +857,12 @@ ieee80211_note_frame(struct ieee80211com *ic, va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); - if_printf(ic->ic_ifp, "[%s] %s\n", - ether_sprintf(ieee80211_getbssid(ic, wh)), buf); + if_printf(vap->iv_ifp, "[%s] %s\n", + ether_sprintf(ieee80211_getbssid(vap, wh)), buf); } void -ieee80211_note_mac(struct ieee80211com *ic, +ieee80211_note_mac(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...) { @@ -3372,22 +872,24 @@ ieee80211_note_mac(struct ieee80211com *ic, va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); - if_printf(ic->ic_ifp, "[%s] %s\n", ether_sprintf(mac), buf); + if_printf(vap->iv_ifp, "[%s] %s\n", ether_sprintf(mac), buf); } void -ieee80211_discard_frame(struct ieee80211com *ic, +ieee80211_discard_frame(struct ieee80211vap *vap, const struct ieee80211_frame *wh, const char *type, const char *fmt, ...) { va_list ap; - printf("[%s:%s] discard ", ic->ic_ifp->if_xname, - ether_sprintf(ieee80211_getbssid(ic, wh))); - if (type != NULL) + if_printf(vap->iv_ifp, "[%s] discard ", + ether_sprintf(ieee80211_getbssid(vap, wh))); + if (type == NULL) { + printf("%s frame, ", ieee80211_mgt_subtype_name[ + (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) >> + IEEE80211_FC0_SUBTYPE_SHIFT]); + } else printf("%s frame, ", type); - else - printf("frame, "); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); @@ -3395,14 +897,14 @@ ieee80211_discard_frame(struct ieee80211com *ic, } void -ieee80211_discard_ie(struct ieee80211com *ic, +ieee80211_discard_ie(struct ieee80211vap *vap, const struct ieee80211_frame *wh, const char *type, const char *fmt, ...) { va_list ap; - printf("[%s:%s] discard ", ic->ic_ifp->if_xname, - ether_sprintf(ieee80211_getbssid(ic, wh))); + if_printf(vap->iv_ifp, "[%s] discard ", + ether_sprintf(ieee80211_getbssid(vap, wh))); if (type != NULL) printf("%s information element, ", type); else @@ -3414,13 +916,13 @@ ieee80211_discard_ie(struct ieee80211com *ic, } void -ieee80211_discard_mac(struct ieee80211com *ic, +ieee80211_discard_mac(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN], const char *type, const char *fmt, ...) { va_list ap; - printf("[%s:%s] discard ", ic->ic_ifp->if_xname, ether_sprintf(mac)); + if_printf(vap->iv_ifp, "[%s] discard ", ether_sprintf(mac)); if (type != NULL) printf("%s frame, ", type); else |