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> 411a1e1d21SSam Leffler 428a1b9b6aSSam Leffler #include <sys/socket.h> 431a1e1d21SSam Leffler 441a1e1d21SSam Leffler #include <net/if.h> 451a1e1d21SSam Leffler #include <net/if_media.h> 461a1e1d21SSam Leffler #include <net/ethernet.h> 471a1e1d21SSam Leffler #include <net/if_llc.h> 488a1b9b6aSSam Leffler #include <net/if_vlan_var.h> 491a1e1d21SSam Leffler 501a1e1d21SSam Leffler #include <net80211/ieee80211_var.h> 511a1e1d21SSam Leffler 521a1e1d21SSam Leffler #include <net/bpf.h> 531a1e1d21SSam Leffler 548a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 558a1b9b6aSSam Leffler #include <machine/stdarg.h> 568a1b9b6aSSam Leffler 578a1b9b6aSSam Leffler /* 588a1b9b6aSSam Leffler * Decide if a received management frame should be 598a1b9b6aSSam Leffler * printed when debugging is enabled. This filters some 608a1b9b6aSSam Leffler * of the less interesting frames that come frequently 618a1b9b6aSSam Leffler * (e.g. beacons). 628a1b9b6aSSam Leffler */ 638a1b9b6aSSam Leffler static __inline int 648a1b9b6aSSam Leffler doprint(struct ieee80211com *ic, int subtype) 658a1b9b6aSSam Leffler { 668a1b9b6aSSam Leffler switch (subtype) { 678a1b9b6aSSam Leffler case IEEE80211_FC0_SUBTYPE_BEACON: 688a1b9b6aSSam Leffler return (ic->ic_flags & IEEE80211_F_SCAN); 698a1b9b6aSSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 708a1b9b6aSSam Leffler return (ic->ic_opmode == IEEE80211_M_IBSS); 718a1b9b6aSSam Leffler } 728a1b9b6aSSam Leffler return 1; 738a1b9b6aSSam Leffler } 748a1b9b6aSSam Leffler 758a1b9b6aSSam Leffler /* 768a1b9b6aSSam Leffler * Emit a debug message about discarding a frame or information 778a1b9b6aSSam Leffler * element. One format is for extracting the mac address from 788a1b9b6aSSam Leffler * the frame header; the other is for when a header is not 798a1b9b6aSSam Leffler * available or otherwise appropriate. 808a1b9b6aSSam Leffler */ 818a1b9b6aSSam Leffler #define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) do { \ 828a1b9b6aSSam Leffler if ((_ic)->ic_debug & (_m)) \ 838a1b9b6aSSam Leffler ieee80211_discard_frame(_ic, _wh, _type, _fmt, __VA_ARGS__);\ 848a1b9b6aSSam Leffler } while (0) 858a1b9b6aSSam Leffler #define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) do { \ 868a1b9b6aSSam Leffler if ((_ic)->ic_debug & (_m)) \ 878a1b9b6aSSam Leffler ieee80211_discard_ie(_ic, _wh, _type, _fmt, __VA_ARGS__);\ 888a1b9b6aSSam Leffler } while (0) 898a1b9b6aSSam Leffler #define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) do { \ 908a1b9b6aSSam Leffler if ((_ic)->ic_debug & (_m)) \ 918a1b9b6aSSam Leffler ieee80211_discard_mac(_ic, _mac, _type, _fmt, __VA_ARGS__);\ 928a1b9b6aSSam Leffler } while (0) 938a1b9b6aSSam Leffler 948a1b9b6aSSam Leffler static const u_int8_t *ieee80211_getbssid(struct ieee80211com *, 958a1b9b6aSSam Leffler const struct ieee80211_frame *); 968a1b9b6aSSam Leffler static void ieee80211_discard_frame(struct ieee80211com *, 978a1b9b6aSSam Leffler const struct ieee80211_frame *, const char *type, const char *fmt, ...); 988a1b9b6aSSam Leffler static void ieee80211_discard_ie(struct ieee80211com *, 998a1b9b6aSSam Leffler const struct ieee80211_frame *, const char *type, const char *fmt, ...); 1008a1b9b6aSSam Leffler static void ieee80211_discard_mac(struct ieee80211com *, 1018a1b9b6aSSam Leffler const u_int8_t mac[IEEE80211_ADDR_LEN], const char *type, 1028a1b9b6aSSam Leffler const char *fmt, ...); 1038a1b9b6aSSam Leffler #else 1048a1b9b6aSSam Leffler #define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) 1058a1b9b6aSSam Leffler #define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) 1068a1b9b6aSSam Leffler #define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) 1078a1b9b6aSSam Leffler #endif /* IEEE80211_DEBUG */ 1088a1b9b6aSSam Leffler 1098a1b9b6aSSam Leffler static struct mbuf *ieee80211_defrag(struct ieee80211com *, 1108a1b9b6aSSam Leffler struct ieee80211_node *, struct mbuf *); 1118a1b9b6aSSam Leffler static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *); 1128a1b9b6aSSam Leffler static void ieee80211_node_pwrsave(struct ieee80211_node *, int enable); 1138a1b9b6aSSam Leffler static void ieee80211_recv_pspoll(struct ieee80211com *, 1148a1b9b6aSSam Leffler struct ieee80211_node *, struct mbuf *); 1151a1e1d21SSam Leffler 1160a915fadSSam Leffler /* 1170a915fadSSam Leffler * Process a received frame. The node associated with the sender 1180a915fadSSam Leffler * should be supplied. If nothing was found in the node table then 1190a915fadSSam Leffler * the caller is assumed to supply a reference to ic_bss instead. 1200a915fadSSam Leffler * The RSSI and a timestamp are also supplied. The RSSI data is used 1210a915fadSSam Leffler * during AP scanning to select a AP to associate with; it can have 1220a915fadSSam Leffler * any units so long as values have consistent units and higher values 1230a915fadSSam Leffler * mean ``better signal''. The receive timestamp is currently not used 1240a915fadSSam Leffler * by the 802.11 layer. 1250a915fadSSam Leffler */ 1261a1e1d21SSam Leffler void 1278a1b9b6aSSam Leffler ieee80211_input(struct ieee80211com *ic, struct mbuf *m, 1288a1b9b6aSSam Leffler struct ieee80211_node *ni, int rssi, u_int32_t rstamp) 1291a1e1d21SSam Leffler { 1308a1b9b6aSSam Leffler #define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) 1318a1b9b6aSSam Leffler #define HAS_SEQ(type) ((type & 0x4) == 0) 1328a1b9b6aSSam Leffler struct ifnet *ifp = ic->ic_ifp; 1331a1e1d21SSam Leffler struct ieee80211_frame *wh; 1348a1b9b6aSSam Leffler struct ieee80211_key *key; 1351a1e1d21SSam Leffler struct ether_header *eh; 1368a1b9b6aSSam Leffler int len, hdrsize, off; 13718f897abSSam Leffler u_int8_t dir, type, subtype; 1381a1e1d21SSam Leffler u_int8_t *bssid; 1391a1e1d21SSam Leffler u_int16_t rxseq; 1401a1e1d21SSam Leffler 1410a915fadSSam Leffler KASSERT(ni != NULL, ("null node")); 1428a1b9b6aSSam Leffler ni->ni_inact = ni->ni_inact_reload; 1430a915fadSSam Leffler 14418f897abSSam Leffler /* trim CRC here so WEP can find its own CRC at the end of packet. */ 1451a1e1d21SSam Leffler if (m->m_flags & M_HASFCS) { 1461a1e1d21SSam Leffler m_adj(m, -IEEE80211_CRC_LEN); 1471a1e1d21SSam Leffler m->m_flags &= ~M_HASFCS; 1481a1e1d21SSam Leffler } 14918f897abSSam Leffler KASSERT(m->m_pkthdr.len >= sizeof(struct ieee80211_frame_min), 15018f897abSSam Leffler ("frame length too short: %u", m->m_pkthdr.len)); 15118f897abSSam Leffler 15218f897abSSam Leffler /* 15318f897abSSam Leffler * In monitor mode, send everything directly to bpf. 15418f897abSSam Leffler * XXX may want to include the CRC 15518f897abSSam Leffler */ 15618f897abSSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR) 15718f897abSSam Leffler goto out; 1581a1e1d21SSam Leffler 1598a1b9b6aSSam Leffler if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { 1608a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, 1618a1b9b6aSSam Leffler ni->ni_macaddr, NULL, 1628a1b9b6aSSam Leffler "too short (1): len %u", m->m_pkthdr.len); 1638a1b9b6aSSam Leffler ic->ic_stats.is_rx_tooshort++; 1648a1b9b6aSSam Leffler goto out; 1658a1b9b6aSSam Leffler } 1668a1b9b6aSSam Leffler /* 1678a1b9b6aSSam Leffler * Bit of a cheat here, we use a pointer for a 3-address 1688a1b9b6aSSam Leffler * frame format but don't reference fields past outside 1698a1b9b6aSSam Leffler * ieee80211_frame_min w/o first validating the data is 1708a1b9b6aSSam Leffler * present. 1718a1b9b6aSSam Leffler */ 1721a1e1d21SSam Leffler wh = mtod(m, struct ieee80211_frame *); 1738a1b9b6aSSam Leffler 1741a1e1d21SSam Leffler if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != 1751a1e1d21SSam Leffler IEEE80211_FC0_VERSION_0) { 1768a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, 1778a1b9b6aSSam Leffler ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]); 1781be50176SSam Leffler ic->ic_stats.is_rx_badversion++; 1791a1e1d21SSam Leffler goto err; 1801a1e1d21SSam Leffler } 1811a1e1d21SSam Leffler 1821a1e1d21SSam Leffler dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 18318f897abSSam Leffler type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 1848a1b9b6aSSam Leffler subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 1858a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 1861a1e1d21SSam Leffler switch (ic->ic_opmode) { 1871a1e1d21SSam Leffler case IEEE80211_M_STA: 1888a1b9b6aSSam Leffler bssid = wh->i_addr2; 1898a1b9b6aSSam Leffler if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) { 1901be50176SSam Leffler /* not interested in */ 1918a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, 1928a1b9b6aSSam Leffler bssid, NULL, "%s", "not to bss"); 1931be50176SSam Leffler ic->ic_stats.is_rx_wrongbss++; 1941a1e1d21SSam Leffler goto out; 1951a1e1d21SSam Leffler } 1961a1e1d21SSam Leffler break; 1971a1e1d21SSam Leffler case IEEE80211_M_IBSS: 1981a1e1d21SSam Leffler case IEEE80211_M_AHDEMO: 1991a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 2008a1b9b6aSSam Leffler if (dir != IEEE80211_FC1_DIR_NODS) 2011a1e1d21SSam Leffler bssid = wh->i_addr1; 2028a1b9b6aSSam Leffler else if (type == IEEE80211_FC0_TYPE_CTL) 2038a1b9b6aSSam Leffler bssid = wh->i_addr1; 2048a1b9b6aSSam Leffler else { 2058a1b9b6aSSam Leffler if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 2068a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, 2078a1b9b6aSSam Leffler IEEE80211_MSG_ANY, ni->ni_macaddr, 2088a1b9b6aSSam Leffler NULL, "too short (2): len %u", 2098a1b9b6aSSam Leffler m->m_pkthdr.len); 2108a1b9b6aSSam Leffler ic->ic_stats.is_rx_tooshort++; 2118a1b9b6aSSam Leffler goto out; 2128a1b9b6aSSam Leffler } 2138a1b9b6aSSam Leffler bssid = wh->i_addr3; 2148a1b9b6aSSam Leffler } 2158a1b9b6aSSam Leffler if (type != IEEE80211_FC0_TYPE_DATA) 2168a1b9b6aSSam Leffler break; 2178a1b9b6aSSam Leffler /* 2188a1b9b6aSSam Leffler * Data frame, validate the bssid. 2198a1b9b6aSSam Leffler */ 2201a1e1d21SSam Leffler if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) && 2211a1e1d21SSam Leffler !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) { 2221a1e1d21SSam Leffler /* not interested in */ 2238a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, 2248a1b9b6aSSam Leffler bssid, NULL, "%s", "not to bss"); 2251be50176SSam Leffler ic->ic_stats.is_rx_wrongbss++; 2261a1e1d21SSam Leffler goto out; 2271a1e1d21SSam Leffler } 2288a1b9b6aSSam Leffler /* 2298a1b9b6aSSam Leffler * For adhoc mode we cons up a node when it doesn't 2308a1b9b6aSSam Leffler * exist. This should probably done after an ACL check. 2318a1b9b6aSSam Leffler */ 2328a1b9b6aSSam Leffler if (ni == ic->ic_bss && 2338a1b9b6aSSam Leffler ic->ic_opmode != IEEE80211_M_HOSTAP) { 2348a1b9b6aSSam Leffler /* 2358a1b9b6aSSam Leffler * Fake up a node for this newly 2368a1b9b6aSSam Leffler * discovered member of the IBSS. 2378a1b9b6aSSam Leffler */ 238acc4f7f5SSam Leffler ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta, 2398a1b9b6aSSam Leffler type == IEEE80211_FC0_TYPE_CTL ? 2408a1b9b6aSSam Leffler wh->i_addr1 : wh->i_addr2); 2418a1b9b6aSSam Leffler if (ni == NULL) { 2428a1b9b6aSSam Leffler /* NB: stat kept for alloc failure */ 2438a1b9b6aSSam Leffler goto err; 2448a1b9b6aSSam Leffler } 2458a1b9b6aSSam Leffler } 2461a1e1d21SSam Leffler break; 2471a1e1d21SSam Leffler default: 2488a1b9b6aSSam Leffler goto out; 2491a1e1d21SSam Leffler } 2501a1e1d21SSam Leffler ni->ni_rssi = rssi; 2511a1e1d21SSam Leffler ni->ni_rstamp = rstamp; 2528a1b9b6aSSam Leffler if (HAS_SEQ(type)) { 2538a1b9b6aSSam Leffler u_int8_t tid; 2548a1b9b6aSSam Leffler if (IEEE80211_QOS_HAS_SEQ(wh)) { 2558a1b9b6aSSam Leffler tid = ((struct ieee80211_qosframe *)wh)-> 2568a1b9b6aSSam Leffler i_qos[0] & IEEE80211_QOS_TID; 2578a1b9b6aSSam Leffler if (tid >= WME_AC_VI) 2588a1b9b6aSSam Leffler ic->ic_wme.wme_hipri_traffic++; 2598a1b9b6aSSam Leffler tid++; 2608a1b9b6aSSam Leffler } else 2618a1b9b6aSSam Leffler tid = 0; 2628a1b9b6aSSam Leffler rxseq = le16toh(*(u_int16_t *)wh->i_seq); 2631a1e1d21SSam Leffler if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && 2648a1b9b6aSSam Leffler SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) { 2658a1b9b6aSSam Leffler /* duplicate, discard */ 2668a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, 2678a1b9b6aSSam Leffler bssid, "duplicate", 2688a1b9b6aSSam Leffler "seqno <%u,%u> fragno <%u,%u> tid %u", 2698a1b9b6aSSam Leffler rxseq >> IEEE80211_SEQ_SEQ_SHIFT, 2708a1b9b6aSSam Leffler ni->ni_rxseqs[tid] >> 2718a1b9b6aSSam Leffler IEEE80211_SEQ_SEQ_SHIFT, 2728a1b9b6aSSam Leffler rxseq & IEEE80211_SEQ_FRAG_MASK, 2738a1b9b6aSSam Leffler ni->ni_rxseqs[tid] & 2748a1b9b6aSSam Leffler IEEE80211_SEQ_FRAG_MASK, 2758a1b9b6aSSam Leffler tid); 2768a1b9b6aSSam Leffler ic->ic_stats.is_rx_dup++; 2778a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_dup); 2781a1e1d21SSam Leffler goto out; 2791a1e1d21SSam Leffler } 2808a1b9b6aSSam Leffler ni->ni_rxseqs[tid] = rxseq; 2818a1b9b6aSSam Leffler } 2821a1e1d21SSam Leffler } 2831a1e1d21SSam Leffler 28418f897abSSam Leffler switch (type) { 2851a1e1d21SSam Leffler case IEEE80211_FC0_TYPE_DATA: 2868a1b9b6aSSam Leffler hdrsize = ieee80211_hdrsize(wh); 2878a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_DATAPAD) 2888a1b9b6aSSam Leffler hdrsize = roundup(hdrsize, sizeof(u_int32_t)); 2898a1b9b6aSSam Leffler if (m->m_len < hdrsize && 2908a1b9b6aSSam Leffler (m = m_pullup(m, hdrsize)) == NULL) { 2918a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, 2928a1b9b6aSSam Leffler wh, "data", "too short: len %u, expecting %u", 2938a1b9b6aSSam Leffler m->m_pkthdr.len, hdrsize); 2948a1b9b6aSSam Leffler ic->ic_stats.is_rx_tooshort++; 2958a1b9b6aSSam Leffler goto out; /* XXX */ 2968a1b9b6aSSam Leffler } 2978a1b9b6aSSam Leffler if (subtype & IEEE80211_FC0_SUBTYPE_QOS) { 2988a1b9b6aSSam Leffler /* XXX discard if node w/o IEEE80211_NODE_QOS? */ 2998a1b9b6aSSam Leffler /* 3008a1b9b6aSSam Leffler * Strip QoS control and any padding so only a 3018a1b9b6aSSam Leffler * stock 802.11 header is at the front. 3028a1b9b6aSSam Leffler */ 3038a1b9b6aSSam Leffler /* XXX 4-address QoS frame */ 3048a1b9b6aSSam Leffler off = hdrsize - sizeof(struct ieee80211_frame); 3058a1b9b6aSSam Leffler ovbcopy(mtod(m, u_int8_t *), mtod(m, u_int8_t *) + off, 3068a1b9b6aSSam Leffler hdrsize - off); 3078a1b9b6aSSam Leffler m_adj(m, off); 3088a1b9b6aSSam Leffler wh = mtod(m, struct ieee80211_frame *); 3098a1b9b6aSSam Leffler wh->i_fc[0] &= ~IEEE80211_FC0_SUBTYPE_QOS; 3108a1b9b6aSSam Leffler } else { 3118a1b9b6aSSam Leffler /* XXX copy up for 4-address frames w/ padding */ 3128a1b9b6aSSam Leffler } 3131a1e1d21SSam Leffler switch (ic->ic_opmode) { 3141a1e1d21SSam Leffler case IEEE80211_M_STA: 3151be50176SSam Leffler if (dir != IEEE80211_FC1_DIR_FROMDS) { 3161be50176SSam Leffler ic->ic_stats.is_rx_wrongdir++; 3171a1e1d21SSam Leffler goto out; 3181be50176SSam Leffler } 3191a1e1d21SSam Leffler if ((ifp->if_flags & IFF_SIMPLEX) && 3201a1e1d21SSam Leffler IEEE80211_IS_MULTICAST(wh->i_addr1) && 3211a1e1d21SSam Leffler IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) { 3221a1e1d21SSam Leffler /* 3231a1e1d21SSam Leffler * In IEEE802.11 network, multicast packet 3241a1e1d21SSam Leffler * sent from me is broadcasted from AP. 3251a1e1d21SSam Leffler * It should be silently discarded for 3261a1e1d21SSam Leffler * SIMPLEX interface. 3271a1e1d21SSam Leffler */ 3288a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 3298a1b9b6aSSam Leffler wh, NULL, "%s", "multicast echo"); 3301be50176SSam Leffler ic->ic_stats.is_rx_mcastecho++; 3311a1e1d21SSam Leffler goto out; 3321a1e1d21SSam Leffler } 3331a1e1d21SSam Leffler break; 3341a1e1d21SSam Leffler case IEEE80211_M_IBSS: 3351a1e1d21SSam Leffler case IEEE80211_M_AHDEMO: 3361be50176SSam Leffler if (dir != IEEE80211_FC1_DIR_NODS) { 3371be50176SSam Leffler ic->ic_stats.is_rx_wrongdir++; 3381a1e1d21SSam Leffler goto out; 3391be50176SSam Leffler } 3408a1b9b6aSSam Leffler /* XXX no power-save support */ 3411a1e1d21SSam Leffler break; 3421a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 3431be50176SSam Leffler if (dir != IEEE80211_FC1_DIR_TODS) { 3441be50176SSam Leffler ic->ic_stats.is_rx_wrongdir++; 3451a1e1d21SSam Leffler goto out; 3461be50176SSam Leffler } 3471a1e1d21SSam Leffler /* check if source STA is associated */ 3480a915fadSSam Leffler if (ni == ic->ic_bss) { 3498a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 3508a1b9b6aSSam Leffler wh, "data", "%s", "unknown src"); 3510a915fadSSam Leffler /* NB: caller deals with reference */ 352acc4f7f5SSam Leffler ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2); 3531a1e1d21SSam Leffler if (ni != NULL) { 3541a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 3551a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH, 3561a1e1d21SSam Leffler IEEE80211_REASON_NOT_AUTHED); 3578a1b9b6aSSam Leffler ieee80211_free_node(ni); 3581a1e1d21SSam Leffler } 3591be50176SSam Leffler ic->ic_stats.is_rx_notassoc++; 3601a1e1d21SSam Leffler goto err; 3611a1e1d21SSam Leffler } 3621a1e1d21SSam Leffler if (ni->ni_associd == 0) { 3638a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 3648a1b9b6aSSam Leffler wh, "data", "%s", "unassoc src"); 3651a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 3661a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_DISASSOC, 3671a1e1d21SSam Leffler IEEE80211_REASON_NOT_ASSOCED); 3681be50176SSam Leffler ic->ic_stats.is_rx_notassoc++; 3691a1e1d21SSam Leffler goto err; 3701a1e1d21SSam Leffler } 3718a1b9b6aSSam Leffler 3728a1b9b6aSSam Leffler /* 3738a1b9b6aSSam Leffler * Check for power save state change. 3748a1b9b6aSSam Leffler */ 3758a1b9b6aSSam Leffler if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^ 3768a1b9b6aSSam Leffler (ni->ni_flags & IEEE80211_NODE_PWR_MGT))) 3778a1b9b6aSSam Leffler ieee80211_node_pwrsave(ni, 3788a1b9b6aSSam Leffler wh->i_fc[1] & IEEE80211_FC1_PWR_MGT); 3791a1e1d21SSam Leffler break; 3808a1b9b6aSSam Leffler default: 3818a1b9b6aSSam Leffler /* XXX here to keep compiler happy */ 3828a1b9b6aSSam Leffler goto out; 3831a1e1d21SSam Leffler } 3848a1b9b6aSSam Leffler 3858a1b9b6aSSam Leffler /* 3868a1b9b6aSSam Leffler * Handle privacy requirements. Note that we 3878a1b9b6aSSam Leffler * must not be preempted from here until after 3888a1b9b6aSSam Leffler * we (potentially) call ieee80211_crypto_demic; 3898a1b9b6aSSam Leffler * otherwise we may violate assumptions in the 3908a1b9b6aSSam Leffler * crypto cipher modules used to do delayed update 3918a1b9b6aSSam Leffler * of replay sequence numbers. 3928a1b9b6aSSam Leffler */ 3931a1e1d21SSam Leffler if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 3948a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { 3958a1b9b6aSSam Leffler /* 3968a1b9b6aSSam Leffler * Discard encrypted frames when privacy is off. 3978a1b9b6aSSam Leffler */ 3988a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 3998a1b9b6aSSam Leffler wh, "WEP", "%s", "PRIVACY off"); 4008a1b9b6aSSam Leffler ic->ic_stats.is_rx_noprivacy++; 4018a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_noprivacy); 4028a1b9b6aSSam Leffler goto out; 4038a1b9b6aSSam Leffler } 4048a1b9b6aSSam Leffler key = ieee80211_crypto_decap(ic, ni, m); 4058a1b9b6aSSam Leffler if (key == NULL) { 4068a1b9b6aSSam Leffler /* NB: stats+msgs handled in crypto_decap */ 4078a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_wepfail); 4088a1b9b6aSSam Leffler goto out; 4091be50176SSam Leffler } 4101a1e1d21SSam Leffler wh = mtod(m, struct ieee80211_frame *); 4118116d318SSam Leffler wh->i_fc[1] &= ~IEEE80211_FC1_WEP; 4121be50176SSam Leffler } else { 4138a1b9b6aSSam Leffler key = NULL; 4148a1b9b6aSSam Leffler } 4158a1b9b6aSSam Leffler 4168a1b9b6aSSam Leffler /* 4178a1b9b6aSSam Leffler * Next up, any fragmentation. 4188a1b9b6aSSam Leffler */ 4198a1b9b6aSSam Leffler if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 4208a1b9b6aSSam Leffler m = ieee80211_defrag(ic, ni, m); 4218a1b9b6aSSam Leffler if (m == NULL) { 4228a1b9b6aSSam Leffler /* Fragment dropped or frame not complete yet */ 4231a1e1d21SSam Leffler goto out; 4241a1e1d21SSam Leffler } 4251be50176SSam Leffler } 4268a1b9b6aSSam Leffler wh = NULL; /* no longer valid, catch any uses */ 4278a1b9b6aSSam Leffler 4288a1b9b6aSSam Leffler /* 4298a1b9b6aSSam Leffler * Next strip any MSDU crypto bits. 4308a1b9b6aSSam Leffler */ 4318a1b9b6aSSam Leffler if (key != NULL && !ieee80211_crypto_demic(ic, key, m)) { 4328a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, 4338a1b9b6aSSam Leffler ni->ni_macaddr, "data", "%s", "demic error"); 4348a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_demicfail); 4358a1b9b6aSSam Leffler goto out; 4368a1b9b6aSSam Leffler } 4378a1b9b6aSSam Leffler 4381a1e1d21SSam Leffler /* copy to listener after decrypt */ 4391a1e1d21SSam Leffler if (ic->ic_rawbpf) 4401a1e1d21SSam Leffler bpf_mtap(ic->ic_rawbpf, m); 4418a1b9b6aSSam Leffler 4428a1b9b6aSSam Leffler /* 4438a1b9b6aSSam Leffler * Finally, strip the 802.11 header. 4448a1b9b6aSSam Leffler */ 4458a1b9b6aSSam Leffler m = ieee80211_decap(ic, m); 4461be50176SSam Leffler if (m == NULL) { 4478a1b9b6aSSam Leffler /* don't count Null data frames as errors */ 4488a1b9b6aSSam Leffler if (subtype == IEEE80211_FC0_SUBTYPE_NODATA) 4498a1b9b6aSSam Leffler goto out; 4508a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, 4518a1b9b6aSSam Leffler ni->ni_macaddr, "data", "%s", "decap error"); 4521be50176SSam Leffler ic->ic_stats.is_rx_decap++; 4538a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_decap); 4541a1e1d21SSam Leffler goto err; 4551be50176SSam Leffler } 4568a1b9b6aSSam Leffler eh = mtod(m, struct ether_header *); 4578a1b9b6aSSam Leffler if (!ieee80211_node_is_authorized(ni)) { 4588a1b9b6aSSam Leffler /* 4598a1b9b6aSSam Leffler * Deny any non-PAE frames received prior to 4608a1b9b6aSSam Leffler * authorization. For open/shared-key 4618a1b9b6aSSam Leffler * authentication the port is mark authorized 4628a1b9b6aSSam Leffler * after authentication completes. For 802.1x 4638a1b9b6aSSam Leffler * the port is not marked authorized by the 4648a1b9b6aSSam Leffler * authenticator until the handshake has completed. 4658a1b9b6aSSam Leffler */ 4668a1b9b6aSSam Leffler if (eh->ether_type != htons(ETHERTYPE_PAE)) { 4678a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, 4688a1b9b6aSSam Leffler eh->ether_shost, "data", 4698a1b9b6aSSam Leffler "unauthorized port: ether type 0x%x len %u", 4708a1b9b6aSSam Leffler eh->ether_type, m->m_pkthdr.len); 4718a1b9b6aSSam Leffler ic->ic_stats.is_rx_unauth++; 4728a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_unauth); 4738a1b9b6aSSam Leffler goto err; 4748a1b9b6aSSam Leffler } 4758a1b9b6aSSam Leffler } else { 4768a1b9b6aSSam Leffler /* 4778a1b9b6aSSam Leffler * When denying unencrypted frames, discard 4788a1b9b6aSSam Leffler * any non-PAE frames received without encryption. 4798a1b9b6aSSam Leffler */ 4808a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_DROPUNENC) && 4818a1b9b6aSSam Leffler key == NULL && 4828a1b9b6aSSam Leffler eh->ether_type != htons(ETHERTYPE_PAE)) { 4838a1b9b6aSSam Leffler /* 4848a1b9b6aSSam Leffler * Drop unencrypted frames. 4858a1b9b6aSSam Leffler */ 4868a1b9b6aSSam Leffler ic->ic_stats.is_rx_unencrypted++; 4878a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_unencrypted); 4888a1b9b6aSSam Leffler goto out; 4898a1b9b6aSSam Leffler } 4908a1b9b6aSSam Leffler } 4911a1e1d21SSam Leffler ifp->if_ipackets++; 4928a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_data); 4938a1b9b6aSSam Leffler IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len); 4941a1e1d21SSam Leffler 4951a1e1d21SSam Leffler /* perform as a bridge within the AP */ 4968a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP && 4978a1b9b6aSSam Leffler (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) { 4988a1b9b6aSSam Leffler struct mbuf *m1 = NULL; 4998a1b9b6aSSam Leffler 5001a1e1d21SSam Leffler if (ETHER_IS_MULTICAST(eh->ether_dhost)) { 5010a915fadSSam Leffler m1 = m_copypacket(m, M_DONTWAIT); 5021a1e1d21SSam Leffler if (m1 == NULL) 5031a1e1d21SSam Leffler ifp->if_oerrors++; 5041a1e1d21SSam Leffler else 5051a1e1d21SSam Leffler m1->m_flags |= M_MCAST; 5061a1e1d21SSam Leffler } else { 5078a1b9b6aSSam Leffler /* XXX this dups work done in ieee80211_encap */ 5088a1b9b6aSSam Leffler /* check if destination is associated */ 5098a1b9b6aSSam Leffler struct ieee80211_node *ni1 = 510acc4f7f5SSam Leffler ieee80211_find_node(&ic->ic_sta, 5118a1b9b6aSSam Leffler eh->ether_dhost); 5128a1b9b6aSSam Leffler if (ni1 != NULL) { 5138a1b9b6aSSam Leffler /* XXX check if authorized */ 5148a1b9b6aSSam Leffler if (ni1->ni_associd != 0) { 5151a1e1d21SSam Leffler m1 = m; 5161a1e1d21SSam Leffler m = NULL; 5171a1e1d21SSam Leffler } 5188a1b9b6aSSam Leffler /* XXX statistic? */ 5198a1b9b6aSSam Leffler ieee80211_free_node(ni1); 5201a1e1d21SSam Leffler } 5211a1e1d21SSam Leffler } 5221a1e1d21SSam Leffler if (m1 != NULL) { 5231a1e1d21SSam Leffler len = m1->m_pkthdr.len; 5241a1e1d21SSam Leffler IF_ENQUEUE(&ifp->if_snd, m1); 5251a1e1d21SSam Leffler if (m != NULL) 5261a1e1d21SSam Leffler ifp->if_omcasts++; 5271a1e1d21SSam Leffler ifp->if_obytes += len; 5281a1e1d21SSam Leffler } 5291a1e1d21SSam Leffler } 5308a1b9b6aSSam Leffler if (m != NULL) { 5318a1b9b6aSSam Leffler if (ni->ni_vlan != 0) { 5328a1b9b6aSSam Leffler /* attach vlan tag */ 5338a1b9b6aSSam Leffler /* XXX goto err? */ 5348a1b9b6aSSam Leffler VLAN_INPUT_TAG(ifp, m, ni->ni_vlan, goto out); 5358a1b9b6aSSam Leffler } 5361a1e1d21SSam Leffler (*ifp->if_input)(ifp, m); 5378a1b9b6aSSam Leffler } 5381a1e1d21SSam Leffler return; 5391a1e1d21SSam Leffler 5401a1e1d21SSam Leffler case IEEE80211_FC0_TYPE_MGT: 5418a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_mgmt); 5421be50176SSam Leffler if (dir != IEEE80211_FC1_DIR_NODS) { 5431be50176SSam Leffler ic->ic_stats.is_rx_wrongdir++; 5441a1e1d21SSam Leffler goto err; 5451be50176SSam Leffler } 5468a1b9b6aSSam Leffler if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 5478a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, 5488a1b9b6aSSam Leffler ni->ni_macaddr, "mgt", "too short: len %u", 5498a1b9b6aSSam Leffler m->m_pkthdr.len); 5508a1b9b6aSSam Leffler ic->ic_stats.is_rx_tooshort++; 5511a1e1d21SSam Leffler goto out; 5521be50176SSam Leffler } 5531a1e1d21SSam Leffler #ifdef IEEE80211_DEBUG 5548a1b9b6aSSam Leffler if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) || 5558a1b9b6aSSam Leffler ieee80211_msg_dumppkts(ic)) { 5568a1b9b6aSSam Leffler if_printf(ic->ic_ifp, "received %s from %s rssi %d\n", 5578a1b9b6aSSam Leffler ieee80211_mgt_subtype_name[subtype >> 5588a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 5591a1e1d21SSam Leffler ether_sprintf(wh->i_addr2), rssi); 5601a1e1d21SSam Leffler } 5618a1b9b6aSSam Leffler #endif 5628a1b9b6aSSam Leffler if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 5638a1b9b6aSSam Leffler if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) { 5648a1b9b6aSSam Leffler /* 5658a1b9b6aSSam Leffler * Only shared key auth frames with a challenge 5668a1b9b6aSSam Leffler * should be encrypted, discard all others. 5678a1b9b6aSSam Leffler */ 5688a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 5698a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> 5708a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 5718a1b9b6aSSam Leffler "%s", "WEP set but not permitted"); 5728a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; /* XXX */ 5738a1b9b6aSSam Leffler goto out; 5748a1b9b6aSSam Leffler } 5758a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { 5768a1b9b6aSSam Leffler /* 5778a1b9b6aSSam Leffler * Discard encrypted frames when privacy is off. 5788a1b9b6aSSam Leffler */ 5798a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 5808a1b9b6aSSam Leffler wh, "mgt", "%s", "WEP set but PRIVACY off"); 5818a1b9b6aSSam Leffler ic->ic_stats.is_rx_noprivacy++; 5828a1b9b6aSSam Leffler goto out; 5838a1b9b6aSSam Leffler } 5848a1b9b6aSSam Leffler key = ieee80211_crypto_decap(ic, ni, m); 5858a1b9b6aSSam Leffler if (key == NULL) { 5868a1b9b6aSSam Leffler /* NB: stats+msgs handled in crypto_decap */ 5878a1b9b6aSSam Leffler goto out; 5888a1b9b6aSSam Leffler } 5898116d318SSam Leffler wh = mtod(m, struct ieee80211_frame *); 5908116d318SSam Leffler wh->i_fc[1] &= ~IEEE80211_FC1_WEP; 5918a1b9b6aSSam Leffler } 5921a1e1d21SSam Leffler if (ic->ic_rawbpf) 5931a1e1d21SSam Leffler bpf_mtap(ic->ic_rawbpf, m); 5940a915fadSSam Leffler (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp); 5951a1e1d21SSam Leffler m_freem(m); 5961a1e1d21SSam Leffler return; 5971a1e1d21SSam Leffler 5981a1e1d21SSam Leffler case IEEE80211_FC0_TYPE_CTL: 5998a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_ctrl); 6001be50176SSam Leffler ic->ic_stats.is_rx_ctl++; 6018a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 6028a1b9b6aSSam Leffler switch (subtype) { 6038a1b9b6aSSam Leffler case IEEE80211_FC0_SUBTYPE_PS_POLL: 6048a1b9b6aSSam Leffler ieee80211_recv_pspoll(ic, ni, m); 6058a1b9b6aSSam Leffler break; 6068a1b9b6aSSam Leffler } 6078a1b9b6aSSam Leffler } 60818f897abSSam Leffler goto out; 6091a1e1d21SSam Leffler default: 6108a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, 6118a1b9b6aSSam Leffler wh, NULL, "bad frame type 0x%x", type); 6121a1e1d21SSam Leffler /* should not come here */ 6131a1e1d21SSam Leffler break; 6141a1e1d21SSam Leffler } 6151a1e1d21SSam Leffler err: 6161a1e1d21SSam Leffler ifp->if_ierrors++; 6171a1e1d21SSam Leffler out: 6181a1e1d21SSam Leffler if (m != NULL) { 6191a1e1d21SSam Leffler if (ic->ic_rawbpf) 6201a1e1d21SSam Leffler bpf_mtap(ic->ic_rawbpf, m); 6211a1e1d21SSam Leffler m_freem(m); 6221a1e1d21SSam Leffler } 6238a1b9b6aSSam Leffler #undef SEQ_LEQ 6241a1e1d21SSam Leffler } 6251a1e1d21SSam Leffler 6268a1b9b6aSSam Leffler /* 6278a1b9b6aSSam Leffler * This function reassemble fragments. 6288a1b9b6aSSam Leffler */ 6298a1b9b6aSSam Leffler static struct mbuf * 6308a1b9b6aSSam Leffler ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, 6318a1b9b6aSSam Leffler struct mbuf *m) 6321a1e1d21SSam Leffler { 6338a1b9b6aSSam Leffler struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); 6348a1b9b6aSSam Leffler struct ieee80211_frame *lwh; 6358a1b9b6aSSam Leffler u_int16_t rxseq; 6368a1b9b6aSSam Leffler u_int8_t fragno; 6378a1b9b6aSSam Leffler u_int8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG; 6388a1b9b6aSSam Leffler struct mbuf *mfrag; 6398a1b9b6aSSam Leffler 6408a1b9b6aSSam Leffler KASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?")); 6418a1b9b6aSSam Leffler 6428a1b9b6aSSam Leffler rxseq = le16toh(*(u_int16_t *)wh->i_seq); 6438a1b9b6aSSam Leffler fragno = rxseq & IEEE80211_SEQ_FRAG_MASK; 6448a1b9b6aSSam Leffler 6458a1b9b6aSSam Leffler /* Quick way out, if there's nothing to defragment */ 6468a1b9b6aSSam Leffler if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL) 6478a1b9b6aSSam Leffler return m; 6488a1b9b6aSSam Leffler 6498a1b9b6aSSam Leffler /* 6508a1b9b6aSSam Leffler * Remove frag to insure it doesn't get reaped by timer. 6518a1b9b6aSSam Leffler */ 6528a1b9b6aSSam Leffler if (ni->ni_table == NULL) { 6538a1b9b6aSSam Leffler /* 6548a1b9b6aSSam Leffler * Should never happen. If the node is orphaned (not in 6558a1b9b6aSSam Leffler * the table) then input packets should not reach here. 6568a1b9b6aSSam Leffler * Otherwise, a concurrent request that yanks the table 6578a1b9b6aSSam Leffler * should be blocked by other interlocking and/or by first 6588a1b9b6aSSam Leffler * shutting the driver down. Regardless, be defensive 6598a1b9b6aSSam Leffler * here and just bail 6608a1b9b6aSSam Leffler */ 6618a1b9b6aSSam Leffler /* XXX need msg+stat */ 6628a1b9b6aSSam Leffler m_freem(m); 6638a1b9b6aSSam Leffler return NULL; 6648a1b9b6aSSam Leffler } 6658a1b9b6aSSam Leffler IEEE80211_NODE_LOCK(ni->ni_table); 6668a1b9b6aSSam Leffler mfrag = ni->ni_rxfrag[0]; 6678a1b9b6aSSam Leffler ni->ni_rxfrag[0] = NULL; 6688a1b9b6aSSam Leffler IEEE80211_NODE_UNLOCK(ni->ni_table); 6698a1b9b6aSSam Leffler 6708a1b9b6aSSam Leffler /* 6718a1b9b6aSSam Leffler * Validate new fragment is in order and 6728a1b9b6aSSam Leffler * related to the previous ones. 6738a1b9b6aSSam Leffler */ 6748a1b9b6aSSam Leffler if (mfrag != NULL) { 6758a1b9b6aSSam Leffler u_int16_t last_rxseq; 6768a1b9b6aSSam Leffler 6778a1b9b6aSSam Leffler lwh = mtod(mfrag, struct ieee80211_frame *); 6788a1b9b6aSSam Leffler last_rxseq = le16toh(*(u_int16_t *)lwh->i_seq); 6798a1b9b6aSSam Leffler /* NB: check seq # and frag together */ 6808a1b9b6aSSam Leffler if (rxseq != last_rxseq+1 || 6818a1b9b6aSSam Leffler !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) || 6828a1b9b6aSSam Leffler !IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) { 6838a1b9b6aSSam Leffler /* 6848a1b9b6aSSam Leffler * Unrelated fragment or no space for it, 6858a1b9b6aSSam Leffler * clear current fragments. 6868a1b9b6aSSam Leffler */ 6878a1b9b6aSSam Leffler m_freem(mfrag); 6888a1b9b6aSSam Leffler mfrag = NULL; 6898a1b9b6aSSam Leffler } 6908a1b9b6aSSam Leffler } 6918a1b9b6aSSam Leffler 6928a1b9b6aSSam Leffler if (mfrag == NULL) { 6938a1b9b6aSSam Leffler if (fragno != 0) { /* !first fragment, discard */ 6948a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_defrag); 6958a1b9b6aSSam Leffler m_freem(m); 6968a1b9b6aSSam Leffler return NULL; 6978a1b9b6aSSam Leffler } 6988a1b9b6aSSam Leffler mfrag = m; 6998a1b9b6aSSam Leffler } else { /* concatenate */ 7008a1b9b6aSSam Leffler m_cat(mfrag, m); 7018a1b9b6aSSam Leffler /* NB: m_cat doesn't update the packet header */ 7028a1b9b6aSSam Leffler mfrag->m_pkthdr.len += m->m_pkthdr.len; 7038a1b9b6aSSam Leffler /* track last seqnum and fragno */ 7048a1b9b6aSSam Leffler lwh = mtod(mfrag, struct ieee80211_frame *); 7058a1b9b6aSSam Leffler *(u_int16_t *) lwh->i_seq = *(u_int16_t *) wh->i_seq; 7068a1b9b6aSSam Leffler } 7078a1b9b6aSSam Leffler if (more_frag) { /* more to come, save */ 7088a1b9b6aSSam Leffler ni->ni_rxfrag[0] = mfrag; 7098a1b9b6aSSam Leffler mfrag = NULL; 7108a1b9b6aSSam Leffler } 7118a1b9b6aSSam Leffler return mfrag; 7128a1b9b6aSSam Leffler } 7138a1b9b6aSSam Leffler 7148a1b9b6aSSam Leffler static struct mbuf * 7158a1b9b6aSSam Leffler ieee80211_decap(struct ieee80211com *ic, struct mbuf *m) 7168a1b9b6aSSam Leffler { 7178a1b9b6aSSam Leffler struct ieee80211_frame wh; /* NB: QoS stripped above */ 7181a1e1d21SSam Leffler struct ether_header *eh; 7191a1e1d21SSam Leffler struct llc *llc; 7201a1e1d21SSam Leffler 7218a1b9b6aSSam Leffler if (m->m_len < sizeof(wh) + sizeof(*llc) && 7228a1b9b6aSSam Leffler (m = m_pullup(m, sizeof(wh) + sizeof(*llc))) == NULL) { 7238a1b9b6aSSam Leffler /* XXX stat, msg */ 7241a1e1d21SSam Leffler return NULL; 7251a1e1d21SSam Leffler } 7261a1e1d21SSam Leffler memcpy(&wh, mtod(m, caddr_t), sizeof(wh)); 7271a1e1d21SSam Leffler llc = (struct llc *)(mtod(m, caddr_t) + sizeof(wh)); 7281a1e1d21SSam Leffler if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP && 7291a1e1d21SSam Leffler llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 && 7301a1e1d21SSam Leffler llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0) { 7311a1e1d21SSam Leffler m_adj(m, sizeof(wh) + sizeof(struct llc) - sizeof(*eh)); 7321a1e1d21SSam Leffler llc = NULL; 7331a1e1d21SSam Leffler } else { 7341a1e1d21SSam Leffler m_adj(m, sizeof(wh) - sizeof(*eh)); 7351a1e1d21SSam Leffler } 7361a1e1d21SSam Leffler eh = mtod(m, struct ether_header *); 7371a1e1d21SSam Leffler switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) { 7381a1e1d21SSam Leffler case IEEE80211_FC1_DIR_NODS: 7391a1e1d21SSam Leffler IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1); 7401a1e1d21SSam Leffler IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2); 7411a1e1d21SSam Leffler break; 7421a1e1d21SSam Leffler case IEEE80211_FC1_DIR_TODS: 7431a1e1d21SSam Leffler IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3); 7441a1e1d21SSam Leffler IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2); 7451a1e1d21SSam Leffler break; 7461a1e1d21SSam Leffler case IEEE80211_FC1_DIR_FROMDS: 7471a1e1d21SSam Leffler IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1); 7481a1e1d21SSam Leffler IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3); 7491a1e1d21SSam Leffler break; 7501a1e1d21SSam Leffler case IEEE80211_FC1_DIR_DSTODS: 7511a1e1d21SSam Leffler /* not yet supported */ 7528a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, 7538a1b9b6aSSam Leffler &wh, "data", "%s", "DS to DS not supported"); 7541a1e1d21SSam Leffler m_freem(m); 7551a1e1d21SSam Leffler return NULL; 7561a1e1d21SSam Leffler } 7571a1e1d21SSam Leffler #ifdef ALIGNED_POINTER 7581a1e1d21SSam Leffler if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) { 7591a1e1d21SSam Leffler struct mbuf *n, *n0, **np; 7601a1e1d21SSam Leffler caddr_t newdata; 7611a1e1d21SSam Leffler int off, pktlen; 7621a1e1d21SSam Leffler 7631a1e1d21SSam Leffler n0 = NULL; 7641a1e1d21SSam Leffler np = &n0; 7651a1e1d21SSam Leffler off = 0; 7661a1e1d21SSam Leffler pktlen = m->m_pkthdr.len; 7671a1e1d21SSam Leffler while (pktlen > off) { 7681a1e1d21SSam Leffler if (n0 == NULL) { 7691a1e1d21SSam Leffler MGETHDR(n, M_DONTWAIT, MT_DATA); 7701a1e1d21SSam Leffler if (n == NULL) { 7711a1e1d21SSam Leffler m_freem(m); 7721a1e1d21SSam Leffler return NULL; 7731a1e1d21SSam Leffler } 7741a1e1d21SSam Leffler M_MOVE_PKTHDR(n, m); 7751a1e1d21SSam Leffler n->m_len = MHLEN; 7761a1e1d21SSam Leffler } else { 7771a1e1d21SSam Leffler MGET(n, M_DONTWAIT, MT_DATA); 7781a1e1d21SSam Leffler if (n == NULL) { 7791a1e1d21SSam Leffler m_freem(m); 7801a1e1d21SSam Leffler m_freem(n0); 7811a1e1d21SSam Leffler return NULL; 7821a1e1d21SSam Leffler } 7831a1e1d21SSam Leffler n->m_len = MLEN; 7841a1e1d21SSam Leffler } 7851a1e1d21SSam Leffler if (pktlen - off >= MINCLSIZE) { 7861a1e1d21SSam Leffler MCLGET(n, M_DONTWAIT); 7871a1e1d21SSam Leffler if (n->m_flags & M_EXT) 7881a1e1d21SSam Leffler n->m_len = n->m_ext.ext_size; 7891a1e1d21SSam Leffler } 7901a1e1d21SSam Leffler if (n0 == NULL) { 7911a1e1d21SSam Leffler newdata = 7921a1e1d21SSam Leffler (caddr_t)ALIGN(n->m_data + sizeof(*eh)) - 7931a1e1d21SSam Leffler sizeof(*eh); 7941a1e1d21SSam Leffler n->m_len -= newdata - n->m_data; 7951a1e1d21SSam Leffler n->m_data = newdata; 7961a1e1d21SSam Leffler } 7971a1e1d21SSam Leffler if (n->m_len > pktlen - off) 7981a1e1d21SSam Leffler n->m_len = pktlen - off; 7991a1e1d21SSam Leffler m_copydata(m, off, n->m_len, mtod(n, caddr_t)); 8001a1e1d21SSam Leffler off += n->m_len; 8011a1e1d21SSam Leffler *np = n; 8021a1e1d21SSam Leffler np = &n->m_next; 8031a1e1d21SSam Leffler } 8041a1e1d21SSam Leffler m_freem(m); 8051a1e1d21SSam Leffler m = n0; 8061a1e1d21SSam Leffler } 8071a1e1d21SSam Leffler #endif /* ALIGNED_POINTER */ 8081a1e1d21SSam Leffler if (llc != NULL) { 8091a1e1d21SSam Leffler eh = mtod(m, struct ether_header *); 8101a1e1d21SSam Leffler eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh)); 8111a1e1d21SSam Leffler } 8121a1e1d21SSam Leffler return m; 8131a1e1d21SSam Leffler } 8141a1e1d21SSam Leffler 8151a1e1d21SSam Leffler /* 8161a1e1d21SSam Leffler * Install received rate set information in the node's state block. 8171a1e1d21SSam Leffler */ 8181a1e1d21SSam Leffler static int 8191a1e1d21SSam Leffler ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni, 8201a1e1d21SSam Leffler u_int8_t *rates, u_int8_t *xrates, int flags) 8211a1e1d21SSam Leffler { 8221a1e1d21SSam Leffler struct ieee80211_rateset *rs = &ni->ni_rates; 8231a1e1d21SSam Leffler 8241a1e1d21SSam Leffler memset(rs, 0, sizeof(*rs)); 8251a1e1d21SSam Leffler rs->rs_nrates = rates[1]; 8261a1e1d21SSam Leffler memcpy(rs->rs_rates, rates + 2, rs->rs_nrates); 8271a1e1d21SSam Leffler if (xrates != NULL) { 8281a1e1d21SSam Leffler u_int8_t nxrates; 8291a1e1d21SSam Leffler /* 8301a1e1d21SSam Leffler * Tack on 11g extended supported rate element. 8311a1e1d21SSam Leffler */ 8321a1e1d21SSam Leffler nxrates = xrates[1]; 8331a1e1d21SSam Leffler if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) { 8341a1e1d21SSam Leffler nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates; 8358a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE, 8368a1b9b6aSSam Leffler "[%s] extended rate set too large;" 8371a1e1d21SSam Leffler " only using %u of %u rates\n", 8388a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), nxrates, xrates[1]); 8391be50176SSam Leffler ic->ic_stats.is_rx_rstoobig++; 8401a1e1d21SSam Leffler } 8411a1e1d21SSam Leffler memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates); 8421a1e1d21SSam Leffler rs->rs_nrates += nxrates; 8431a1e1d21SSam Leffler } 8441a1e1d21SSam Leffler return ieee80211_fix_rate(ic, ni, flags); 8451a1e1d21SSam Leffler } 8461a1e1d21SSam Leffler 8478a1b9b6aSSam Leffler static void 8488a1b9b6aSSam Leffler ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, 8498a1b9b6aSSam Leffler struct ieee80211_node *ni, int rssi, u_int32_t rstamp, u_int16_t seq, 8508a1b9b6aSSam Leffler u_int16_t status) 8518a1b9b6aSSam Leffler { 8528a1b9b6aSSam Leffler 8538a1b9b6aSSam Leffler switch (ic->ic_opmode) { 8548a1b9b6aSSam Leffler case IEEE80211_M_IBSS: 8558a1b9b6aSSam Leffler if (ic->ic_state != IEEE80211_S_RUN || 8568a1b9b6aSSam Leffler seq != IEEE80211_AUTH_OPEN_REQUEST) { 8578a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 8588a1b9b6aSSam Leffler return; 8598a1b9b6aSSam Leffler } 8608a1b9b6aSSam Leffler ieee80211_new_state(ic, IEEE80211_S_AUTH, 8618a1b9b6aSSam Leffler wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); 8628a1b9b6aSSam Leffler break; 8638a1b9b6aSSam Leffler 8648a1b9b6aSSam Leffler case IEEE80211_M_AHDEMO: 8658a1b9b6aSSam Leffler /* should not come here */ 8668a1b9b6aSSam Leffler break; 8678a1b9b6aSSam Leffler 8688a1b9b6aSSam Leffler case IEEE80211_M_HOSTAP: 8698a1b9b6aSSam Leffler if (ic->ic_state != IEEE80211_S_RUN || 8708a1b9b6aSSam Leffler seq != IEEE80211_AUTH_OPEN_REQUEST) { 8718a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 8728a1b9b6aSSam Leffler return; 8738a1b9b6aSSam Leffler } 8748a1b9b6aSSam Leffler /* always accept open authentication requests */ 8758a1b9b6aSSam Leffler if (ni == ic->ic_bss) { 876acc4f7f5SSam Leffler ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2); 8778a1b9b6aSSam Leffler if (ni == NULL) 8788a1b9b6aSSam Leffler return; 8798a1b9b6aSSam Leffler } else 8808a1b9b6aSSam Leffler (void) ieee80211_ref_node(ni); 8818a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 8828a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); 8838a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 8848a1b9b6aSSam Leffler "[%s] station authenticated (open)\n", 8858a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr)); 8868a1b9b6aSSam Leffler break; 8878a1b9b6aSSam Leffler 8888a1b9b6aSSam Leffler case IEEE80211_M_STA: 8898a1b9b6aSSam Leffler if (ic->ic_state != IEEE80211_S_AUTH || 8908a1b9b6aSSam Leffler seq != IEEE80211_AUTH_OPEN_RESPONSE) { 8918a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 8928a1b9b6aSSam Leffler return; 8938a1b9b6aSSam Leffler } 8948a1b9b6aSSam Leffler if (status != 0) { 8958a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, 8968a1b9b6aSSam Leffler IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 8978a1b9b6aSSam Leffler "[%s] open auth failed (reason %d)\n", 8988a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), status); 8998a1b9b6aSSam Leffler /* XXX can this happen? */ 9008a1b9b6aSSam Leffler if (ni != ic->ic_bss) 9018a1b9b6aSSam Leffler ni->ni_fails++; 9028a1b9b6aSSam Leffler ic->ic_stats.is_rx_auth_fail++; 9038a1b9b6aSSam Leffler return; 9048a1b9b6aSSam Leffler } 9058a1b9b6aSSam Leffler ieee80211_new_state(ic, IEEE80211_S_ASSOC, 9068a1b9b6aSSam Leffler wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); 9078a1b9b6aSSam Leffler break; 9088a1b9b6aSSam Leffler case IEEE80211_M_MONITOR: 9098a1b9b6aSSam Leffler break; 9108a1b9b6aSSam Leffler } 9118a1b9b6aSSam Leffler } 9128a1b9b6aSSam Leffler 9138a1b9b6aSSam Leffler static int 9148a1b9b6aSSam Leffler alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni) 9158a1b9b6aSSam Leffler { 9168a1b9b6aSSam Leffler if (ni->ni_challenge == NULL) 9178a1b9b6aSSam Leffler MALLOC(ni->ni_challenge, u_int32_t*, IEEE80211_CHALLENGE_LEN, 9188a1b9b6aSSam Leffler M_DEVBUF, M_NOWAIT); 9198a1b9b6aSSam Leffler if (ni->ni_challenge == NULL) { 9208a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 9218a1b9b6aSSam Leffler "[%s] shared key challenge alloc failed\n", 9228a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr)); 9238a1b9b6aSSam Leffler /* XXX statistic */ 9248a1b9b6aSSam Leffler } 9258a1b9b6aSSam Leffler return (ni->ni_challenge != NULL); 9268a1b9b6aSSam Leffler } 9278a1b9b6aSSam Leffler 9288a1b9b6aSSam Leffler /* XXX TODO: add statistics */ 9298a1b9b6aSSam Leffler static void 9308a1b9b6aSSam Leffler ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, 9318a1b9b6aSSam Leffler u_int8_t *frm, u_int8_t *efrm, struct ieee80211_node *ni, int rssi, 9328a1b9b6aSSam Leffler u_int32_t rstamp, u_int16_t seq, u_int16_t status) 9338a1b9b6aSSam Leffler { 9348a1b9b6aSSam Leffler u_int8_t *challenge; 9358a1b9b6aSSam Leffler int allocbs, estatus; 9368a1b9b6aSSam Leffler 9378a1b9b6aSSam Leffler /* 9388a1b9b6aSSam Leffler * NB: this can happen as we allow pre-shared key 9398a1b9b6aSSam Leffler * authentication to be enabled w/o wep being turned 9408a1b9b6aSSam Leffler * on so that configuration of these can be done 9418a1b9b6aSSam Leffler * in any order. It may be better to enforce the 9428a1b9b6aSSam Leffler * ordering in which case this check would just be 9438a1b9b6aSSam Leffler * for sanity/consistency. 9448a1b9b6aSSam Leffler */ 9458a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { 9468a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 9478a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 9488a1b9b6aSSam Leffler "%s", " PRIVACY is disabled"); 9498a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_ALG; 9508a1b9b6aSSam Leffler goto bad; 9518a1b9b6aSSam Leffler } 9528a1b9b6aSSam Leffler /* 9538a1b9b6aSSam Leffler * Pre-shared key authentication is evil; accept 9548a1b9b6aSSam Leffler * it only if explicitly configured (it is supported 9558a1b9b6aSSam Leffler * mainly for compatibility with clients like OS X). 9568a1b9b6aSSam Leffler */ 9578a1b9b6aSSam Leffler if (ni->ni_authmode != IEEE80211_AUTH_AUTO && 9588a1b9b6aSSam Leffler ni->ni_authmode != IEEE80211_AUTH_SHARED) { 9598a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 9608a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 9618a1b9b6aSSam Leffler "bad sta auth mode %u", ni->ni_authmode); 9628a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */ 9638a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_ALG; 9648a1b9b6aSSam Leffler goto bad; 9658a1b9b6aSSam Leffler } 9668a1b9b6aSSam Leffler 9678a1b9b6aSSam Leffler challenge = NULL; 9688a1b9b6aSSam Leffler if (frm + 1 < efrm) { 9698a1b9b6aSSam Leffler if ((frm[1] + 2) > (efrm - frm)) { 9708a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 9718a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 9728a1b9b6aSSam Leffler "ie %d/%d too long", 9738a1b9b6aSSam Leffler frm[0], (frm[1] + 2) - (efrm - frm)); 9748a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 9758a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 9768a1b9b6aSSam Leffler goto bad; 9778a1b9b6aSSam Leffler } 9788a1b9b6aSSam Leffler if (*frm == IEEE80211_ELEMID_CHALLENGE) 9798a1b9b6aSSam Leffler challenge = frm; 9808a1b9b6aSSam Leffler frm += frm[1] + 2; 9818a1b9b6aSSam Leffler } 9828a1b9b6aSSam Leffler switch (seq) { 9838a1b9b6aSSam Leffler case IEEE80211_AUTH_SHARED_CHALLENGE: 9848a1b9b6aSSam Leffler case IEEE80211_AUTH_SHARED_RESPONSE: 9858a1b9b6aSSam Leffler if (challenge == NULL) { 9868a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 9878a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 9888a1b9b6aSSam Leffler "%s", "no challenge"); 9898a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 9908a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 9918a1b9b6aSSam Leffler goto bad; 9928a1b9b6aSSam Leffler } 9938a1b9b6aSSam Leffler if (challenge[1] != IEEE80211_CHALLENGE_LEN) { 9948a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 9958a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 9968a1b9b6aSSam Leffler "bad challenge len %d", challenge[1]); 9978a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 9988a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 9998a1b9b6aSSam Leffler goto bad; 10008a1b9b6aSSam Leffler } 10018a1b9b6aSSam Leffler default: 10028a1b9b6aSSam Leffler break; 10038a1b9b6aSSam Leffler } 10048a1b9b6aSSam Leffler switch (ic->ic_opmode) { 10058a1b9b6aSSam Leffler case IEEE80211_M_MONITOR: 10068a1b9b6aSSam Leffler case IEEE80211_M_AHDEMO: 10078a1b9b6aSSam Leffler case IEEE80211_M_IBSS: 10088a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 10098a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 10108a1b9b6aSSam Leffler "bad operating mode %u", ic->ic_opmode); 10118a1b9b6aSSam Leffler return; 10128a1b9b6aSSam Leffler case IEEE80211_M_HOSTAP: 10138a1b9b6aSSam Leffler if (ic->ic_state != IEEE80211_S_RUN) { 10148a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 10158a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 10168a1b9b6aSSam Leffler "bad state %u", ic->ic_state); 10178a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_ALG; /* XXX */ 10188a1b9b6aSSam Leffler goto bad; 10198a1b9b6aSSam Leffler } 10208a1b9b6aSSam Leffler switch (seq) { 10218a1b9b6aSSam Leffler case IEEE80211_AUTH_SHARED_REQUEST: 10228a1b9b6aSSam Leffler if (ni == ic->ic_bss) { 1023acc4f7f5SSam Leffler ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2); 10248a1b9b6aSSam Leffler if (ni == NULL) { 10258a1b9b6aSSam Leffler /* NB: no way to return an error */ 10268a1b9b6aSSam Leffler return; 10278a1b9b6aSSam Leffler } 10288a1b9b6aSSam Leffler allocbs = 1; 10298a1b9b6aSSam Leffler } else { 10308a1b9b6aSSam Leffler (void) ieee80211_ref_node(ni); 10318a1b9b6aSSam Leffler allocbs = 0; 10328a1b9b6aSSam Leffler } 10338a1b9b6aSSam Leffler ni->ni_rssi = rssi; 10348a1b9b6aSSam Leffler ni->ni_rstamp = rstamp; 10358a1b9b6aSSam Leffler if (!alloc_challenge(ic, ni)) { 10368a1b9b6aSSam Leffler /* NB: don't return error so they rexmit */ 10378a1b9b6aSSam Leffler return; 10388a1b9b6aSSam Leffler } 10398a1b9b6aSSam Leffler get_random_bytes(ni->ni_challenge, 10408a1b9b6aSSam Leffler IEEE80211_CHALLENGE_LEN); 10418a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, 10428a1b9b6aSSam Leffler IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 10438a1b9b6aSSam Leffler "[%s] shared key %sauth request\n", 10448a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), 10458a1b9b6aSSam Leffler allocbs ? "" : "re"); 10468a1b9b6aSSam Leffler break; 10478a1b9b6aSSam Leffler case IEEE80211_AUTH_SHARED_RESPONSE: 10488a1b9b6aSSam Leffler if (ni == ic->ic_bss) { 10498a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 10508a1b9b6aSSam Leffler ni->ni_macaddr, "shared key response", 10518a1b9b6aSSam Leffler "%s", "unknown station"); 10528a1b9b6aSSam Leffler /* NB: don't send a response */ 10538a1b9b6aSSam Leffler return; 10548a1b9b6aSSam Leffler } 10558a1b9b6aSSam Leffler if (ni->ni_challenge == NULL) { 10568a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 10578a1b9b6aSSam Leffler ni->ni_macaddr, "shared key response", 10588a1b9b6aSSam Leffler "%s", "no challenge recorded"); 10598a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 10608a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 10618a1b9b6aSSam Leffler goto bad; 10628a1b9b6aSSam Leffler } 10638a1b9b6aSSam Leffler if (memcmp(ni->ni_challenge, &challenge[2], 10648a1b9b6aSSam Leffler challenge[1]) != 0) { 10658a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 10668a1b9b6aSSam Leffler ni->ni_macaddr, "shared key response", 10678a1b9b6aSSam Leffler "%s", "challenge mismatch"); 10688a1b9b6aSSam Leffler ic->ic_stats.is_rx_auth_fail++; 10698a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 10708a1b9b6aSSam Leffler goto bad; 10718a1b9b6aSSam Leffler } 10728a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, 10738a1b9b6aSSam Leffler IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 10748a1b9b6aSSam Leffler "[%s] station authenticated (shared key)\n", 10758a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr)); 10768a1b9b6aSSam Leffler break; 10778a1b9b6aSSam Leffler default: 10788a1b9b6aSSam Leffler IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, 10798a1b9b6aSSam Leffler ni->ni_macaddr, "shared key auth", 10808a1b9b6aSSam Leffler "bad seq %d", seq); 10818a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 10828a1b9b6aSSam Leffler estatus = IEEE80211_STATUS_SEQUENCE; 10838a1b9b6aSSam Leffler goto bad; 10848a1b9b6aSSam Leffler } 10858a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 10868a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); 10878a1b9b6aSSam Leffler break; 10888a1b9b6aSSam Leffler 10898a1b9b6aSSam Leffler case IEEE80211_M_STA: 10908a1b9b6aSSam Leffler if (ic->ic_state != IEEE80211_S_AUTH) 10918a1b9b6aSSam Leffler return; 10928a1b9b6aSSam Leffler switch (seq) { 10938a1b9b6aSSam Leffler case IEEE80211_AUTH_SHARED_PASS: 10948a1b9b6aSSam Leffler if (ni->ni_challenge != NULL) { 10958a1b9b6aSSam Leffler FREE(ni->ni_challenge, M_DEVBUF); 10968a1b9b6aSSam Leffler ni->ni_challenge = NULL; 10978a1b9b6aSSam Leffler } 10988a1b9b6aSSam Leffler if (status != 0) { 10998a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, 11008a1b9b6aSSam Leffler IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 11018a1b9b6aSSam Leffler "[%s] shared key auth failed (reason %d)\n", 11028a1b9b6aSSam Leffler ether_sprintf(ieee80211_getbssid(ic, wh)), 11038a1b9b6aSSam Leffler status); 11048a1b9b6aSSam Leffler /* XXX can this happen? */ 11058a1b9b6aSSam Leffler if (ni != ic->ic_bss) 11068a1b9b6aSSam Leffler ni->ni_fails++; 11078a1b9b6aSSam Leffler ic->ic_stats.is_rx_auth_fail++; 11088a1b9b6aSSam Leffler return; 11098a1b9b6aSSam Leffler } 11108a1b9b6aSSam Leffler ieee80211_new_state(ic, IEEE80211_S_ASSOC, 11118a1b9b6aSSam Leffler wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); 11128a1b9b6aSSam Leffler break; 11138a1b9b6aSSam Leffler case IEEE80211_AUTH_SHARED_CHALLENGE: 11148a1b9b6aSSam Leffler if (!alloc_challenge(ic, ni)) 11158a1b9b6aSSam Leffler return; 11168a1b9b6aSSam Leffler /* XXX could optimize by passing recvd challenge */ 11178a1b9b6aSSam Leffler memcpy(ni->ni_challenge, &challenge[2], challenge[1]); 11188a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 11198a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); 11208a1b9b6aSSam Leffler break; 11218a1b9b6aSSam Leffler default: 11228a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_AUTH, 11238a1b9b6aSSam Leffler wh, "shared key auth", "bad seq %d", seq); 11248a1b9b6aSSam Leffler ic->ic_stats.is_rx_bad_auth++; 11258a1b9b6aSSam Leffler return; 11268a1b9b6aSSam Leffler } 11278a1b9b6aSSam Leffler break; 11288a1b9b6aSSam Leffler } 11298a1b9b6aSSam Leffler return; 11308a1b9b6aSSam Leffler bad: 11318a1b9b6aSSam Leffler /* 11328a1b9b6aSSam Leffler * Send an error response; but only when operating as an AP. 11338a1b9b6aSSam Leffler */ 11348a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 11358a1b9b6aSSam Leffler /* XXX hack to workaround calling convention */ 11368a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 11378a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 11388a1b9b6aSSam Leffler (seq + 1) | (estatus<<16)); 11398a1b9b6aSSam Leffler } 11408a1b9b6aSSam Leffler } 11418a1b9b6aSSam Leffler 11421a1e1d21SSam Leffler /* Verify the existence and length of __elem or get out. */ 11431a1e1d21SSam Leffler #define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \ 11441a1e1d21SSam Leffler if ((__elem) == NULL) { \ 11458a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ 11468a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> \ 11478a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], \ 11488a1b9b6aSSam Leffler "%s", "no " #__elem ); \ 11491be50176SSam Leffler ic->ic_stats.is_rx_elem_missing++; \ 11501a1e1d21SSam Leffler return; \ 11511a1e1d21SSam Leffler } \ 11521a1e1d21SSam Leffler if ((__elem)[1] > (__maxlen)) { \ 11538a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ 11548a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> \ 11551a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], \ 11568a1b9b6aSSam Leffler "bad " #__elem " len %d", (__elem)[1]); \ 11571be50176SSam Leffler ic->ic_stats.is_rx_elem_toobig++; \ 11581a1e1d21SSam Leffler return; \ 11591a1e1d21SSam Leffler } \ 11601a1e1d21SSam Leffler } while (0) 11611a1e1d21SSam Leffler 11621a1e1d21SSam Leffler #define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \ 11631a1e1d21SSam Leffler if ((_len) < (_minlen)) { \ 11648a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ 11658a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> \ 11661a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], \ 11678a1b9b6aSSam Leffler "%s", "ie too short"); \ 11681be50176SSam Leffler ic->ic_stats.is_rx_elem_toosmall++; \ 11691a1e1d21SSam Leffler return; \ 11701a1e1d21SSam Leffler } \ 11711a1e1d21SSam Leffler } while (0) 11721a1e1d21SSam Leffler 11738a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 11748a1b9b6aSSam Leffler static void 11758a1b9b6aSSam Leffler ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag, 11768a1b9b6aSSam Leffler u_int8_t mac[IEEE80211_ADDR_LEN], u_int8_t *ssid) 11778a1b9b6aSSam Leffler { 11788a1b9b6aSSam Leffler printf("[%s] discard %s frame, ssid mismatch: ", 11798a1b9b6aSSam Leffler ether_sprintf(mac), tag); 11808a1b9b6aSSam Leffler ieee80211_print_essid(ssid + 2, ssid[1]); 11818a1b9b6aSSam Leffler printf("\n"); 11828a1b9b6aSSam Leffler } 11838a1b9b6aSSam Leffler 11848a1b9b6aSSam Leffler #define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \ 11858a1b9b6aSSam Leffler if ((_ssid)[1] != 0 && \ 11868a1b9b6aSSam Leffler ((_ssid)[1] != (_ni)->ni_esslen || \ 11878a1b9b6aSSam Leffler memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \ 11888a1b9b6aSSam Leffler if (ieee80211_msg_input(ic)) \ 11898a1b9b6aSSam Leffler ieee80211_ssid_mismatch(ic, \ 11908a1b9b6aSSam Leffler ieee80211_mgt_subtype_name[subtype >> \ 11918a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], \ 11928a1b9b6aSSam Leffler wh->i_addr2, _ssid); \ 11938a1b9b6aSSam Leffler ic->ic_stats.is_rx_ssidmismatch++; \ 11948a1b9b6aSSam Leffler return; \ 11958a1b9b6aSSam Leffler } \ 11968a1b9b6aSSam Leffler } while (0) 11978a1b9b6aSSam Leffler #else /* !IEEE80211_DEBUG */ 11988a1b9b6aSSam Leffler #define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \ 11998a1b9b6aSSam Leffler if ((_ssid)[1] != 0 && \ 12008a1b9b6aSSam Leffler ((_ssid)[1] != (_ni)->ni_esslen || \ 12018a1b9b6aSSam Leffler memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \ 12028a1b9b6aSSam Leffler ic->ic_stats.is_rx_ssidmismatch++; \ 12038a1b9b6aSSam Leffler return; \ 12048a1b9b6aSSam Leffler } \ 12058a1b9b6aSSam Leffler } while (0) 12068a1b9b6aSSam Leffler #endif /* !IEEE80211_DEBUG */ 12078a1b9b6aSSam Leffler 12088a1b9b6aSSam Leffler /* unalligned little endian access */ 12098a1b9b6aSSam Leffler #define LE_READ_2(p) \ 12108a1b9b6aSSam Leffler ((u_int16_t) \ 12118a1b9b6aSSam Leffler ((((const u_int8_t *)(p))[0] ) | \ 12128a1b9b6aSSam Leffler (((const u_int8_t *)(p))[1] << 8))) 12138a1b9b6aSSam Leffler #define LE_READ_4(p) \ 12148a1b9b6aSSam Leffler ((u_int32_t) \ 12158a1b9b6aSSam Leffler ((((const u_int8_t *)(p))[0] ) | \ 12168a1b9b6aSSam Leffler (((const u_int8_t *)(p))[1] << 8) | \ 12178a1b9b6aSSam Leffler (((const u_int8_t *)(p))[2] << 16) | \ 12188a1b9b6aSSam Leffler (((const u_int8_t *)(p))[3] << 24))) 12198a1b9b6aSSam Leffler 12208a1b9b6aSSam Leffler static int __inline 12218a1b9b6aSSam Leffler iswpaoui(const u_int8_t *frm) 12228a1b9b6aSSam Leffler { 12238a1b9b6aSSam Leffler return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); 12248a1b9b6aSSam Leffler } 12258a1b9b6aSSam Leffler 12268a1b9b6aSSam Leffler static int __inline 12278a1b9b6aSSam Leffler iswmeoui(const u_int8_t *frm) 12288a1b9b6aSSam Leffler { 12298a1b9b6aSSam Leffler return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI); 12308a1b9b6aSSam Leffler } 12318a1b9b6aSSam Leffler 12328a1b9b6aSSam Leffler static int __inline 12338a1b9b6aSSam Leffler iswmeparam(const u_int8_t *frm) 12348a1b9b6aSSam Leffler { 12358a1b9b6aSSam Leffler return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && 12368a1b9b6aSSam Leffler frm[6] == WME_PARAM_OUI_SUBTYPE; 12378a1b9b6aSSam Leffler } 12388a1b9b6aSSam Leffler 12398a1b9b6aSSam Leffler static int __inline 12408a1b9b6aSSam Leffler iswmeinfo(const u_int8_t *frm) 12418a1b9b6aSSam Leffler { 12428a1b9b6aSSam Leffler return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && 12438a1b9b6aSSam Leffler frm[6] == WME_INFO_OUI_SUBTYPE; 12448a1b9b6aSSam Leffler } 12458a1b9b6aSSam Leffler 12468a1b9b6aSSam Leffler static int __inline 12478a1b9b6aSSam Leffler isatherosoui(const u_int8_t *frm) 12488a1b9b6aSSam Leffler { 12498a1b9b6aSSam Leffler return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); 12508a1b9b6aSSam Leffler } 12518a1b9b6aSSam Leffler 12528a1b9b6aSSam Leffler /* 12538a1b9b6aSSam Leffler * Convert a WPA cipher selector OUI to an internal 12548a1b9b6aSSam Leffler * cipher algorithm. Where appropriate we also 12558a1b9b6aSSam Leffler * record any key length. 12568a1b9b6aSSam Leffler */ 12578a1b9b6aSSam Leffler static int 12588a1b9b6aSSam Leffler wpa_cipher(u_int8_t *sel, u_int8_t *keylen) 12598a1b9b6aSSam Leffler { 12608a1b9b6aSSam Leffler #define WPA_SEL(x) (((x)<<24)|WPA_OUI) 12618a1b9b6aSSam Leffler u_int32_t w = LE_READ_4(sel); 12628a1b9b6aSSam Leffler 12638a1b9b6aSSam Leffler switch (w) { 12648a1b9b6aSSam Leffler case WPA_SEL(WPA_CSE_NULL): 12658a1b9b6aSSam Leffler return IEEE80211_CIPHER_NONE; 12668a1b9b6aSSam Leffler case WPA_SEL(WPA_CSE_WEP40): 12678a1b9b6aSSam Leffler if (keylen) 12688a1b9b6aSSam Leffler *keylen = 40 / NBBY; 12698a1b9b6aSSam Leffler return IEEE80211_CIPHER_WEP; 12708a1b9b6aSSam Leffler case WPA_SEL(WPA_CSE_WEP104): 12718a1b9b6aSSam Leffler if (keylen) 12728a1b9b6aSSam Leffler *keylen = 104 / NBBY; 12738a1b9b6aSSam Leffler return IEEE80211_CIPHER_WEP; 12748a1b9b6aSSam Leffler case WPA_SEL(WPA_CSE_TKIP): 12758a1b9b6aSSam Leffler return IEEE80211_CIPHER_TKIP; 12768a1b9b6aSSam Leffler case WPA_SEL(WPA_CSE_CCMP): 12778a1b9b6aSSam Leffler return IEEE80211_CIPHER_AES_CCM; 12788a1b9b6aSSam Leffler } 12798a1b9b6aSSam Leffler return 32; /* NB: so 1<< is discarded */ 12808a1b9b6aSSam Leffler #undef WPA_SEL 12818a1b9b6aSSam Leffler } 12828a1b9b6aSSam Leffler 12838a1b9b6aSSam Leffler /* 12848a1b9b6aSSam Leffler * Convert a WPA key management/authentication algorithm 12858a1b9b6aSSam Leffler * to an internal code. 12868a1b9b6aSSam Leffler */ 12878a1b9b6aSSam Leffler static int 12888a1b9b6aSSam Leffler wpa_keymgmt(u_int8_t *sel) 12898a1b9b6aSSam Leffler { 12908a1b9b6aSSam Leffler #define WPA_SEL(x) (((x)<<24)|WPA_OUI) 12918a1b9b6aSSam Leffler u_int32_t w = LE_READ_4(sel); 12928a1b9b6aSSam Leffler 12938a1b9b6aSSam Leffler switch (w) { 12948a1b9b6aSSam Leffler case WPA_SEL(WPA_ASE_8021X_UNSPEC): 12958a1b9b6aSSam Leffler return WPA_ASE_8021X_UNSPEC; 12968a1b9b6aSSam Leffler case WPA_SEL(WPA_ASE_8021X_PSK): 12978a1b9b6aSSam Leffler return WPA_ASE_8021X_PSK; 12988a1b9b6aSSam Leffler case WPA_SEL(WPA_ASE_NONE): 12998a1b9b6aSSam Leffler return WPA_ASE_NONE; 13008a1b9b6aSSam Leffler } 13018a1b9b6aSSam Leffler return 0; /* NB: so is discarded */ 13028a1b9b6aSSam Leffler #undef WPA_SEL 13038a1b9b6aSSam Leffler } 13048a1b9b6aSSam Leffler 13058a1b9b6aSSam Leffler /* 13068a1b9b6aSSam Leffler * Parse a WPA information element to collect parameters 13078a1b9b6aSSam Leffler * and validate the parameters against what has been 13088a1b9b6aSSam Leffler * configured for the system. 13098a1b9b6aSSam Leffler */ 13108a1b9b6aSSam Leffler static int 13118a1b9b6aSSam Leffler ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm, 13128a1b9b6aSSam Leffler struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) 13138a1b9b6aSSam Leffler { 13148a1b9b6aSSam Leffler u_int8_t len = frm[1]; 13158a1b9b6aSSam Leffler u_int32_t w; 13168a1b9b6aSSam Leffler int n; 13178a1b9b6aSSam Leffler 13188a1b9b6aSSam Leffler /* 13198a1b9b6aSSam Leffler * Check the length once for fixed parts: OUI, type, 13208a1b9b6aSSam Leffler * version, mcast cipher, and 2 selector counts. 13218a1b9b6aSSam Leffler * Other, variable-length data, must be checked separately. 13228a1b9b6aSSam Leffler */ 13238a1b9b6aSSam Leffler KASSERT(ic->ic_flags & IEEE80211_F_WPA1, 13248a1b9b6aSSam Leffler ("not WPA, flags 0x%x", ic->ic_flags)); 13258a1b9b6aSSam Leffler if (len < 14) { 13268a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 13278a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 13288a1b9b6aSSam Leffler wh, "WPA", "too short, len %u", len); 13298a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 13308a1b9b6aSSam Leffler } 13318a1b9b6aSSam Leffler frm += 6, len -= 4; /* NB: len is payload only */ 13328a1b9b6aSSam Leffler /* NB: iswapoui already validated the OUI and type */ 13338a1b9b6aSSam Leffler w = LE_READ_2(frm); 13348a1b9b6aSSam Leffler if (w != WPA_VERSION) { 13358a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 13368a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 13378a1b9b6aSSam Leffler wh, "WPA", "bad version %u", w); 13388a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 13398a1b9b6aSSam Leffler } 13408a1b9b6aSSam Leffler frm += 2, len -= 2; 13418a1b9b6aSSam Leffler 13428a1b9b6aSSam Leffler /* multicast/group cipher */ 13438a1b9b6aSSam Leffler w = wpa_cipher(frm, &rsn->rsn_mcastkeylen); 13448a1b9b6aSSam Leffler if (w != rsn->rsn_mcastcipher) { 13458a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 13468a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 13478a1b9b6aSSam Leffler wh, "WPA", "mcast cipher mismatch; got %u, expected %u", 13488a1b9b6aSSam Leffler w, rsn->rsn_mcastcipher); 13498a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 13508a1b9b6aSSam Leffler } 13518a1b9b6aSSam Leffler frm += 4, len -= 4; 13528a1b9b6aSSam Leffler 13538a1b9b6aSSam Leffler /* unicast ciphers */ 13548a1b9b6aSSam Leffler n = LE_READ_2(frm); 13558a1b9b6aSSam Leffler frm += 2, len -= 2; 13568a1b9b6aSSam Leffler if (len < n*4+2) { 13578a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 13588a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 13598a1b9b6aSSam Leffler wh, "WPA", "ucast cipher data too short; len %u, n %u", 13608a1b9b6aSSam Leffler len, n); 13618a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 13628a1b9b6aSSam Leffler } 13638a1b9b6aSSam Leffler w = 0; 13648a1b9b6aSSam Leffler for (; n > 0; n--) { 13658a1b9b6aSSam Leffler w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen); 13668a1b9b6aSSam Leffler frm += 4, len -= 4; 13678a1b9b6aSSam Leffler } 13688a1b9b6aSSam Leffler w &= rsn->rsn_ucastcipherset; 13698a1b9b6aSSam Leffler if (w == 0) { 13708a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 13718a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 13728a1b9b6aSSam Leffler wh, "WPA", "%s", "ucast cipher set empty"); 13738a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 13748a1b9b6aSSam Leffler } 13758a1b9b6aSSam Leffler if (w & (1<<IEEE80211_CIPHER_TKIP)) 13768a1b9b6aSSam Leffler rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP; 13778a1b9b6aSSam Leffler else 13788a1b9b6aSSam Leffler rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; 13798a1b9b6aSSam Leffler 13808a1b9b6aSSam Leffler /* key management algorithms */ 13818a1b9b6aSSam Leffler n = LE_READ_2(frm); 13828a1b9b6aSSam Leffler frm += 2, len -= 2; 13838a1b9b6aSSam Leffler if (len < n*4) { 13848a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 13858a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 13868a1b9b6aSSam Leffler wh, "WPA", "key mgmt alg data too short; len %u, n %u", 13878a1b9b6aSSam Leffler len, n); 13888a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 13898a1b9b6aSSam Leffler } 13908a1b9b6aSSam Leffler w = 0; 13918a1b9b6aSSam Leffler for (; n > 0; n--) { 13928a1b9b6aSSam Leffler w |= wpa_keymgmt(frm); 13938a1b9b6aSSam Leffler frm += 4, len -= 4; 13948a1b9b6aSSam Leffler } 13958a1b9b6aSSam Leffler w &= rsn->rsn_keymgmtset; 13968a1b9b6aSSam Leffler if (w == 0) { 13978a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 13988a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 13998a1b9b6aSSam Leffler wh, "WPA", "%s", "no acceptable key mgmt alg"); 14008a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 14018a1b9b6aSSam Leffler } 14028a1b9b6aSSam Leffler if (w & WPA_ASE_8021X_UNSPEC) 14038a1b9b6aSSam Leffler rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC; 14048a1b9b6aSSam Leffler else 14058a1b9b6aSSam Leffler rsn->rsn_keymgmt = WPA_ASE_8021X_PSK; 14068a1b9b6aSSam Leffler 14078a1b9b6aSSam Leffler if (len > 2) /* optional capabilities */ 14088a1b9b6aSSam Leffler rsn->rsn_caps = LE_READ_2(frm); 14098a1b9b6aSSam Leffler 14108a1b9b6aSSam Leffler return 0; 14118a1b9b6aSSam Leffler } 14128a1b9b6aSSam Leffler 14138a1b9b6aSSam Leffler /* 14148a1b9b6aSSam Leffler * Convert an RSN cipher selector OUI to an internal 14158a1b9b6aSSam Leffler * cipher algorithm. Where appropriate we also 14168a1b9b6aSSam Leffler * record any key length. 14178a1b9b6aSSam Leffler */ 14188a1b9b6aSSam Leffler static int 14198a1b9b6aSSam Leffler rsn_cipher(u_int8_t *sel, u_int8_t *keylen) 14208a1b9b6aSSam Leffler { 14218a1b9b6aSSam Leffler #define RSN_SEL(x) (((x)<<24)|RSN_OUI) 14228a1b9b6aSSam Leffler u_int32_t w = LE_READ_4(sel); 14238a1b9b6aSSam Leffler 14248a1b9b6aSSam Leffler switch (w) { 14258a1b9b6aSSam Leffler case RSN_SEL(RSN_CSE_NULL): 14268a1b9b6aSSam Leffler return IEEE80211_CIPHER_NONE; 14278a1b9b6aSSam Leffler case RSN_SEL(RSN_CSE_WEP40): 14288a1b9b6aSSam Leffler if (keylen) 14298a1b9b6aSSam Leffler *keylen = 40 / NBBY; 14308a1b9b6aSSam Leffler return IEEE80211_CIPHER_WEP; 14318a1b9b6aSSam Leffler case RSN_SEL(RSN_CSE_WEP104): 14328a1b9b6aSSam Leffler if (keylen) 14338a1b9b6aSSam Leffler *keylen = 104 / NBBY; 14348a1b9b6aSSam Leffler return IEEE80211_CIPHER_WEP; 14358a1b9b6aSSam Leffler case RSN_SEL(RSN_CSE_TKIP): 14368a1b9b6aSSam Leffler return IEEE80211_CIPHER_TKIP; 14378a1b9b6aSSam Leffler case RSN_SEL(RSN_CSE_CCMP): 14388a1b9b6aSSam Leffler return IEEE80211_CIPHER_AES_CCM; 14398a1b9b6aSSam Leffler case RSN_SEL(RSN_CSE_WRAP): 14408a1b9b6aSSam Leffler return IEEE80211_CIPHER_AES_OCB; 14418a1b9b6aSSam Leffler } 14428a1b9b6aSSam Leffler return 32; /* NB: so 1<< is discarded */ 14438a1b9b6aSSam Leffler #undef WPA_SEL 14448a1b9b6aSSam Leffler } 14458a1b9b6aSSam Leffler 14468a1b9b6aSSam Leffler /* 14478a1b9b6aSSam Leffler * Convert an RSN key management/authentication algorithm 14488a1b9b6aSSam Leffler * to an internal code. 14498a1b9b6aSSam Leffler */ 14508a1b9b6aSSam Leffler static int 14518a1b9b6aSSam Leffler rsn_keymgmt(u_int8_t *sel) 14528a1b9b6aSSam Leffler { 14538a1b9b6aSSam Leffler #define RSN_SEL(x) (((x)<<24)|RSN_OUI) 14548a1b9b6aSSam Leffler u_int32_t w = LE_READ_4(sel); 14558a1b9b6aSSam Leffler 14568a1b9b6aSSam Leffler switch (w) { 14578a1b9b6aSSam Leffler case RSN_SEL(RSN_ASE_8021X_UNSPEC): 14588a1b9b6aSSam Leffler return RSN_ASE_8021X_UNSPEC; 14598a1b9b6aSSam Leffler case RSN_SEL(RSN_ASE_8021X_PSK): 14608a1b9b6aSSam Leffler return RSN_ASE_8021X_PSK; 14618a1b9b6aSSam Leffler case RSN_SEL(RSN_ASE_NONE): 14628a1b9b6aSSam Leffler return RSN_ASE_NONE; 14638a1b9b6aSSam Leffler } 14648a1b9b6aSSam Leffler return 0; /* NB: so is discarded */ 14658a1b9b6aSSam Leffler #undef RSN_SEL 14668a1b9b6aSSam Leffler } 14678a1b9b6aSSam Leffler 14688a1b9b6aSSam Leffler /* 14698a1b9b6aSSam Leffler * Parse a WPA/RSN information element to collect parameters 14708a1b9b6aSSam Leffler * and validate the parameters against what has been 14718a1b9b6aSSam Leffler * configured for the system. 14728a1b9b6aSSam Leffler */ 14738a1b9b6aSSam Leffler static int 14748a1b9b6aSSam Leffler ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm, 14758a1b9b6aSSam Leffler struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) 14768a1b9b6aSSam Leffler { 14778a1b9b6aSSam Leffler u_int8_t len = frm[1]; 14788a1b9b6aSSam Leffler u_int32_t w; 14798a1b9b6aSSam Leffler int n; 14808a1b9b6aSSam Leffler 14818a1b9b6aSSam Leffler /* 14828a1b9b6aSSam Leffler * Check the length once for fixed parts: 14838a1b9b6aSSam Leffler * version, mcast cipher, and 2 selector counts. 14848a1b9b6aSSam Leffler * Other, variable-length data, must be checked separately. 14858a1b9b6aSSam Leffler */ 14868a1b9b6aSSam Leffler KASSERT(ic->ic_flags & IEEE80211_F_WPA2, 14878a1b9b6aSSam Leffler ("not RSN, flags 0x%x", ic->ic_flags)); 14888a1b9b6aSSam Leffler if (len < 10) { 14898a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 14908a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 14918a1b9b6aSSam Leffler wh, "RSN", "too short, len %u", len); 14928a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 14938a1b9b6aSSam Leffler } 14948a1b9b6aSSam Leffler frm += 2; 14958a1b9b6aSSam Leffler w = LE_READ_2(frm); 14968a1b9b6aSSam Leffler if (w != RSN_VERSION) { 14978a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 14988a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 14998a1b9b6aSSam Leffler wh, "RSN", "bad version %u", w); 15008a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 15018a1b9b6aSSam Leffler } 15028a1b9b6aSSam Leffler frm += 2, len -= 2; 15038a1b9b6aSSam Leffler 15048a1b9b6aSSam Leffler /* multicast/group cipher */ 15058a1b9b6aSSam Leffler w = rsn_cipher(frm, &rsn->rsn_mcastkeylen); 15068a1b9b6aSSam Leffler if (w != rsn->rsn_mcastcipher) { 15078a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 15088a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 15098a1b9b6aSSam Leffler wh, "RSN", "mcast cipher mismatch; got %u, expected %u", 15108a1b9b6aSSam Leffler w, rsn->rsn_mcastcipher); 15118a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 15128a1b9b6aSSam Leffler } 15138a1b9b6aSSam Leffler frm += 4, len -= 4; 15148a1b9b6aSSam Leffler 15158a1b9b6aSSam Leffler /* unicast ciphers */ 15168a1b9b6aSSam Leffler n = LE_READ_2(frm); 15178a1b9b6aSSam Leffler frm += 2, len -= 2; 15188a1b9b6aSSam Leffler if (len < n*4+2) { 15198a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 15208a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 15218a1b9b6aSSam Leffler wh, "RSN", "ucast cipher data too short; len %u, n %u", 15228a1b9b6aSSam Leffler len, n); 15238a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 15248a1b9b6aSSam Leffler } 15258a1b9b6aSSam Leffler w = 0; 15268a1b9b6aSSam Leffler for (; n > 0; n--) { 15278a1b9b6aSSam Leffler w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen); 15288a1b9b6aSSam Leffler frm += 4, len -= 4; 15298a1b9b6aSSam Leffler } 15308a1b9b6aSSam Leffler w &= rsn->rsn_ucastcipherset; 15318a1b9b6aSSam Leffler if (w == 0) { 15328a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 15338a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 15348a1b9b6aSSam Leffler wh, "RSN", "%s", "ucast cipher set empty"); 15358a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 15368a1b9b6aSSam Leffler } 15378a1b9b6aSSam Leffler if (w & (1<<IEEE80211_CIPHER_TKIP)) 15388a1b9b6aSSam Leffler rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP; 15398a1b9b6aSSam Leffler else 15408a1b9b6aSSam Leffler rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; 15418a1b9b6aSSam Leffler 15428a1b9b6aSSam Leffler /* key management algorithms */ 15438a1b9b6aSSam Leffler n = LE_READ_2(frm); 15448a1b9b6aSSam Leffler frm += 2, len -= 2; 15458a1b9b6aSSam Leffler if (len < n*4) { 15468a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 15478a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 15488a1b9b6aSSam Leffler wh, "RSN", "key mgmt alg data too short; len %u, n %u", 15498a1b9b6aSSam Leffler len, n); 15508a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 15518a1b9b6aSSam Leffler } 15528a1b9b6aSSam Leffler w = 0; 15538a1b9b6aSSam Leffler for (; n > 0; n--) { 15548a1b9b6aSSam Leffler w |= rsn_keymgmt(frm); 15558a1b9b6aSSam Leffler frm += 4, len -= 4; 15568a1b9b6aSSam Leffler } 15578a1b9b6aSSam Leffler w &= rsn->rsn_keymgmtset; 15588a1b9b6aSSam Leffler if (w == 0) { 15598a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 15608a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 15618a1b9b6aSSam Leffler wh, "RSN", "%s", "no acceptable key mgmt alg"); 15628a1b9b6aSSam Leffler return IEEE80211_REASON_IE_INVALID; 15638a1b9b6aSSam Leffler } 15648a1b9b6aSSam Leffler if (w & RSN_ASE_8021X_UNSPEC) 15658a1b9b6aSSam Leffler rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC; 15668a1b9b6aSSam Leffler else 15678a1b9b6aSSam Leffler rsn->rsn_keymgmt = RSN_ASE_8021X_PSK; 15688a1b9b6aSSam Leffler 15698a1b9b6aSSam Leffler /* optional RSN capabilities */ 15708a1b9b6aSSam Leffler if (len > 2) 15718a1b9b6aSSam Leffler rsn->rsn_caps = LE_READ_2(frm); 15728a1b9b6aSSam Leffler /* XXXPMKID */ 15738a1b9b6aSSam Leffler 15748a1b9b6aSSam Leffler return 0; 15758a1b9b6aSSam Leffler } 15768a1b9b6aSSam Leffler 15778a1b9b6aSSam Leffler static int 15788a1b9b6aSSam Leffler ieee80211_parse_wmeparams(struct ieee80211com *ic, u_int8_t *frm, 15798a1b9b6aSSam Leffler const struct ieee80211_frame *wh) 15808a1b9b6aSSam Leffler { 15818a1b9b6aSSam Leffler #define MS(_v, _f) (((_v) & _f) >> _f##_S) 15828a1b9b6aSSam Leffler struct ieee80211_wme_state *wme = &ic->ic_wme; 15838a1b9b6aSSam Leffler u_int len = frm[1], qosinfo; 15848a1b9b6aSSam Leffler int i; 15858a1b9b6aSSam Leffler 15868a1b9b6aSSam Leffler if (len < sizeof(struct ieee80211_wme_param)-2) { 15878a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 15888a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME, 15898a1b9b6aSSam Leffler wh, "WME", "too short, len %u", len); 1590c0fa32ceSSam Leffler return -1; 15918a1b9b6aSSam Leffler } 15928a1b9b6aSSam Leffler qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)]; 15938a1b9b6aSSam Leffler qosinfo &= WME_QOSINFO_COUNT; 15948a1b9b6aSSam Leffler /* XXX do proper check for wraparound */ 15958a1b9b6aSSam Leffler if (qosinfo == wme->wme_wmeChanParams.cap_info) 15968a1b9b6aSSam Leffler return 0; 15978a1b9b6aSSam Leffler frm += __offsetof(struct ieee80211_wme_param, params_acParams); 15988a1b9b6aSSam Leffler for (i = 0; i < WME_NUM_AC; i++) { 15998a1b9b6aSSam Leffler struct wmeParams *wmep = 16008a1b9b6aSSam Leffler &wme->wme_wmeChanParams.cap_wmeParams[i]; 16018a1b9b6aSSam Leffler /* NB: ACI not used */ 16028a1b9b6aSSam Leffler wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM); 16038a1b9b6aSSam Leffler wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN); 16048a1b9b6aSSam Leffler wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN); 16058a1b9b6aSSam Leffler wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX); 16068a1b9b6aSSam Leffler wmep->wmep_txopLimit = LE_READ_2(frm+2); 16078a1b9b6aSSam Leffler frm += 4; 16088a1b9b6aSSam Leffler } 16098a1b9b6aSSam Leffler wme->wme_wmeChanParams.cap_info = qosinfo; 16108a1b9b6aSSam Leffler return 1; 16118a1b9b6aSSam Leffler #undef MS 16128a1b9b6aSSam Leffler } 16138a1b9b6aSSam Leffler 16148a1b9b6aSSam Leffler static void 16158a1b9b6aSSam Leffler ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie) 16168a1b9b6aSSam Leffler { 16178a1b9b6aSSam Leffler u_int ielen = ie[1]+2; 16188a1b9b6aSSam Leffler /* 16198a1b9b6aSSam Leffler * Record information element for later use. 16208a1b9b6aSSam Leffler */ 16218a1b9b6aSSam Leffler if (*iep == NULL || (*iep)[1] != ie[1]) { 16228a1b9b6aSSam Leffler if (*iep != NULL) 16238a1b9b6aSSam Leffler FREE(*iep, M_DEVBUF); 16248a1b9b6aSSam Leffler MALLOC(*iep, void*, ielen, M_DEVBUF, M_NOWAIT); 16258a1b9b6aSSam Leffler } 16268a1b9b6aSSam Leffler if (*iep != NULL) 16278a1b9b6aSSam Leffler memcpy(*iep, ie, ielen); 16288a1b9b6aSSam Leffler /* XXX note failure */ 16298a1b9b6aSSam Leffler } 16308a1b9b6aSSam Leffler 16318a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 16328a1b9b6aSSam Leffler static void 16338a1b9b6aSSam Leffler dump_probe_beacon(u_int8_t subtype, int isnew, 16348a1b9b6aSSam Leffler const u_int8_t mac[IEEE80211_ADDR_LEN], 16358a1b9b6aSSam Leffler u_int8_t chan, u_int8_t bchan, u_int16_t capinfo, u_int16_t bintval, 16368a1b9b6aSSam Leffler u_int8_t erp, u_int8_t *ssid, u_int8_t *country) 16378a1b9b6aSSam Leffler { 16388a1b9b6aSSam Leffler printf("[%s] %s%s on chan %u (bss chan %u) ", 16398a1b9b6aSSam Leffler ether_sprintf(mac), isnew ? "new " : "", 16408a1b9b6aSSam Leffler ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], 16418a1b9b6aSSam Leffler chan, bchan); 16428a1b9b6aSSam Leffler ieee80211_print_essid(ssid + 2, ssid[1]); 16438a1b9b6aSSam Leffler printf("\n"); 16448a1b9b6aSSam Leffler 16458a1b9b6aSSam Leffler if (isnew) { 16468a1b9b6aSSam Leffler printf("[%s] caps 0x%x bintval %u erp 0x%x", 16478a1b9b6aSSam Leffler ether_sprintf(mac), capinfo, bintval, erp); 16488a1b9b6aSSam Leffler if (country) { 16498a1b9b6aSSam Leffler #ifdef __FreeBSD__ 16508a1b9b6aSSam Leffler printf(" country info %*D", country[1], country+2, " "); 16518a1b9b6aSSam Leffler #else 16528a1b9b6aSSam Leffler int i; 16538a1b9b6aSSam Leffler printf(" country info"); 16548a1b9b6aSSam Leffler for (i = 0; i < country[1]; i++) 16558a1b9b6aSSam Leffler printf(" %02x", country[i+2]); 16568a1b9b6aSSam Leffler #endif 16578a1b9b6aSSam Leffler } 16588a1b9b6aSSam Leffler printf("\n"); 16598a1b9b6aSSam Leffler } 16608a1b9b6aSSam Leffler } 16618a1b9b6aSSam Leffler #endif /* IEEE80211_DEBUG */ 16628a1b9b6aSSam Leffler 16631a1e1d21SSam Leffler void 16640a915fadSSam Leffler ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, 16650a915fadSSam Leffler struct ieee80211_node *ni, 16660a915fadSSam Leffler int subtype, int rssi, u_int32_t rstamp) 16671a1e1d21SSam Leffler { 16688a1b9b6aSSam Leffler #define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) 16698a1b9b6aSSam Leffler #define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP) 16701a1e1d21SSam Leffler struct ieee80211_frame *wh; 16711a1e1d21SSam Leffler u_int8_t *frm, *efrm; 16728a1b9b6aSSam Leffler u_int8_t *ssid, *rates, *xrates, *wpa, *wme; 16738a1b9b6aSSam Leffler int reassoc, resp, allocbs; 16741a1e1d21SSam Leffler 16751a1e1d21SSam Leffler wh = mtod(m0, struct ieee80211_frame *); 16761a1e1d21SSam Leffler frm = (u_int8_t *)&wh[1]; 16771a1e1d21SSam Leffler efrm = mtod(m0, u_int8_t *) + m0->m_len; 16781a1e1d21SSam Leffler switch (subtype) { 16791a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 16801a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_BEACON: { 16818a1b9b6aSSam Leffler u_int8_t *tstamp, *country; 16821a1e1d21SSam Leffler u_int8_t chan, bchan, fhindex, erp; 16838a1b9b6aSSam Leffler u_int16_t capinfo, bintval, timoff; 16841a1e1d21SSam Leffler u_int16_t fhdwell; 16851a1e1d21SSam Leffler 16868a1b9b6aSSam Leffler /* 16878a1b9b6aSSam Leffler * We process beacon/probe response frames: 16888a1b9b6aSSam Leffler * o when scanning, or 16898a1b9b6aSSam Leffler * o station mode when associated (to collect state 16908a1b9b6aSSam Leffler * updates such as 802.11g slot time), or 16918a1b9b6aSSam Leffler * o adhoc mode (to discover neighbors) 16928a1b9b6aSSam Leffler * Frames otherwise received are discarded. 16938a1b9b6aSSam Leffler */ 16948a1b9b6aSSam Leffler if (!((ic->ic_flags & IEEE80211_F_SCAN) || 16958a1b9b6aSSam Leffler (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd) || 16965784a371SSam Leffler ic->ic_opmode == IEEE80211_M_IBSS)) { 16978a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 16981a1e1d21SSam Leffler return; 16991a1e1d21SSam Leffler } 17001a1e1d21SSam Leffler /* 17011a1e1d21SSam Leffler * beacon/probe response frame format 17021a1e1d21SSam Leffler * [8] time stamp 17031a1e1d21SSam Leffler * [2] beacon interval 17041a1e1d21SSam Leffler * [2] capability information 17051a1e1d21SSam Leffler * [tlv] ssid 17061a1e1d21SSam Leffler * [tlv] supported rates 17071a1e1d21SSam Leffler * [tlv] country information 17081a1e1d21SSam Leffler * [tlv] parameter set (FH/DS) 17091a1e1d21SSam Leffler * [tlv] erp information 17101a1e1d21SSam Leffler * [tlv] extended supported rates 17118a1b9b6aSSam Leffler * [tlv] WME 17128a1b9b6aSSam Leffler * [tlv] WPA or RSN 17131a1e1d21SSam Leffler */ 17141a1e1d21SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, 12); 17151a1e1d21SSam Leffler tstamp = frm; frm += 8; 17168a1b9b6aSSam Leffler bintval = le16toh(*(u_int16_t *)frm); frm += 2; 17178a1b9b6aSSam Leffler capinfo = le16toh(*(u_int16_t *)frm); frm += 2; 17188a1b9b6aSSam Leffler ssid = rates = xrates = country = wpa = wme = NULL; 17191a1e1d21SSam Leffler bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan); 17201a1e1d21SSam Leffler chan = bchan; 17211a1e1d21SSam Leffler fhdwell = 0; 17221a1e1d21SSam Leffler fhindex = 0; 17231a1e1d21SSam Leffler erp = 0; 17248a1b9b6aSSam Leffler timoff = 0; 17251a1e1d21SSam Leffler while (frm < efrm) { 17261a1e1d21SSam Leffler switch (*frm) { 17271a1e1d21SSam Leffler case IEEE80211_ELEMID_SSID: 17281a1e1d21SSam Leffler ssid = frm; 17291a1e1d21SSam Leffler break; 17301a1e1d21SSam Leffler case IEEE80211_ELEMID_RATES: 17311a1e1d21SSam Leffler rates = frm; 17321a1e1d21SSam Leffler break; 17331a1e1d21SSam Leffler case IEEE80211_ELEMID_COUNTRY: 17341a1e1d21SSam Leffler country = frm; 17351a1e1d21SSam Leffler break; 17361a1e1d21SSam Leffler case IEEE80211_ELEMID_FHPARMS: 17371a1e1d21SSam Leffler if (ic->ic_phytype == IEEE80211_T_FH) { 17388a1b9b6aSSam Leffler fhdwell = LE_READ_2(&frm[2]); 17391a1e1d21SSam Leffler chan = IEEE80211_FH_CHAN(frm[4], frm[5]); 17401a1e1d21SSam Leffler fhindex = frm[6]; 17411a1e1d21SSam Leffler } 17421a1e1d21SSam Leffler break; 17431a1e1d21SSam Leffler case IEEE80211_ELEMID_DSPARMS: 17441a1e1d21SSam Leffler /* 17451a1e1d21SSam Leffler * XXX hack this since depending on phytype 17461a1e1d21SSam Leffler * is problematic for multi-mode devices. 17471a1e1d21SSam Leffler */ 17481a1e1d21SSam Leffler if (ic->ic_phytype != IEEE80211_T_FH) 17491a1e1d21SSam Leffler chan = frm[2]; 17501a1e1d21SSam Leffler break; 17511a1e1d21SSam Leffler case IEEE80211_ELEMID_TIM: 17528a1b9b6aSSam Leffler /* XXX ATIM? */ 17538a1b9b6aSSam Leffler timoff = frm - mtod(m0, u_int8_t *); 17541a1e1d21SSam Leffler break; 17554bd067c5SSam Leffler case IEEE80211_ELEMID_IBSSPARMS: 17564bd067c5SSam Leffler break; 17571a1e1d21SSam Leffler case IEEE80211_ELEMID_XRATES: 17581a1e1d21SSam Leffler xrates = frm; 17591a1e1d21SSam Leffler break; 17601a1e1d21SSam Leffler case IEEE80211_ELEMID_ERP: 17611a1e1d21SSam Leffler if (frm[1] != 1) { 17628a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, 17638a1b9b6aSSam Leffler IEEE80211_MSG_ELEMID, wh, "ERP", 17648a1b9b6aSSam Leffler "bad len %u", frm[1]); 17651be50176SSam Leffler ic->ic_stats.is_rx_elem_toobig++; 17661a1e1d21SSam Leffler break; 17671a1e1d21SSam Leffler } 17681a1e1d21SSam Leffler erp = frm[2]; 17691a1e1d21SSam Leffler break; 17708a1b9b6aSSam Leffler case IEEE80211_ELEMID_RSN: 17718a1b9b6aSSam Leffler wpa = frm; 17728a1b9b6aSSam Leffler break; 17738a1b9b6aSSam Leffler case IEEE80211_ELEMID_VENDOR: 17748a1b9b6aSSam Leffler if (iswpaoui(frm)) 17758a1b9b6aSSam Leffler wpa = frm; 17768a1b9b6aSSam Leffler else if (iswmeparam(frm) || iswmeinfo(frm)) 17778a1b9b6aSSam Leffler wme = frm; 17788a1b9b6aSSam Leffler /* XXX Atheros OUI support */ 17798a1b9b6aSSam Leffler break; 17801a1e1d21SSam Leffler default: 17818a1b9b6aSSam Leffler IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID, 17828a1b9b6aSSam Leffler wh, "unhandled", 17838a1b9b6aSSam Leffler "id %u, len %u", *frm, frm[1]); 17841be50176SSam Leffler ic->ic_stats.is_rx_elem_unknown++; 17851a1e1d21SSam Leffler break; 17861a1e1d21SSam Leffler } 17871a1e1d21SSam Leffler frm += frm[1] + 2; 17881a1e1d21SSam Leffler } 1789dd0e6ea6SSam Leffler IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); 17901a1e1d21SSam Leffler IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); 1791a11c9a5cSSam Leffler if ( 1792a11c9a5cSSam Leffler #if IEEE80211_CHAN_MAX < 255 1793a11c9a5cSSam Leffler chan > IEEE80211_CHAN_MAX || 1794a11c9a5cSSam Leffler #endif 1795a11c9a5cSSam Leffler isclr(ic->ic_chan_active, chan)) { 17968a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, 17978a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> 17988a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 17998a1b9b6aSSam Leffler "invalid channel %u", chan); 18001be50176SSam Leffler ic->ic_stats.is_rx_badchan++; 18011a1e1d21SSam Leffler return; 18021a1e1d21SSam Leffler } 18034844aa7dSAtsushi Onoe if (chan != bchan && ic->ic_phytype != IEEE80211_T_FH) { 18041a1e1d21SSam Leffler /* 18051a1e1d21SSam Leffler * Frame was received on a channel different from the 18064844aa7dSAtsushi Onoe * one indicated in the DS params element id; 18071a1e1d21SSam Leffler * silently discard it. 18081a1e1d21SSam Leffler * 18091a1e1d21SSam Leffler * NB: this can happen due to signal leakage. 18104844aa7dSAtsushi Onoe * But we should take it for FH phy because 18114844aa7dSAtsushi Onoe * the rssi value should be correct even for 18124844aa7dSAtsushi Onoe * different hop pattern in FH. 18131a1e1d21SSam Leffler */ 18148a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, 18158a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> 18168a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 18176b312781SSam Leffler "for off-channel %u", chan); 18181be50176SSam Leffler ic->ic_stats.is_rx_chanmismatch++; 18191a1e1d21SSam Leffler return; 18201a1e1d21SSam Leffler } 18211a1e1d21SSam Leffler 18221a1e1d21SSam Leffler /* 182344c72e42SSam Leffler * Count frame now that we know it's to be processed. 182444c72e42SSam Leffler */ 182544c72e42SSam Leffler if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { 182644c72e42SSam Leffler ic->ic_stats.is_rx_beacon++; /* XXX remove */ 182744c72e42SSam Leffler IEEE80211_NODE_STAT(ni, rx_beacons); 182844c72e42SSam Leffler } else 182944c72e42SSam Leffler IEEE80211_NODE_STAT(ni, rx_proberesp); 183044c72e42SSam Leffler 183144c72e42SSam Leffler /* 18328a1b9b6aSSam Leffler * When operating in station mode, check for state updates. 18338a1b9b6aSSam Leffler * Be careful to ignore beacons received while doing a 18348a1b9b6aSSam Leffler * background scan. We consider only 11g/WMM stuff right now. 18351a1e1d21SSam Leffler */ 18368a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA && 18378a1b9b6aSSam Leffler ni->ni_associd != 0 && 18388a1b9b6aSSam Leffler ((ic->ic_flags & IEEE80211_F_SCAN) == 0 || 18398a1b9b6aSSam Leffler IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) { 18408a1b9b6aSSam Leffler if (ni->ni_erp != erp) { 18418a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 18428a1b9b6aSSam Leffler "[%s] erp change: was 0x%x, now 0x%x\n", 18438a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 18448a1b9b6aSSam Leffler ni->ni_erp, erp); 18458a1b9b6aSSam Leffler if (erp & IEEE80211_ERP_USE_PROTECTION) 18468a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_USEPROT; 18478a1b9b6aSSam Leffler else 18488a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_USEPROT; 18498a1b9b6aSSam Leffler ni->ni_erp = erp; 18508a1b9b6aSSam Leffler /* XXX statistic */ 18511a1e1d21SSam Leffler } 18528a1b9b6aSSam Leffler if ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) { 18538a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 18548a1b9b6aSSam Leffler "[%s] capabilities change: before 0x%x," 18558a1b9b6aSSam Leffler " now 0x%x\n", 18568a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 18578a1b9b6aSSam Leffler ni->ni_capinfo, capinfo); 18588a1b9b6aSSam Leffler /* 18598a1b9b6aSSam Leffler * NB: we assume short preamble doesn't 18608a1b9b6aSSam Leffler * change dynamically 18618a1b9b6aSSam Leffler */ 18628a1b9b6aSSam Leffler ieee80211_set_shortslottime(ic, 18638a1b9b6aSSam Leffler ic->ic_curmode == IEEE80211_MODE_11A || 18648a1b9b6aSSam Leffler (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); 18658a1b9b6aSSam Leffler ni->ni_capinfo = capinfo; 18668a1b9b6aSSam Leffler /* XXX statistic */ 18678a1b9b6aSSam Leffler } 18688a1b9b6aSSam Leffler if (wme != NULL && 1869c0fa32ceSSam Leffler ieee80211_parse_wmeparams(ic, wme, wh) > 0) 18708a1b9b6aSSam Leffler ieee80211_wme_updateparams(ic); 18718a1b9b6aSSam Leffler /* NB: don't need the rest of this */ 187265e278a8SSam Leffler if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) 18738a1b9b6aSSam Leffler return; 18748a1b9b6aSSam Leffler } 18758a1b9b6aSSam Leffler 18768a1b9b6aSSam Leffler if (ni == ic->ic_bss) { 18778a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 18788a1b9b6aSSam Leffler if (ieee80211_msg_scan(ic)) 18798a1b9b6aSSam Leffler dump_probe_beacon(subtype, 1, 18808a1b9b6aSSam Leffler wh->i_addr2, chan, bchan, capinfo, 18818a1b9b6aSSam Leffler bintval, erp, ssid, country); 18821a1e1d21SSam Leffler #endif 18838a1b9b6aSSam Leffler /* 18848a1b9b6aSSam Leffler * Create a new entry. If scanning the entry goes 18858a1b9b6aSSam Leffler * in the scan cache. Otherwise, be particular when 18868a1b9b6aSSam Leffler * operating in adhoc mode--only take nodes marked 18878a1b9b6aSSam Leffler * as ibss participants so we don't populate our 18888a1b9b6aSSam Leffler * neighbor table with unintersting sta's. 18898a1b9b6aSSam Leffler */ 18908a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 18918a1b9b6aSSam Leffler if ((capinfo & IEEE80211_CAPINFO_IBSS) == 0) 18928a1b9b6aSSam Leffler return; 1893acc4f7f5SSam Leffler ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta, 18948a1b9b6aSSam Leffler wh->i_addr2); 18958a1b9b6aSSam Leffler } else 18968a1b9b6aSSam Leffler ni = ieee80211_dup_bss(&ic->ic_scan, wh->i_addr2); 1897c64bfa0fSSam Leffler if (ni == NULL) 18981a1e1d21SSam Leffler return; 18991a1e1d21SSam Leffler ni->ni_esslen = ssid[1]; 19001a1e1d21SSam Leffler memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); 19011a1e1d21SSam Leffler memcpy(ni->ni_essid, ssid + 2, ssid[1]); 19028a1b9b6aSSam Leffler } else if (ssid[1] != 0 && 19038a1b9b6aSSam Leffler (ISPROBE(subtype) || ni->ni_esslen == 0)) { 19041a1e1d21SSam Leffler /* 19058a1b9b6aSSam Leffler * Update ESSID at probe response to adopt 19068a1b9b6aSSam Leffler * hidden AP by Lucent/Cisco, which announces 19078a1b9b6aSSam Leffler * null ESSID in beacon. 19081a1e1d21SSam Leffler */ 19098a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 19108a1b9b6aSSam Leffler if (ieee80211_msg_scan(ic) || 19118a1b9b6aSSam Leffler ieee80211_msg_debug(ic)) 19128a1b9b6aSSam Leffler dump_probe_beacon(subtype, 0, 19138a1b9b6aSSam Leffler wh->i_addr2, chan, bchan, capinfo, 19148a1b9b6aSSam Leffler bintval, erp, ssid, country); 19158a1b9b6aSSam Leffler #endif 19161a1e1d21SSam Leffler ni->ni_esslen = ssid[1]; 19171a1e1d21SSam Leffler memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); 19181a1e1d21SSam Leffler memcpy(ni->ni_essid, ssid + 2, ssid[1]); 19198a1b9b6aSSam Leffler } 19208a1b9b6aSSam Leffler ni->ni_scangen = ic->ic_scan.nt_scangen; 19211a1e1d21SSam Leffler IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); 19221a1e1d21SSam Leffler ni->ni_rssi = rssi; 19231a1e1d21SSam Leffler ni->ni_rstamp = rstamp; 19248a1b9b6aSSam Leffler memcpy(ni->ni_tstamp.data, tstamp, sizeof(ni->ni_tstamp)); 19258a1b9b6aSSam Leffler ni->ni_intval = bintval; 19268a1b9b6aSSam Leffler ni->ni_capinfo = capinfo; 19271a1e1d21SSam Leffler ni->ni_chan = &ic->ic_channels[chan]; 19281a1e1d21SSam Leffler ni->ni_fhdwell = fhdwell; 19291a1e1d21SSam Leffler ni->ni_fhindex = fhindex; 19301a1e1d21SSam Leffler ni->ni_erp = erp; 19318a1b9b6aSSam Leffler /* 19328a1b9b6aSSam Leffler * Record the byte offset from the mac header to 19338a1b9b6aSSam Leffler * the start of the TIM information element for 19348a1b9b6aSSam Leffler * use by hardware and/or to speedup software 19358a1b9b6aSSam Leffler * processing of beacon frames. 19368a1b9b6aSSam Leffler */ 19378a1b9b6aSSam Leffler ni->ni_timoff = timoff; 19388a1b9b6aSSam Leffler /* 19398a1b9b6aSSam Leffler * Record optional information elements that might be 19408a1b9b6aSSam Leffler * used by applications or drivers. 19418a1b9b6aSSam Leffler */ 19428a1b9b6aSSam Leffler if (wme != NULL) 19438a1b9b6aSSam Leffler ieee80211_saveie(&ni->ni_wme_ie, wme); 19448a1b9b6aSSam Leffler if (wpa != NULL) 19458a1b9b6aSSam Leffler ieee80211_saveie(&ni->ni_wpa_ie, wpa); 19461a1e1d21SSam Leffler /* NB: must be after ni_chan is setup */ 19471a1e1d21SSam Leffler ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT); 19481a1e1d21SSam Leffler break; 19491a1e1d21SSam Leffler } 19501a1e1d21SSam Leffler 19511a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_REQ: { 19521a1e1d21SSam Leffler u_int8_t rate; 19531a1e1d21SSam Leffler 19548a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA || 19558a1b9b6aSSam Leffler ic->ic_state != IEEE80211_S_RUN) { 19568a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 19571a1e1d21SSam Leffler return; 19588a1b9b6aSSam Leffler } 19598a1b9b6aSSam Leffler if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { 19608a1b9b6aSSam Leffler /* frame must be directed */ 19618a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; /* XXX stat */ 19621a1e1d21SSam Leffler return; 19638a1b9b6aSSam Leffler } 19641a1e1d21SSam Leffler 19651a1e1d21SSam Leffler /* 19661a1e1d21SSam Leffler * prreq frame format 19671a1e1d21SSam Leffler * [tlv] ssid 19681a1e1d21SSam Leffler * [tlv] supported rates 19691a1e1d21SSam Leffler * [tlv] extended supported rates 19701a1e1d21SSam Leffler */ 19711a1e1d21SSam Leffler ssid = rates = xrates = NULL; 19721a1e1d21SSam Leffler while (frm < efrm) { 19731a1e1d21SSam Leffler switch (*frm) { 19741a1e1d21SSam Leffler case IEEE80211_ELEMID_SSID: 19751a1e1d21SSam Leffler ssid = frm; 19761a1e1d21SSam Leffler break; 19771a1e1d21SSam Leffler case IEEE80211_ELEMID_RATES: 19781a1e1d21SSam Leffler rates = frm; 19791a1e1d21SSam Leffler break; 19801a1e1d21SSam Leffler case IEEE80211_ELEMID_XRATES: 19811a1e1d21SSam Leffler xrates = frm; 19821a1e1d21SSam Leffler break; 19831a1e1d21SSam Leffler } 19841a1e1d21SSam Leffler frm += frm[1] + 2; 19851a1e1d21SSam Leffler } 1986dd0e6ea6SSam Leffler IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); 19871a1e1d21SSam Leffler IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); 19888a1b9b6aSSam Leffler IEEE80211_VERIFY_SSID(ic->ic_bss, ssid); 19894ef04d32SSam Leffler if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) { 19904ef04d32SSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, 19914ef04d32SSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> 19924ef04d32SSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 19934ef04d32SSam Leffler "%s", "no ssid with ssid suppression enabled"); 19944ef04d32SSam Leffler ic->ic_stats.is_rx_ssidmismatch++; /*XXX*/ 19954ef04d32SSam Leffler return; 19964ef04d32SSam Leffler } 19971a1e1d21SSam Leffler 19980a915fadSSam Leffler if (ni == ic->ic_bss) { 19998a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) { 20008a1b9b6aSSam Leffler /* 20018a1b9b6aSSam Leffler * XXX Cannot tell if the sender is operating 20028a1b9b6aSSam Leffler * in ibss mode. But we need a new node to 20038a1b9b6aSSam Leffler * send the response so blindly add them to the 20048a1b9b6aSSam Leffler * neighbor table. 20058a1b9b6aSSam Leffler */ 2006acc4f7f5SSam Leffler ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta, 20078a1b9b6aSSam Leffler wh->i_addr2); 20088a1b9b6aSSam Leffler } else 2009acc4f7f5SSam Leffler ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2); 2010c64bfa0fSSam Leffler if (ni == NULL) 20111a1e1d21SSam Leffler return; 20121a1e1d21SSam Leffler allocbs = 1; 20131a1e1d21SSam Leffler } else 20141a1e1d21SSam Leffler allocbs = 0; 20158a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 20168a1b9b6aSSam Leffler "[%s] recv probe req\n", ether_sprintf(wh->i_addr2)); 20171a1e1d21SSam Leffler ni->ni_rssi = rssi; 20181a1e1d21SSam Leffler ni->ni_rstamp = rstamp; 20191a1e1d21SSam Leffler rate = ieee80211_setup_rates(ic, ni, rates, xrates, 20201a1e1d21SSam Leffler IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE 20211a1e1d21SSam Leffler | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); 20221a1e1d21SSam Leffler if (rate & IEEE80211_RATE_BASIC) { 20238a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_XRATE, 20248a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> 20258a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 20268a1b9b6aSSam Leffler "%s", "recv'd rate set invalid"); 20271a1e1d21SSam Leffler } else { 20280a915fadSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 20290a915fadSSam Leffler IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0); 20301a1e1d21SSam Leffler } 20318a1b9b6aSSam Leffler if (allocbs && ic->ic_opmode != IEEE80211_M_IBSS) { 20328a1b9b6aSSam Leffler /* reclaim immediately */ 20338a1b9b6aSSam Leffler ieee80211_free_node(ni); 20348a1b9b6aSSam Leffler } 20351a1e1d21SSam Leffler break; 20361a1e1d21SSam Leffler } 20371a1e1d21SSam Leffler 20381a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_AUTH: { 20391a1e1d21SSam Leffler u_int16_t algo, seq, status; 20401a1e1d21SSam Leffler /* 20411a1e1d21SSam Leffler * auth frame format 20421a1e1d21SSam Leffler * [2] algorithm 20431a1e1d21SSam Leffler * [2] sequence 20441a1e1d21SSam Leffler * [2] status 20451a1e1d21SSam Leffler * [tlv*] challenge 20461a1e1d21SSam Leffler */ 20471a1e1d21SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, 6); 20481a1e1d21SSam Leffler algo = le16toh(*(u_int16_t *)frm); 20491a1e1d21SSam Leffler seq = le16toh(*(u_int16_t *)(frm + 2)); 20501a1e1d21SSam Leffler status = le16toh(*(u_int16_t *)(frm + 4)); 20518a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, 20528a1b9b6aSSam Leffler "[%s] recv auth frame with algorithm %d seq %d\n", 20538a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), algo, seq); 20548a1b9b6aSSam Leffler /* 20558a1b9b6aSSam Leffler * Consult the ACL policy module if setup. 20568a1b9b6aSSam Leffler */ 20578a1b9b6aSSam Leffler if (ic->ic_acl != NULL && 20588a1b9b6aSSam Leffler !ic->ic_acl->iac_check(ic, wh->i_addr2)) { 20598a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ACL, 20608a1b9b6aSSam Leffler wh, "auth", "%s", "disallowed by ACL"); 20618a1b9b6aSSam Leffler ic->ic_stats.is_rx_acl++; 20621a1e1d21SSam Leffler return; 20631a1e1d21SSam Leffler } 20648a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_COUNTERM) { 20658a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, 20668a1b9b6aSSam Leffler IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO, 20678a1b9b6aSSam Leffler wh, "auth", "%s", "TKIP countermeasures enabled"); 20688a1b9b6aSSam Leffler ic->ic_stats.is_rx_auth_countermeasures++; 20698a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 20700a915fadSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 20718a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 20728a1b9b6aSSam Leffler IEEE80211_REASON_MIC_FAILURE); 207359571d2bSSam Leffler } 20741a1e1d21SSam Leffler return; 20751a1e1d21SSam Leffler } 20768a1b9b6aSSam Leffler if (algo == IEEE80211_AUTH_ALG_SHARED) 20778a1b9b6aSSam Leffler ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi, 20788a1b9b6aSSam Leffler rstamp, seq, status); 20798a1b9b6aSSam Leffler else if (algo == IEEE80211_AUTH_ALG_OPEN) 20808a1b9b6aSSam Leffler ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq, 20818a1b9b6aSSam Leffler status); 20828a1b9b6aSSam Leffler else { 20838a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, 20848a1b9b6aSSam Leffler wh, "auth", "unsupported alg %d", algo); 20858a1b9b6aSSam Leffler ic->ic_stats.is_rx_auth_unsupported++; 20868a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 20878a1b9b6aSSam Leffler /* XXX not right */ 20888a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 20898a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 20908a1b9b6aSSam Leffler (seq+1) | (IEEE80211_STATUS_ALG<<16)); 20918a1b9b6aSSam Leffler } 20928a1b9b6aSSam Leffler return; 20931a1e1d21SSam Leffler } 20941a1e1d21SSam Leffler break; 20951a1e1d21SSam Leffler } 20961a1e1d21SSam Leffler 20971a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 20981a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { 20991a1e1d21SSam Leffler u_int16_t capinfo, bintval; 21008a1b9b6aSSam Leffler struct ieee80211_rsnparms rsn; 21018a1b9b6aSSam Leffler u_int8_t reason; 21021a1e1d21SSam Leffler 21031a1e1d21SSam Leffler if (ic->ic_opmode != IEEE80211_M_HOSTAP || 21048a1b9b6aSSam Leffler ic->ic_state != IEEE80211_S_RUN) { 21058a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 21061a1e1d21SSam Leffler return; 21078a1b9b6aSSam Leffler } 21081a1e1d21SSam Leffler 21091a1e1d21SSam Leffler if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { 21101a1e1d21SSam Leffler reassoc = 1; 21111a1e1d21SSam Leffler resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP; 21121a1e1d21SSam Leffler } else { 21131a1e1d21SSam Leffler reassoc = 0; 21141a1e1d21SSam Leffler resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP; 21151a1e1d21SSam Leffler } 21161a1e1d21SSam Leffler /* 21171a1e1d21SSam Leffler * asreq frame format 21181a1e1d21SSam Leffler * [2] capability information 21191a1e1d21SSam Leffler * [2] listen interval 21201a1e1d21SSam Leffler * [6*] current AP address (reassoc only) 21211a1e1d21SSam Leffler * [tlv] ssid 21221a1e1d21SSam Leffler * [tlv] supported rates 21231a1e1d21SSam Leffler * [tlv] extended supported rates 21248a1b9b6aSSam Leffler * [tlv] WPA or RSN 21251a1e1d21SSam Leffler */ 21261a1e1d21SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4)); 21271a1e1d21SSam Leffler if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) { 21288a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, 21298a1b9b6aSSam Leffler wh, ieee80211_mgt_subtype_name[subtype >> 21308a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 21318a1b9b6aSSam Leffler "%s", "wrong bssid"); 21321be50176SSam Leffler ic->ic_stats.is_rx_assoc_bss++; 21331a1e1d21SSam Leffler return; 21341a1e1d21SSam Leffler } 21351a1e1d21SSam Leffler capinfo = le16toh(*(u_int16_t *)frm); frm += 2; 21361a1e1d21SSam Leffler bintval = le16toh(*(u_int16_t *)frm); frm += 2; 21371a1e1d21SSam Leffler if (reassoc) 21381a1e1d21SSam Leffler frm += 6; /* ignore current AP info */ 21398a1b9b6aSSam Leffler ssid = rates = xrates = wpa = wme = NULL; 21401a1e1d21SSam Leffler while (frm < efrm) { 21411a1e1d21SSam Leffler switch (*frm) { 21421a1e1d21SSam Leffler case IEEE80211_ELEMID_SSID: 21431a1e1d21SSam Leffler ssid = frm; 21441a1e1d21SSam Leffler break; 21451a1e1d21SSam Leffler case IEEE80211_ELEMID_RATES: 21461a1e1d21SSam Leffler rates = frm; 21471a1e1d21SSam Leffler break; 21481a1e1d21SSam Leffler case IEEE80211_ELEMID_XRATES: 21491a1e1d21SSam Leffler xrates = frm; 21501a1e1d21SSam Leffler break; 21518a1b9b6aSSam Leffler /* XXX verify only one of RSN and WPA ie's? */ 21528a1b9b6aSSam Leffler case IEEE80211_ELEMID_RSN: 21538a1b9b6aSSam Leffler wpa = frm; 21548a1b9b6aSSam Leffler break; 21558a1b9b6aSSam Leffler case IEEE80211_ELEMID_VENDOR: 21568a1b9b6aSSam Leffler if (iswpaoui(frm)) { 21578a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_WPA1) 21588a1b9b6aSSam Leffler wpa = frm; 21598a1b9b6aSSam Leffler } else if (iswmeinfo(frm)) 21608a1b9b6aSSam Leffler wme = frm; 21618a1b9b6aSSam Leffler /* XXX Atheros OUI support */ 21628a1b9b6aSSam Leffler break; 21631a1e1d21SSam Leffler } 21641a1e1d21SSam Leffler frm += frm[1] + 2; 21651a1e1d21SSam Leffler } 2166dd0e6ea6SSam Leffler IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); 21671a1e1d21SSam Leffler IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); 21688a1b9b6aSSam Leffler IEEE80211_VERIFY_SSID(ic->ic_bss, ssid); 21698a1b9b6aSSam Leffler 21700a915fadSSam Leffler if (ni == ic->ic_bss) { 21718a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 21728a1b9b6aSSam Leffler "[%s] deny %s request, sta not authenticated\n", 21738a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 21748a1b9b6aSSam Leffler reassoc ? "reassoc" : "assoc"); 2175acc4f7f5SSam Leffler ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2); 21760a915fadSSam Leffler if (ni != NULL) { 21770a915fadSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 21780a915fadSSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH, 21791a1e1d21SSam Leffler IEEE80211_REASON_ASSOC_NOT_AUTHED); 21808a1b9b6aSSam Leffler ieee80211_free_node(ni); 21810a915fadSSam Leffler } 21821be50176SSam Leffler ic->ic_stats.is_rx_assoc_notauth++; 21831a1e1d21SSam Leffler return; 21841a1e1d21SSam Leffler } 21858a1b9b6aSSam Leffler if (wpa != NULL) { 21868a1b9b6aSSam Leffler /* 21878a1b9b6aSSam Leffler * Parse WPA information element. Note that 21888a1b9b6aSSam Leffler * we initialize the param block from the node 21898a1b9b6aSSam Leffler * state so that information in the IE overrides 21908a1b9b6aSSam Leffler * our defaults. The resulting parameters are 21918a1b9b6aSSam Leffler * installed below after the association is assured. 21928a1b9b6aSSam Leffler */ 21938a1b9b6aSSam Leffler rsn = ni->ni_rsn; 21948a1b9b6aSSam Leffler if (wpa[0] != IEEE80211_ELEMID_RSN) 21958a1b9b6aSSam Leffler reason = ieee80211_parse_wpa(ic, wpa, &rsn, wh); 21968a1b9b6aSSam Leffler else 21978a1b9b6aSSam Leffler reason = ieee80211_parse_rsn(ic, wpa, &rsn, wh); 21988a1b9b6aSSam Leffler if (reason != 0) { 21998a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 22008a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH, reason); 22018a1b9b6aSSam Leffler ieee80211_node_leave(ic, ni); 22028a1b9b6aSSam Leffler /* XXX distinguish WPA/RSN? */ 22038a1b9b6aSSam Leffler ic->ic_stats.is_rx_assoc_badwpaie++; 22048a1b9b6aSSam Leffler return; 22058a1b9b6aSSam Leffler } 22068a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, 22078a1b9b6aSSam Leffler IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, 22088a1b9b6aSSam Leffler "[%s] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n", 22098a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 22108a1b9b6aSSam Leffler wpa[0] != IEEE80211_ELEMID_RSN ? "WPA" : "RSN", 22118a1b9b6aSSam Leffler rsn.rsn_mcastcipher, rsn.rsn_mcastkeylen, 22128a1b9b6aSSam Leffler rsn.rsn_ucastcipher, rsn.rsn_ucastkeylen, 22138a1b9b6aSSam Leffler rsn.rsn_keymgmt, rsn.rsn_caps); 22148a1b9b6aSSam Leffler } 22158a1b9b6aSSam Leffler /* discard challenge after association */ 22168a1b9b6aSSam Leffler if (ni->ni_challenge != NULL) { 22178a1b9b6aSSam Leffler FREE(ni->ni_challenge, M_DEVBUF); 22188a1b9b6aSSam Leffler ni->ni_challenge = NULL; 22198a1b9b6aSSam Leffler } 22201a1e1d21SSam Leffler /* XXX some stations use the privacy bit for handling APs 22211a1e1d21SSam Leffler that suport both encrypted and unencrypted traffic */ 22228a1b9b6aSSam Leffler /* NB: PRIVACY flag bits are assumed to match */ 22231a1e1d21SSam Leffler if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 || 22248a1b9b6aSSam Leffler (capinfo & IEEE80211_CAPINFO_PRIVACY) ^ 22258a1b9b6aSSam Leffler (ic->ic_flags & IEEE80211_F_PRIVACY)) { 22268a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 22278a1b9b6aSSam Leffler "[%s] deny %s request, capability mismatch 0x%x\n", 22288a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 22298a1b9b6aSSam Leffler reassoc ? "reassoc" : "assoc", capinfo); 22300a915fadSSam Leffler IEEE80211_SEND_MGMT(ic, ni, resp, 22310a915fadSSam Leffler IEEE80211_STATUS_CAPINFO); 22328a1b9b6aSSam Leffler ieee80211_node_leave(ic, ni); 22331be50176SSam Leffler ic->ic_stats.is_rx_assoc_capmismatch++; 22341a1e1d21SSam Leffler return; 22351a1e1d21SSam Leffler } 22361a1e1d21SSam Leffler ieee80211_setup_rates(ic, ni, rates, xrates, 22371a1e1d21SSam Leffler IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | 22381a1e1d21SSam Leffler IEEE80211_F_DONEGO | IEEE80211_F_DODEL); 22391a1e1d21SSam Leffler if (ni->ni_rates.rs_nrates == 0) { 22408a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 22418a1b9b6aSSam Leffler "[%s] deny %s request, rate set mismatch\n", 22428a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 22438a1b9b6aSSam Leffler reassoc ? "reassoc" : "assoc"); 22440a915fadSSam Leffler IEEE80211_SEND_MGMT(ic, ni, resp, 22450a915fadSSam Leffler IEEE80211_STATUS_BASIC_RATE); 22468a1b9b6aSSam Leffler ieee80211_node_leave(ic, ni); 22471be50176SSam Leffler ic->ic_stats.is_rx_assoc_norate++; 22481a1e1d21SSam Leffler return; 22491a1e1d21SSam Leffler } 22501a1e1d21SSam Leffler ni->ni_rssi = rssi; 22511a1e1d21SSam Leffler ni->ni_rstamp = rstamp; 22521a1e1d21SSam Leffler ni->ni_intval = bintval; 22531a1e1d21SSam Leffler ni->ni_capinfo = capinfo; 22541a1e1d21SSam Leffler ni->ni_chan = ic->ic_bss->ni_chan; 22551a1e1d21SSam Leffler ni->ni_fhdwell = ic->ic_bss->ni_fhdwell; 22561a1e1d21SSam Leffler ni->ni_fhindex = ic->ic_bss->ni_fhindex; 22578a1b9b6aSSam Leffler if (wpa != NULL) { 22588a1b9b6aSSam Leffler /* 22598a1b9b6aSSam Leffler * Record WPA/RSN parameters for station, mark 22608a1b9b6aSSam Leffler * node as using WPA and record information element 22618a1b9b6aSSam Leffler * for applications that require it. 22628a1b9b6aSSam Leffler */ 22638a1b9b6aSSam Leffler ni->ni_rsn = rsn; 22648a1b9b6aSSam Leffler ieee80211_saveie(&ni->ni_wpa_ie, wpa); 22658a1b9b6aSSam Leffler } else if (ni->ni_wpa_ie != NULL) { 22668a1b9b6aSSam Leffler /* 22678a1b9b6aSSam Leffler * Flush any state from a previous association. 22688a1b9b6aSSam Leffler */ 22698a1b9b6aSSam Leffler FREE(ni->ni_wpa_ie, M_DEVBUF); 22708a1b9b6aSSam Leffler ni->ni_wpa_ie = NULL; 22718a1b9b6aSSam Leffler } 22728a1b9b6aSSam Leffler if (wme != NULL) { 22738a1b9b6aSSam Leffler /* 22748a1b9b6aSSam Leffler * Record WME parameters for station, mark node 22758a1b9b6aSSam Leffler * as capable of QoS and record information 22768a1b9b6aSSam Leffler * element for applications that require it. 22778a1b9b6aSSam Leffler */ 22788a1b9b6aSSam Leffler ieee80211_saveie(&ni->ni_wme_ie, wme); 22798a1b9b6aSSam Leffler ni->ni_flags |= IEEE80211_NODE_QOS; 22808a1b9b6aSSam Leffler } else if (ni->ni_wme_ie != NULL) { 22818a1b9b6aSSam Leffler /* 22828a1b9b6aSSam Leffler * Flush any state from a previous association. 22838a1b9b6aSSam Leffler */ 22848a1b9b6aSSam Leffler FREE(ni->ni_wme_ie, M_DEVBUF); 22858a1b9b6aSSam Leffler ni->ni_wme_ie = NULL; 22868a1b9b6aSSam Leffler ni->ni_flags &= ~IEEE80211_NODE_QOS; 22878a1b9b6aSSam Leffler } 22888a1b9b6aSSam Leffler ieee80211_node_join(ic, ni, resp); 22891a1e1d21SSam Leffler break; 22901a1e1d21SSam Leffler } 22911a1e1d21SSam Leffler 22921a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 22931a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { 22948a1b9b6aSSam Leffler u_int16_t capinfo, associd; 22951a1e1d21SSam Leffler u_int16_t status; 22961a1e1d21SSam Leffler 22971a1e1d21SSam Leffler if (ic->ic_opmode != IEEE80211_M_STA || 22988a1b9b6aSSam Leffler ic->ic_state != IEEE80211_S_ASSOC) { 22998a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 23001a1e1d21SSam Leffler return; 23018a1b9b6aSSam Leffler } 23021a1e1d21SSam Leffler 23031a1e1d21SSam Leffler /* 23041a1e1d21SSam Leffler * asresp frame format 23051a1e1d21SSam Leffler * [2] capability information 23061a1e1d21SSam Leffler * [2] status 23071a1e1d21SSam Leffler * [2] association ID 23081a1e1d21SSam Leffler * [tlv] supported rates 23091a1e1d21SSam Leffler * [tlv] extended supported rates 23108a1b9b6aSSam Leffler * [tlv] WME 23111a1e1d21SSam Leffler */ 23121a1e1d21SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, 6); 23131a1e1d21SSam Leffler ni = ic->ic_bss; 23148a1b9b6aSSam Leffler capinfo = le16toh(*(u_int16_t *)frm); 23151a1e1d21SSam Leffler frm += 2; 23161a1e1d21SSam Leffler status = le16toh(*(u_int16_t *)frm); 23171a1e1d21SSam Leffler frm += 2; 23181a1e1d21SSam Leffler if (status != 0) { 23198a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 23208a1b9b6aSSam Leffler "[%s] %sassoc failed (reason %d)\n", 23218a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 23228a1b9b6aSSam Leffler ISREASSOC(subtype) ? "re" : "", status); 23238a1b9b6aSSam Leffler if (ni != ic->ic_bss) /* XXX never true? */ 23241a1e1d21SSam Leffler ni->ni_fails++; 23258a1b9b6aSSam Leffler ic->ic_stats.is_rx_auth_fail++; /* XXX */ 23261a1e1d21SSam Leffler return; 23271a1e1d21SSam Leffler } 23288a1b9b6aSSam Leffler associd = le16toh(*(u_int16_t *)frm); 23291a1e1d21SSam Leffler frm += 2; 23301a1e1d21SSam Leffler 23318a1b9b6aSSam Leffler rates = xrates = wpa = wme = NULL; 23321a1e1d21SSam Leffler while (frm < efrm) { 23331a1e1d21SSam Leffler switch (*frm) { 23341a1e1d21SSam Leffler case IEEE80211_ELEMID_RATES: 23351a1e1d21SSam Leffler rates = frm; 23361a1e1d21SSam Leffler break; 23371a1e1d21SSam Leffler case IEEE80211_ELEMID_XRATES: 23381a1e1d21SSam Leffler xrates = frm; 23391a1e1d21SSam Leffler break; 23408a1b9b6aSSam Leffler case IEEE80211_ELEMID_VENDOR: 23418a1b9b6aSSam Leffler if (iswmeoui(frm)) 23428a1b9b6aSSam Leffler wme = frm; 23438a1b9b6aSSam Leffler /* XXX Atheros OUI support */ 23448a1b9b6aSSam Leffler break; 23451a1e1d21SSam Leffler } 23461a1e1d21SSam Leffler frm += frm[1] + 2; 23471a1e1d21SSam Leffler } 23481a1e1d21SSam Leffler 2349dd0e6ea6SSam Leffler IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); 23501a1e1d21SSam Leffler ieee80211_setup_rates(ic, ni, rates, xrates, 23511a1e1d21SSam Leffler IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | 23521a1e1d21SSam Leffler IEEE80211_F_DONEGO | IEEE80211_F_DODEL); 23538a1b9b6aSSam Leffler if (ni->ni_rates.rs_nrates == 0) { 23548a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 23558a1b9b6aSSam Leffler "[%s] %sassoc failed (rate set mismatch)\n", 23568a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 23578a1b9b6aSSam Leffler ISREASSOC(subtype) ? "re" : ""); 23588a1b9b6aSSam Leffler if (ni != ic->ic_bss) /* XXX never true? */ 23598a1b9b6aSSam Leffler ni->ni_fails++; 23608a1b9b6aSSam Leffler ic->ic_stats.is_rx_assoc_norate++; 23618a1b9b6aSSam Leffler return; 23628a1b9b6aSSam Leffler } 23638a1b9b6aSSam Leffler 23648a1b9b6aSSam Leffler ni->ni_capinfo = capinfo; 23658a1b9b6aSSam Leffler ni->ni_associd = associd; 2366c0fa32ceSSam Leffler if (wme != NULL && 2367c0fa32ceSSam Leffler ieee80211_parse_wmeparams(ic, wme, wh) >= 0) { 23688a1b9b6aSSam Leffler ni->ni_flags |= IEEE80211_NODE_QOS; 23698a1b9b6aSSam Leffler ieee80211_wme_updateparams(ic); 23708a1b9b6aSSam Leffler } else 23718a1b9b6aSSam Leffler ni->ni_flags &= ~IEEE80211_NODE_QOS; 23728a1b9b6aSSam Leffler /* 23738a1b9b6aSSam Leffler * Configure state now that we are associated. 23748a1b9b6aSSam Leffler * 23758a1b9b6aSSam Leffler * XXX may need different/additional driver callbacks? 23768a1b9b6aSSam Leffler */ 23778a1b9b6aSSam Leffler if (ic->ic_curmode == IEEE80211_MODE_11A || 23788a1b9b6aSSam Leffler (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { 23798a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 23808a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_USEBARKER; 23818a1b9b6aSSam Leffler } else { 23828a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 23838a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_USEBARKER; 23848a1b9b6aSSam Leffler } 23858a1b9b6aSSam Leffler ieee80211_set_shortslottime(ic, 23868a1b9b6aSSam Leffler ic->ic_curmode == IEEE80211_MODE_11A || 23878a1b9b6aSSam Leffler (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); 23888a1b9b6aSSam Leffler /* 23898a1b9b6aSSam Leffler * Honor ERP protection. 23908a1b9b6aSSam Leffler * 23918a1b9b6aSSam Leffler * NB: ni_erp should zero for non-11g operation. 23928a1b9b6aSSam Leffler * XXX check ic_curmode anyway? 23938a1b9b6aSSam Leffler */ 23948a1b9b6aSSam Leffler if (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION) 23958a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_USEPROT; 23968a1b9b6aSSam Leffler else 23978a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_USEPROT; 23988a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 23998a1b9b6aSSam Leffler "[%s] %sassoc success: %s preamble, %s slot time%s%s\n", 24008a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2), 24018a1b9b6aSSam Leffler ISREASSOC(subtype) ? "re" : "", 24028a1b9b6aSSam Leffler ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", 24038a1b9b6aSSam Leffler ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", 24048a1b9b6aSSam Leffler ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "", 24058a1b9b6aSSam Leffler ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "" 24068a1b9b6aSSam Leffler ); 24078a1b9b6aSSam Leffler ieee80211_new_state(ic, IEEE80211_S_RUN, subtype); 24081a1e1d21SSam Leffler break; 24091a1e1d21SSam Leffler } 24101a1e1d21SSam Leffler 24111a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_DEAUTH: { 24121a1e1d21SSam Leffler u_int16_t reason; 24138a1b9b6aSSam Leffler 24148a1b9b6aSSam Leffler if (ic->ic_state == IEEE80211_S_SCAN) { 24158a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 24168a1b9b6aSSam Leffler return; 24178a1b9b6aSSam Leffler } 24181a1e1d21SSam Leffler /* 24191a1e1d21SSam Leffler * deauth frame format 24201a1e1d21SSam Leffler * [2] reason 24211a1e1d21SSam Leffler */ 24221a1e1d21SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, 2); 24231a1e1d21SSam Leffler reason = le16toh(*(u_int16_t *)frm); 24241be50176SSam Leffler ic->ic_stats.is_rx_deauth++; 24258a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_deauth); 24261a1e1d21SSam Leffler switch (ic->ic_opmode) { 24271a1e1d21SSam Leffler case IEEE80211_M_STA: 2428a11c9a5cSSam Leffler ieee80211_new_state(ic, IEEE80211_S_AUTH, 24291a1e1d21SSam Leffler wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); 24301a1e1d21SSam Leffler break; 24311a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 24320a915fadSSam Leffler if (ni != ic->ic_bss) { 24338a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, 24348a1b9b6aSSam Leffler "station %s deauthenticated by peer " 24358a1b9b6aSSam Leffler "(reason %d)\n", 24361a1e1d21SSam Leffler ether_sprintf(ni->ni_macaddr), reason); 24378a1b9b6aSSam Leffler ieee80211_node_leave(ic, ni); 24381a1e1d21SSam Leffler } 24391a1e1d21SSam Leffler break; 24401a1e1d21SSam Leffler default: 24418a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 24421a1e1d21SSam Leffler break; 24431a1e1d21SSam Leffler } 24441a1e1d21SSam Leffler break; 24451a1e1d21SSam Leffler } 24461a1e1d21SSam Leffler 24471a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_DISASSOC: { 24481a1e1d21SSam Leffler u_int16_t reason; 24498a1b9b6aSSam Leffler 24508a1b9b6aSSam Leffler if (ic->ic_state != IEEE80211_S_RUN && 24518a1b9b6aSSam Leffler ic->ic_state != IEEE80211_S_AUTH) { 24528a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 24538a1b9b6aSSam Leffler return; 24548a1b9b6aSSam Leffler } 24551a1e1d21SSam Leffler /* 24561a1e1d21SSam Leffler * disassoc frame format 24571a1e1d21SSam Leffler * [2] reason 24581a1e1d21SSam Leffler */ 24591a1e1d21SSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, 2); 24601a1e1d21SSam Leffler reason = le16toh(*(u_int16_t *)frm); 24611be50176SSam Leffler ic->ic_stats.is_rx_disassoc++; 24628a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, rx_disassoc); 24631a1e1d21SSam Leffler switch (ic->ic_opmode) { 24641a1e1d21SSam Leffler case IEEE80211_M_STA: 2465a11c9a5cSSam Leffler ieee80211_new_state(ic, IEEE80211_S_ASSOC, 24661a1e1d21SSam Leffler wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); 24671a1e1d21SSam Leffler break; 24681a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 24690a915fadSSam Leffler if (ni != ic->ic_bss) { 24708a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 24718a1b9b6aSSam Leffler "[%s] sta disassociated by peer (reason %d)\n", 24721a1e1d21SSam Leffler ether_sprintf(ni->ni_macaddr), reason); 24738a1b9b6aSSam Leffler ieee80211_node_leave(ic, ni); 24741a1e1d21SSam Leffler } 24751a1e1d21SSam Leffler break; 24761a1e1d21SSam Leffler default: 24778a1b9b6aSSam Leffler ic->ic_stats.is_rx_mgtdiscard++; 24781a1e1d21SSam Leffler break; 24791a1e1d21SSam Leffler } 24801a1e1d21SSam Leffler break; 24811a1e1d21SSam Leffler } 24821a1e1d21SSam Leffler default: 24838a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, 24848a1b9b6aSSam Leffler wh, "mgt", "subtype 0x%x not handled", subtype); 24851be50176SSam Leffler ic->ic_stats.is_rx_badsubtype++; 24861a1e1d21SSam Leffler break; 24871a1e1d21SSam Leffler } 24888a1b9b6aSSam Leffler #undef ISREASSOC 24898a1b9b6aSSam Leffler #undef ISPROBE 24901a1e1d21SSam Leffler } 24911a1e1d21SSam Leffler #undef IEEE80211_VERIFY_LENGTH 24921a1e1d21SSam Leffler #undef IEEE80211_VERIFY_ELEMENT 24938a1b9b6aSSam Leffler 24948a1b9b6aSSam Leffler /* 24958a1b9b6aSSam Leffler * Handle station power-save state change. 24968a1b9b6aSSam Leffler */ 24978a1b9b6aSSam Leffler static void 24988a1b9b6aSSam Leffler ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable) 24998a1b9b6aSSam Leffler { 25008a1b9b6aSSam Leffler struct ieee80211com *ic = ni->ni_ic; 25018a1b9b6aSSam Leffler struct mbuf *m; 25028a1b9b6aSSam Leffler 25038a1b9b6aSSam Leffler if (enable) { 25048a1b9b6aSSam Leffler if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) 25058a1b9b6aSSam Leffler ic->ic_ps_sta++; 25068a1b9b6aSSam Leffler ni->ni_flags |= IEEE80211_NODE_PWR_MGT; 25078a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 25088a1b9b6aSSam Leffler "[%s] power save mode on, %u sta's in ps mode\n", 25098a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta); 25108a1b9b6aSSam Leffler return; 25118a1b9b6aSSam Leffler } 25128a1b9b6aSSam Leffler 25138a1b9b6aSSam Leffler if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) 25148a1b9b6aSSam Leffler ic->ic_ps_sta--; 25158a1b9b6aSSam Leffler ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; 25168a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 25178a1b9b6aSSam Leffler "[%s] power save mode off, %u sta's in ps mode\n", 25188a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta); 25198a1b9b6aSSam Leffler /* XXX if no stations in ps mode, flush mc frames */ 25208a1b9b6aSSam Leffler 25218a1b9b6aSSam Leffler /* 25228a1b9b6aSSam Leffler * Flush queued unicast frames. 25238a1b9b6aSSam Leffler */ 25248a1b9b6aSSam Leffler if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) { 25258a1b9b6aSSam Leffler ic->ic_set_tim(ic, ni, 0); /* just in case */ 25268a1b9b6aSSam Leffler return; 25278a1b9b6aSSam Leffler } 25288a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 25298a1b9b6aSSam Leffler "[%s] flush ps queue, %u packets queued\n", 25308a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), IEEE80211_NODE_SAVEQ_QLEN(ni)); 25318a1b9b6aSSam Leffler for (;;) { 25328a1b9b6aSSam Leffler int qlen; 25338a1b9b6aSSam Leffler 25348a1b9b6aSSam Leffler IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen); 25358a1b9b6aSSam Leffler if (m == NULL) 25368a1b9b6aSSam Leffler break; 25378a1b9b6aSSam Leffler /* 25388a1b9b6aSSam Leffler * If this is the last packet, turn off the TIM bit. 25398a1b9b6aSSam Leffler * If there are more packets, set the more packets bit 25408a1b9b6aSSam Leffler * in the packet dispatched to the station. 25418a1b9b6aSSam Leffler */ 25428a1b9b6aSSam Leffler if (qlen != 0) { 25438a1b9b6aSSam Leffler struct ieee80211_frame_min *wh = 25448a1b9b6aSSam Leffler mtod(m, struct ieee80211_frame_min *); 25458a1b9b6aSSam Leffler wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; 25468a1b9b6aSSam Leffler } 25478a1b9b6aSSam Leffler /* XXX need different driver interface */ 25488a1b9b6aSSam Leffler /* XXX bypasses q max */ 25498a1b9b6aSSam Leffler IF_ENQUEUE(&ic->ic_ifp->if_snd, m); 25508a1b9b6aSSam Leffler } 25518a1b9b6aSSam Leffler } 25528a1b9b6aSSam Leffler 25538a1b9b6aSSam Leffler /* 25548a1b9b6aSSam Leffler * Process a received ps-poll frame. 25558a1b9b6aSSam Leffler */ 25568a1b9b6aSSam Leffler static void 25578a1b9b6aSSam Leffler ieee80211_recv_pspoll(struct ieee80211com *ic, 25588a1b9b6aSSam Leffler struct ieee80211_node *ni, struct mbuf *m0) 25598a1b9b6aSSam Leffler { 25608a1b9b6aSSam Leffler struct ieee80211_frame_min *wh; 25618a1b9b6aSSam Leffler struct mbuf *m; 25628a1b9b6aSSam Leffler u_int16_t aid; 25638a1b9b6aSSam Leffler int qlen; 25648a1b9b6aSSam Leffler 25658a1b9b6aSSam Leffler wh = mtod(m0, struct ieee80211_frame_min *); 25668a1b9b6aSSam Leffler if (ni->ni_associd == 0) { 25678a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, 25688a1b9b6aSSam Leffler (struct ieee80211_frame *) wh, "ps-poll", 25698a1b9b6aSSam Leffler "%s", "unassociated station"); 25708a1b9b6aSSam Leffler ic->ic_stats.is_ps_unassoc++; 25718a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, 25728a1b9b6aSSam Leffler IEEE80211_REASON_NOT_ASSOCED); 25738a1b9b6aSSam Leffler return; 25748a1b9b6aSSam Leffler } 25758a1b9b6aSSam Leffler 25768a1b9b6aSSam Leffler aid = le16toh(*(u_int16_t *)wh->i_dur); 25778a1b9b6aSSam Leffler if (aid != ni->ni_associd) { 25788a1b9b6aSSam Leffler IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, 25798a1b9b6aSSam Leffler (struct ieee80211_frame *) wh, "ps-poll", 25808a1b9b6aSSam Leffler "aid mismatch: sta aid 0x%x poll aid 0x%x", 25818a1b9b6aSSam Leffler ni->ni_associd, aid); 25828a1b9b6aSSam Leffler ic->ic_stats.is_ps_badaid++; 25838a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, 25848a1b9b6aSSam Leffler IEEE80211_REASON_NOT_ASSOCED); 25858a1b9b6aSSam Leffler return; 25868a1b9b6aSSam Leffler } 25878a1b9b6aSSam Leffler 25888a1b9b6aSSam Leffler /* Okay, take the first queued packet and put it out... */ 25898a1b9b6aSSam Leffler IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen); 25908a1b9b6aSSam Leffler if (m == NULL) { 25918a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 25928a1b9b6aSSam Leffler "[%s] recv ps-poll, but queue empty\n", 25938a1b9b6aSSam Leffler ether_sprintf(wh->i_addr2)); 25948a1b9b6aSSam Leffler ieee80211_send_nulldata(ic, ni); 25958a1b9b6aSSam Leffler ic->ic_stats.is_ps_qempty++; /* XXX node stat */ 25968a1b9b6aSSam Leffler ic->ic_set_tim(ic, ni, 0); /* just in case */ 25978a1b9b6aSSam Leffler return; 25988a1b9b6aSSam Leffler } 25998a1b9b6aSSam Leffler /* 26008a1b9b6aSSam Leffler * If there are more packets, set the more packets bit 26018a1b9b6aSSam Leffler * in the packet dispatched to the station; otherwise 26028a1b9b6aSSam Leffler * turn off the TIM bit. 26038a1b9b6aSSam Leffler */ 26048a1b9b6aSSam Leffler if (qlen != 0) { 26058a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 26068a1b9b6aSSam Leffler "[%s] recv ps-poll, send packet, %u still queued\n", 26078a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), qlen); 26088a1b9b6aSSam Leffler wh = mtod(m, struct ieee80211_frame_min *); 26098a1b9b6aSSam Leffler wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; 26108a1b9b6aSSam Leffler } else { 26118a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 26128a1b9b6aSSam Leffler "[%s] recv ps-poll, send packet, queue empty\n", 26138a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr)); 26148a1b9b6aSSam Leffler ic->ic_set_tim(ic, ni, 0); 26158a1b9b6aSSam Leffler } 26168a1b9b6aSSam Leffler m->m_flags |= M_PWR_SAV; /* bypass PS handling */ 26178a1b9b6aSSam Leffler IF_ENQUEUE(&ic->ic_ifp->if_snd, m); 26188a1b9b6aSSam Leffler } 26198a1b9b6aSSam Leffler 26208a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 26218a1b9b6aSSam Leffler /* 26228a1b9b6aSSam Leffler * Debugging support. 26238a1b9b6aSSam Leffler */ 26248a1b9b6aSSam Leffler 26258a1b9b6aSSam Leffler /* 26268a1b9b6aSSam Leffler * Return the bssid of a frame. 26278a1b9b6aSSam Leffler */ 26288a1b9b6aSSam Leffler static const u_int8_t * 26298a1b9b6aSSam Leffler ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh) 26308a1b9b6aSSam Leffler { 26318a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) 26328a1b9b6aSSam Leffler return wh->i_addr2; 26338a1b9b6aSSam Leffler if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS) 26348a1b9b6aSSam Leffler return wh->i_addr1; 26358a1b9b6aSSam Leffler if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL) 26368a1b9b6aSSam Leffler return wh->i_addr1; 26378a1b9b6aSSam Leffler return wh->i_addr3; 26388a1b9b6aSSam Leffler } 26398a1b9b6aSSam Leffler 26408a1b9b6aSSam Leffler static void 26418a1b9b6aSSam Leffler ieee80211_discard_frame(struct ieee80211com *ic, 26428a1b9b6aSSam Leffler const struct ieee80211_frame *wh, 26438a1b9b6aSSam Leffler const char *type, const char *fmt, ...) 26448a1b9b6aSSam Leffler { 26458a1b9b6aSSam Leffler va_list ap; 26468a1b9b6aSSam Leffler 26478a1b9b6aSSam Leffler printf("[%s] discard ", ether_sprintf(ieee80211_getbssid(ic, wh))); 26488a1b9b6aSSam Leffler if (type != NULL) 26498a1b9b6aSSam Leffler printf(" %s frame, ", type); 26508a1b9b6aSSam Leffler else 26518a1b9b6aSSam Leffler printf(" frame, "); 26528a1b9b6aSSam Leffler va_start(ap, fmt); 26538a1b9b6aSSam Leffler vprintf(fmt, ap); 26548a1b9b6aSSam Leffler va_end(ap); 26558a1b9b6aSSam Leffler printf("\n"); 26568a1b9b6aSSam Leffler } 26578a1b9b6aSSam Leffler 26588a1b9b6aSSam Leffler static void 26598a1b9b6aSSam Leffler ieee80211_discard_ie(struct ieee80211com *ic, 26608a1b9b6aSSam Leffler const struct ieee80211_frame *wh, 26618a1b9b6aSSam Leffler const char *type, const char *fmt, ...) 26628a1b9b6aSSam Leffler { 26638a1b9b6aSSam Leffler va_list ap; 26648a1b9b6aSSam Leffler 26658a1b9b6aSSam Leffler printf("[%s] discard ", ether_sprintf(ieee80211_getbssid(ic, wh))); 26668a1b9b6aSSam Leffler if (type != NULL) 26678a1b9b6aSSam Leffler printf(" %s information element, ", type); 26688a1b9b6aSSam Leffler else 26698a1b9b6aSSam Leffler printf(" information element, "); 26708a1b9b6aSSam Leffler va_start(ap, fmt); 26718a1b9b6aSSam Leffler vprintf(fmt, ap); 26728a1b9b6aSSam Leffler va_end(ap); 26738a1b9b6aSSam Leffler printf("\n"); 26748a1b9b6aSSam Leffler } 26758a1b9b6aSSam Leffler 26768a1b9b6aSSam Leffler static void 26778a1b9b6aSSam Leffler ieee80211_discard_mac(struct ieee80211com *ic, 26788a1b9b6aSSam Leffler const u_int8_t mac[IEEE80211_ADDR_LEN], 26798a1b9b6aSSam Leffler const char *type, const char *fmt, ...) 26808a1b9b6aSSam Leffler { 26818a1b9b6aSSam Leffler va_list ap; 26828a1b9b6aSSam Leffler 26838a1b9b6aSSam Leffler printf("[%s] discard ", ether_sprintf(mac)); 26848a1b9b6aSSam Leffler if (type != NULL) 26858a1b9b6aSSam Leffler printf(" %s frame, ", type); 26868a1b9b6aSSam Leffler else 26878a1b9b6aSSam Leffler printf(" frame, "); 26888a1b9b6aSSam Leffler va_start(ap, fmt); 26898a1b9b6aSSam Leffler vprintf(fmt, ap); 26908a1b9b6aSSam Leffler va_end(ap); 26918a1b9b6aSSam Leffler printf("\n"); 26928a1b9b6aSSam Leffler } 26938a1b9b6aSSam Leffler #endif /* IEEE80211_DEBUG */ 2694