11a1e1d21SSam Leffler /*- 27535e66aSSam Leffler * Copyright (c) 2001 Atsushi Onoe 31f1d7810SSam Leffler * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting 41a1e1d21SSam Leffler * All rights reserved. 51a1e1d21SSam Leffler * 61a1e1d21SSam Leffler * Redistribution and use in source and binary forms, with or without 71a1e1d21SSam Leffler * modification, are permitted provided that the following conditions 81a1e1d21SSam Leffler * are met: 91a1e1d21SSam Leffler * 1. Redistributions of source code must retain the above copyright 107535e66aSSam Leffler * notice, this list of conditions and the following disclaimer. 117535e66aSSam Leffler * 2. Redistributions in binary form must reproduce the above copyright 127535e66aSSam Leffler * notice, this list of conditions and the following disclaimer in the 137535e66aSSam Leffler * documentation and/or other materials provided with the distribution. 147535e66aSSam Leffler * 3. The name of the author may not be used to endorse or promote products 157535e66aSSam Leffler * derived from this software without specific prior written permission. 161a1e1d21SSam Leffler * 171a1e1d21SSam Leffler * Alternatively, this software may be distributed under the terms of the 181a1e1d21SSam Leffler * GNU General Public License ("GPL") version 2 as published by the Free 191a1e1d21SSam Leffler * Software Foundation. 201a1e1d21SSam Leffler * 217535e66aSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 227535e66aSSam Leffler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 237535e66aSSam Leffler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 247535e66aSSam Leffler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 257535e66aSSam Leffler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 267535e66aSSam Leffler * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 277535e66aSSam Leffler * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 287535e66aSSam Leffler * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 297535e66aSSam Leffler * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 307535e66aSSam Leffler * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 311a1e1d21SSam Leffler */ 321a1e1d21SSam Leffler 331a1e1d21SSam Leffler #include <sys/cdefs.h> 341a1e1d21SSam Leffler __FBSDID("$FreeBSD$"); 351a1e1d21SSam Leffler 361a1e1d21SSam Leffler #include <sys/param.h> 371a1e1d21SSam Leffler #include <sys/systm.h> 381a1e1d21SSam Leffler #include <sys/mbuf.h> 391a1e1d21SSam Leffler #include <sys/malloc.h> 401a1e1d21SSam Leffler #include <sys/endian.h> 41a0cc3f85SSam Leffler #include <sys/kernel.h> 421a1e1d21SSam Leffler 438a1b9b6aSSam Leffler #include <sys/socket.h> 441a1e1d21SSam Leffler 451a1e1d21SSam Leffler #include <net/if.h> 461a1e1d21SSam Leffler #include <net/if_media.h> 471a1e1d21SSam Leffler #include <net/ethernet.h> 481a1e1d21SSam Leffler #include <net/if_llc.h> 498a1b9b6aSSam Leffler #include <net/if_vlan_var.h> 501a1e1d21SSam Leffler 511a1e1d21SSam Leffler #include <net80211/ieee80211_var.h> 521a1e1d21SSam Leffler 531a1e1d21SSam Leffler #include <net/bpf.h> 541a1e1d21SSam Leffler 558a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 568a1b9b6aSSam Leffler #include <machine/stdarg.h> 578a1b9b6aSSam Leffler 588a1b9b6aSSam Leffler /* 598a1b9b6aSSam Leffler * Decide if a received management frame should be 608a1b9b6aSSam Leffler * printed when debugging is enabled. This filters some 618a1b9b6aSSam Leffler * of the less interesting frames that come frequently 628a1b9b6aSSam Leffler * (e.g. beacons). 638a1b9b6aSSam Leffler */ 648a1b9b6aSSam Leffler static __inline int 658a1b9b6aSSam Leffler doprint(struct ieee80211com *ic, int subtype) 668a1b9b6aSSam Leffler { 678a1b9b6aSSam Leffler switch (subtype) { 688a1b9b6aSSam Leffler case IEEE80211_FC0_SUBTYPE_BEACON: 698a1b9b6aSSam Leffler return (ic->ic_flags & IEEE80211_F_SCAN); 708a1b9b6aSSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 718a1b9b6aSSam Leffler return (ic->ic_opmode == IEEE80211_M_IBSS); 728a1b9b6aSSam Leffler } 738a1b9b6aSSam Leffler return 1; 748a1b9b6aSSam Leffler } 758a1b9b6aSSam Leffler 768a1b9b6aSSam Leffler /* 778a1b9b6aSSam Leffler * Emit a debug message about discarding a frame or information 788a1b9b6aSSam Leffler * element. One format is for extracting the mac address from 798a1b9b6aSSam Leffler * the frame header; the other is for when a header is not 808a1b9b6aSSam Leffler * available or otherwise appropriate. 818a1b9b6aSSam Leffler */ 828a1b9b6aSSam Leffler #define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) do { \ 838a1b9b6aSSam Leffler if ((_ic)->ic_debug & (_m)) \ 848a1b9b6aSSam Leffler ieee80211_discard_frame(_ic, _wh, _type, _fmt, __VA_ARGS__);\ 858a1b9b6aSSam Leffler } while (0) 868a1b9b6aSSam Leffler #define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) do { \ 878a1b9b6aSSam Leffler if ((_ic)->ic_debug & (_m)) \ 888a1b9b6aSSam Leffler ieee80211_discard_ie(_ic, _wh, _type, _fmt, __VA_ARGS__);\ 898a1b9b6aSSam Leffler } while (0) 908a1b9b6aSSam Leffler #define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) do { \ 918a1b9b6aSSam Leffler if ((_ic)->ic_debug & (_m)) \ 928a1b9b6aSSam Leffler ieee80211_discard_mac(_ic, _mac, _type, _fmt, __VA_ARGS__);\ 938a1b9b6aSSam Leffler } while (0) 948a1b9b6aSSam Leffler 958a1b9b6aSSam Leffler static const u_int8_t *ieee80211_getbssid(struct ieee80211com *, 968a1b9b6aSSam Leffler const struct ieee80211_frame *); 978a1b9b6aSSam Leffler static void ieee80211_discard_frame(struct ieee80211com *, 988a1b9b6aSSam Leffler const struct ieee80211_frame *, const char *type, const char *fmt, ...); 998a1b9b6aSSam Leffler static void ieee80211_discard_ie(struct ieee80211com *, 1008a1b9b6aSSam Leffler const struct ieee80211_frame *, const char *type, const char *fmt, ...); 1018a1b9b6aSSam Leffler static void ieee80211_discard_mac(struct ieee80211com *, 1028a1b9b6aSSam Leffler const u_int8_t mac[IEEE80211_ADDR_LEN], const char *type, 1038a1b9b6aSSam Leffler const char *fmt, ...); 1048a1b9b6aSSam Leffler #else 1058a1b9b6aSSam Leffler #define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) 1068a1b9b6aSSam Leffler #define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) 1078a1b9b6aSSam Leffler #define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) 1088a1b9b6aSSam Leffler #endif /* IEEE80211_DEBUG */ 1098a1b9b6aSSam Leffler 1108a1b9b6aSSam Leffler static struct mbuf *ieee80211_defrag(struct ieee80211com *, 1112cc12adeSSam Leffler struct ieee80211_node *, struct mbuf *, int); 1122cc12adeSSam Leffler static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *, int); 11384eb84c4SSam Leffler static void ieee80211_send_error(struct ieee80211com *, struct ieee80211_node *, 11484eb84c4SSam Leffler const u_int8_t *mac, int subtype, int arg); 1151bd482efSSam Leffler static void ieee80211_deliver_data(struct ieee80211com *, 1161bd482efSSam Leffler struct ieee80211_node *, struct mbuf *); 1178a1b9b6aSSam Leffler static void ieee80211_node_pwrsave(struct ieee80211_node *, int enable); 1188a1b9b6aSSam Leffler static void ieee80211_recv_pspoll(struct ieee80211com *, 1198a1b9b6aSSam Leffler struct ieee80211_node *, struct mbuf *); 1201a1e1d21SSam Leffler 1210a915fadSSam Leffler /* 1220a915fadSSam Leffler * Process a received frame. The node associated with the sender 1230a915fadSSam Leffler * should be supplied. If nothing was found in the node table then 1240a915fadSSam Leffler * the caller is assumed to supply a reference to ic_bss instead. 1250a915fadSSam Leffler * The RSSI and a timestamp are also supplied. The RSSI data is used 1260a915fadSSam Leffler * during AP scanning to select a AP to associate with; it can have 1270a915fadSSam Leffler * any units so long as values have consistent units and higher values 1280a915fadSSam Leffler * mean ``better signal''. The receive timestamp is currently not used 1290a915fadSSam Leffler * by the 802.11 layer. 1300a915fadSSam Leffler */ 1311f298879SSam Leffler int 1328a1b9b6aSSam Leffler ieee80211_input(struct ieee80211com *ic, struct mbuf *m, 1338a1b9b6aSSam Leffler struct ieee80211_node *ni, int rssi, u_int32_t rstamp) 1341a1e1d21SSam Leffler { 1358a1b9b6aSSam Leffler #define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) 1368a1b9b6aSSam Leffler #define HAS_SEQ(type) ((type & 0x4) == 0) 1378a1b9b6aSSam Leffler struct ifnet *ifp = ic->ic_ifp; 1381a1e1d21SSam Leffler struct ieee80211_frame *wh; 1398a1b9b6aSSam Leffler struct ieee80211_key *key; 1401a1e1d21SSam Leffler struct ether_header *eh; 141b138c150SSam Leffler int hdrspace; 14218f897abSSam Leffler u_int8_t dir, type, subtype; 1431a1e1d21SSam Leffler u_int8_t *bssid; 1441a1e1d21SSam Leffler u_int16_t rxseq; 1451a1e1d21SSam Leffler 1460a915fadSSam Leffler KASSERT(ni != NULL, ("null node")); 1478a1b9b6aSSam Leffler ni->ni_inact = ni->ni_inact_reload; 1480a915fadSSam Leffler 14918f897abSSam Leffler /* trim CRC here so WEP can find its own CRC at the end of packet. */ 1501a1e1d21SSam Leffler if (m->m_flags & M_HASFCS) { 1511a1e1d21SSam Leffler m_adj(m, -IEEE80211_CRC_LEN); 1521a1e1d21SSam Leffler m->m_flags &= ~M_HASFCS; 1531a1e1d21SSam Leffler } 1541f298879SSam Leffler type = -1; /* undefined */ 15518f897abSSam Leffler /* 15618f897abSSam Leffler * In monitor mode, send everything directly to bpf. 15718f897abSSam Leffler * XXX may want to include the CRC 15818f897abSSam Leffler */ 15918f897abSSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR) 16018f897abSSam Leffler goto out; 1611a1e1d21SSam Leffler 1628a1b9b6aSSam Leffler if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { 1638a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, 1648a1b9b6aSSam Leffler ni->ni_macaddr, NULL, 1658a1b9b6aSSam Leffler "too short (1): len %u", m->m_pkthdr.len); 1668a1b9b6aSSam Leffler ic->ic_stats.is_rx_tooshort++; 1678a1b9b6aSSam Leffler goto out; 1688a1b9b6aSSam Leffler } 1698a1b9b6aSSam Leffler /* 1708a1b9b6aSSam Leffler * Bit of a cheat here, we use a pointer for a 3-address 1718a1b9b6aSSam Leffler * frame format but don't reference fields past outside 1728a1b9b6aSSam Leffler * ieee80211_frame_min w/o first validating the data is 1738a1b9b6aSSam Leffler * present. 1748a1b9b6aSSam Leffler */ 1751a1e1d21SSam Leffler wh = mtod(m, struct ieee80211_frame *); 1768a1b9b6aSSam Leffler 1771a1e1d21SSam Leffler if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != 1781a1e1d21SSam Leffler IEEE80211_FC0_VERSION_0) { 1798a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, 1808a1b9b6aSSam Leffler ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]); 1811be50176SSam Leffler ic->ic_stats.is_rx_badversion++; 1821a1e1d21SSam Leffler goto err; 1831a1e1d21SSam Leffler } 1841a1e1d21SSam Leffler 1851a1e1d21SSam Leffler dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 18618f897abSSam Leffler type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 1878a1b9b6aSSam Leffler subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 1888a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 1891a1e1d21SSam Leffler switch (ic->ic_opmode) { 1901a1e1d21SSam Leffler case IEEE80211_M_STA: 1918a1b9b6aSSam Leffler bssid = wh->i_addr2; 1928a1b9b6aSSam Leffler if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) { 1931be50176SSam Leffler /* not interested in */ 1948a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, 1958a1b9b6aSSam Leffler bssid, NULL, "%s", "not to bss"); 1961be50176SSam Leffler ic->ic_stats.is_rx_wrongbss++; 1971a1e1d21SSam Leffler goto out; 1981a1e1d21SSam Leffler } 1991a1e1d21SSam Leffler break; 2001a1e1d21SSam Leffler case IEEE80211_M_IBSS: 2011a1e1d21SSam Leffler case IEEE80211_M_AHDEMO: 2021a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 2038a1b9b6aSSam Leffler if (dir != IEEE80211_FC1_DIR_NODS) 2041a1e1d21SSam Leffler bssid = wh->i_addr1; 2058a1b9b6aSSam Leffler else if (type == IEEE80211_FC0_TYPE_CTL) 2068a1b9b6aSSam Leffler bssid = wh->i_addr1; 2078a1b9b6aSSam Leffler else { 2088a1b9b6aSSam Leffler if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 2098a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, 2108a1b9b6aSSam Leffler IEEE80211_MSG_ANY, ni->ni_macaddr, 2118a1b9b6aSSam Leffler NULL, "too short (2): len %u", 2128a1b9b6aSSam Leffler m->m_pkthdr.len); 2138a1b9b6aSSam Leffler ic->ic_stats.is_rx_tooshort++; 2148a1b9b6aSSam Leffler goto out; 2158a1b9b6aSSam Leffler } 2168a1b9b6aSSam Leffler bssid = wh->i_addr3; 2178a1b9b6aSSam Leffler } 218bc031652SSam Leffler if (type != IEEE80211_FC0_TYPE_DATA) 219bc031652SSam Leffler break; 2208a1b9b6aSSam Leffler /* 2218a1b9b6aSSam Leffler * Data frame, validate the bssid. 2228a1b9b6aSSam Leffler */ 2231a1e1d21SSam Leffler if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) && 2241a1e1d21SSam Leffler !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) { 2251a1e1d21SSam Leffler /* not interested in */ 2268a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, 2278a1b9b6aSSam Leffler bssid, NULL, "%s", "not to bss"); 2281be50176SSam Leffler ic->ic_stats.is_rx_wrongbss++; 2291a1e1d21SSam Leffler goto out; 2301a1e1d21SSam Leffler } 2318a1b9b6aSSam Leffler /* 2328a1b9b6aSSam Leffler * For adhoc mode we cons up a node when it doesn't 2338a1b9b6aSSam Leffler * exist. This should probably done after an ACL check. 2348a1b9b6aSSam Leffler */ 2358a1b9b6aSSam Leffler if (ni == ic->ic_bss && 236be425a0fSSam Leffler ic->ic_opmode != IEEE80211_M_HOSTAP && 237be425a0fSSam Leffler !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 2388a1b9b6aSSam Leffler /* 2398a1b9b6aSSam Leffler * Fake up a node for this newly 2408a1b9b6aSSam Leffler * discovered member of the IBSS. 2418a1b9b6aSSam Leffler */ 242acc4f7f5SSam Leffler ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta, 2430796482dSSam Leffler wh->i_addr2); 2448a1b9b6aSSam Leffler if (ni == NULL) { 2458a1b9b6aSSam Leffler /* NB: stat kept for alloc failure */ 2468a1b9b6aSSam Leffler goto err; 2478a1b9b6aSSam Leffler } 2488a1b9b6aSSam Leffler } 2491a1e1d21SSam Leffler break; 2501a1e1d21SSam Leffler default: 2518a1b9b6aSSam Leffler goto out; 2521a1e1d21SSam Leffler } 2531a1e1d21SSam Leffler ni->ni_rssi = rssi; 2541a1e1d21SSam Leffler ni->ni_rstamp = rstamp; 2558a1b9b6aSSam Leffler if (HAS_SEQ(type)) { 2568a1b9b6aSSam Leffler u_int8_t tid; 2578a1b9b6aSSam Leffler if (IEEE80211_QOS_HAS_SEQ(wh)) { 2588a1b9b6aSSam Leffler tid = ((struct ieee80211_qosframe *)wh)-> 2598a1b9b6aSSam Leffler i_qos[0] & IEEE80211_QOS_TID; 2601a08800dSSam Leffler if (TID_TO_WME_AC(tid) >= WME_AC_VI) 2618a1b9b6aSSam Leffler ic->ic_wme.wme_hipri_traffic++; 2628a1b9b6aSSam Leffler tid++; 2638a1b9b6aSSam Leffler } else 2648a1b9b6aSSam Leffler tid = 0; 2658a1b9b6aSSam Leffler rxseq = le16toh(*(u_int16_t *)wh->i_seq); 2661a1e1d21SSam Leffler if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && 2678a1b9b6aSSam Leffler SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) { 2688a1b9b6aSSam Leffler /* duplicate, discard */ 2698a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, 2708a1b9b6aSSam Leffler bssid, "duplicate", 2718a1b9b6aSSam Leffler "seqno <%u,%u> fragno <%u,%u> tid %u", 2728a1b9b6aSSam Leffler rxseq >> IEEE80211_SEQ_SEQ_SHIFT, 2738a1b9b6aSSam Leffler ni->ni_rxseqs[tid] >> 2748a1b9b6aSSam Leffler IEEE80211_SEQ_SEQ_SHIFT, 2758a1b9b6aSSam Leffler rxseq & IEEE80211_SEQ_FRAG_MASK, 2768a1b9b6aSSam Leffler ni->ni_rxseqs[tid] & 2778a1b9b6aSSam Leffler IEEE80211_SEQ_FRAG_MASK, 2788a1b9b6aSSam Leffler tid); 2798a1b9b6aSSam Leffler ic->ic_stats.is_rx_dup++; 2808a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_dup); 2811a1e1d21SSam Leffler goto out; 2821a1e1d21SSam Leffler } 2838a1b9b6aSSam Leffler ni->ni_rxseqs[tid] = rxseq; 2848a1b9b6aSSam Leffler } 2851a1e1d21SSam Leffler } 2861a1e1d21SSam Leffler 28718f897abSSam Leffler switch (type) { 2881a1e1d21SSam Leffler case IEEE80211_FC0_TYPE_DATA: 2892cc12adeSSam Leffler hdrspace = ieee80211_hdrspace(ic, wh); 2902cc12adeSSam Leffler if (m->m_len < hdrspace && 2912cc12adeSSam Leffler (m = m_pullup(m, hdrspace)) == NULL) { 29220098591SSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, 29320098591SSam Leffler ni->ni_macaddr, NULL, 2942cc12adeSSam Leffler "data too short: expecting %u", hdrspace); 2958a1b9b6aSSam Leffler ic->ic_stats.is_rx_tooshort++; 2968a1b9b6aSSam Leffler goto out; /* XXX */ 2978a1b9b6aSSam Leffler } 2981a1e1d21SSam Leffler switch (ic->ic_opmode) { 2991a1e1d21SSam Leffler case IEEE80211_M_STA: 3001be50176SSam Leffler if (dir != IEEE80211_FC1_DIR_FROMDS) { 301bd6f09d9SSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 302bd6f09d9SSam Leffler wh, "data", "%s", "unknown dir 0x%x", dir); 3031be50176SSam Leffler ic->ic_stats.is_rx_wrongdir++; 3041a1e1d21SSam Leffler goto out; 3051be50176SSam Leffler } 3061a1e1d21SSam Leffler if ((ifp->if_flags & IFF_SIMPLEX) && 3071a1e1d21SSam Leffler IEEE80211_IS_MULTICAST(wh->i_addr1) && 3081a1e1d21SSam Leffler IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) { 3091a1e1d21SSam Leffler /* 3101a1e1d21SSam Leffler * In IEEE802.11 network, multicast packet 3111a1e1d21SSam Leffler * sent from me is broadcasted from AP. 3121a1e1d21SSam Leffler * It should be silently discarded for 3131a1e1d21SSam Leffler * SIMPLEX interface. 3141a1e1d21SSam Leffler */ 3158a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 3168a1b9b6aSSam Leffler wh, NULL, "%s", "multicast echo"); 3171be50176SSam Leffler ic->ic_stats.is_rx_mcastecho++; 3181a1e1d21SSam Leffler goto out; 3191a1e1d21SSam Leffler } 3201a1e1d21SSam Leffler break; 3211a1e1d21SSam Leffler case IEEE80211_M_IBSS: 3221a1e1d21SSam Leffler case IEEE80211_M_AHDEMO: 3231be50176SSam Leffler if (dir != IEEE80211_FC1_DIR_NODS) { 324bd6f09d9SSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 325bd6f09d9SSam Leffler wh, "data", "%s", "unknown dir 0x%x", dir); 3261be50176SSam Leffler ic->ic_stats.is_rx_wrongdir++; 3271a1e1d21SSam Leffler goto out; 3281be50176SSam Leffler } 3298a1b9b6aSSam Leffler /* XXX no power-save support */ 3301a1e1d21SSam Leffler break; 3311a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 3321be50176SSam Leffler if (dir != IEEE80211_FC1_DIR_TODS) { 333bd6f09d9SSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 334bd6f09d9SSam Leffler wh, "data", "%s", "unknown dir 0x%x", dir); 3351be50176SSam Leffler ic->ic_stats.is_rx_wrongdir++; 3361a1e1d21SSam Leffler goto out; 3371be50176SSam Leffler } 3381a1e1d21SSam Leffler /* check if source STA is associated */ 3390a915fadSSam Leffler if (ni == ic->ic_bss) { 3408a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 3418a1b9b6aSSam Leffler wh, "data", "%s", "unknown src"); 34284eb84c4SSam Leffler ieee80211_send_error(ic, ni, wh->i_addr2, 3431a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH, 3441a1e1d21SSam Leffler IEEE80211_REASON_NOT_AUTHED); 3451be50176SSam Leffler ic->ic_stats.is_rx_notassoc++; 3461a1e1d21SSam Leffler goto err; 3471a1e1d21SSam Leffler } 3481a1e1d21SSam Leffler if (ni->ni_associd == 0) { 3498a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 3508a1b9b6aSSam Leffler wh, "data", "%s", "unassoc src"); 3511a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 3521a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_DISASSOC, 3531a1e1d21SSam Leffler IEEE80211_REASON_NOT_ASSOCED); 3541be50176SSam Leffler ic->ic_stats.is_rx_notassoc++; 3551a1e1d21SSam Leffler goto err; 3561a1e1d21SSam Leffler } 3578a1b9b6aSSam Leffler 3588a1b9b6aSSam Leffler /* 3598a1b9b6aSSam Leffler * Check for power save state change. 3608a1b9b6aSSam Leffler */ 3618a1b9b6aSSam Leffler if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^ 3628a1b9b6aSSam Leffler (ni->ni_flags & IEEE80211_NODE_PWR_MGT))) 3638a1b9b6aSSam Leffler ieee80211_node_pwrsave(ni, 3648a1b9b6aSSam Leffler wh->i_fc[1] & IEEE80211_FC1_PWR_MGT); 3651a1e1d21SSam Leffler break; 3668a1b9b6aSSam Leffler default: 3678a1b9b6aSSam Leffler /* XXX here to keep compiler happy */ 3688a1b9b6aSSam Leffler goto out; 3691a1e1d21SSam Leffler } 3708a1b9b6aSSam Leffler 3718a1b9b6aSSam Leffler /* 3728a1b9b6aSSam Leffler * Handle privacy requirements. Note that we 3738a1b9b6aSSam Leffler * must not be preempted from here until after 3748a1b9b6aSSam Leffler * we (potentially) call ieee80211_crypto_demic; 3758a1b9b6aSSam Leffler * otherwise we may violate assumptions in the 3768a1b9b6aSSam Leffler * crypto cipher modules used to do delayed update 3778a1b9b6aSSam Leffler * of replay sequence numbers. 3788a1b9b6aSSam Leffler */ 3791a1e1d21SSam Leffler if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 3808a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { 3818a1b9b6aSSam Leffler /* 3828a1b9b6aSSam Leffler * Discard encrypted frames when privacy is off. 3838a1b9b6aSSam Leffler */ 3848a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 3858a1b9b6aSSam Leffler wh, "WEP", "%s", "PRIVACY off"); 3868a1b9b6aSSam Leffler ic->ic_stats.is_rx_noprivacy++; 3878a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_noprivacy); 3888a1b9b6aSSam Leffler goto out; 3898a1b9b6aSSam Leffler } 3902cc12adeSSam Leffler key = ieee80211_crypto_decap(ic, ni, m, hdrspace); 3918a1b9b6aSSam Leffler if (key == NULL) { 3928a1b9b6aSSam Leffler /* NB: stats+msgs handled in crypto_decap */ 3938a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_wepfail); 3948a1b9b6aSSam Leffler goto out; 3951be50176SSam Leffler } 3961a1e1d21SSam Leffler wh = mtod(m, struct ieee80211_frame *); 3978116d318SSam Leffler wh->i_fc[1] &= ~IEEE80211_FC1_WEP; 3981be50176SSam Leffler } else { 3998a1b9b6aSSam Leffler key = NULL; 4008a1b9b6aSSam Leffler } 4018a1b9b6aSSam Leffler 4028a1b9b6aSSam Leffler /* 4038a1b9b6aSSam Leffler * Next up, any fragmentation. 4048a1b9b6aSSam Leffler */ 4058a1b9b6aSSam Leffler if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 4062cc12adeSSam Leffler m = ieee80211_defrag(ic, ni, m, hdrspace); 4078a1b9b6aSSam Leffler if (m == NULL) { 4088a1b9b6aSSam Leffler /* Fragment dropped or frame not complete yet */ 4091a1e1d21SSam Leffler goto out; 4101a1e1d21SSam Leffler } 4111be50176SSam Leffler } 4128a1b9b6aSSam Leffler wh = NULL; /* no longer valid, catch any uses */ 4138a1b9b6aSSam Leffler 4148a1b9b6aSSam Leffler /* 4158a1b9b6aSSam Leffler * Next strip any MSDU crypto bits. 4168a1b9b6aSSam Leffler */ 41796d88463SSam Leffler if (key != NULL && !ieee80211_crypto_demic(ic, key, m, 0)) { 4188a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, 4198a1b9b6aSSam Leffler ni->ni_macaddr, "data", "%s", "demic error"); 4208a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_demicfail); 4218a1b9b6aSSam Leffler goto out; 4228a1b9b6aSSam Leffler } 4238a1b9b6aSSam Leffler 4241a1e1d21SSam Leffler /* copy to listener after decrypt */ 4251a1e1d21SSam Leffler if (ic->ic_rawbpf) 4261a1e1d21SSam Leffler bpf_mtap(ic->ic_rawbpf, m); 4278a1b9b6aSSam Leffler 4288a1b9b6aSSam Leffler /* 4298a1b9b6aSSam Leffler * Finally, strip the 802.11 header. 4308a1b9b6aSSam Leffler */ 4312cc12adeSSam Leffler m = ieee80211_decap(ic, m, hdrspace); 4321be50176SSam Leffler if (m == NULL) { 4338a1b9b6aSSam Leffler /* don't count Null data frames as errors */ 4348a1b9b6aSSam Leffler if (subtype == IEEE80211_FC0_SUBTYPE_NODATA) 4358a1b9b6aSSam Leffler goto out; 4368a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, 4378a1b9b6aSSam Leffler ni->ni_macaddr, "data", "%s", "decap error"); 4381be50176SSam Leffler ic->ic_stats.is_rx_decap++; 4398a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_decap); 4401a1e1d21SSam Leffler goto err; 4411be50176SSam Leffler } 4428a1b9b6aSSam Leffler eh = mtod(m, struct ether_header *); 4438a1b9b6aSSam Leffler if (!ieee80211_node_is_authorized(ni)) { 4448a1b9b6aSSam Leffler /* 4458a1b9b6aSSam Leffler * Deny any non-PAE frames received prior to 4468a1b9b6aSSam Leffler * authorization. For open/shared-key 4478a1b9b6aSSam Leffler * authentication the port is mark authorized 4488a1b9b6aSSam Leffler * after authentication completes. For 802.1x 4498a1b9b6aSSam Leffler * the port is not marked authorized by the 4508a1b9b6aSSam Leffler * authenticator until the handshake has completed. 4518a1b9b6aSSam Leffler */ 4528a1b9b6aSSam Leffler if (eh->ether_type != htons(ETHERTYPE_PAE)) { 4538a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, 4548a1b9b6aSSam Leffler eh->ether_shost, "data", 4558a1b9b6aSSam Leffler "unauthorized port: ether type 0x%x len %u", 4568a1b9b6aSSam Leffler eh->ether_type, m->m_pkthdr.len); 4578a1b9b6aSSam Leffler ic->ic_stats.is_rx_unauth++; 4588a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_unauth); 4598a1b9b6aSSam Leffler goto err; 4608a1b9b6aSSam Leffler } 4618a1b9b6aSSam Leffler } else { 4628a1b9b6aSSam Leffler /* 4638a1b9b6aSSam Leffler * When denying unencrypted frames, discard 4648a1b9b6aSSam Leffler * any non-PAE frames received without encryption. 4658a1b9b6aSSam Leffler */ 4668a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_DROPUNENC) && 4678a1b9b6aSSam Leffler key == NULL && 4688a1b9b6aSSam Leffler eh->ether_type != htons(ETHERTYPE_PAE)) { 4698a1b9b6aSSam Leffler /* 4708a1b9b6aSSam Leffler * Drop unencrypted frames. 4718a1b9b6aSSam Leffler */ 4728a1b9b6aSSam Leffler ic->ic_stats.is_rx_unencrypted++; 4738a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_unencrypted); 4748a1b9b6aSSam Leffler goto out; 4758a1b9b6aSSam Leffler } 4768a1b9b6aSSam Leffler } 4771a1e1d21SSam Leffler ifp->if_ipackets++; 4788a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_data); 4798a1b9b6aSSam Leffler IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len); 4801a1e1d21SSam Leffler 4811bd482efSSam Leffler ieee80211_deliver_data(ic, ni, m); 4821f298879SSam Leffler return IEEE80211_FC0_TYPE_DATA; 4831a1e1d21SSam Leffler 4841a1e1d21SSam Leffler case IEEE80211_FC0_TYPE_MGT: 4858a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_mgmt); 4861be50176SSam Leffler if (dir != IEEE80211_FC1_DIR_NODS) { 487bd6f09d9SSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 488bd6f09d9SSam Leffler wh, "data", "%s", "unknown dir 0x%x", dir); 4891be50176SSam Leffler ic->ic_stats.is_rx_wrongdir++; 4901a1e1d21SSam Leffler goto err; 4911be50176SSam Leffler } 4928a1b9b6aSSam Leffler if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 4938a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, 4948a1b9b6aSSam Leffler ni->ni_macaddr, "mgt", "too short: len %u", 4958a1b9b6aSSam Leffler m->m_pkthdr.len); 4968a1b9b6aSSam Leffler ic->ic_stats.is_rx_tooshort++; 4971a1e1d21SSam Leffler goto out; 4981be50176SSam Leffler } 4991a1e1d21SSam Leffler #ifdef IEEE80211_DEBUG 5008a1b9b6aSSam Leffler if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) || 5018a1b9b6aSSam Leffler ieee80211_msg_dumppkts(ic)) { 5028a1b9b6aSSam Leffler if_printf(ic->ic_ifp, "received %s from %s rssi %d\n", 5038a1b9b6aSSam Leffler ieee80211_mgt_subtype_name[subtype >> 5048a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 5051a1e1d21SSam Leffler ether_sprintf(wh->i_addr2), rssi); 5061a1e1d21SSam Leffler } 5078a1b9b6aSSam Leffler #endif 5088a1b9b6aSSam Leffler if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 5098a1b9b6aSSam Leffler if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) { 5108a1b9b6aSSam Leffler /* 5118a1b9b6aSSam Leffler * Only shared key auth frames with a challenge 5128a1b9b6aSSam Leffler * should be encrypted, discard all others. 5138a1b9b6aSSam Leffler */ 5148a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 5158a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> 5168a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 5178a1b9b6aSSam Leffler "%s", "WEP set but not permitted"); 5188a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; /* XXX */ 5198a1b9b6aSSam Leffler goto out; 5208a1b9b6aSSam Leffler } 5218a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { 5228a1b9b6aSSam Leffler /* 5238a1b9b6aSSam Leffler * Discard encrypted frames when privacy is off. 5248a1b9b6aSSam Leffler */ 5258a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 5268a1b9b6aSSam Leffler wh, "mgt", "%s", "WEP set but PRIVACY off"); 5278a1b9b6aSSam Leffler ic->ic_stats.is_rx_noprivacy++; 5288a1b9b6aSSam Leffler goto out; 5298a1b9b6aSSam Leffler } 5302cc12adeSSam Leffler hdrspace = ieee80211_hdrspace(ic, wh); 5312cc12adeSSam Leffler key = ieee80211_crypto_decap(ic, ni, m, hdrspace); 5328a1b9b6aSSam Leffler if (key == NULL) { 5338a1b9b6aSSam Leffler /* NB: stats+msgs handled in crypto_decap */ 5348a1b9b6aSSam Leffler goto out; 5358a1b9b6aSSam Leffler } 5368116d318SSam Leffler wh = mtod(m, struct ieee80211_frame *); 5378116d318SSam Leffler wh->i_fc[1] &= ~IEEE80211_FC1_WEP; 5388a1b9b6aSSam Leffler } 5391a1e1d21SSam Leffler if (ic->ic_rawbpf) 5401a1e1d21SSam Leffler bpf_mtap(ic->ic_rawbpf, m); 5410a915fadSSam Leffler (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp); 5421a1e1d21SSam Leffler m_freem(m); 5431f298879SSam Leffler return type; 5441a1e1d21SSam Leffler 5451a1e1d21SSam Leffler case IEEE80211_FC0_TYPE_CTL: 5468a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_ctrl); 5471be50176SSam Leffler ic->ic_stats.is_rx_ctl++; 5488a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 5498a1b9b6aSSam Leffler switch (subtype) { 5508a1b9b6aSSam Leffler case IEEE80211_FC0_SUBTYPE_PS_POLL: 5518a1b9b6aSSam Leffler ieee80211_recv_pspoll(ic, ni, m); 5528a1b9b6aSSam Leffler break; 5538a1b9b6aSSam Leffler } 5548a1b9b6aSSam Leffler } 55518f897abSSam Leffler goto out; 5561a1e1d21SSam Leffler default: 5578a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, 5588a1b9b6aSSam Leffler wh, NULL, "bad frame type 0x%x", type); 5591a1e1d21SSam Leffler /* should not come here */ 5601a1e1d21SSam Leffler break; 5611a1e1d21SSam Leffler } 5621a1e1d21SSam Leffler err: 5631a1e1d21SSam Leffler ifp->if_ierrors++; 5641a1e1d21SSam Leffler out: 5651a1e1d21SSam Leffler if (m != NULL) { 5661a1e1d21SSam Leffler if (ic->ic_rawbpf) 5671a1e1d21SSam Leffler bpf_mtap(ic->ic_rawbpf, m); 5681a1e1d21SSam Leffler m_freem(m); 5691a1e1d21SSam Leffler } 5701f298879SSam Leffler return type; 5718a1b9b6aSSam Leffler #undef SEQ_LEQ 5721a1e1d21SSam Leffler } 5731a1e1d21SSam Leffler 5748a1b9b6aSSam Leffler /* 5758a1b9b6aSSam Leffler * This function reassemble fragments. 5768a1b9b6aSSam Leffler */ 5778a1b9b6aSSam Leffler static struct mbuf * 5788a1b9b6aSSam Leffler ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, 5792cc12adeSSam Leffler struct mbuf *m, int hdrspace) 5801a1e1d21SSam Leffler { 5818a1b9b6aSSam Leffler struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); 5828a1b9b6aSSam Leffler struct ieee80211_frame *lwh; 5838a1b9b6aSSam Leffler u_int16_t rxseq; 5848a1b9b6aSSam Leffler u_int8_t fragno; 5858a1b9b6aSSam Leffler u_int8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG; 5868a1b9b6aSSam Leffler struct mbuf *mfrag; 5878a1b9b6aSSam Leffler 5888a1b9b6aSSam Leffler KASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?")); 5898a1b9b6aSSam Leffler 5908a1b9b6aSSam Leffler rxseq = le16toh(*(u_int16_t *)wh->i_seq); 5918a1b9b6aSSam Leffler fragno = rxseq & IEEE80211_SEQ_FRAG_MASK; 5928a1b9b6aSSam Leffler 5938a1b9b6aSSam Leffler /* Quick way out, if there's nothing to defragment */ 5948a1b9b6aSSam Leffler if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL) 5958a1b9b6aSSam Leffler return m; 5968a1b9b6aSSam Leffler 5978a1b9b6aSSam Leffler /* 5988a1b9b6aSSam Leffler * Remove frag to insure it doesn't get reaped by timer. 5998a1b9b6aSSam Leffler */ 6008a1b9b6aSSam Leffler if (ni->ni_table == NULL) { 6018a1b9b6aSSam Leffler /* 6028a1b9b6aSSam Leffler * Should never happen. If the node is orphaned (not in 6038a1b9b6aSSam Leffler * the table) then input packets should not reach here. 6048a1b9b6aSSam Leffler * Otherwise, a concurrent request that yanks the table 6058a1b9b6aSSam Leffler * should be blocked by other interlocking and/or by first 6068a1b9b6aSSam Leffler * shutting the driver down. Regardless, be defensive 6078a1b9b6aSSam Leffler * here and just bail 6088a1b9b6aSSam Leffler */ 6098a1b9b6aSSam Leffler /* XXX need msg+stat */ 6108a1b9b6aSSam Leffler m_freem(m); 6118a1b9b6aSSam Leffler return NULL; 6128a1b9b6aSSam Leffler } 6138a1b9b6aSSam Leffler IEEE80211_NODE_LOCK(ni->ni_table); 6148a1b9b6aSSam Leffler mfrag = ni->ni_rxfrag[0]; 6158a1b9b6aSSam Leffler ni->ni_rxfrag[0] = NULL; 6168a1b9b6aSSam Leffler IEEE80211_NODE_UNLOCK(ni->ni_table); 6178a1b9b6aSSam Leffler 6188a1b9b6aSSam Leffler /* 6198a1b9b6aSSam Leffler * Validate new fragment is in order and 6208a1b9b6aSSam Leffler * related to the previous ones. 6218a1b9b6aSSam Leffler */ 6228a1b9b6aSSam Leffler if (mfrag != NULL) { 6238a1b9b6aSSam Leffler u_int16_t last_rxseq; 6248a1b9b6aSSam Leffler 6258a1b9b6aSSam Leffler lwh = mtod(mfrag, struct ieee80211_frame *); 6268a1b9b6aSSam Leffler last_rxseq = le16toh(*(u_int16_t *)lwh->i_seq); 6278a1b9b6aSSam Leffler /* NB: check seq # and frag together */ 6288a1b9b6aSSam Leffler if (rxseq != last_rxseq+1 || 6298a1b9b6aSSam Leffler !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) || 6308a1b9b6aSSam Leffler !IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) { 6318a1b9b6aSSam Leffler /* 6328a1b9b6aSSam Leffler * Unrelated fragment or no space for it, 6338a1b9b6aSSam Leffler * clear current fragments. 6348a1b9b6aSSam Leffler */ 6358a1b9b6aSSam Leffler m_freem(mfrag); 6368a1b9b6aSSam Leffler mfrag = NULL; 6378a1b9b6aSSam Leffler } 6388a1b9b6aSSam Leffler } 6398a1b9b6aSSam Leffler 6408a1b9b6aSSam Leffler if (mfrag == NULL) { 6418a1b9b6aSSam Leffler if (fragno != 0) { /* !first fragment, discard */ 6428a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_defrag); 6438a1b9b6aSSam Leffler m_freem(m); 6448a1b9b6aSSam Leffler return NULL; 6458a1b9b6aSSam Leffler } 6468a1b9b6aSSam Leffler mfrag = m; 6478a1b9b6aSSam Leffler } else { /* concatenate */ 6482cc12adeSSam Leffler m_adj(m, hdrspace); /* strip header */ 6498a1b9b6aSSam Leffler m_cat(mfrag, m); 6508a1b9b6aSSam Leffler /* NB: m_cat doesn't update the packet header */ 6518a1b9b6aSSam Leffler mfrag->m_pkthdr.len += m->m_pkthdr.len; 6528a1b9b6aSSam Leffler /* track last seqnum and fragno */ 6538a1b9b6aSSam Leffler lwh = mtod(mfrag, struct ieee80211_frame *); 6548a1b9b6aSSam Leffler *(u_int16_t *) lwh->i_seq = *(u_int16_t *) wh->i_seq; 6558a1b9b6aSSam Leffler } 6568a1b9b6aSSam Leffler if (more_frag) { /* more to come, save */ 657a0cc3f85SSam Leffler ni->ni_rxfragstamp = ticks; 6588a1b9b6aSSam Leffler ni->ni_rxfrag[0] = mfrag; 6598a1b9b6aSSam Leffler mfrag = NULL; 6608a1b9b6aSSam Leffler } 6618a1b9b6aSSam Leffler return mfrag; 6628a1b9b6aSSam Leffler } 6638a1b9b6aSSam Leffler 6641bd482efSSam Leffler static void 6651bd482efSSam Leffler ieee80211_deliver_data(struct ieee80211com *ic, 6661bd482efSSam Leffler struct ieee80211_node *ni, struct mbuf *m) 6671bd482efSSam Leffler { 6681bd482efSSam Leffler struct ether_header *eh = mtod(m, struct ether_header *); 6691bd482efSSam Leffler struct ifnet *ifp = ic->ic_ifp; 6701bd482efSSam Leffler 6711bd482efSSam Leffler /* perform as a bridge within the AP */ 6721bd482efSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP && 6731bd482efSSam Leffler (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) { 6741bd482efSSam Leffler struct mbuf *m1 = NULL; 6751bd482efSSam Leffler 6761bd482efSSam Leffler if (ETHER_IS_MULTICAST(eh->ether_dhost)) { 6771bd482efSSam Leffler m1 = m_copypacket(m, M_DONTWAIT); 6781bd482efSSam Leffler if (m1 == NULL) 6791bd482efSSam Leffler ifp->if_oerrors++; 6801bd482efSSam Leffler else 6811bd482efSSam Leffler m1->m_flags |= M_MCAST; 6821bd482efSSam Leffler } else { 683767cee4fSSam Leffler /* 684767cee4fSSam Leffler * Check if the destination is known; if so 685767cee4fSSam Leffler * and the port is authorized dispatch directly. 686767cee4fSSam Leffler */ 687767cee4fSSam Leffler struct ieee80211_node *sta = 688767cee4fSSam Leffler ieee80211_find_node(&ic->ic_sta, eh->ether_dhost); 689767cee4fSSam Leffler if (sta != NULL) { 690767cee4fSSam Leffler if (ieee80211_node_is_authorized(sta)) { 691767cee4fSSam Leffler /* 692767cee4fSSam Leffler * Beware of sending to ourself; this 693767cee4fSSam Leffler * needs to happen via the normal 694767cee4fSSam Leffler * input path. 695767cee4fSSam Leffler */ 696767cee4fSSam Leffler if (sta != ic->ic_bss) { 6971bd482efSSam Leffler m1 = m; 6981bd482efSSam Leffler m = NULL; 6991bd482efSSam Leffler } 700767cee4fSSam Leffler } else { 701767cee4fSSam Leffler ic->ic_stats.is_rx_unauth++; 702767cee4fSSam Leffler IEEE80211_NODE_STAT(sta, rx_unauth); 703767cee4fSSam Leffler } 704767cee4fSSam Leffler ieee80211_free_node(sta); 7051bd482efSSam Leffler } 7061bd482efSSam Leffler } 7071bd482efSSam Leffler if (m1 != NULL) 7081bd482efSSam Leffler IF_HANDOFF(&ifp->if_snd, m1, ifp); 7091bd482efSSam Leffler } 7101bd482efSSam Leffler if (m != NULL) { 7111bd482efSSam Leffler if (ni->ni_vlan != 0) { 7121bd482efSSam Leffler /* attach vlan tag */ 713d147662cSGleb Smirnoff VLAN_INPUT_TAG(ifp, m, ni->ni_vlan); 714d147662cSGleb Smirnoff if (m == NULL) 715d147662cSGleb Smirnoff goto out; /* XXX goto err? */ 7161bd482efSSam Leffler } 7171bd482efSSam Leffler (*ifp->if_input)(ifp, m); 7181bd482efSSam Leffler } 7191bd482efSSam Leffler return; 7201bd482efSSam Leffler out: 7211bd482efSSam Leffler if (m != NULL) { 7221bd482efSSam Leffler if (ic->ic_rawbpf) 7231bd482efSSam Leffler bpf_mtap(ic->ic_rawbpf, m); 7241bd482efSSam Leffler m_freem(m); 7251bd482efSSam Leffler } 7261bd482efSSam Leffler } 7271bd482efSSam Leffler 7288a1b9b6aSSam Leffler static struct mbuf * 7292cc12adeSSam Leffler ieee80211_decap(struct ieee80211com *ic, struct mbuf *m, int hdrlen) 7308a1b9b6aSSam Leffler { 7312cc12adeSSam Leffler struct ieee80211_qosframe_addr4 wh; /* Max size address frames */ 7321a1e1d21SSam Leffler struct ether_header *eh; 7331a1e1d21SSam Leffler struct llc *llc; 7341a1e1d21SSam Leffler 7352cc12adeSSam Leffler if (m->m_len < hdrlen + sizeof(*llc) && 7362cc12adeSSam Leffler (m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) { 7378a1b9b6aSSam Leffler /* XXX stat, msg */ 7381a1e1d21SSam Leffler return NULL; 7391a1e1d21SSam Leffler } 7402cc12adeSSam Leffler memcpy(&wh, mtod(m, caddr_t), hdrlen); 7412cc12adeSSam Leffler llc = (struct llc *)(mtod(m, caddr_t) + hdrlen); 7421a1e1d21SSam Leffler if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP && 7431a1e1d21SSam Leffler llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 && 7441a1e1d21SSam Leffler llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0) { 7452cc12adeSSam Leffler m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh)); 7461a1e1d21SSam Leffler llc = NULL; 7471a1e1d21SSam Leffler } else { 7482cc12adeSSam Leffler m_adj(m, hdrlen - sizeof(*eh)); 7491a1e1d21SSam Leffler } 7501a1e1d21SSam Leffler eh = mtod(m, struct ether_header *); 7511a1e1d21SSam Leffler switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) { 7521a1e1d21SSam Leffler case IEEE80211_FC1_DIR_NODS: 7531a1e1d21SSam Leffler IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1); 7541a1e1d21SSam Leffler IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2); 7551a1e1d21SSam Leffler break; 7561a1e1d21SSam Leffler case IEEE80211_FC1_DIR_TODS: 7571a1e1d21SSam Leffler IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3); 7581a1e1d21SSam Leffler IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2); 7591a1e1d21SSam Leffler break; 7601a1e1d21SSam Leffler case IEEE80211_FC1_DIR_FROMDS: 7611a1e1d21SSam Leffler IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1); 7621a1e1d21SSam Leffler IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3); 7631a1e1d21SSam Leffler break; 7641a1e1d21SSam Leffler case IEEE80211_FC1_DIR_DSTODS: 7652cc12adeSSam Leffler IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3); 7662cc12adeSSam Leffler IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr4); 7672cc12adeSSam Leffler break; 7681a1e1d21SSam Leffler } 7691a1e1d21SSam Leffler #ifdef ALIGNED_POINTER 7701a1e1d21SSam Leffler if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) { 7711a1e1d21SSam Leffler struct mbuf *n, *n0, **np; 7721a1e1d21SSam Leffler caddr_t newdata; 7731a1e1d21SSam Leffler int off, pktlen; 7741a1e1d21SSam Leffler 7751a1e1d21SSam Leffler n0 = NULL; 7761a1e1d21SSam Leffler np = &n0; 7771a1e1d21SSam Leffler off = 0; 7781a1e1d21SSam Leffler pktlen = m->m_pkthdr.len; 7791a1e1d21SSam Leffler while (pktlen > off) { 7801a1e1d21SSam Leffler if (n0 == NULL) { 7811a1e1d21SSam Leffler MGETHDR(n, M_DONTWAIT, MT_DATA); 7821a1e1d21SSam Leffler if (n == NULL) { 7831a1e1d21SSam Leffler m_freem(m); 7841a1e1d21SSam Leffler return NULL; 7851a1e1d21SSam Leffler } 7861a1e1d21SSam Leffler M_MOVE_PKTHDR(n, m); 7871a1e1d21SSam Leffler n->m_len = MHLEN; 7881a1e1d21SSam Leffler } else { 7891a1e1d21SSam Leffler MGET(n, M_DONTWAIT, MT_DATA); 7901a1e1d21SSam Leffler if (n == NULL) { 7911a1e1d21SSam Leffler m_freem(m); 7921a1e1d21SSam Leffler m_freem(n0); 7931a1e1d21SSam Leffler return NULL; 7941a1e1d21SSam Leffler } 7951a1e1d21SSam Leffler n->m_len = MLEN; 7961a1e1d21SSam Leffler } 7971a1e1d21SSam Leffler if (pktlen - off >= MINCLSIZE) { 7981a1e1d21SSam Leffler MCLGET(n, M_DONTWAIT); 7991a1e1d21SSam Leffler if (n->m_flags & M_EXT) 8001a1e1d21SSam Leffler n->m_len = n->m_ext.ext_size; 8011a1e1d21SSam Leffler } 8021a1e1d21SSam Leffler if (n0 == NULL) { 8031a1e1d21SSam Leffler newdata = 8041a1e1d21SSam Leffler (caddr_t)ALIGN(n->m_data + sizeof(*eh)) - 8051a1e1d21SSam Leffler sizeof(*eh); 8061a1e1d21SSam Leffler n->m_len -= newdata - n->m_data; 8071a1e1d21SSam Leffler n->m_data = newdata; 8081a1e1d21SSam Leffler } 8091a1e1d21SSam Leffler if (n->m_len > pktlen - off) 8101a1e1d21SSam Leffler n->m_len = pktlen - off; 8111a1e1d21SSam Leffler m_copydata(m, off, n->m_len, mtod(n, caddr_t)); 8121a1e1d21SSam Leffler off += n->m_len; 8131a1e1d21SSam Leffler *np = n; 8141a1e1d21SSam Leffler np = &n->m_next; 8151a1e1d21SSam Leffler } 8161a1e1d21SSam Leffler m_freem(m); 8171a1e1d21SSam Leffler m = n0; 8181a1e1d21SSam Leffler } 8191a1e1d21SSam Leffler #endif /* ALIGNED_POINTER */ 8201a1e1d21SSam Leffler if (llc != NULL) { 8211a1e1d21SSam Leffler eh = mtod(m, struct ether_header *); 8221a1e1d21SSam Leffler eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh)); 8231a1e1d21SSam Leffler } 8241a1e1d21SSam Leffler return m; 8251a1e1d21SSam Leffler } 8261a1e1d21SSam Leffler 8271a1e1d21SSam Leffler /* 8281a1e1d21SSam Leffler * Install received rate set information in the node's state block. 8291a1e1d21SSam Leffler */ 8307d77cd53SSam Leffler int 8317d77cd53SSam Leffler ieee80211_setup_rates(struct ieee80211_node *ni, 8327d77cd53SSam Leffler const u_int8_t *rates, const u_int8_t *xrates, int flags) 8331a1e1d21SSam Leffler { 8347d77cd53SSam Leffler struct ieee80211com *ic = ni->ni_ic; 8351a1e1d21SSam Leffler struct ieee80211_rateset *rs = &ni->ni_rates; 8361a1e1d21SSam Leffler 8371a1e1d21SSam Leffler memset(rs, 0, sizeof(*rs)); 8381a1e1d21SSam Leffler rs->rs_nrates = rates[1]; 8391a1e1d21SSam Leffler memcpy(rs->rs_rates, rates + 2, rs->rs_nrates); 8401a1e1d21SSam Leffler if (xrates != NULL) { 8411a1e1d21SSam Leffler u_int8_t nxrates; 8421a1e1d21SSam Leffler /* 8431a1e1d21SSam Leffler * Tack on 11g extended supported rate element. 8441a1e1d21SSam Leffler */ 8451a1e1d21SSam Leffler nxrates = xrates[1]; 8461a1e1d21SSam Leffler if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) { 8471a1e1d21SSam Leffler nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates; 8488a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE, 8498a1b9b6aSSam Leffler "[%s] extended rate set too large;" 8501a1e1d21SSam Leffler " only using %u of %u rates\n", 8518a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), nxrates, xrates[1]); 8521be50176SSam Leffler ic->ic_stats.is_rx_rstoobig++; 8531a1e1d21SSam Leffler } 8541a1e1d21SSam Leffler memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates); 8551a1e1d21SSam Leffler rs->rs_nrates += nxrates; 8561a1e1d21SSam Leffler } 8577d77cd53SSam Leffler return ieee80211_fix_rate(ni, flags); 8581a1e1d21SSam Leffler } 8591a1e1d21SSam Leffler 8608a1b9b6aSSam Leffler static void 8618a1b9b6aSSam Leffler ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, 8628a1b9b6aSSam Leffler struct ieee80211_node *ni, int rssi, u_int32_t rstamp, u_int16_t seq, 8638a1b9b6aSSam Leffler u_int16_t status) 8648a1b9b6aSSam Leffler { 8658a1b9b6aSSam Leffler 866b8d05d3cSSam Leffler if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { 867b8d05d3cSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 868b8d05d3cSSam Leffler ni->ni_macaddr, "open auth", 869b8d05d3cSSam Leffler "bad sta auth mode %u", ni->ni_authmode); 870b8d05d3cSSam Leffler ic->ic_stats.is_rx_bad_auth++; /* XXX */ 871b8d05d3cSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 872b8d05d3cSSam Leffler /* XXX hack to workaround calling convention */ 873b8d05d3cSSam Leffler ieee80211_send_error(ic, ni, wh->i_addr2, 874b8d05d3cSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 875b8d05d3cSSam Leffler (seq + 1) | (IEEE80211_STATUS_ALG<<16)); 876b8d05d3cSSam Leffler } 877b8d05d3cSSam Leffler return; 878b8d05d3cSSam Leffler } 8798a1b9b6aSSam Leffler switch (ic->ic_opmode) { 8808a1b9b6aSSam Leffler case IEEE80211_M_IBSS: 8818a1b9b6aSSam Leffler case IEEE80211_M_AHDEMO: 8824fd1a57dSSam Leffler case IEEE80211_M_MONITOR: 8838a1b9b6aSSam Leffler /* should not come here */ 8844fd1a57dSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 8854fd1a57dSSam Leffler ni->ni_macaddr, "open auth", 8864fd1a57dSSam Leffler "bad operating mode %u", ic->ic_opmode); 8878a1b9b6aSSam Leffler break; 8888a1b9b6aSSam Leffler 8898a1b9b6aSSam Leffler case IEEE80211_M_HOSTAP: 8908a1b9b6aSSam Leffler if (ic->ic_state != IEEE80211_S_RUN || 8918a1b9b6aSSam Leffler seq != IEEE80211_AUTH_OPEN_REQUEST) { 8928a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 8938a1b9b6aSSam Leffler return; 8948a1b9b6aSSam Leffler } 8958a1b9b6aSSam Leffler /* always accept open authentication requests */ 8968a1b9b6aSSam Leffler if (ni == ic->ic_bss) { 897acc4f7f5SSam Leffler ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2); 8988a1b9b6aSSam Leffler if (ni == NULL) 8998a1b9b6aSSam Leffler return; 900ebdda46cSSam Leffler } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) 9018a1b9b6aSSam Leffler (void) ieee80211_ref_node(ni); 902ebdda46cSSam Leffler /* 903ebdda46cSSam Leffler * Mark the node as referenced to reflect that it's 904ebdda46cSSam Leffler * reference count has been bumped to insure it remains 905ebdda46cSSam Leffler * after the transaction completes. 906ebdda46cSSam Leffler */ 907ebdda46cSSam Leffler ni->ni_flags |= IEEE80211_NODE_AREF; 908ebdda46cSSam Leffler 9098a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 9108a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); 9118a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 9128a1b9b6aSSam Leffler "[%s] station authenticated (open)\n", 9138a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr)); 914da17aba1SSam Leffler /* 915da17aba1SSam Leffler * When 802.1x is not in use mark the port 916da17aba1SSam Leffler * authorized at this point so traffic can flow. 917da17aba1SSam Leffler */ 918da17aba1SSam Leffler if (ni->ni_authmode != IEEE80211_AUTH_8021X) 919e4918ecdSSam Leffler ieee80211_node_authorize(ni); 9208a1b9b6aSSam Leffler break; 9218a1b9b6aSSam Leffler 9228a1b9b6aSSam Leffler case IEEE80211_M_STA: 9238a1b9b6aSSam Leffler if (ic->ic_state != IEEE80211_S_AUTH || 9248a1b9b6aSSam Leffler seq != IEEE80211_AUTH_OPEN_RESPONSE) { 9258a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 9268a1b9b6aSSam Leffler return; 9278a1b9b6aSSam Leffler } 9288a1b9b6aSSam Leffler if (status != 0) { 9298a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, 9308a1b9b6aSSam Leffler IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 9318a1b9b6aSSam Leffler "[%s] open auth failed (reason %d)\n", 9328a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), status); 9338a1b9b6aSSam Leffler /* XXX can this happen? */ 9348a1b9b6aSSam Leffler if (ni != ic->ic_bss) 9358a1b9b6aSSam Leffler ni->ni_fails++; 9368a1b9b6aSSam Leffler ic->ic_stats.is_rx_auth_fail++; 937181181acSSam Leffler ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 938181181acSSam Leffler } else 9398a1b9b6aSSam Leffler ieee80211_new_state(ic, IEEE80211_S_ASSOC, 9408a1b9b6aSSam Leffler wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); 9418a1b9b6aSSam Leffler break; 9428a1b9b6aSSam Leffler } 9438a1b9b6aSSam Leffler } 9448a1b9b6aSSam Leffler 94584eb84c4SSam Leffler /* 94684eb84c4SSam Leffler * Send a management frame error response to the specified 94784eb84c4SSam Leffler * station. If ni is associated with the station then use 94884eb84c4SSam Leffler * it; otherwise allocate a temporary node suitable for 94984eb84c4SSam Leffler * transmitting the frame and then free the reference so 95084eb84c4SSam Leffler * it will go away as soon as the frame has been transmitted. 95184eb84c4SSam Leffler */ 95284eb84c4SSam Leffler static void 95384eb84c4SSam Leffler ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni, 95484eb84c4SSam Leffler const u_int8_t *mac, int subtype, int arg) 95584eb84c4SSam Leffler { 95684eb84c4SSam Leffler int istmp; 95784eb84c4SSam Leffler 95884eb84c4SSam Leffler if (ni == ic->ic_bss) { 95997c973adSSam Leffler ni = ieee80211_tmp_node(ic, mac); 96084eb84c4SSam Leffler if (ni == NULL) { 96184eb84c4SSam Leffler /* XXX msg */ 96284eb84c4SSam Leffler return; 96384eb84c4SSam Leffler } 96484eb84c4SSam Leffler istmp = 1; 96584eb84c4SSam Leffler } else 96684eb84c4SSam Leffler istmp = 0; 96784eb84c4SSam Leffler IEEE80211_SEND_MGMT(ic, ni, subtype, arg); 96884eb84c4SSam Leffler if (istmp) 96984eb84c4SSam Leffler ieee80211_free_node(ni); 97084eb84c4SSam Leffler } 97184eb84c4SSam Leffler 9728a1b9b6aSSam Leffler static int 9738a1b9b6aSSam Leffler alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni) 9748a1b9b6aSSam Leffler { 9758a1b9b6aSSam Leffler if (ni->ni_challenge == NULL) 9768a1b9b6aSSam Leffler MALLOC(ni->ni_challenge, u_int32_t*, IEEE80211_CHALLENGE_LEN, 9778a1b9b6aSSam Leffler M_DEVBUF, M_NOWAIT); 9788a1b9b6aSSam Leffler if (ni->ni_challenge == NULL) { 9798a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 9808a1b9b6aSSam Leffler "[%s] shared key challenge alloc failed\n", 9818a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr)); 9828a1b9b6aSSam Leffler /* XXX statistic */ 9838a1b9b6aSSam Leffler } 9848a1b9b6aSSam Leffler return (ni->ni_challenge != NULL); 9858a1b9b6aSSam Leffler } 9868a1b9b6aSSam Leffler 9878a1b9b6aSSam Leffler /* XXX TODO: add statistics */ 9888a1b9b6aSSam Leffler static void 9898a1b9b6aSSam Leffler ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, 9908a1b9b6aSSam Leffler u_int8_t *frm, u_int8_t *efrm, struct ieee80211_node *ni, int rssi, 9918a1b9b6aSSam Leffler u_int32_t rstamp, u_int16_t seq, u_int16_t status) 9928a1b9b6aSSam Leffler { 9938a1b9b6aSSam Leffler u_int8_t *challenge; 9948a1b9b6aSSam Leffler int allocbs, estatus; 9958a1b9b6aSSam Leffler 9968a1b9b6aSSam Leffler /* 9978a1b9b6aSSam Leffler * NB: this can happen as we allow pre-shared key 9988a1b9b6aSSam Leffler * authentication to be enabled w/o wep being turned 9998a1b9b6aSSam Leffler * on so that configuration of these can be done 10008a1b9b6aSSam Leffler * in any order. It may be better to enforce the 10018a1b9b6aSSam Leffler * ordering in which case this check would just be 10028a1b9b6aSSam Leffler * for sanity/consistency. 10038a1b9b6aSSam Leffler */ 10048a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { 10058a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 10068a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 10078a1b9b6aSSam Leffler "%s", " PRIVACY is disabled"); 10088a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_ALG; 10098a1b9b6aSSam Leffler goto bad; 10108a1b9b6aSSam Leffler } 10118a1b9b6aSSam Leffler /* 10128a1b9b6aSSam Leffler * Pre-shared key authentication is evil; accept 10138a1b9b6aSSam Leffler * it only if explicitly configured (it is supported 10148a1b9b6aSSam Leffler * mainly for compatibility with clients like OS X). 10158a1b9b6aSSam Leffler */ 10168a1b9b6aSSam Leffler if (ni->ni_authmode != IEEE80211_AUTH_AUTO && 10178a1b9b6aSSam Leffler ni->ni_authmode != IEEE80211_AUTH_SHARED) { 10188a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 10198a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 10208a1b9b6aSSam Leffler "bad sta auth mode %u", ni->ni_authmode); 10218a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */ 10228a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_ALG; 10238a1b9b6aSSam Leffler goto bad; 10248a1b9b6aSSam Leffler } 10258a1b9b6aSSam Leffler 10268a1b9b6aSSam Leffler challenge = NULL; 10278a1b9b6aSSam Leffler if (frm + 1 < efrm) { 10288a1b9b6aSSam Leffler if ((frm[1] + 2) > (efrm - frm)) { 10298a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 10308a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 10318a1b9b6aSSam Leffler "ie %d/%d too long", 10328a1b9b6aSSam Leffler frm[0], (frm[1] + 2) - (efrm - frm)); 10338a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 10348a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 10358a1b9b6aSSam Leffler goto bad; 10368a1b9b6aSSam Leffler } 10378a1b9b6aSSam Leffler if (*frm == IEEE80211_ELEMID_CHALLENGE) 10388a1b9b6aSSam Leffler challenge = frm; 10398a1b9b6aSSam Leffler frm += frm[1] + 2; 10408a1b9b6aSSam Leffler } 10418a1b9b6aSSam Leffler switch (seq) { 10428a1b9b6aSSam Leffler case IEEE80211_AUTH_SHARED_CHALLENGE: 10438a1b9b6aSSam Leffler case IEEE80211_AUTH_SHARED_RESPONSE: 10448a1b9b6aSSam Leffler if (challenge == NULL) { 10458a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 10468a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 10478a1b9b6aSSam Leffler "%s", "no challenge"); 10488a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 10498a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 10508a1b9b6aSSam Leffler goto bad; 10518a1b9b6aSSam Leffler } 10528a1b9b6aSSam Leffler if (challenge[1] != IEEE80211_CHALLENGE_LEN) { 10538a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 10548a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 10558a1b9b6aSSam Leffler "bad challenge len %d", challenge[1]); 10568a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 10578a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 10588a1b9b6aSSam Leffler goto bad; 10598a1b9b6aSSam Leffler } 10608a1b9b6aSSam Leffler default: 10618a1b9b6aSSam Leffler break; 10628a1b9b6aSSam Leffler } 10638a1b9b6aSSam Leffler switch (ic->ic_opmode) { 10648a1b9b6aSSam Leffler case IEEE80211_M_MONITOR: 10658a1b9b6aSSam Leffler case IEEE80211_M_AHDEMO: 10668a1b9b6aSSam Leffler case IEEE80211_M_IBSS: 10678a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 10688a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 10698a1b9b6aSSam Leffler "bad operating mode %u", ic->ic_opmode); 10708a1b9b6aSSam Leffler return; 10718a1b9b6aSSam Leffler case IEEE80211_M_HOSTAP: 10728a1b9b6aSSam Leffler if (ic->ic_state != IEEE80211_S_RUN) { 10738a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 10748a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 10758a1b9b6aSSam Leffler "bad state %u", ic->ic_state); 10768a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_ALG; /* XXX */ 10778a1b9b6aSSam Leffler goto bad; 10788a1b9b6aSSam Leffler } 10798a1b9b6aSSam Leffler switch (seq) { 10808a1b9b6aSSam Leffler case IEEE80211_AUTH_SHARED_REQUEST: 10818a1b9b6aSSam Leffler if (ni == ic->ic_bss) { 1082acc4f7f5SSam Leffler ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2); 10838a1b9b6aSSam Leffler if (ni == NULL) { 10848a1b9b6aSSam Leffler /* NB: no way to return an error */ 10858a1b9b6aSSam Leffler return; 10868a1b9b6aSSam Leffler } 10878a1b9b6aSSam Leffler allocbs = 1; 10888a1b9b6aSSam Leffler } else { 1089ebdda46cSSam Leffler if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) 10908a1b9b6aSSam Leffler (void) ieee80211_ref_node(ni); 10918a1b9b6aSSam Leffler allocbs = 0; 10928a1b9b6aSSam Leffler } 1093ebdda46cSSam Leffler /* 1094ebdda46cSSam Leffler * Mark the node as referenced to reflect that it's 1095ebdda46cSSam Leffler * reference count has been bumped to insure it remains 1096ebdda46cSSam Leffler * after the transaction completes. 1097ebdda46cSSam Leffler */ 1098ebdda46cSSam Leffler ni->ni_flags |= IEEE80211_NODE_AREF; 10998a1b9b6aSSam Leffler ni->ni_rssi = rssi; 11008a1b9b6aSSam Leffler ni->ni_rstamp = rstamp; 11018a1b9b6aSSam Leffler if (!alloc_challenge(ic, ni)) { 11028a1b9b6aSSam Leffler /* NB: don't return error so they rexmit */ 11038a1b9b6aSSam Leffler return; 11048a1b9b6aSSam Leffler } 11058a1b9b6aSSam Leffler get_random_bytes(ni->ni_challenge, 11068a1b9b6aSSam Leffler IEEE80211_CHALLENGE_LEN); 11078a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, 11088a1b9b6aSSam Leffler IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 11098a1b9b6aSSam Leffler "[%s] shared key %sauth request\n", 11108a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), 11118a1b9b6aSSam Leffler allocbs ? "" : "re"); 11128a1b9b6aSSam Leffler break; 11138a1b9b6aSSam Leffler case IEEE80211_AUTH_SHARED_RESPONSE: 11148a1b9b6aSSam Leffler if (ni == ic->ic_bss) { 11158a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 11168a1b9b6aSSam Leffler ni->ni_macaddr, "shared key response", 11178a1b9b6aSSam Leffler "%s", "unknown station"); 11188a1b9b6aSSam Leffler /* NB: don't send a response */ 11198a1b9b6aSSam Leffler return; 11208a1b9b6aSSam Leffler } 11218a1b9b6aSSam Leffler if (ni->ni_challenge == NULL) { 11228a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 11238a1b9b6aSSam Leffler ni->ni_macaddr, "shared key response", 11248a1b9b6aSSam Leffler "%s", "no challenge recorded"); 11258a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 11268a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 11278a1b9b6aSSam Leffler goto bad; 11288a1b9b6aSSam Leffler } 11298a1b9b6aSSam Leffler if (memcmp(ni->ni_challenge, &challenge[2], 11308a1b9b6aSSam Leffler challenge[1]) != 0) { 11318a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 11328a1b9b6aSSam Leffler ni->ni_macaddr, "shared key response", 11338a1b9b6aSSam Leffler "%s", "challenge mismatch"); 11348a1b9b6aSSam Leffler ic->ic_stats.is_rx_auth_fail++; 11358a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 11368a1b9b6aSSam Leffler goto bad; 11378a1b9b6aSSam Leffler } 11388a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, 11398a1b9b6aSSam Leffler IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 11408a1b9b6aSSam Leffler "[%s] station authenticated (shared key)\n", 11418a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr)); 1142e4918ecdSSam Leffler ieee80211_node_authorize(ni); 11438a1b9b6aSSam Leffler break; 11448a1b9b6aSSam Leffler default: 11458a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 11468a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 11478a1b9b6aSSam Leffler "bad seq %d", seq); 11488a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 11498a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_SEQUENCE; 11508a1b9b6aSSam Leffler goto bad; 11518a1b9b6aSSam Leffler } 11528a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 11538a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); 11548a1b9b6aSSam Leffler break; 11558a1b9b6aSSam Leffler 11568a1b9b6aSSam Leffler case IEEE80211_M_STA: 11578a1b9b6aSSam Leffler if (ic->ic_state != IEEE80211_S_AUTH) 11588a1b9b6aSSam Leffler return; 11598a1b9b6aSSam Leffler switch (seq) { 11608a1b9b6aSSam Leffler case IEEE80211_AUTH_SHARED_PASS: 11618a1b9b6aSSam Leffler if (ni->ni_challenge != NULL) { 11628a1b9b6aSSam Leffler FREE(ni->ni_challenge, M_DEVBUF); 11638a1b9b6aSSam Leffler ni->ni_challenge = NULL; 11648a1b9b6aSSam Leffler } 11658a1b9b6aSSam Leffler if (status != 0) { 11668a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, 11678a1b9b6aSSam Leffler IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 11688a1b9b6aSSam Leffler "[%s] shared key auth failed (reason %d)\n", 11698a1b9b6aSSam Leffler ether_sprintf(ieee80211_getbssid(ic, wh)), 11708a1b9b6aSSam Leffler status); 11718a1b9b6aSSam Leffler /* XXX can this happen? */ 11728a1b9b6aSSam Leffler if (ni != ic->ic_bss) 11738a1b9b6aSSam Leffler ni->ni_fails++; 11748a1b9b6aSSam Leffler ic->ic_stats.is_rx_auth_fail++; 11758a1b9b6aSSam Leffler return; 11768a1b9b6aSSam Leffler } 11778a1b9b6aSSam Leffler ieee80211_new_state(ic, IEEE80211_S_ASSOC, 11788a1b9b6aSSam Leffler wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); 11798a1b9b6aSSam Leffler break; 11808a1b9b6aSSam Leffler case IEEE80211_AUTH_SHARED_CHALLENGE: 11818a1b9b6aSSam Leffler if (!alloc_challenge(ic, ni)) 11828a1b9b6aSSam Leffler return; 11838a1b9b6aSSam Leffler /* XXX could optimize by passing recvd challenge */ 11848a1b9b6aSSam Leffler memcpy(ni->ni_challenge, &challenge[2], challenge[1]); 11858a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 11868a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); 11878a1b9b6aSSam Leffler break; 11888a1b9b6aSSam Leffler default: 11898a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_AUTH, 11908a1b9b6aSSam Leffler wh, "shared key auth", "bad seq %d", seq); 11918a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 11928a1b9b6aSSam Leffler return; 11938a1b9b6aSSam Leffler } 11948a1b9b6aSSam Leffler break; 11958a1b9b6aSSam Leffler } 11968a1b9b6aSSam Leffler return; 11978a1b9b6aSSam Leffler bad: 11988a1b9b6aSSam Leffler /* 11998a1b9b6aSSam Leffler * Send an error response; but only when operating as an AP. 12008a1b9b6aSSam Leffler */ 12018a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 12028a1b9b6aSSam Leffler /* XXX hack to workaround calling convention */ 120384eb84c4SSam Leffler ieee80211_send_error(ic, ni, wh->i_addr2, 12048a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 12058a1b9b6aSSam Leffler (seq + 1) | (estatus<<16)); 1206181181acSSam Leffler } else if (ic->ic_opmode == IEEE80211_M_STA) { 1207181181acSSam Leffler /* 1208181181acSSam Leffler * Kick the state machine. This short-circuits 1209181181acSSam Leffler * using the mgt frame timeout to trigger the 1210181181acSSam Leffler * state transition. 1211181181acSSam Leffler */ 1212181181acSSam Leffler if (ic->ic_state == IEEE80211_S_AUTH) 1213181181acSSam Leffler ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 12148a1b9b6aSSam Leffler } 12158a1b9b6aSSam Leffler } 12168a1b9b6aSSam Leffler 12171a1e1d21SSam Leffler /* Verify the existence and length of __elem or get out. */ 12181a1e1d21SSam Leffler #define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \ 12191a1e1d21SSam Leffler if ((__elem) == NULL) { \ 12208a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ 12218a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> \ 12228a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], \ 12238a1b9b6aSSam Leffler "%s", "no " #__elem ); \ 12241be50176SSam Leffler ic->ic_stats.is_rx_elem_missing++; \ 12251a1e1d21SSam Leffler return; \ 12261a1e1d21SSam Leffler } \ 12271a1e1d21SSam Leffler if ((__elem)[1] > (__maxlen)) { \ 12288a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ 12298a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> \ 12301a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], \ 12318a1b9b6aSSam Leffler "bad " #__elem " len %d", (__elem)[1]); \ 12321be50176SSam Leffler ic->ic_stats.is_rx_elem_toobig++; \ 12331a1e1d21SSam Leffler return; \ 12341a1e1d21SSam Leffler } \ 12351a1e1d21SSam Leffler } while (0) 12361a1e1d21SSam Leffler 12371a1e1d21SSam Leffler #define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \ 12381a1e1d21SSam Leffler if ((_len) < (_minlen)) { \ 12398a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ 12408a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> \ 12411a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], \ 12428a1b9b6aSSam Leffler "%s", "ie too short"); \ 12431be50176SSam Leffler ic->ic_stats.is_rx_elem_toosmall++; \ 12441a1e1d21SSam Leffler return; \ 12451a1e1d21SSam Leffler } \ 12461a1e1d21SSam Leffler } while (0) 12471a1e1d21SSam Leffler 12488a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 12498a1b9b6aSSam Leffler static void 12508a1b9b6aSSam Leffler ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag, 12518a1b9b6aSSam Leffler u_int8_t mac[IEEE80211_ADDR_LEN], u_int8_t *ssid) 12528a1b9b6aSSam Leffler { 12538a1b9b6aSSam Leffler printf("[%s] discard %s frame, ssid mismatch: ", 12548a1b9b6aSSam Leffler ether_sprintf(mac), tag); 12558a1b9b6aSSam Leffler ieee80211_print_essid(ssid + 2, ssid[1]); 12568a1b9b6aSSam Leffler printf("\n"); 12578a1b9b6aSSam Leffler } 12588a1b9b6aSSam Leffler 12598a1b9b6aSSam Leffler #define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \ 12608a1b9b6aSSam Leffler if ((_ssid)[1] != 0 && \ 12618a1b9b6aSSam Leffler ((_ssid)[1] != (_ni)->ni_esslen || \ 12628a1b9b6aSSam Leffler memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \ 12638a1b9b6aSSam Leffler if (ieee80211_msg_input(ic)) \ 12648a1b9b6aSSam Leffler ieee80211_ssid_mismatch(ic, \ 12658a1b9b6aSSam Leffler ieee80211_mgt_subtype_name[subtype >> \ 12668a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], \ 12678a1b9b6aSSam Leffler wh->i_addr2, _ssid); \ 12688a1b9b6aSSam Leffler ic->ic_stats.is_rx_ssidmismatch++; \ 12698a1b9b6aSSam Leffler return; \ 12708a1b9b6aSSam Leffler } \ 12718a1b9b6aSSam Leffler } while (0) 12728a1b9b6aSSam Leffler #else /* !IEEE80211_DEBUG */ 12738a1b9b6aSSam Leffler #define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \ 12748a1b9b6aSSam Leffler if ((_ssid)[1] != 0 && \ 12758a1b9b6aSSam Leffler ((_ssid)[1] != (_ni)->ni_esslen || \ 12768a1b9b6aSSam Leffler memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \ 12778a1b9b6aSSam Leffler ic->ic_stats.is_rx_ssidmismatch++; \ 12788a1b9b6aSSam Leffler return; \ 12798a1b9b6aSSam Leffler } \ 12808a1b9b6aSSam Leffler } while (0) 12818a1b9b6aSSam Leffler #endif /* !IEEE80211_DEBUG */ 12828a1b9b6aSSam Leffler 12838a1b9b6aSSam Leffler /* unalligned little endian access */ 12848a1b9b6aSSam Leffler #define LE_READ_2(p) \ 12858a1b9b6aSSam Leffler ((u_int16_t) \ 12868a1b9b6aSSam Leffler ((((const u_int8_t *)(p))[0] ) | \ 12878a1b9b6aSSam Leffler (((const u_int8_t *)(p))[1] << 8))) 12888a1b9b6aSSam Leffler #define LE_READ_4(p) \ 12898a1b9b6aSSam Leffler ((u_int32_t) \ 12908a1b9b6aSSam Leffler ((((const u_int8_t *)(p))[0] ) | \ 12918a1b9b6aSSam Leffler (((const u_int8_t *)(p))[1] << 8) | \ 12928a1b9b6aSSam Leffler (((const u_int8_t *)(p))[2] << 16) | \ 12938a1b9b6aSSam Leffler (((const u_int8_t *)(p))[3] << 24))) 12948a1b9b6aSSam Leffler 12958a1b9b6aSSam Leffler static int __inline 12968a1b9b6aSSam Leffler iswpaoui(const u_int8_t *frm) 12978a1b9b6aSSam Leffler { 12988a1b9b6aSSam Leffler return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); 12998a1b9b6aSSam Leffler } 13008a1b9b6aSSam Leffler 13018a1b9b6aSSam Leffler static int __inline 13028a1b9b6aSSam Leffler iswmeoui(const u_int8_t *frm) 13038a1b9b6aSSam Leffler { 13048a1b9b6aSSam Leffler return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI); 13058a1b9b6aSSam Leffler } 13068a1b9b6aSSam Leffler 13078a1b9b6aSSam Leffler static int __inline 13088a1b9b6aSSam Leffler iswmeparam(const u_int8_t *frm) 13098a1b9b6aSSam Leffler { 13108a1b9b6aSSam Leffler return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && 13118a1b9b6aSSam Leffler frm[6] == WME_PARAM_OUI_SUBTYPE; 13128a1b9b6aSSam Leffler } 13138a1b9b6aSSam Leffler 13148a1b9b6aSSam Leffler static int __inline 13158a1b9b6aSSam Leffler iswmeinfo(const u_int8_t *frm) 13168a1b9b6aSSam Leffler { 13178a1b9b6aSSam Leffler return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && 13188a1b9b6aSSam Leffler frm[6] == WME_INFO_OUI_SUBTYPE; 13198a1b9b6aSSam Leffler } 13208a1b9b6aSSam Leffler 13218a1b9b6aSSam Leffler static int __inline 13228a1b9b6aSSam Leffler isatherosoui(const u_int8_t *frm) 13238a1b9b6aSSam Leffler { 13248a1b9b6aSSam Leffler return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); 13258a1b9b6aSSam Leffler } 13268a1b9b6aSSam Leffler 13278a1b9b6aSSam Leffler /* 13288a1b9b6aSSam Leffler * Convert a WPA cipher selector OUI to an internal 13298a1b9b6aSSam Leffler * cipher algorithm. Where appropriate we also 13308a1b9b6aSSam Leffler * record any key length. 13318a1b9b6aSSam Leffler */ 13328a1b9b6aSSam Leffler static int 13338a1b9b6aSSam Leffler wpa_cipher(u_int8_t *sel, u_int8_t *keylen) 13348a1b9b6aSSam Leffler { 13358a1b9b6aSSam Leffler #define WPA_SEL(x) (((x)<<24)|WPA_OUI) 13368a1b9b6aSSam Leffler u_int32_t w = LE_READ_4(sel); 13378a1b9b6aSSam Leffler 13388a1b9b6aSSam Leffler switch (w) { 13398a1b9b6aSSam Leffler case WPA_SEL(WPA_CSE_NULL): 13408a1b9b6aSSam Leffler return IEEE80211_CIPHER_NONE; 13418a1b9b6aSSam Leffler case WPA_SEL(WPA_CSE_WEP40): 13428a1b9b6aSSam Leffler if (keylen) 13438a1b9b6aSSam Leffler *keylen = 40 / NBBY; 13448a1b9b6aSSam Leffler return IEEE80211_CIPHER_WEP; 13458a1b9b6aSSam Leffler case WPA_SEL(WPA_CSE_WEP104): 13468a1b9b6aSSam Leffler if (keylen) 13478a1b9b6aSSam Leffler *keylen = 104 / NBBY; 13488a1b9b6aSSam Leffler return IEEE80211_CIPHER_WEP; 13498a1b9b6aSSam Leffler case WPA_SEL(WPA_CSE_TKIP): 13508a1b9b6aSSam Leffler return IEEE80211_CIPHER_TKIP; 13518a1b9b6aSSam Leffler case WPA_SEL(WPA_CSE_CCMP): 13528a1b9b6aSSam Leffler return IEEE80211_CIPHER_AES_CCM; 13538a1b9b6aSSam Leffler } 13548a1b9b6aSSam Leffler return 32; /* NB: so 1<< is discarded */ 13558a1b9b6aSSam Leffler #undef WPA_SEL 13568a1b9b6aSSam Leffler } 13578a1b9b6aSSam Leffler 13588a1b9b6aSSam Leffler /* 13598a1b9b6aSSam Leffler * Convert a WPA key management/authentication algorithm 13608a1b9b6aSSam Leffler * to an internal code. 13618a1b9b6aSSam Leffler */ 13628a1b9b6aSSam Leffler static int 13638a1b9b6aSSam Leffler wpa_keymgmt(u_int8_t *sel) 13648a1b9b6aSSam Leffler { 13658a1b9b6aSSam Leffler #define WPA_SEL(x) (((x)<<24)|WPA_OUI) 13668a1b9b6aSSam Leffler u_int32_t w = LE_READ_4(sel); 13678a1b9b6aSSam Leffler 13688a1b9b6aSSam Leffler switch (w) { 13698a1b9b6aSSam Leffler case WPA_SEL(WPA_ASE_8021X_UNSPEC): 13708a1b9b6aSSam Leffler return WPA_ASE_8021X_UNSPEC; 13718a1b9b6aSSam Leffler case WPA_SEL(WPA_ASE_8021X_PSK): 13728a1b9b6aSSam Leffler return WPA_ASE_8021X_PSK; 13738a1b9b6aSSam Leffler case WPA_SEL(WPA_ASE_NONE): 13748a1b9b6aSSam Leffler return WPA_ASE_NONE; 13758a1b9b6aSSam Leffler } 13768a1b9b6aSSam Leffler return 0; /* NB: so is discarded */ 13778a1b9b6aSSam Leffler #undef WPA_SEL 13788a1b9b6aSSam Leffler } 13798a1b9b6aSSam Leffler 13808a1b9b6aSSam Leffler /* 13818a1b9b6aSSam Leffler * Parse a WPA information element to collect parameters 13828a1b9b6aSSam Leffler * and validate the parameters against what has been 13838a1b9b6aSSam Leffler * configured for the system. 13848a1b9b6aSSam Leffler */ 13858a1b9b6aSSam Leffler static int 13868a1b9b6aSSam Leffler ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm, 13878a1b9b6aSSam Leffler struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) 13888a1b9b6aSSam Leffler { 13898a1b9b6aSSam Leffler u_int8_t len = frm[1]; 13908a1b9b6aSSam Leffler u_int32_t w; 13918a1b9b6aSSam Leffler int n; 13928a1b9b6aSSam Leffler 13938a1b9b6aSSam Leffler /* 13948a1b9b6aSSam Leffler * Check the length once for fixed parts: OUI, type, 13958a1b9b6aSSam Leffler * version, mcast cipher, and 2 selector counts. 13968a1b9b6aSSam Leffler * Other, variable-length data, must be checked separately. 13978a1b9b6aSSam Leffler */ 1398bdad3a10SSam Leffler if ((ic->ic_flags & IEEE80211_F_WPA1) == 0) { 1399bdad3a10SSam Leffler IEEE80211_DISCARD_IE(ic, 1400bdad3a10SSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1401bdad3a10SSam Leffler wh, "WPA", "not WPA, flags 0x%x", ic->ic_flags); 1402bdad3a10SSam Leffler return IEEE80211_REASON_IE_INVALID; 1403bdad3a10SSam Leffler } 14048a1b9b6aSSam Leffler if (len < 14) { 14058a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 14068a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 14078a1b9b6aSSam Leffler wh, "WPA", "too short, len %u", len); 14088a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 14098a1b9b6aSSam Leffler } 14108a1b9b6aSSam Leffler frm += 6, len -= 4; /* NB: len is payload only */ 14118a1b9b6aSSam Leffler /* NB: iswapoui already validated the OUI and type */ 14128a1b9b6aSSam Leffler w = LE_READ_2(frm); 14138a1b9b6aSSam Leffler if (w != WPA_VERSION) { 14148a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 14158a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 14168a1b9b6aSSam Leffler wh, "WPA", "bad version %u", w); 14178a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 14188a1b9b6aSSam Leffler } 14198a1b9b6aSSam Leffler frm += 2, len -= 2; 14208a1b9b6aSSam Leffler 14218a1b9b6aSSam Leffler /* multicast/group cipher */ 14228a1b9b6aSSam Leffler w = wpa_cipher(frm, &rsn->rsn_mcastkeylen); 14238a1b9b6aSSam Leffler if (w != rsn->rsn_mcastcipher) { 14248a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 14258a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 14268a1b9b6aSSam Leffler wh, "WPA", "mcast cipher mismatch; got %u, expected %u", 14278a1b9b6aSSam Leffler w, rsn->rsn_mcastcipher); 14288a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 14298a1b9b6aSSam Leffler } 14308a1b9b6aSSam Leffler frm += 4, len -= 4; 14318a1b9b6aSSam Leffler 14328a1b9b6aSSam Leffler /* unicast ciphers */ 14338a1b9b6aSSam Leffler n = LE_READ_2(frm); 14348a1b9b6aSSam Leffler frm += 2, len -= 2; 14358a1b9b6aSSam Leffler if (len < n*4+2) { 14368a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 14378a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 14388a1b9b6aSSam Leffler wh, "WPA", "ucast cipher data too short; len %u, n %u", 14398a1b9b6aSSam Leffler len, n); 14408a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 14418a1b9b6aSSam Leffler } 14428a1b9b6aSSam Leffler w = 0; 14438a1b9b6aSSam Leffler for (; n > 0; n--) { 14448a1b9b6aSSam Leffler w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen); 14458a1b9b6aSSam Leffler frm += 4, len -= 4; 14468a1b9b6aSSam Leffler } 14478a1b9b6aSSam Leffler w &= rsn->rsn_ucastcipherset; 14488a1b9b6aSSam Leffler if (w == 0) { 14498a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 14508a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 14518a1b9b6aSSam Leffler wh, "WPA", "%s", "ucast cipher set empty"); 14528a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 14538a1b9b6aSSam Leffler } 14548a1b9b6aSSam Leffler if (w & (1<<IEEE80211_CIPHER_TKIP)) 14558a1b9b6aSSam Leffler rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP; 14568a1b9b6aSSam Leffler else 14578a1b9b6aSSam Leffler rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; 14588a1b9b6aSSam Leffler 14598a1b9b6aSSam Leffler /* key management algorithms */ 14608a1b9b6aSSam Leffler n = LE_READ_2(frm); 14618a1b9b6aSSam Leffler frm += 2, len -= 2; 14628a1b9b6aSSam Leffler if (len < n*4) { 14638a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 14648a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 14658a1b9b6aSSam Leffler wh, "WPA", "key mgmt alg data too short; len %u, n %u", 14668a1b9b6aSSam Leffler len, n); 14678a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 14688a1b9b6aSSam Leffler } 14698a1b9b6aSSam Leffler w = 0; 14708a1b9b6aSSam Leffler for (; n > 0; n--) { 14718a1b9b6aSSam Leffler w |= wpa_keymgmt(frm); 14728a1b9b6aSSam Leffler frm += 4, len -= 4; 14738a1b9b6aSSam Leffler } 14748a1b9b6aSSam Leffler w &= rsn->rsn_keymgmtset; 14758a1b9b6aSSam Leffler if (w == 0) { 14768a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 14778a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 14788a1b9b6aSSam Leffler wh, "WPA", "%s", "no acceptable key mgmt alg"); 14798a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 14808a1b9b6aSSam Leffler } 14818a1b9b6aSSam Leffler if (w & WPA_ASE_8021X_UNSPEC) 14828a1b9b6aSSam Leffler rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC; 14838a1b9b6aSSam Leffler else 14848a1b9b6aSSam Leffler rsn->rsn_keymgmt = WPA_ASE_8021X_PSK; 14858a1b9b6aSSam Leffler 14868a1b9b6aSSam Leffler if (len > 2) /* optional capabilities */ 14878a1b9b6aSSam Leffler rsn->rsn_caps = LE_READ_2(frm); 14888a1b9b6aSSam Leffler 14898a1b9b6aSSam Leffler return 0; 14908a1b9b6aSSam Leffler } 14918a1b9b6aSSam Leffler 14928a1b9b6aSSam Leffler /* 14938a1b9b6aSSam Leffler * Convert an RSN cipher selector OUI to an internal 14948a1b9b6aSSam Leffler * cipher algorithm. Where appropriate we also 14958a1b9b6aSSam Leffler * record any key length. 14968a1b9b6aSSam Leffler */ 14978a1b9b6aSSam Leffler static int 14988a1b9b6aSSam Leffler rsn_cipher(u_int8_t *sel, u_int8_t *keylen) 14998a1b9b6aSSam Leffler { 15008a1b9b6aSSam Leffler #define RSN_SEL(x) (((x)<<24)|RSN_OUI) 15018a1b9b6aSSam Leffler u_int32_t w = LE_READ_4(sel); 15028a1b9b6aSSam Leffler 15038a1b9b6aSSam Leffler switch (w) { 15048a1b9b6aSSam Leffler case RSN_SEL(RSN_CSE_NULL): 15058a1b9b6aSSam Leffler return IEEE80211_CIPHER_NONE; 15068a1b9b6aSSam Leffler case RSN_SEL(RSN_CSE_WEP40): 15078a1b9b6aSSam Leffler if (keylen) 15088a1b9b6aSSam Leffler *keylen = 40 / NBBY; 15098a1b9b6aSSam Leffler return IEEE80211_CIPHER_WEP; 15108a1b9b6aSSam Leffler case RSN_SEL(RSN_CSE_WEP104): 15118a1b9b6aSSam Leffler if (keylen) 15128a1b9b6aSSam Leffler *keylen = 104 / NBBY; 15138a1b9b6aSSam Leffler return IEEE80211_CIPHER_WEP; 15148a1b9b6aSSam Leffler case RSN_SEL(RSN_CSE_TKIP): 15158a1b9b6aSSam Leffler return IEEE80211_CIPHER_TKIP; 15168a1b9b6aSSam Leffler case RSN_SEL(RSN_CSE_CCMP): 15178a1b9b6aSSam Leffler return IEEE80211_CIPHER_AES_CCM; 15188a1b9b6aSSam Leffler case RSN_SEL(RSN_CSE_WRAP): 15198a1b9b6aSSam Leffler return IEEE80211_CIPHER_AES_OCB; 15208a1b9b6aSSam Leffler } 15218a1b9b6aSSam Leffler return 32; /* NB: so 1<< is discarded */ 15228a1b9b6aSSam Leffler #undef WPA_SEL 15238a1b9b6aSSam Leffler } 15248a1b9b6aSSam Leffler 15258a1b9b6aSSam Leffler /* 15268a1b9b6aSSam Leffler * Convert an RSN key management/authentication algorithm 15278a1b9b6aSSam Leffler * to an internal code. 15288a1b9b6aSSam Leffler */ 15298a1b9b6aSSam Leffler static int 15308a1b9b6aSSam Leffler rsn_keymgmt(u_int8_t *sel) 15318a1b9b6aSSam Leffler { 15328a1b9b6aSSam Leffler #define RSN_SEL(x) (((x)<<24)|RSN_OUI) 15338a1b9b6aSSam Leffler u_int32_t w = LE_READ_4(sel); 15348a1b9b6aSSam Leffler 15358a1b9b6aSSam Leffler switch (w) { 15368a1b9b6aSSam Leffler case RSN_SEL(RSN_ASE_8021X_UNSPEC): 15378a1b9b6aSSam Leffler return RSN_ASE_8021X_UNSPEC; 15388a1b9b6aSSam Leffler case RSN_SEL(RSN_ASE_8021X_PSK): 15398a1b9b6aSSam Leffler return RSN_ASE_8021X_PSK; 15408a1b9b6aSSam Leffler case RSN_SEL(RSN_ASE_NONE): 15418a1b9b6aSSam Leffler return RSN_ASE_NONE; 15428a1b9b6aSSam Leffler } 15438a1b9b6aSSam Leffler return 0; /* NB: so is discarded */ 15448a1b9b6aSSam Leffler #undef RSN_SEL 15458a1b9b6aSSam Leffler } 15468a1b9b6aSSam Leffler 15478a1b9b6aSSam Leffler /* 15488a1b9b6aSSam Leffler * Parse a WPA/RSN information element to collect parameters 15498a1b9b6aSSam Leffler * and validate the parameters against what has been 15508a1b9b6aSSam Leffler * configured for the system. 15518a1b9b6aSSam Leffler */ 15528a1b9b6aSSam Leffler static int 15538a1b9b6aSSam Leffler ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm, 15548a1b9b6aSSam Leffler struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) 15558a1b9b6aSSam Leffler { 15568a1b9b6aSSam Leffler u_int8_t len = frm[1]; 15578a1b9b6aSSam Leffler u_int32_t w; 15588a1b9b6aSSam Leffler int n; 15598a1b9b6aSSam Leffler 15608a1b9b6aSSam Leffler /* 15618a1b9b6aSSam Leffler * Check the length once for fixed parts: 15628a1b9b6aSSam Leffler * version, mcast cipher, and 2 selector counts. 15638a1b9b6aSSam Leffler * Other, variable-length data, must be checked separately. 15648a1b9b6aSSam Leffler */ 1565bdad3a10SSam Leffler if ((ic->ic_flags & IEEE80211_F_WPA2) == 0) { 1566bdad3a10SSam Leffler IEEE80211_DISCARD_IE(ic, 1567bdad3a10SSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1568bdad3a10SSam Leffler wh, "WPA", "not RSN, flags 0x%x", ic->ic_flags); 1569bdad3a10SSam Leffler return IEEE80211_REASON_IE_INVALID; 1570bdad3a10SSam Leffler } 15718a1b9b6aSSam Leffler if (len < 10) { 15728a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 15738a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 15748a1b9b6aSSam Leffler wh, "RSN", "too short, len %u", len); 15758a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 15768a1b9b6aSSam Leffler } 15778a1b9b6aSSam Leffler frm += 2; 15788a1b9b6aSSam Leffler w = LE_READ_2(frm); 15798a1b9b6aSSam Leffler if (w != RSN_VERSION) { 15808a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 15818a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 15828a1b9b6aSSam Leffler wh, "RSN", "bad version %u", w); 15838a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 15848a1b9b6aSSam Leffler } 15858a1b9b6aSSam Leffler frm += 2, len -= 2; 15868a1b9b6aSSam Leffler 15878a1b9b6aSSam Leffler /* multicast/group cipher */ 15888a1b9b6aSSam Leffler w = rsn_cipher(frm, &rsn->rsn_mcastkeylen); 15898a1b9b6aSSam Leffler if (w != rsn->rsn_mcastcipher) { 15908a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 15918a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 15928a1b9b6aSSam Leffler wh, "RSN", "mcast cipher mismatch; got %u, expected %u", 15938a1b9b6aSSam Leffler w, rsn->rsn_mcastcipher); 15948a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 15958a1b9b6aSSam Leffler } 15968a1b9b6aSSam Leffler frm += 4, len -= 4; 15978a1b9b6aSSam Leffler 15988a1b9b6aSSam Leffler /* unicast ciphers */ 15998a1b9b6aSSam Leffler n = LE_READ_2(frm); 16008a1b9b6aSSam Leffler frm += 2, len -= 2; 16018a1b9b6aSSam Leffler if (len < n*4+2) { 16028a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 16038a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 16048a1b9b6aSSam Leffler wh, "RSN", "ucast cipher data too short; len %u, n %u", 16058a1b9b6aSSam Leffler len, n); 16068a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 16078a1b9b6aSSam Leffler } 16088a1b9b6aSSam Leffler w = 0; 16098a1b9b6aSSam Leffler for (; n > 0; n--) { 16108a1b9b6aSSam Leffler w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen); 16118a1b9b6aSSam Leffler frm += 4, len -= 4; 16128a1b9b6aSSam Leffler } 16138a1b9b6aSSam Leffler w &= rsn->rsn_ucastcipherset; 16148a1b9b6aSSam Leffler if (w == 0) { 16158a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 16168a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 16178a1b9b6aSSam Leffler wh, "RSN", "%s", "ucast cipher set empty"); 16188a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 16198a1b9b6aSSam Leffler } 16208a1b9b6aSSam Leffler if (w & (1<<IEEE80211_CIPHER_TKIP)) 16218a1b9b6aSSam Leffler rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP; 16228a1b9b6aSSam Leffler else 16238a1b9b6aSSam Leffler rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; 16248a1b9b6aSSam Leffler 16258a1b9b6aSSam Leffler /* key management algorithms */ 16268a1b9b6aSSam Leffler n = LE_READ_2(frm); 16278a1b9b6aSSam Leffler frm += 2, len -= 2; 16288a1b9b6aSSam Leffler if (len < n*4) { 16298a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 16308a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 16318a1b9b6aSSam Leffler wh, "RSN", "key mgmt alg data too short; len %u, n %u", 16328a1b9b6aSSam Leffler len, n); 16338a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 16348a1b9b6aSSam Leffler } 16358a1b9b6aSSam Leffler w = 0; 16368a1b9b6aSSam Leffler for (; n > 0; n--) { 16378a1b9b6aSSam Leffler w |= rsn_keymgmt(frm); 16388a1b9b6aSSam Leffler frm += 4, len -= 4; 16398a1b9b6aSSam Leffler } 16408a1b9b6aSSam Leffler w &= rsn->rsn_keymgmtset; 16418a1b9b6aSSam Leffler if (w == 0) { 16428a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 16438a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 16448a1b9b6aSSam Leffler wh, "RSN", "%s", "no acceptable key mgmt alg"); 16458a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 16468a1b9b6aSSam Leffler } 16478a1b9b6aSSam Leffler if (w & RSN_ASE_8021X_UNSPEC) 16488a1b9b6aSSam Leffler rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC; 16498a1b9b6aSSam Leffler else 16508a1b9b6aSSam Leffler rsn->rsn_keymgmt = RSN_ASE_8021X_PSK; 16518a1b9b6aSSam Leffler 16528a1b9b6aSSam Leffler /* optional RSN capabilities */ 16538a1b9b6aSSam Leffler if (len > 2) 16548a1b9b6aSSam Leffler rsn->rsn_caps = LE_READ_2(frm); 16558a1b9b6aSSam Leffler /* XXXPMKID */ 16568a1b9b6aSSam Leffler 16578a1b9b6aSSam Leffler return 0; 16588a1b9b6aSSam Leffler } 16598a1b9b6aSSam Leffler 16608a1b9b6aSSam Leffler static int 16618a1b9b6aSSam Leffler ieee80211_parse_wmeparams(struct ieee80211com *ic, u_int8_t *frm, 16628a1b9b6aSSam Leffler const struct ieee80211_frame *wh) 16638a1b9b6aSSam Leffler { 16648a1b9b6aSSam Leffler #define MS(_v, _f) (((_v) & _f) >> _f##_S) 16658a1b9b6aSSam Leffler struct ieee80211_wme_state *wme = &ic->ic_wme; 16668a1b9b6aSSam Leffler u_int len = frm[1], qosinfo; 16678a1b9b6aSSam Leffler int i; 16688a1b9b6aSSam Leffler 16698a1b9b6aSSam Leffler if (len < sizeof(struct ieee80211_wme_param)-2) { 16708a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 16718a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME, 16728a1b9b6aSSam Leffler wh, "WME", "too short, len %u", len); 1673c0fa32ceSSam Leffler return -1; 16748a1b9b6aSSam Leffler } 16758a1b9b6aSSam Leffler qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)]; 16768a1b9b6aSSam Leffler qosinfo &= WME_QOSINFO_COUNT; 16778a1b9b6aSSam Leffler /* XXX do proper check for wraparound */ 16788a1b9b6aSSam Leffler if (qosinfo == wme->wme_wmeChanParams.cap_info) 16798a1b9b6aSSam Leffler return 0; 16808a1b9b6aSSam Leffler frm += __offsetof(struct ieee80211_wme_param, params_acParams); 16818a1b9b6aSSam Leffler for (i = 0; i < WME_NUM_AC; i++) { 16828a1b9b6aSSam Leffler struct wmeParams *wmep = 16838a1b9b6aSSam Leffler &wme->wme_wmeChanParams.cap_wmeParams[i]; 16848a1b9b6aSSam Leffler /* NB: ACI not used */ 16858a1b9b6aSSam Leffler wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM); 16868a1b9b6aSSam Leffler wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN); 16878a1b9b6aSSam Leffler wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN); 16888a1b9b6aSSam Leffler wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX); 16898a1b9b6aSSam Leffler wmep->wmep_txopLimit = LE_READ_2(frm+2); 16908a1b9b6aSSam Leffler frm += 4; 16918a1b9b6aSSam Leffler } 16928a1b9b6aSSam Leffler wme->wme_wmeChanParams.cap_info = qosinfo; 16938a1b9b6aSSam Leffler return 1; 16948a1b9b6aSSam Leffler #undef MS 16958a1b9b6aSSam Leffler } 16968a1b9b6aSSam Leffler 1697b5c99415SSam Leffler void 16988a1b9b6aSSam Leffler ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie) 16998a1b9b6aSSam Leffler { 17008a1b9b6aSSam Leffler u_int ielen = ie[1]+2; 17018a1b9b6aSSam Leffler /* 17028a1b9b6aSSam Leffler * Record information element for later use. 17038a1b9b6aSSam Leffler */ 17048a1b9b6aSSam Leffler if (*iep == NULL || (*iep)[1] != ie[1]) { 17058a1b9b6aSSam Leffler if (*iep != NULL) 17068a1b9b6aSSam Leffler FREE(*iep, M_DEVBUF); 17078a1b9b6aSSam Leffler MALLOC(*iep, void*, ielen, M_DEVBUF, M_NOWAIT); 17088a1b9b6aSSam Leffler } 17098a1b9b6aSSam Leffler if (*iep != NULL) 17108a1b9b6aSSam Leffler memcpy(*iep, ie, ielen); 17118a1b9b6aSSam Leffler /* XXX note failure */ 17128a1b9b6aSSam Leffler } 17138a1b9b6aSSam Leffler 171466ef3969SSam Leffler /* XXX find a better place for definition */ 171566ef3969SSam Leffler struct l2_update_frame { 171666ef3969SSam Leffler struct ether_header eh; 171766ef3969SSam Leffler u_int8_t dsap; 171866ef3969SSam Leffler u_int8_t ssap; 171966ef3969SSam Leffler u_int8_t control; 172066ef3969SSam Leffler u_int8_t xid[3]; 172166ef3969SSam Leffler } __packed; 172266ef3969SSam Leffler 172366ef3969SSam Leffler /* 172466ef3969SSam Leffler * Deliver a TGf L2UF frame on behalf of a station. 172566ef3969SSam Leffler * This primes any bridge when the station is roaming 172666ef3969SSam Leffler * between ap's on the same wired network. 172766ef3969SSam Leffler */ 172866ef3969SSam Leffler static void 172966ef3969SSam Leffler ieee80211_deliver_l2uf(struct ieee80211_node *ni) 173066ef3969SSam Leffler { 173166ef3969SSam Leffler struct ieee80211com *ic = ni->ni_ic; 173266ef3969SSam Leffler struct ifnet *ifp = ic->ic_ifp; 173366ef3969SSam Leffler struct mbuf *m; 173466ef3969SSam Leffler struct l2_update_frame *l2uf; 173566ef3969SSam Leffler struct ether_header *eh; 173666ef3969SSam Leffler 173766ef3969SSam Leffler m = m_gethdr(M_NOWAIT, MT_DATA); 173866ef3969SSam Leffler if (m == NULL) { 173966ef3969SSam Leffler IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni, 174066ef3969SSam Leffler "%s", "no mbuf for l2uf frame"); 174166ef3969SSam Leffler ic->ic_stats.is_rx_nobuf++; /* XXX not right */ 174266ef3969SSam Leffler return; 174366ef3969SSam Leffler } 174466ef3969SSam Leffler l2uf = mtod(m, struct l2_update_frame *); 174566ef3969SSam Leffler eh = &l2uf->eh; 174666ef3969SSam Leffler /* dst: Broadcast address */ 174766ef3969SSam Leffler IEEE80211_ADDR_COPY(eh->ether_dhost, ifp->if_broadcastaddr); 174866ef3969SSam Leffler /* src: associated STA */ 174966ef3969SSam Leffler IEEE80211_ADDR_COPY(eh->ether_shost, ni->ni_macaddr); 175066ef3969SSam Leffler eh->ether_type = htons(sizeof(*l2uf) - sizeof(*eh)); 175166ef3969SSam Leffler 175266ef3969SSam Leffler l2uf->dsap = 0; 175366ef3969SSam Leffler l2uf->ssap = 0; 175466ef3969SSam Leffler l2uf->control = 0xf5; 175566ef3969SSam Leffler l2uf->xid[0] = 0x81; 175666ef3969SSam Leffler l2uf->xid[1] = 0x80; 175766ef3969SSam Leffler l2uf->xid[2] = 0x00; 175866ef3969SSam Leffler 175966ef3969SSam Leffler m->m_pkthdr.len = m->m_len = sizeof(*l2uf); 176066ef3969SSam Leffler m->m_pkthdr.rcvif = ifp; 176166ef3969SSam Leffler ieee80211_deliver_data(ic, ni, m); 176266ef3969SSam Leffler } 176366ef3969SSam Leffler 17641a1e1d21SSam Leffler void 17650a915fadSSam Leffler ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, 17660a915fadSSam Leffler struct ieee80211_node *ni, 17670a915fadSSam Leffler int subtype, int rssi, u_int32_t rstamp) 17681a1e1d21SSam Leffler { 17698a1b9b6aSSam Leffler #define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) 17708a1b9b6aSSam Leffler #define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP) 17711a1e1d21SSam Leffler struct ieee80211_frame *wh; 17721a1e1d21SSam Leffler u_int8_t *frm, *efrm; 17738a1b9b6aSSam Leffler u_int8_t *ssid, *rates, *xrates, *wpa, *wme; 17748a1b9b6aSSam Leffler int reassoc, resp, allocbs; 177549d4c02fSSam Leffler u_int8_t rate; 17761a1e1d21SSam Leffler 17771a1e1d21SSam Leffler wh = mtod(m0, struct ieee80211_frame *); 17781a1e1d21SSam Leffler frm = (u_int8_t *)&wh[1]; 17791a1e1d21SSam Leffler efrm = mtod(m0, u_int8_t *) + m0->m_len; 17801a1e1d21SSam Leffler switch (subtype) { 17811a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 17821a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_BEACON: { 1783b5c99415SSam Leffler struct ieee80211_scanparams scan; 17841a1e1d21SSam Leffler 17858a1b9b6aSSam Leffler /* 17868a1b9b6aSSam Leffler * We process beacon/probe response frames: 17878a1b9b6aSSam Leffler * o when scanning, or 17888a1b9b6aSSam Leffler * o station mode when associated (to collect state 17898a1b9b6aSSam Leffler * updates such as 802.11g slot time), or 17908a1b9b6aSSam Leffler * o adhoc mode (to discover neighbors) 17918a1b9b6aSSam Leffler * Frames otherwise received are discarded. 17928a1b9b6aSSam Leffler */ 17938a1b9b6aSSam Leffler if (!((ic->ic_flags & IEEE80211_F_SCAN) || 17948a1b9b6aSSam Leffler (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd) || 17955784a371SSam Leffler ic->ic_opmode == IEEE80211_M_IBSS)) { 17968a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 17971a1e1d21SSam Leffler return; 17981a1e1d21SSam Leffler } 17991a1e1d21SSam Leffler /* 18001a1e1d21SSam Leffler * beacon/probe response frame format 18011a1e1d21SSam Leffler * [8] time stamp 18021a1e1d21SSam Leffler * [2] beacon interval 18031a1e1d21SSam Leffler * [2] capability information 18041a1e1d21SSam Leffler * [tlv] ssid 18051a1e1d21SSam Leffler * [tlv] supported rates 18061a1e1d21SSam Leffler * [tlv] country information 18071a1e1d21SSam Leffler * [tlv] parameter set (FH/DS) 18081a1e1d21SSam Leffler * [tlv] erp information 18091a1e1d21SSam Leffler * [tlv] extended supported rates 18108a1b9b6aSSam Leffler * [tlv] WME 18118a1b9b6aSSam Leffler * [tlv] WPA or RSN 18121a1e1d21SSam Leffler */ 18131a1e1d21SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, 12); 1814b5c99415SSam Leffler memset(&scan, 0, sizeof(scan)); 1815b5c99415SSam Leffler scan.tstamp = frm; frm += 8; 1816b5c99415SSam Leffler scan.bintval = le16toh(*(u_int16_t *)frm); frm += 2; 1817b5c99415SSam Leffler scan.capinfo = le16toh(*(u_int16_t *)frm); frm += 2; 1818b5c99415SSam Leffler scan.bchan = ieee80211_chan2ieee(ic, ic->ic_curchan); 1819b5c99415SSam Leffler scan.chan = scan.bchan; 1820b5c99415SSam Leffler 18211a1e1d21SSam Leffler while (frm < efrm) { 1822336ec6a1SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]); 18231a1e1d21SSam Leffler switch (*frm) { 18241a1e1d21SSam Leffler case IEEE80211_ELEMID_SSID: 1825b5c99415SSam Leffler scan.ssid = frm; 18261a1e1d21SSam Leffler break; 18271a1e1d21SSam Leffler case IEEE80211_ELEMID_RATES: 1828b5c99415SSam Leffler scan.rates = frm; 18291a1e1d21SSam Leffler break; 18301a1e1d21SSam Leffler case IEEE80211_ELEMID_COUNTRY: 1831b5c99415SSam Leffler scan.country = frm; 18321a1e1d21SSam Leffler break; 18331a1e1d21SSam Leffler case IEEE80211_ELEMID_FHPARMS: 18341a1e1d21SSam Leffler if (ic->ic_phytype == IEEE80211_T_FH) { 1835b5c99415SSam Leffler scan.fhdwell = LE_READ_2(&frm[2]); 1836b5c99415SSam Leffler scan.chan = IEEE80211_FH_CHAN(frm[4], frm[5]); 1837b5c99415SSam Leffler scan.fhindex = frm[6]; 18381a1e1d21SSam Leffler } 18391a1e1d21SSam Leffler break; 18401a1e1d21SSam Leffler case IEEE80211_ELEMID_DSPARMS: 18411a1e1d21SSam Leffler /* 18421a1e1d21SSam Leffler * XXX hack this since depending on phytype 18431a1e1d21SSam Leffler * is problematic for multi-mode devices. 18441a1e1d21SSam Leffler */ 18451a1e1d21SSam Leffler if (ic->ic_phytype != IEEE80211_T_FH) 1846b5c99415SSam Leffler scan.chan = frm[2]; 18471a1e1d21SSam Leffler break; 18481a1e1d21SSam Leffler case IEEE80211_ELEMID_TIM: 18498a1b9b6aSSam Leffler /* XXX ATIM? */ 1850b5c99415SSam Leffler scan.tim = frm; 1851b5c99415SSam Leffler scan.timoff = frm - mtod(m0, u_int8_t *); 18521a1e1d21SSam Leffler break; 18534bd067c5SSam Leffler case IEEE80211_ELEMID_IBSSPARMS: 18544bd067c5SSam Leffler break; 18551a1e1d21SSam Leffler case IEEE80211_ELEMID_XRATES: 1856b5c99415SSam Leffler scan.xrates = frm; 18571a1e1d21SSam Leffler break; 18581a1e1d21SSam Leffler case IEEE80211_ELEMID_ERP: 18591a1e1d21SSam Leffler if (frm[1] != 1) { 18608a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 18618a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID, wh, "ERP", 18628a1b9b6aSSam Leffler "bad len %u", frm[1]); 18631be50176SSam Leffler ic->ic_stats.is_rx_elem_toobig++; 18641a1e1d21SSam Leffler break; 18651a1e1d21SSam Leffler } 1866b5c99415SSam Leffler scan.erp = frm[2]; 18671a1e1d21SSam Leffler break; 18688a1b9b6aSSam Leffler case IEEE80211_ELEMID_RSN: 1869b5c99415SSam Leffler scan.wpa = frm; 18708a1b9b6aSSam Leffler break; 18718a1b9b6aSSam Leffler case IEEE80211_ELEMID_VENDOR: 18728a1b9b6aSSam Leffler if (iswpaoui(frm)) 1873b5c99415SSam Leffler scan.wpa = frm; 18748a1b9b6aSSam Leffler else if (iswmeparam(frm) || iswmeinfo(frm)) 1875b5c99415SSam Leffler scan.wme = frm; 18768a1b9b6aSSam Leffler /* XXX Atheros OUI support */ 18778a1b9b6aSSam Leffler break; 18781a1e1d21SSam Leffler default: 18798a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID, 18808a1b9b6aSSam Leffler wh, "unhandled", 18818a1b9b6aSSam Leffler "id %u, len %u", *frm, frm[1]); 18821be50176SSam Leffler ic->ic_stats.is_rx_elem_unknown++; 18831a1e1d21SSam Leffler break; 18841a1e1d21SSam Leffler } 18851a1e1d21SSam Leffler frm += frm[1] + 2; 18861a1e1d21SSam Leffler } 1887b5c99415SSam Leffler IEEE80211_VERIFY_ELEMENT(scan.rates, IEEE80211_RATE_MAXSIZE); 1888b5c99415SSam Leffler IEEE80211_VERIFY_ELEMENT(scan.ssid, IEEE80211_NWID_LEN); 1889a11c9a5cSSam Leffler if ( 1890a11c9a5cSSam Leffler #if IEEE80211_CHAN_MAX < 255 1891b5c99415SSam Leffler scan.chan > IEEE80211_CHAN_MAX || 1892a11c9a5cSSam Leffler #endif 1893b5c99415SSam Leffler isclr(ic->ic_chan_active, scan.chan)) { 1894b5c99415SSam Leffler IEEE80211_DISCARD(ic, 1895b5c99415SSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, 18968a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> 18978a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 1898b5c99415SSam Leffler "invalid channel %u", scan.chan); 18991be50176SSam Leffler ic->ic_stats.is_rx_badchan++; 19001a1e1d21SSam Leffler return; 19011a1e1d21SSam Leffler } 1902b5c99415SSam Leffler if (scan.chan != scan.bchan && 1903b5c99415SSam Leffler ic->ic_phytype != IEEE80211_T_FH) { 19041a1e1d21SSam Leffler /* 19051a1e1d21SSam Leffler * Frame was received on a channel different from the 19064844aa7dSAtsushi Onoe * one indicated in the DS params element id; 19071a1e1d21SSam Leffler * silently discard it. 19081a1e1d21SSam Leffler * 19091a1e1d21SSam Leffler * NB: this can happen due to signal leakage. 19104844aa7dSAtsushi Onoe * But we should take it for FH phy because 19114844aa7dSAtsushi Onoe * the rssi value should be correct even for 19124844aa7dSAtsushi Onoe * different hop pattern in FH. 19131a1e1d21SSam Leffler */ 1914d365f9c7SSam Leffler IEEE80211_DISCARD(ic, 1915d365f9c7SSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, 1916d365f9c7SSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> 1917d365f9c7SSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 1918b5c99415SSam Leffler "for off-channel %u", scan.chan); 1919b5c99415SSam Leffler ic->ic_stats.is_rx_chanmismatch++; 1920b5c99415SSam Leffler return; 1921b5c99415SSam Leffler } 1922b5c99415SSam Leffler if (!(IEEE80211_BINTVAL_MIN <= scan.bintval && 1923b5c99415SSam Leffler scan.bintval <= IEEE80211_BINTVAL_MAX)) { 1924b5c99415SSam Leffler IEEE80211_DISCARD(ic, 1925b5c99415SSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, 1926b5c99415SSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> 1927b5c99415SSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 1928b5c99415SSam Leffler "bogus beacon interval", scan.bintval); 1929d365f9c7SSam Leffler ic->ic_stats.is_rx_badbintval++; 1930d365f9c7SSam Leffler return; 1931d365f9c7SSam Leffler } 19321a1e1d21SSam Leffler 19331a1e1d21SSam Leffler /* 193444c72e42SSam Leffler * Count frame now that we know it's to be processed. 193544c72e42SSam Leffler */ 193644c72e42SSam Leffler if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { 193744c72e42SSam Leffler ic->ic_stats.is_rx_beacon++; /* XXX remove */ 193844c72e42SSam Leffler IEEE80211_NODE_STAT(ni, rx_beacons); 193944c72e42SSam Leffler } else 194044c72e42SSam Leffler IEEE80211_NODE_STAT(ni, rx_proberesp); 194144c72e42SSam Leffler 194244c72e42SSam Leffler /* 19438a1b9b6aSSam Leffler * When operating in station mode, check for state updates. 19448a1b9b6aSSam Leffler * Be careful to ignore beacons received while doing a 19458a1b9b6aSSam Leffler * background scan. We consider only 11g/WMM stuff right now. 19461a1e1d21SSam Leffler */ 19478a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA && 19488a1b9b6aSSam Leffler ni->ni_associd != 0 && 19498a1b9b6aSSam Leffler ((ic->ic_flags & IEEE80211_F_SCAN) == 0 || 19508a1b9b6aSSam Leffler IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) { 1951f1e95a23SSam Leffler /* record tsf of last beacon */ 1952b5c99415SSam Leffler memcpy(ni->ni_tstamp.data, scan.tstamp, 1953f1e95a23SSam Leffler sizeof(ni->ni_tstamp)); 1954e99662a6SSam Leffler /* count beacon frame for s/w bmiss handling */ 1955e99662a6SSam Leffler ic->ic_swbmiss_count++; 1956e99662a6SSam Leffler ic->ic_bmiss_count = 0; 1957b5c99415SSam Leffler if (ni->ni_erp != scan.erp) { 19588a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 19598a1b9b6aSSam Leffler "[%s] erp change: was 0x%x, now 0x%x\n", 19608a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 1961b5c99415SSam Leffler ni->ni_erp, scan.erp); 1962ec425115SSam Leffler if (ic->ic_curmode == IEEE80211_MODE_11G && 1963ec425115SSam Leffler (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) 19648a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_USEPROT; 19658a1b9b6aSSam Leffler else 19668a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_USEPROT; 1967b5c99415SSam Leffler ni->ni_erp = scan.erp; 19688a1b9b6aSSam Leffler /* XXX statistic */ 19691a1e1d21SSam Leffler } 1970b5c99415SSam Leffler if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) { 19718a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 19728a1b9b6aSSam Leffler "[%s] capabilities change: before 0x%x," 19738a1b9b6aSSam Leffler " now 0x%x\n", 19748a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 1975b5c99415SSam Leffler ni->ni_capinfo, scan.capinfo); 19768a1b9b6aSSam Leffler /* 19778a1b9b6aSSam Leffler * NB: we assume short preamble doesn't 19788a1b9b6aSSam Leffler * change dynamically 19798a1b9b6aSSam Leffler */ 19808a1b9b6aSSam Leffler ieee80211_set_shortslottime(ic, 19818a1b9b6aSSam Leffler ic->ic_curmode == IEEE80211_MODE_11A || 1982f219c9d3SSam Leffler (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); 1983b5c99415SSam Leffler ni->ni_capinfo = scan.capinfo; 19848a1b9b6aSSam Leffler /* XXX statistic */ 19858a1b9b6aSSam Leffler } 1986b5c99415SSam Leffler if (scan.wme != NULL && 19873fd5a5aaSSam Leffler (ni->ni_flags & IEEE80211_NODE_QOS) && 1988b5c99415SSam Leffler ieee80211_parse_wmeparams(ic, scan.wme, wh) > 0) 19898a1b9b6aSSam Leffler ieee80211_wme_updateparams(ic); 1990b5c99415SSam Leffler if (scan.tim != NULL) { 1991a634d6a7SSam Leffler struct ieee80211_tim_ie *ie = 1992b5c99415SSam Leffler (struct ieee80211_tim_ie *) scan.tim; 1993a634d6a7SSam Leffler 1994a634d6a7SSam Leffler ni->ni_dtim_count = ie->tim_count; 1995a634d6a7SSam Leffler ni->ni_dtim_period = ie->tim_period; 1996a634d6a7SSam Leffler } 1997b5c99415SSam Leffler if (ic->ic_flags & IEEE80211_F_SCAN) 1998b5c99415SSam Leffler ieee80211_add_scan(ic, &scan, wh, 1999b5c99415SSam Leffler subtype, rssi, rstamp); 20008a1b9b6aSSam Leffler return; 20018a1b9b6aSSam Leffler } 20028a1b9b6aSSam Leffler /* 2003b5c99415SSam Leffler * If scanning, just pass information to the scan module. 20048a1b9b6aSSam Leffler */ 2005b5c99415SSam Leffler if (ic->ic_flags & IEEE80211_F_SCAN) { 2006097131ffSSam Leffler if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) { 2007097131ffSSam Leffler /* 2008097131ffSSam Leffler * Actively scanning a channel marked passive; 2009097131ffSSam Leffler * send a probe request now that we know there 2010097131ffSSam Leffler * is 802.11 traffic present. 2011097131ffSSam Leffler * 2012097131ffSSam Leffler * XXX check if the beacon we recv'd gives 2013097131ffSSam Leffler * us what we need and suppress the probe req 2014097131ffSSam Leffler */ 2015097131ffSSam Leffler ieee80211_probe_curchan(ic, 1); 2016097131ffSSam Leffler ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; 2017097131ffSSam Leffler } 2018b5c99415SSam Leffler ieee80211_add_scan(ic, &scan, wh, 2019b5c99415SSam Leffler subtype, rssi, rstamp); 20208a1b9b6aSSam Leffler return; 20218a1b9b6aSSam Leffler } 2022b5c99415SSam Leffler if (scan.capinfo & IEEE80211_CAPINFO_IBSS) { 2023b5c99415SSam Leffler if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 2024b5c99415SSam Leffler /* 2025b5c99415SSam Leffler * Create a new entry in the neighbor table. 2026b5c99415SSam Leffler */ 2027b5c99415SSam Leffler ni = ieee80211_add_neighbor(ic, wh, &scan); 2028be425a0fSSam Leffler } else if (ni->ni_capinfo == 0) { 2029be425a0fSSam Leffler /* 2030be425a0fSSam Leffler * Update faked node created on transmit. 2031be425a0fSSam Leffler * Note this also updates the tsf. 2032be425a0fSSam Leffler */ 2033be425a0fSSam Leffler ieee80211_init_neighbor(ni, wh, &scan); 2034b5c99415SSam Leffler } else { 2035b5c99415SSam Leffler /* 2036b5c99415SSam Leffler * Record tsf for potential resync. 2037b5c99415SSam Leffler */ 2038b5c99415SSam Leffler memcpy(ni->ni_tstamp.data, scan.tstamp, 2039b5c99415SSam Leffler sizeof(ni->ni_tstamp)); 2040b5c99415SSam Leffler } 2041b5c99415SSam Leffler if (ni != NULL) { 20421a1e1d21SSam Leffler ni->ni_rssi = rssi; 20431a1e1d21SSam Leffler ni->ni_rstamp = rstamp; 2044a634d6a7SSam Leffler } 2045b5c99415SSam Leffler } 20461a1e1d21SSam Leffler break; 20471a1e1d21SSam Leffler } 20481a1e1d21SSam Leffler 204949d4c02fSSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 20508a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA || 20518a1b9b6aSSam Leffler ic->ic_state != IEEE80211_S_RUN) { 20528a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 20531a1e1d21SSam Leffler return; 20548a1b9b6aSSam Leffler } 20558a1b9b6aSSam Leffler if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { 20568a1b9b6aSSam Leffler /* frame must be directed */ 20578a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; /* XXX stat */ 20581a1e1d21SSam Leffler return; 20598a1b9b6aSSam Leffler } 20601a1e1d21SSam Leffler 20611a1e1d21SSam Leffler /* 20621a1e1d21SSam Leffler * prreq frame format 20631a1e1d21SSam Leffler * [tlv] ssid 20641a1e1d21SSam Leffler * [tlv] supported rates 20651a1e1d21SSam Leffler * [tlv] extended supported rates 20661a1e1d21SSam Leffler */ 20671a1e1d21SSam Leffler ssid = rates = xrates = NULL; 20681a1e1d21SSam Leffler while (frm < efrm) { 2069336ec6a1SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]); 20701a1e1d21SSam Leffler switch (*frm) { 20711a1e1d21SSam Leffler case IEEE80211_ELEMID_SSID: 20721a1e1d21SSam Leffler ssid = frm; 20731a1e1d21SSam Leffler break; 20741a1e1d21SSam Leffler case IEEE80211_ELEMID_RATES: 20751a1e1d21SSam Leffler rates = frm; 20761a1e1d21SSam Leffler break; 20771a1e1d21SSam Leffler case IEEE80211_ELEMID_XRATES: 20781a1e1d21SSam Leffler xrates = frm; 20791a1e1d21SSam Leffler break; 20801a1e1d21SSam Leffler } 20811a1e1d21SSam Leffler frm += frm[1] + 2; 20821a1e1d21SSam Leffler } 2083dd0e6ea6SSam Leffler IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); 20841a1e1d21SSam Leffler IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); 20858a1b9b6aSSam Leffler IEEE80211_VERIFY_SSID(ic->ic_bss, ssid); 20864ef04d32SSam Leffler if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) { 20874ef04d32SSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 20884ef04d32SSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> 20894ef04d32SSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 20904ef04d32SSam Leffler "%s", "no ssid with ssid suppression enabled"); 20914ef04d32SSam Leffler ic->ic_stats.is_rx_ssidmismatch++; /*XXX*/ 20924ef04d32SSam Leffler return; 20934ef04d32SSam Leffler } 20941a1e1d21SSam Leffler 2095be425a0fSSam Leffler allocbs = 0; 20960a915fadSSam Leffler if (ni == ic->ic_bss) { 2097be425a0fSSam Leffler if (ic->ic_opmode != IEEE80211_M_IBSS) { 2098be425a0fSSam Leffler ni = ieee80211_tmp_node(ic, wh->i_addr2); 2099be425a0fSSam Leffler allocbs = 1; 2100be425a0fSSam Leffler } else if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 21018a1b9b6aSSam Leffler /* 21028a1b9b6aSSam Leffler * XXX Cannot tell if the sender is operating 21038a1b9b6aSSam Leffler * in ibss mode. But we need a new node to 21048a1b9b6aSSam Leffler * send the response so blindly add them to the 21058a1b9b6aSSam Leffler * neighbor table. 21068a1b9b6aSSam Leffler */ 2107acc4f7f5SSam Leffler ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta, 21088a1b9b6aSSam Leffler wh->i_addr2); 2109be425a0fSSam Leffler } 2110c64bfa0fSSam Leffler if (ni == NULL) 21111a1e1d21SSam Leffler return; 2112be425a0fSSam Leffler } 21138a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 21148a1b9b6aSSam Leffler "[%s] recv probe req\n", ether_sprintf(wh->i_addr2)); 21151a1e1d21SSam Leffler ni->ni_rssi = rssi; 21161a1e1d21SSam Leffler ni->ni_rstamp = rstamp; 21177d77cd53SSam Leffler rate = ieee80211_setup_rates(ni, rates, xrates, 21181a1e1d21SSam Leffler IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE 21191a1e1d21SSam Leffler | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); 21201a1e1d21SSam Leffler if (rate & IEEE80211_RATE_BASIC) { 21218a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_XRATE, 21228a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> 21238a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 21248a1b9b6aSSam Leffler "%s", "recv'd rate set invalid"); 21251a1e1d21SSam Leffler } else { 21260a915fadSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 21270a915fadSSam Leffler IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0); 21281a1e1d21SSam Leffler } 2129be425a0fSSam Leffler if (allocbs) { 2130be425a0fSSam Leffler /* 2131be425a0fSSam Leffler * Temporary node created just to send a 2132be425a0fSSam Leffler * response, reclaim immediately. 2133be425a0fSSam Leffler */ 21348a1b9b6aSSam Leffler ieee80211_free_node(ni); 21358a1b9b6aSSam Leffler } 21361a1e1d21SSam Leffler break; 21371a1e1d21SSam Leffler 21381a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_AUTH: { 21391a1e1d21SSam Leffler u_int16_t algo, seq, status; 21401a1e1d21SSam Leffler /* 21411a1e1d21SSam Leffler * auth frame format 21421a1e1d21SSam Leffler * [2] algorithm 21431a1e1d21SSam Leffler * [2] sequence 21441a1e1d21SSam Leffler * [2] status 21451a1e1d21SSam Leffler * [tlv*] challenge 21461a1e1d21SSam Leffler */ 21471a1e1d21SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, 6); 21481a1e1d21SSam Leffler algo = le16toh(*(u_int16_t *)frm); 21491a1e1d21SSam Leffler seq = le16toh(*(u_int16_t *)(frm + 2)); 21501a1e1d21SSam Leffler status = le16toh(*(u_int16_t *)(frm + 4)); 21518a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, 21528a1b9b6aSSam Leffler "[%s] recv auth frame with algorithm %d seq %d\n", 21538a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), algo, seq); 21548a1b9b6aSSam Leffler /* 21558a1b9b6aSSam Leffler * Consult the ACL policy module if setup. 21568a1b9b6aSSam Leffler */ 21578a1b9b6aSSam Leffler if (ic->ic_acl != NULL && 21588a1b9b6aSSam Leffler !ic->ic_acl->iac_check(ic, wh->i_addr2)) { 21598a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ACL, 21608a1b9b6aSSam Leffler wh, "auth", "%s", "disallowed by ACL"); 21618a1b9b6aSSam Leffler ic->ic_stats.is_rx_acl++; 2162a3d1edc2SSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 2163a3d1edc2SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 2164a3d1edc2SSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 2165a3d1edc2SSam Leffler (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16)); 2166a3d1edc2SSam Leffler } 21671a1e1d21SSam Leffler return; 21681a1e1d21SSam Leffler } 21698a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_COUNTERM) { 21708a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, 21718a1b9b6aSSam Leffler IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO, 21728a1b9b6aSSam Leffler wh, "auth", "%s", "TKIP countermeasures enabled"); 21738a1b9b6aSSam Leffler ic->ic_stats.is_rx_auth_countermeasures++; 21748a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 21750a915fadSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 21768a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 21778a1b9b6aSSam Leffler IEEE80211_REASON_MIC_FAILURE); 217859571d2bSSam Leffler } 21791a1e1d21SSam Leffler return; 21801a1e1d21SSam Leffler } 21818a1b9b6aSSam Leffler if (algo == IEEE80211_AUTH_ALG_SHARED) 21828a1b9b6aSSam Leffler ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi, 21838a1b9b6aSSam Leffler rstamp, seq, status); 21848a1b9b6aSSam Leffler else if (algo == IEEE80211_AUTH_ALG_OPEN) 21858a1b9b6aSSam Leffler ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq, 21868a1b9b6aSSam Leffler status); 21878a1b9b6aSSam Leffler else { 21888a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, 21898a1b9b6aSSam Leffler wh, "auth", "unsupported alg %d", algo); 21908a1b9b6aSSam Leffler ic->ic_stats.is_rx_auth_unsupported++; 21918a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 21928a1b9b6aSSam Leffler /* XXX not right */ 21938a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 21948a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 21958a1b9b6aSSam Leffler (seq+1) | (IEEE80211_STATUS_ALG<<16)); 21968a1b9b6aSSam Leffler } 21978a1b9b6aSSam Leffler return; 21981a1e1d21SSam Leffler } 21991a1e1d21SSam Leffler break; 22001a1e1d21SSam Leffler } 22011a1e1d21SSam Leffler 22021a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 22031a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { 2204d365f9c7SSam Leffler u_int16_t capinfo, lintval; 22058a1b9b6aSSam Leffler struct ieee80211_rsnparms rsn; 22068a1b9b6aSSam Leffler u_int8_t reason; 22071a1e1d21SSam Leffler 22081a1e1d21SSam Leffler if (ic->ic_opmode != IEEE80211_M_HOSTAP || 22098a1b9b6aSSam Leffler ic->ic_state != IEEE80211_S_RUN) { 22108a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 22111a1e1d21SSam Leffler return; 22128a1b9b6aSSam Leffler } 22131a1e1d21SSam Leffler 22141a1e1d21SSam Leffler if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { 22151a1e1d21SSam Leffler reassoc = 1; 22161a1e1d21SSam Leffler resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP; 22171a1e1d21SSam Leffler } else { 22181a1e1d21SSam Leffler reassoc = 0; 22191a1e1d21SSam Leffler resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP; 22201a1e1d21SSam Leffler } 22211a1e1d21SSam Leffler /* 22221a1e1d21SSam Leffler * asreq frame format 22231a1e1d21SSam Leffler * [2] capability information 22241a1e1d21SSam Leffler * [2] listen interval 22251a1e1d21SSam Leffler * [6*] current AP address (reassoc only) 22261a1e1d21SSam Leffler * [tlv] ssid 22271a1e1d21SSam Leffler * [tlv] supported rates 22281a1e1d21SSam Leffler * [tlv] extended supported rates 22298a1b9b6aSSam Leffler * [tlv] WPA or RSN 22301a1e1d21SSam Leffler */ 22311a1e1d21SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4)); 22321a1e1d21SSam Leffler if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) { 22338a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, 22348a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> 22358a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 22368a1b9b6aSSam Leffler "%s", "wrong bssid"); 22371be50176SSam Leffler ic->ic_stats.is_rx_assoc_bss++; 22381a1e1d21SSam Leffler return; 22391a1e1d21SSam Leffler } 22401a1e1d21SSam Leffler capinfo = le16toh(*(u_int16_t *)frm); frm += 2; 2241d365f9c7SSam Leffler lintval = le16toh(*(u_int16_t *)frm); frm += 2; 22421a1e1d21SSam Leffler if (reassoc) 22431a1e1d21SSam Leffler frm += 6; /* ignore current AP info */ 22448a1b9b6aSSam Leffler ssid = rates = xrates = wpa = wme = NULL; 22451a1e1d21SSam Leffler while (frm < efrm) { 2246336ec6a1SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]); 22471a1e1d21SSam Leffler switch (*frm) { 22481a1e1d21SSam Leffler case IEEE80211_ELEMID_SSID: 22491a1e1d21SSam Leffler ssid = frm; 22501a1e1d21SSam Leffler break; 22511a1e1d21SSam Leffler case IEEE80211_ELEMID_RATES: 22521a1e1d21SSam Leffler rates = frm; 22531a1e1d21SSam Leffler break; 22541a1e1d21SSam Leffler case IEEE80211_ELEMID_XRATES: 22551a1e1d21SSam Leffler xrates = frm; 22561a1e1d21SSam Leffler break; 22578a1b9b6aSSam Leffler /* XXX verify only one of RSN and WPA ie's? */ 22588a1b9b6aSSam Leffler case IEEE80211_ELEMID_RSN: 22598a1b9b6aSSam Leffler wpa = frm; 22608a1b9b6aSSam Leffler break; 22618a1b9b6aSSam Leffler case IEEE80211_ELEMID_VENDOR: 2262bdad3a10SSam Leffler if (iswpaoui(frm)) 22638a1b9b6aSSam Leffler wpa = frm; 2264bdad3a10SSam Leffler else if (iswmeinfo(frm)) 22658a1b9b6aSSam Leffler wme = frm; 22668a1b9b6aSSam Leffler /* XXX Atheros OUI support */ 22678a1b9b6aSSam Leffler break; 22681a1e1d21SSam Leffler } 22691a1e1d21SSam Leffler frm += frm[1] + 2; 22701a1e1d21SSam Leffler } 2271dd0e6ea6SSam Leffler IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); 22721a1e1d21SSam Leffler IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); 22738a1b9b6aSSam Leffler IEEE80211_VERIFY_SSID(ic->ic_bss, ssid); 22748a1b9b6aSSam Leffler 22750a915fadSSam Leffler if (ni == ic->ic_bss) { 22768a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 22778a1b9b6aSSam Leffler "[%s] deny %s request, sta not authenticated\n", 22788a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 22798a1b9b6aSSam Leffler reassoc ? "reassoc" : "assoc"); 228084eb84c4SSam Leffler ieee80211_send_error(ic, ni, wh->i_addr2, 22810a915fadSSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH, 22821a1e1d21SSam Leffler IEEE80211_REASON_ASSOC_NOT_AUTHED); 22831be50176SSam Leffler ic->ic_stats.is_rx_assoc_notauth++; 22841a1e1d21SSam Leffler return; 22851a1e1d21SSam Leffler } 2286c9a4bb99SSam Leffler /* assert right associstion security credentials */ 2287c9a4bb99SSam Leffler if (wpa == NULL && (ic->ic_flags & IEEE80211_F_WPA)) { 2288c9a4bb99SSam Leffler IEEE80211_DPRINTF(ic, 2289c9a4bb99SSam Leffler IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, 2290c9a4bb99SSam Leffler "[%s] no WPA/RSN IE in association request\n", 2291c9a4bb99SSam Leffler ether_sprintf(wh->i_addr2)); 2292c9a4bb99SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 2293c9a4bb99SSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH, 2294c9a4bb99SSam Leffler IEEE80211_REASON_RSN_REQUIRED); 2295c9a4bb99SSam Leffler ieee80211_node_leave(ic, ni); 2296c9a4bb99SSam Leffler /* XXX distinguish WPA/RSN? */ 2297c9a4bb99SSam Leffler ic->ic_stats.is_rx_assoc_badwpaie++; 2298c9a4bb99SSam Leffler return; 2299c9a4bb99SSam Leffler } 23008a1b9b6aSSam Leffler if (wpa != NULL) { 23018a1b9b6aSSam Leffler /* 23028a1b9b6aSSam Leffler * Parse WPA information element. Note that 23038a1b9b6aSSam Leffler * we initialize the param block from the node 23048a1b9b6aSSam Leffler * state so that information in the IE overrides 23058a1b9b6aSSam Leffler * our defaults. The resulting parameters are 23068a1b9b6aSSam Leffler * installed below after the association is assured. 23078a1b9b6aSSam Leffler */ 23088a1b9b6aSSam Leffler rsn = ni->ni_rsn; 23098a1b9b6aSSam Leffler if (wpa[0] != IEEE80211_ELEMID_RSN) 23108a1b9b6aSSam Leffler reason = ieee80211_parse_wpa(ic, wpa, &rsn, wh); 23118a1b9b6aSSam Leffler else 23128a1b9b6aSSam Leffler reason = ieee80211_parse_rsn(ic, wpa, &rsn, wh); 23138a1b9b6aSSam Leffler if (reason != 0) { 23148a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 23158a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH, reason); 23168a1b9b6aSSam Leffler ieee80211_node_leave(ic, ni); 23178a1b9b6aSSam Leffler /* XXX distinguish WPA/RSN? */ 23188a1b9b6aSSam Leffler ic->ic_stats.is_rx_assoc_badwpaie++; 23198a1b9b6aSSam Leffler return; 23208a1b9b6aSSam Leffler } 23218a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, 23228a1b9b6aSSam Leffler IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, 23238a1b9b6aSSam Leffler "[%s] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n", 23248a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 23258a1b9b6aSSam Leffler wpa[0] != IEEE80211_ELEMID_RSN ? "WPA" : "RSN", 23268a1b9b6aSSam Leffler rsn.rsn_mcastcipher, rsn.rsn_mcastkeylen, 23278a1b9b6aSSam Leffler rsn.rsn_ucastcipher, rsn.rsn_ucastkeylen, 23288a1b9b6aSSam Leffler rsn.rsn_keymgmt, rsn.rsn_caps); 23298a1b9b6aSSam Leffler } 23308a1b9b6aSSam Leffler /* discard challenge after association */ 23318a1b9b6aSSam Leffler if (ni->ni_challenge != NULL) { 23328a1b9b6aSSam Leffler FREE(ni->ni_challenge, M_DEVBUF); 23338a1b9b6aSSam Leffler ni->ni_challenge = NULL; 23348a1b9b6aSSam Leffler } 23359b4db829SSam Leffler /* NB: 802.11 spec says to ignore station's privacy bit */ 23369b4db829SSam Leffler if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) { 23378a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 23388a1b9b6aSSam Leffler "[%s] deny %s request, capability mismatch 0x%x\n", 23398a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 23408a1b9b6aSSam Leffler reassoc ? "reassoc" : "assoc", capinfo); 23410a915fadSSam Leffler IEEE80211_SEND_MGMT(ic, ni, resp, 23420a915fadSSam Leffler IEEE80211_STATUS_CAPINFO); 23438a1b9b6aSSam Leffler ieee80211_node_leave(ic, ni); 23441be50176SSam Leffler ic->ic_stats.is_rx_assoc_capmismatch++; 23451a1e1d21SSam Leffler return; 23461a1e1d21SSam Leffler } 23477d77cd53SSam Leffler rate = ieee80211_setup_rates(ni, rates, xrates, 23481a1e1d21SSam Leffler IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | 23491a1e1d21SSam Leffler IEEE80211_F_DONEGO | IEEE80211_F_DODEL); 2350c4f040c3SSam Leffler /* 2351c4f040c3SSam Leffler * If constrained to 11g-only stations reject an 2352c4f040c3SSam Leffler * 11b-only station. We cheat a bit here by looking 2353c4f040c3SSam Leffler * at the max negotiated xmit rate and assuming anyone 2354c4f040c3SSam Leffler * with a best rate <24Mb/s is an 11b station. 2355c4f040c3SSam Leffler */ 2356c4f040c3SSam Leffler if ((rate & IEEE80211_RATE_BASIC) || 2357c4f040c3SSam Leffler ((ic->ic_flags & IEEE80211_F_PUREG) && rate < 48)) { 23588a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 23598a1b9b6aSSam Leffler "[%s] deny %s request, rate set mismatch\n", 23608a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 23618a1b9b6aSSam Leffler reassoc ? "reassoc" : "assoc"); 23620a915fadSSam Leffler IEEE80211_SEND_MGMT(ic, ni, resp, 23630a915fadSSam Leffler IEEE80211_STATUS_BASIC_RATE); 23648a1b9b6aSSam Leffler ieee80211_node_leave(ic, ni); 23651be50176SSam Leffler ic->ic_stats.is_rx_assoc_norate++; 23661a1e1d21SSam Leffler return; 23671a1e1d21SSam Leffler } 23681a1e1d21SSam Leffler ni->ni_rssi = rssi; 23691a1e1d21SSam Leffler ni->ni_rstamp = rstamp; 2370d365f9c7SSam Leffler ni->ni_intval = lintval; 23711a1e1d21SSam Leffler ni->ni_capinfo = capinfo; 23721a1e1d21SSam Leffler ni->ni_chan = ic->ic_bss->ni_chan; 23731a1e1d21SSam Leffler ni->ni_fhdwell = ic->ic_bss->ni_fhdwell; 23741a1e1d21SSam Leffler ni->ni_fhindex = ic->ic_bss->ni_fhindex; 23758a1b9b6aSSam Leffler if (wpa != NULL) { 23768a1b9b6aSSam Leffler /* 23778a1b9b6aSSam Leffler * Record WPA/RSN parameters for station, mark 23788a1b9b6aSSam Leffler * node as using WPA and record information element 23798a1b9b6aSSam Leffler * for applications that require it. 23808a1b9b6aSSam Leffler */ 23818a1b9b6aSSam Leffler ni->ni_rsn = rsn; 23828a1b9b6aSSam Leffler ieee80211_saveie(&ni->ni_wpa_ie, wpa); 23838a1b9b6aSSam Leffler } else if (ni->ni_wpa_ie != NULL) { 23848a1b9b6aSSam Leffler /* 23858a1b9b6aSSam Leffler * Flush any state from a previous association. 23868a1b9b6aSSam Leffler */ 23878a1b9b6aSSam Leffler FREE(ni->ni_wpa_ie, M_DEVBUF); 23888a1b9b6aSSam Leffler ni->ni_wpa_ie = NULL; 23898a1b9b6aSSam Leffler } 23908a1b9b6aSSam Leffler if (wme != NULL) { 23918a1b9b6aSSam Leffler /* 23928a1b9b6aSSam Leffler * Record WME parameters for station, mark node 23938a1b9b6aSSam Leffler * as capable of QoS and record information 23948a1b9b6aSSam Leffler * element for applications that require it. 23958a1b9b6aSSam Leffler */ 23968a1b9b6aSSam Leffler ieee80211_saveie(&ni->ni_wme_ie, wme); 23978a1b9b6aSSam Leffler ni->ni_flags |= IEEE80211_NODE_QOS; 23988a1b9b6aSSam Leffler } else if (ni->ni_wme_ie != NULL) { 23998a1b9b6aSSam Leffler /* 24008a1b9b6aSSam Leffler * Flush any state from a previous association. 24018a1b9b6aSSam Leffler */ 24028a1b9b6aSSam Leffler FREE(ni->ni_wme_ie, M_DEVBUF); 24038a1b9b6aSSam Leffler ni->ni_wme_ie = NULL; 24048a1b9b6aSSam Leffler ni->ni_flags &= ~IEEE80211_NODE_QOS; 24058a1b9b6aSSam Leffler } 240666ef3969SSam Leffler ieee80211_deliver_l2uf(ni); 24078a1b9b6aSSam Leffler ieee80211_node_join(ic, ni, resp); 24081a1e1d21SSam Leffler break; 24091a1e1d21SSam Leffler } 24101a1e1d21SSam Leffler 24111a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 24121a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { 24138a1b9b6aSSam Leffler u_int16_t capinfo, associd; 24141a1e1d21SSam Leffler u_int16_t status; 24151a1e1d21SSam Leffler 24161a1e1d21SSam Leffler if (ic->ic_opmode != IEEE80211_M_STA || 24178a1b9b6aSSam Leffler ic->ic_state != IEEE80211_S_ASSOC) { 24188a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 24191a1e1d21SSam Leffler return; 24208a1b9b6aSSam Leffler } 24211a1e1d21SSam Leffler 24221a1e1d21SSam Leffler /* 24231a1e1d21SSam Leffler * asresp frame format 24241a1e1d21SSam Leffler * [2] capability information 24251a1e1d21SSam Leffler * [2] status 24261a1e1d21SSam Leffler * [2] association ID 24271a1e1d21SSam Leffler * [tlv] supported rates 24281a1e1d21SSam Leffler * [tlv] extended supported rates 24298a1b9b6aSSam Leffler * [tlv] WME 24301a1e1d21SSam Leffler */ 24311a1e1d21SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, 6); 24321a1e1d21SSam Leffler ni = ic->ic_bss; 24338a1b9b6aSSam Leffler capinfo = le16toh(*(u_int16_t *)frm); 24341a1e1d21SSam Leffler frm += 2; 24351a1e1d21SSam Leffler status = le16toh(*(u_int16_t *)frm); 24361a1e1d21SSam Leffler frm += 2; 24371a1e1d21SSam Leffler if (status != 0) { 24388a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 24398a1b9b6aSSam Leffler "[%s] %sassoc failed (reason %d)\n", 24408a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 24418a1b9b6aSSam Leffler ISREASSOC(subtype) ? "re" : "", status); 24428a1b9b6aSSam Leffler if (ni != ic->ic_bss) /* XXX never true? */ 24431a1e1d21SSam Leffler ni->ni_fails++; 24448a1b9b6aSSam Leffler ic->ic_stats.is_rx_auth_fail++; /* XXX */ 24451a1e1d21SSam Leffler return; 24461a1e1d21SSam Leffler } 24478a1b9b6aSSam Leffler associd = le16toh(*(u_int16_t *)frm); 24481a1e1d21SSam Leffler frm += 2; 24491a1e1d21SSam Leffler 24508a1b9b6aSSam Leffler rates = xrates = wpa = wme = NULL; 24511a1e1d21SSam Leffler while (frm < efrm) { 2452336ec6a1SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]); 24531a1e1d21SSam Leffler switch (*frm) { 24541a1e1d21SSam Leffler case IEEE80211_ELEMID_RATES: 24551a1e1d21SSam Leffler rates = frm; 24561a1e1d21SSam Leffler break; 24571a1e1d21SSam Leffler case IEEE80211_ELEMID_XRATES: 24581a1e1d21SSam Leffler xrates = frm; 24591a1e1d21SSam Leffler break; 24608a1b9b6aSSam Leffler case IEEE80211_ELEMID_VENDOR: 24618a1b9b6aSSam Leffler if (iswmeoui(frm)) 24628a1b9b6aSSam Leffler wme = frm; 24638a1b9b6aSSam Leffler /* XXX Atheros OUI support */ 24648a1b9b6aSSam Leffler break; 24651a1e1d21SSam Leffler } 24661a1e1d21SSam Leffler frm += frm[1] + 2; 24671a1e1d21SSam Leffler } 24681a1e1d21SSam Leffler 2469dd0e6ea6SSam Leffler IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); 24707d77cd53SSam Leffler rate = ieee80211_setup_rates(ni, rates, xrates, 24711a1e1d21SSam Leffler IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | 24721a1e1d21SSam Leffler IEEE80211_F_DONEGO | IEEE80211_F_DODEL); 247349d4c02fSSam Leffler if (rate & IEEE80211_RATE_BASIC) { 24748a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 24758a1b9b6aSSam Leffler "[%s] %sassoc failed (rate set mismatch)\n", 24768a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 24778a1b9b6aSSam Leffler ISREASSOC(subtype) ? "re" : ""); 24788a1b9b6aSSam Leffler if (ni != ic->ic_bss) /* XXX never true? */ 24798a1b9b6aSSam Leffler ni->ni_fails++; 24808a1b9b6aSSam Leffler ic->ic_stats.is_rx_assoc_norate++; 2481181181acSSam Leffler ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 24828a1b9b6aSSam Leffler return; 24838a1b9b6aSSam Leffler } 24848a1b9b6aSSam Leffler 24858a1b9b6aSSam Leffler ni->ni_capinfo = capinfo; 24868a1b9b6aSSam Leffler ni->ni_associd = associd; 2487c0fa32ceSSam Leffler if (wme != NULL && 2488c0fa32ceSSam Leffler ieee80211_parse_wmeparams(ic, wme, wh) >= 0) { 24898a1b9b6aSSam Leffler ni->ni_flags |= IEEE80211_NODE_QOS; 24908a1b9b6aSSam Leffler ieee80211_wme_updateparams(ic); 24918a1b9b6aSSam Leffler } else 24928a1b9b6aSSam Leffler ni->ni_flags &= ~IEEE80211_NODE_QOS; 24938a1b9b6aSSam Leffler /* 24948a1b9b6aSSam Leffler * Configure state now that we are associated. 24958a1b9b6aSSam Leffler * 24968a1b9b6aSSam Leffler * XXX may need different/additional driver callbacks? 24978a1b9b6aSSam Leffler */ 24988a1b9b6aSSam Leffler if (ic->ic_curmode == IEEE80211_MODE_11A || 24998a1b9b6aSSam Leffler (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { 25008a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 25018a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_USEBARKER; 25028a1b9b6aSSam Leffler } else { 25038a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 25048a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_USEBARKER; 25058a1b9b6aSSam Leffler } 25068a1b9b6aSSam Leffler ieee80211_set_shortslottime(ic, 25078a1b9b6aSSam Leffler ic->ic_curmode == IEEE80211_MODE_11A || 25088a1b9b6aSSam Leffler (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); 25098a1b9b6aSSam Leffler /* 25108a1b9b6aSSam Leffler * Honor ERP protection. 25118a1b9b6aSSam Leffler * 25128a1b9b6aSSam Leffler * NB: ni_erp should zero for non-11g operation. 25138a1b9b6aSSam Leffler * XXX check ic_curmode anyway? 25148a1b9b6aSSam Leffler */ 2515ec425115SSam Leffler if (ic->ic_curmode == IEEE80211_MODE_11G && 2516ec425115SSam Leffler (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) 25178a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_USEPROT; 25188a1b9b6aSSam Leffler else 25198a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_USEPROT; 25208a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 25218a1b9b6aSSam Leffler "[%s] %sassoc success: %s preamble, %s slot time%s%s\n", 25228a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 25238a1b9b6aSSam Leffler ISREASSOC(subtype) ? "re" : "", 25248a1b9b6aSSam Leffler ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", 25258a1b9b6aSSam Leffler ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", 25268a1b9b6aSSam Leffler ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "", 25278a1b9b6aSSam Leffler ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "" 25288a1b9b6aSSam Leffler ); 25298a1b9b6aSSam Leffler ieee80211_new_state(ic, IEEE80211_S_RUN, subtype); 25301a1e1d21SSam Leffler break; 25311a1e1d21SSam Leffler } 25321a1e1d21SSam Leffler 25331a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_DEAUTH: { 25341a1e1d21SSam Leffler u_int16_t reason; 25358a1b9b6aSSam Leffler 25368a1b9b6aSSam Leffler if (ic->ic_state == IEEE80211_S_SCAN) { 25378a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 25388a1b9b6aSSam Leffler return; 25398a1b9b6aSSam Leffler } 25401a1e1d21SSam Leffler /* 25411a1e1d21SSam Leffler * deauth frame format 25421a1e1d21SSam Leffler * [2] reason 25431a1e1d21SSam Leffler */ 25441a1e1d21SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, 2); 25451a1e1d21SSam Leffler reason = le16toh(*(u_int16_t *)frm); 25461be50176SSam Leffler ic->ic_stats.is_rx_deauth++; 25478a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_deauth); 25484720ec19SSam Leffler 25494720ec19SSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, 25504720ec19SSam Leffler "[%s] recv deauthenticate (reason %d)\n", 25514720ec19SSam Leffler ether_sprintf(ni->ni_macaddr), reason); 25521a1e1d21SSam Leffler switch (ic->ic_opmode) { 25531a1e1d21SSam Leffler case IEEE80211_M_STA: 2554a11c9a5cSSam Leffler ieee80211_new_state(ic, IEEE80211_S_AUTH, 25551a1e1d21SSam Leffler wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); 25561a1e1d21SSam Leffler break; 25571a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 25584720ec19SSam Leffler if (ni != ic->ic_bss) 25598a1b9b6aSSam Leffler ieee80211_node_leave(ic, ni); 25601a1e1d21SSam Leffler break; 25611a1e1d21SSam Leffler default: 25628a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 25631a1e1d21SSam Leffler break; 25641a1e1d21SSam Leffler } 25651a1e1d21SSam Leffler break; 25661a1e1d21SSam Leffler } 25671a1e1d21SSam Leffler 25681a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_DISASSOC: { 25691a1e1d21SSam Leffler u_int16_t reason; 25708a1b9b6aSSam Leffler 25718a1b9b6aSSam Leffler if (ic->ic_state != IEEE80211_S_RUN && 2572404265d4SSam Leffler ic->ic_state != IEEE80211_S_ASSOC && 25738a1b9b6aSSam Leffler ic->ic_state != IEEE80211_S_AUTH) { 25748a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 25758a1b9b6aSSam Leffler return; 25768a1b9b6aSSam Leffler } 25771a1e1d21SSam Leffler /* 25781a1e1d21SSam Leffler * disassoc frame format 25791a1e1d21SSam Leffler * [2] reason 25801a1e1d21SSam Leffler */ 25811a1e1d21SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, 2); 25821a1e1d21SSam Leffler reason = le16toh(*(u_int16_t *)frm); 25831be50176SSam Leffler ic->ic_stats.is_rx_disassoc++; 25848a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_disassoc); 25854720ec19SSam Leffler 25864720ec19SSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 2587e35ac6b9SSam Leffler "[%s] recv disassociate (reason %d)\n", 25884720ec19SSam Leffler ether_sprintf(ni->ni_macaddr), reason); 25891a1e1d21SSam Leffler switch (ic->ic_opmode) { 25901a1e1d21SSam Leffler case IEEE80211_M_STA: 2591a11c9a5cSSam Leffler ieee80211_new_state(ic, IEEE80211_S_ASSOC, 25921a1e1d21SSam Leffler wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); 25931a1e1d21SSam Leffler break; 25941a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 25954720ec19SSam Leffler if (ni != ic->ic_bss) 25968a1b9b6aSSam Leffler ieee80211_node_leave(ic, ni); 25971a1e1d21SSam Leffler break; 25981a1e1d21SSam Leffler default: 25998a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 26001a1e1d21SSam Leffler break; 26011a1e1d21SSam Leffler } 26021a1e1d21SSam Leffler break; 26031a1e1d21SSam Leffler } 26041a1e1d21SSam Leffler default: 26058a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, 26068a1b9b6aSSam Leffler wh, "mgt", "subtype 0x%x not handled", subtype); 26071be50176SSam Leffler ic->ic_stats.is_rx_badsubtype++; 26081a1e1d21SSam Leffler break; 26091a1e1d21SSam Leffler } 26108a1b9b6aSSam Leffler #undef ISREASSOC 26118a1b9b6aSSam Leffler #undef ISPROBE 26121a1e1d21SSam Leffler } 26131a1e1d21SSam Leffler #undef IEEE80211_VERIFY_LENGTH 26141a1e1d21SSam Leffler #undef IEEE80211_VERIFY_ELEMENT 26158a1b9b6aSSam Leffler 26168a1b9b6aSSam Leffler /* 26178a1b9b6aSSam Leffler * Handle station power-save state change. 26188a1b9b6aSSam Leffler */ 26198a1b9b6aSSam Leffler static void 26208a1b9b6aSSam Leffler ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable) 26218a1b9b6aSSam Leffler { 26228a1b9b6aSSam Leffler struct ieee80211com *ic = ni->ni_ic; 26238a1b9b6aSSam Leffler struct mbuf *m; 26248a1b9b6aSSam Leffler 26258a1b9b6aSSam Leffler if (enable) { 26268a1b9b6aSSam Leffler if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) 26278a1b9b6aSSam Leffler ic->ic_ps_sta++; 26288a1b9b6aSSam Leffler ni->ni_flags |= IEEE80211_NODE_PWR_MGT; 26298a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 26308a1b9b6aSSam Leffler "[%s] power save mode on, %u sta's in ps mode\n", 26318a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta); 26328a1b9b6aSSam Leffler return; 26338a1b9b6aSSam Leffler } 26348a1b9b6aSSam Leffler 26358a1b9b6aSSam Leffler if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) 26368a1b9b6aSSam Leffler ic->ic_ps_sta--; 26378a1b9b6aSSam Leffler ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; 26388a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 26398a1b9b6aSSam Leffler "[%s] power save mode off, %u sta's in ps mode\n", 26408a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta); 26418a1b9b6aSSam Leffler /* XXX if no stations in ps mode, flush mc frames */ 26428a1b9b6aSSam Leffler 26438a1b9b6aSSam Leffler /* 26448a1b9b6aSSam Leffler * Flush queued unicast frames. 26458a1b9b6aSSam Leffler */ 26468a1b9b6aSSam Leffler if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) { 2647c789ea8bSSam Leffler if (ic->ic_set_tim != NULL) 2648edfa57d0SSam Leffler ic->ic_set_tim(ni, 0); /* just in case */ 26498a1b9b6aSSam Leffler return; 26508a1b9b6aSSam Leffler } 26518a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 26528a1b9b6aSSam Leffler "[%s] flush ps queue, %u packets queued\n", 26538a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), IEEE80211_NODE_SAVEQ_QLEN(ni)); 26548a1b9b6aSSam Leffler for (;;) { 26558a1b9b6aSSam Leffler int qlen; 26568a1b9b6aSSam Leffler 26578a1b9b6aSSam Leffler IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen); 26588a1b9b6aSSam Leffler if (m == NULL) 26598a1b9b6aSSam Leffler break; 26608a1b9b6aSSam Leffler /* 26618a1b9b6aSSam Leffler * If this is the last packet, turn off the TIM bit. 26628a1b9b6aSSam Leffler * If there are more packets, set the more packets bit 2663bc5627d9SSam Leffler * in the mbuf so ieee80211_encap will mark the 802.11 2664bc5627d9SSam Leffler * head to indicate more data frames will follow. 26658a1b9b6aSSam Leffler */ 2666bc5627d9SSam Leffler if (qlen != 0) 2667bc5627d9SSam Leffler m->m_flags |= M_MORE_DATA; 26688a1b9b6aSSam Leffler /* XXX need different driver interface */ 26698a1b9b6aSSam Leffler /* XXX bypasses q max */ 26708a1b9b6aSSam Leffler IF_ENQUEUE(&ic->ic_ifp->if_snd, m); 26718a1b9b6aSSam Leffler } 2672c789ea8bSSam Leffler if (ic->ic_set_tim != NULL) 2673edfa57d0SSam Leffler ic->ic_set_tim(ni, 0); 26748a1b9b6aSSam Leffler } 26758a1b9b6aSSam Leffler 26768a1b9b6aSSam Leffler /* 26778a1b9b6aSSam Leffler * Process a received ps-poll frame. 26788a1b9b6aSSam Leffler */ 26798a1b9b6aSSam Leffler static void 26808a1b9b6aSSam Leffler ieee80211_recv_pspoll(struct ieee80211com *ic, 26818a1b9b6aSSam Leffler struct ieee80211_node *ni, struct mbuf *m0) 26828a1b9b6aSSam Leffler { 26838a1b9b6aSSam Leffler struct ieee80211_frame_min *wh; 26848a1b9b6aSSam Leffler struct mbuf *m; 26858a1b9b6aSSam Leffler u_int16_t aid; 26868a1b9b6aSSam Leffler int qlen; 26878a1b9b6aSSam Leffler 26888a1b9b6aSSam Leffler wh = mtod(m0, struct ieee80211_frame_min *); 26898a1b9b6aSSam Leffler if (ni->ni_associd == 0) { 26908a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, 26918a1b9b6aSSam Leffler (struct ieee80211_frame *) wh, "ps-poll", 26928a1b9b6aSSam Leffler "%s", "unassociated station"); 26938a1b9b6aSSam Leffler ic->ic_stats.is_ps_unassoc++; 26948a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, 26958a1b9b6aSSam Leffler IEEE80211_REASON_NOT_ASSOCED); 26968a1b9b6aSSam Leffler return; 26978a1b9b6aSSam Leffler } 26988a1b9b6aSSam Leffler 26998a1b9b6aSSam Leffler aid = le16toh(*(u_int16_t *)wh->i_dur); 27008a1b9b6aSSam Leffler if (aid != ni->ni_associd) { 27018a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, 27028a1b9b6aSSam Leffler (struct ieee80211_frame *) wh, "ps-poll", 27038a1b9b6aSSam Leffler "aid mismatch: sta aid 0x%x poll aid 0x%x", 27048a1b9b6aSSam Leffler ni->ni_associd, aid); 27058a1b9b6aSSam Leffler ic->ic_stats.is_ps_badaid++; 27068a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, 27078a1b9b6aSSam Leffler IEEE80211_REASON_NOT_ASSOCED); 27088a1b9b6aSSam Leffler return; 27098a1b9b6aSSam Leffler } 27108a1b9b6aSSam Leffler 27118a1b9b6aSSam Leffler /* Okay, take the first queued packet and put it out... */ 27128a1b9b6aSSam Leffler IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen); 27138a1b9b6aSSam Leffler if (m == NULL) { 27148a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 27158a1b9b6aSSam Leffler "[%s] recv ps-poll, but queue empty\n", 27168a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2)); 271719ad2dd7SSam Leffler ieee80211_send_nulldata(ieee80211_ref_node(ni)); 27188a1b9b6aSSam Leffler ic->ic_stats.is_ps_qempty++; /* XXX node stat */ 2719c789ea8bSSam Leffler if (ic->ic_set_tim != NULL) 2720edfa57d0SSam Leffler ic->ic_set_tim(ni, 0); /* just in case */ 27218a1b9b6aSSam Leffler return; 27228a1b9b6aSSam Leffler } 27238a1b9b6aSSam Leffler /* 27248a1b9b6aSSam Leffler * If there are more packets, set the more packets bit 27258a1b9b6aSSam Leffler * in the packet dispatched to the station; otherwise 27268a1b9b6aSSam Leffler * turn off the TIM bit. 27278a1b9b6aSSam Leffler */ 27288a1b9b6aSSam Leffler if (qlen != 0) { 27298a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 27308a1b9b6aSSam Leffler "[%s] recv ps-poll, send packet, %u still queued\n", 27318a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), qlen); 2732c52dab62SSam Leffler m->m_flags |= M_MORE_DATA; 27338a1b9b6aSSam Leffler } else { 27348a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 27358a1b9b6aSSam Leffler "[%s] recv ps-poll, send packet, queue empty\n", 27368a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr)); 2737c789ea8bSSam Leffler if (ic->ic_set_tim != NULL) 2738edfa57d0SSam Leffler ic->ic_set_tim(ni, 0); 27398a1b9b6aSSam Leffler } 27408a1b9b6aSSam Leffler m->m_flags |= M_PWR_SAV; /* bypass PS handling */ 27418a1b9b6aSSam Leffler IF_ENQUEUE(&ic->ic_ifp->if_snd, m); 27428a1b9b6aSSam Leffler } 27438a1b9b6aSSam Leffler 27448a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 27458a1b9b6aSSam Leffler /* 27468a1b9b6aSSam Leffler * Debugging support. 27478a1b9b6aSSam Leffler */ 27488a1b9b6aSSam Leffler 27498a1b9b6aSSam Leffler /* 27508a1b9b6aSSam Leffler * Return the bssid of a frame. 27518a1b9b6aSSam Leffler */ 27528a1b9b6aSSam Leffler static const u_int8_t * 27538a1b9b6aSSam Leffler ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh) 27548a1b9b6aSSam Leffler { 27558a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) 27568a1b9b6aSSam Leffler return wh->i_addr2; 27578a1b9b6aSSam Leffler if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS) 27588a1b9b6aSSam Leffler return wh->i_addr1; 27598a1b9b6aSSam Leffler if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL) 27608a1b9b6aSSam Leffler return wh->i_addr1; 27618a1b9b6aSSam Leffler return wh->i_addr3; 27628a1b9b6aSSam Leffler } 27638a1b9b6aSSam Leffler 2764f6df3191SSam Leffler void 2765f6df3191SSam Leffler ieee80211_note(struct ieee80211com *ic, const char *fmt, ...) 2766f6df3191SSam Leffler { 2767f6df3191SSam Leffler char buf[128]; /* XXX */ 2768f6df3191SSam Leffler va_list ap; 2769f6df3191SSam Leffler 2770f6df3191SSam Leffler va_start(ap, fmt); 2771f6df3191SSam Leffler vsnprintf(buf, sizeof(buf), fmt, ap); 2772f6df3191SSam Leffler va_end(ap); 2773f6df3191SSam Leffler 2774f6df3191SSam Leffler if_printf(ic->ic_ifp, "%s", buf); /* NB: no \n */ 2775f6df3191SSam Leffler } 2776f6df3191SSam Leffler 2777f6df3191SSam Leffler void 2778f6df3191SSam Leffler ieee80211_note_frame(struct ieee80211com *ic, 2779f6df3191SSam Leffler const struct ieee80211_frame *wh, 2780f6df3191SSam Leffler const char *fmt, ...) 2781f6df3191SSam Leffler { 2782f6df3191SSam Leffler char buf[128]; /* XXX */ 2783f6df3191SSam Leffler va_list ap; 2784f6df3191SSam Leffler 2785f6df3191SSam Leffler va_start(ap, fmt); 2786f6df3191SSam Leffler vsnprintf(buf, sizeof(buf), fmt, ap); 2787f6df3191SSam Leffler va_end(ap); 2788f6df3191SSam Leffler if_printf(ic->ic_ifp, "[%s] %s\n", 2789f6df3191SSam Leffler ether_sprintf(ieee80211_getbssid(ic, wh)), buf); 2790f6df3191SSam Leffler } 2791f6df3191SSam Leffler 2792f6df3191SSam Leffler void 2793f6df3191SSam Leffler ieee80211_note_mac(struct ieee80211com *ic, 2794f6df3191SSam Leffler const u_int8_t mac[IEEE80211_ADDR_LEN], 2795f6df3191SSam Leffler const char *fmt, ...) 2796f6df3191SSam Leffler { 2797f6df3191SSam Leffler char buf[128]; /* XXX */ 2798f6df3191SSam Leffler va_list ap; 2799f6df3191SSam Leffler 2800f6df3191SSam Leffler va_start(ap, fmt); 2801f6df3191SSam Leffler vsnprintf(buf, sizeof(buf), fmt, ap); 2802f6df3191SSam Leffler va_end(ap); 2803f6df3191SSam Leffler if_printf(ic->ic_ifp, "[%s] %s\n", ether_sprintf(mac), buf); 2804f6df3191SSam Leffler } 2805f6df3191SSam Leffler 28068a1b9b6aSSam Leffler static void 28078a1b9b6aSSam Leffler ieee80211_discard_frame(struct ieee80211com *ic, 28088a1b9b6aSSam Leffler const struct ieee80211_frame *wh, 28098a1b9b6aSSam Leffler const char *type, const char *fmt, ...) 28108a1b9b6aSSam Leffler { 28118a1b9b6aSSam Leffler va_list ap; 28128a1b9b6aSSam Leffler 2813497c84aeSSam Leffler printf("[%s:%s] discard ", ic->ic_ifp->if_xname, 2814497c84aeSSam Leffler ether_sprintf(ieee80211_getbssid(ic, wh))); 28158a1b9b6aSSam Leffler if (type != NULL) 28168a1b9b6aSSam Leffler printf("%s frame, ", type); 28178a1b9b6aSSam Leffler else 28188a1b9b6aSSam Leffler printf("frame, "); 28198a1b9b6aSSam Leffler va_start(ap, fmt); 28208a1b9b6aSSam Leffler vprintf(fmt, ap); 28218a1b9b6aSSam Leffler va_end(ap); 28228a1b9b6aSSam Leffler printf("\n"); 28238a1b9b6aSSam Leffler } 28248a1b9b6aSSam Leffler 28258a1b9b6aSSam Leffler static void 28268a1b9b6aSSam Leffler ieee80211_discard_ie(struct ieee80211com *ic, 28278a1b9b6aSSam Leffler const struct ieee80211_frame *wh, 28288a1b9b6aSSam Leffler const char *type, const char *fmt, ...) 28298a1b9b6aSSam Leffler { 28308a1b9b6aSSam Leffler va_list ap; 28318a1b9b6aSSam Leffler 2832497c84aeSSam Leffler printf("[%s:%s] discard ", ic->ic_ifp->if_xname, 2833497c84aeSSam Leffler ether_sprintf(ieee80211_getbssid(ic, wh))); 28348a1b9b6aSSam Leffler if (type != NULL) 28358a1b9b6aSSam Leffler printf("%s information element, ", type); 28368a1b9b6aSSam Leffler else 28378a1b9b6aSSam Leffler printf("information element, "); 28388a1b9b6aSSam Leffler va_start(ap, fmt); 28398a1b9b6aSSam Leffler vprintf(fmt, ap); 28408a1b9b6aSSam Leffler va_end(ap); 28418a1b9b6aSSam Leffler printf("\n"); 28428a1b9b6aSSam Leffler } 28438a1b9b6aSSam Leffler 28448a1b9b6aSSam Leffler static void 28458a1b9b6aSSam Leffler ieee80211_discard_mac(struct ieee80211com *ic, 28468a1b9b6aSSam Leffler const u_int8_t mac[IEEE80211_ADDR_LEN], 28478a1b9b6aSSam Leffler const char *type, const char *fmt, ...) 28488a1b9b6aSSam Leffler { 28498a1b9b6aSSam Leffler va_list ap; 28508a1b9b6aSSam Leffler 2851aa8c14c4SSam Leffler printf("[%s:%s] discard ", ic->ic_ifp->if_xname, ether_sprintf(mac)); 28528a1b9b6aSSam Leffler if (type != NULL) 28538a1b9b6aSSam Leffler printf("%s frame, ", type); 28548a1b9b6aSSam Leffler else 28558a1b9b6aSSam Leffler printf("frame, "); 28568a1b9b6aSSam Leffler va_start(ap, fmt); 28578a1b9b6aSSam Leffler vprintf(fmt, ap); 28588a1b9b6aSSam Leffler va_end(ap); 28598a1b9b6aSSam Leffler printf("\n"); 28608a1b9b6aSSam Leffler } 28618a1b9b6aSSam Leffler #endif /* IEEE80211_DEBUG */ 2862