xref: /netbsd-src/sys/net80211/ieee80211_input.c (revision b471b82f348640e201feffc9011b09972848cc7e)
1*b471b82fSyamt /*	$NetBSD: ieee80211_input.c,v 1.117 2022/11/19 07:57:51 yamt Exp $	*/
26d2a143aSmaxv 
36d2a143aSmaxv /*
440e261aaSdyoung  * Copyright (c) 2001 Atsushi Onoe
590634029Sdyoung  * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
640e261aaSdyoung  * All rights reserved.
740e261aaSdyoung  *
840e261aaSdyoung  * Redistribution and use in source and binary forms, with or without
940e261aaSdyoung  * modification, are permitted provided that the following conditions
1040e261aaSdyoung  * are met:
1140e261aaSdyoung  * 1. Redistributions of source code must retain the above copyright
1240e261aaSdyoung  *    notice, this list of conditions and the following disclaimer.
1340e261aaSdyoung  * 2. Redistributions in binary form must reproduce the above copyright
1440e261aaSdyoung  *    notice, this list of conditions and the following disclaimer in the
1540e261aaSdyoung  *    documentation and/or other materials provided with the distribution.
1640e261aaSdyoung  * 3. The name of the author may not be used to endorse or promote products
1740e261aaSdyoung  *    derived from this software without specific prior written permission.
1840e261aaSdyoung  *
1940e261aaSdyoung  * Alternatively, this software may be distributed under the terms of the
2040e261aaSdyoung  * GNU General Public License ("GPL") version 2 as published by the Free
2140e261aaSdyoung  * Software Foundation.
2240e261aaSdyoung  *
2340e261aaSdyoung  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2440e261aaSdyoung  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2540e261aaSdyoung  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2640e261aaSdyoung  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2740e261aaSdyoung  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2840e261aaSdyoung  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2940e261aaSdyoung  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3040e261aaSdyoung  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3140e261aaSdyoung  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3240e261aaSdyoung  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3340e261aaSdyoung  */
3440e261aaSdyoung 
3540e261aaSdyoung #include <sys/cdefs.h>
366e8d0b71Sdyoung #ifdef __FreeBSD__
3787515e34Sskrll __FBSDID("$FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.81 2005/08/10 16:22:29 sam Exp $");
3890634029Sdyoung #endif
3990634029Sdyoung #ifdef __NetBSD__
40*b471b82fSyamt __KERNEL_RCSID(0, "$NetBSD: ieee80211_input.c,v 1.117 2022/11/19 07:57:51 yamt Exp $");
416e8d0b71Sdyoung #endif
4240e261aaSdyoung 
431c4a50f1Spooka #ifdef _KERNEL_OPT
4440e261aaSdyoung #include "opt_inet.h"
451c4a50f1Spooka #endif
4640e261aaSdyoung 
4740e261aaSdyoung #include <sys/param.h>
4840e261aaSdyoung #include <sys/systm.h>
4940e261aaSdyoung #include <sys/mbuf.h>
5040e261aaSdyoung #include <sys/malloc.h>
5190634029Sdyoung #include <sys/endian.h>
5240e261aaSdyoung #include <sys/kernel.h>
5390634029Sdyoung 
5440e261aaSdyoung #include <sys/socket.h>
5540e261aaSdyoung #include <sys/sockio.h>
5640e261aaSdyoung #include <sys/endian.h>
5740e261aaSdyoung #include <sys/errno.h>
5840e261aaSdyoung #include <sys/proc.h>
5940e261aaSdyoung #include <sys/sysctl.h>
605ffb0503Snonaka #include <sys/cpu.h>
6140e261aaSdyoung 
6240e261aaSdyoung #include <net/if.h>
6340e261aaSdyoung #include <net/if_media.h>
6440e261aaSdyoung #include <net/if_arp.h>
65c059ea00Sdyoung #include <net/if_ether.h>
6640e261aaSdyoung #include <net/if_llc.h>
6740e261aaSdyoung 
6840e261aaSdyoung #include <net80211/ieee80211_var.h>
6940e261aaSdyoung 
7040e261aaSdyoung #include <net/bpf.h>
7140e261aaSdyoung 
7240e261aaSdyoung #ifdef INET
7340e261aaSdyoung #include <netinet/in.h>
74c059ea00Sdyoung #include <net/if_ether.h>
75c059ea00Sdyoung #endif
7640e261aaSdyoung 
77ce488d2bSdyoung const struct timeval ieee80211_merge_print_intvl = {.tv_sec = 1, .tv_usec = 0};
78ce488d2bSdyoung 
79a02b46e8Smycroft #ifdef IEEE80211_DEBUG
8090634029Sdyoung 
81a02b46e8Smycroft /*
82a02b46e8Smycroft  * Decide if a received management frame should be
83a02b46e8Smycroft  * printed when debugging is enabled.  This filters some
84a02b46e8Smycroft  * of the less interesting frames that come frequently
85a02b46e8Smycroft  * (e.g. beacons).
86a02b46e8Smycroft  */
87a02b46e8Smycroft static __inline int
doprint(struct ieee80211com * ic,int subtype)88a02b46e8Smycroft doprint(struct ieee80211com *ic, int subtype)
89a02b46e8Smycroft {
90a02b46e8Smycroft 	switch (subtype) {
91a02b46e8Smycroft 	case IEEE80211_FC0_SUBTYPE_BEACON:
9290634029Sdyoung 		return (ic->ic_flags & IEEE80211_F_SCAN);
93a02b46e8Smycroft 	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
94a02b46e8Smycroft 		return (ic->ic_opmode == IEEE80211_M_IBSS);
95a02b46e8Smycroft 	}
96a02b46e8Smycroft 	return 1;
97a02b46e8Smycroft }
9890634029Sdyoung 
9990634029Sdyoung /*
10090634029Sdyoung  * Emit a debug message about discarding a frame or information
10190634029Sdyoung  * element.  One format is for extracting the mac address from
10290634029Sdyoung  * the frame header; the other is for when a header is not
10390634029Sdyoung  * available or otherwise appropriate.
10490634029Sdyoung  */
10590634029Sdyoung #define	IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) do {		\
10690634029Sdyoung 	if ((_ic)->ic_debug & (_m))					\
10790634029Sdyoung 		ieee80211_discard_frame(_ic, _wh, _type, _fmt, __VA_ARGS__);\
10890634029Sdyoung } while (0)
10990634029Sdyoung #define	IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) do {	\
11090634029Sdyoung 	if ((_ic)->ic_debug & (_m))					\
11190634029Sdyoung 		ieee80211_discard_ie(_ic, _wh, _type, _fmt, __VA_ARGS__);\
11290634029Sdyoung } while (0)
11390634029Sdyoung #define	IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) do {	\
11490634029Sdyoung 	if ((_ic)->ic_debug & (_m))					\
11590634029Sdyoung 		ieee80211_discard_mac(_ic, _mac, _type, _fmt, __VA_ARGS__);\
11690634029Sdyoung } while (0)
117c2ec5838Schristos #define	IEEE80211_DEBUGVAR(a) a
11890634029Sdyoung 
11990634029Sdyoung static const u_int8_t *ieee80211_getbssid(struct ieee80211com *,
12090634029Sdyoung 	const struct ieee80211_frame *);
12190634029Sdyoung static void ieee80211_discard_frame(struct ieee80211com *,
12290634029Sdyoung 	const struct ieee80211_frame *, const char *type, const char *fmt, ...);
12390634029Sdyoung static void ieee80211_discard_ie(struct ieee80211com *,
12490634029Sdyoung 	const struct ieee80211_frame *, const char *type, const char *fmt, ...);
12590634029Sdyoung static void ieee80211_discard_mac(struct ieee80211com *,
12690634029Sdyoung 	const u_int8_t mac[IEEE80211_ADDR_LEN], const char *type,
12790634029Sdyoung 	const char *fmt, ...);
12890634029Sdyoung #else
12990634029Sdyoung #define	IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...)
13090634029Sdyoung #define	IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...)
13190634029Sdyoung #define	IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...)
132c2ec5838Schristos #define	IEEE80211_DEBUGVAR(a)
13390634029Sdyoung #endif /* IEEE80211_DEBUG */
13490634029Sdyoung 
1351f639d06Smaxv static struct mbuf *ieee80211_defrag(struct ieee80211_node *,
1361f639d06Smaxv     struct mbuf *, int);
1371f639d06Smaxv static struct mbuf *ieee80211_decap(struct mbuf *, int);
13880b91eeaSdyoung static void ieee80211_send_error(struct ieee80211com *, struct ieee80211_node *,
13980b91eeaSdyoung 	const u_int8_t *mac, int subtype, int arg);
14087515e34Sskrll static void ieee80211_deliver_data(struct ieee80211com *,
14187515e34Sskrll 	struct ieee80211_node *, struct mbuf *);
14225e9e914Sdyoung #ifndef IEEE80211_NO_HOSTAP
14390634029Sdyoung static void ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
14490634029Sdyoung static void ieee80211_recv_pspoll(struct ieee80211com *,
14590634029Sdyoung 	struct ieee80211_node *, struct mbuf *);
14674988b0fSdyoung #endif /* !IEEE80211_NO_HOSTAP */
1477b6a25edSdyoung static void ieee80211_update_adhoc_node(struct ieee80211com *,
1487b6a25edSdyoung     struct ieee80211_node *, struct ieee80211_frame *,
1497b6a25edSdyoung     struct ieee80211_scanparams *, int, u_int32_t);
150a02b46e8Smycroft 
1511151deddSmaxv /* -------------------------------------------------------------------------- */
1521151deddSmaxv 
15340e261aaSdyoung /*
1541151deddSmaxv  * Input code for a DATA frame.
15540e261aaSdyoung  */
1561151deddSmaxv static int
ieee80211_input_data(struct ieee80211com * ic,struct mbuf ** mp,struct ieee80211_node * ni)1571151deddSmaxv ieee80211_input_data(struct ieee80211com *ic, struct mbuf **mp,
1581151deddSmaxv     struct ieee80211_node *ni)
15940e261aaSdyoung {
16090634029Sdyoung 	struct ifnet *ifp = ic->ic_ifp;
16190634029Sdyoung 	struct ieee80211_key *key;
1621151deddSmaxv 	struct ieee80211_frame *wh;
1631151deddSmaxv 	u_int8_t dir, subtype;
16440e261aaSdyoung 	struct ether_header *eh;
1651151deddSmaxv 	struct mbuf *m = *mp;
16680b91eeaSdyoung 	int hdrspace;
16740e261aaSdyoung 
16840e261aaSdyoung 	wh = mtod(m, struct ieee80211_frame *);
16940e261aaSdyoung 	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
17090634029Sdyoung 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
1716d2a143aSmaxv 
17280b91eeaSdyoung 	hdrspace = ieee80211_hdrspace(ic, wh);
1731151deddSmaxv 
17480b91eeaSdyoung 	if (m->m_len < hdrspace &&
17580b91eeaSdyoung 	    (m = m_pullup(m, hdrspace)) == NULL) {
17690634029Sdyoung 		ic->ic_stats.is_rx_tooshort++;
17701f382e3Smaxv 		goto out;
17890634029Sdyoung 	}
179e63bca16Smaxv 	wh = mtod(m, struct ieee80211_frame *);
180e63bca16Smaxv 
18140e261aaSdyoung 	switch (ic->ic_opmode) {
18240e261aaSdyoung 	case IEEE80211_M_STA:
1839280f4b4Sdyoung 		if (dir != IEEE80211_FC1_DIR_FROMDS) {
18487515e34Sskrll 			IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
185*b471b82fSyamt 			    wh, "data", "unknown dir 0x%x", dir);
1869280f4b4Sdyoung 			ic->ic_stats.is_rx_wrongdir++;
18740e261aaSdyoung 			goto out;
1889280f4b4Sdyoung 		}
18940e261aaSdyoung 		if ((ifp->if_flags & IFF_SIMPLEX) &&
19040e261aaSdyoung 		    IEEE80211_IS_MULTICAST(wh->i_addr1) &&
19140e261aaSdyoung 		    IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) {
19240e261aaSdyoung 			/*
19340e261aaSdyoung 			 * In IEEE802.11 network, multicast packet
1946d2a143aSmaxv 			 * sent from me is broadcast from AP.
19540e261aaSdyoung 			 * It should be silently discarded for
19640e261aaSdyoung 			 * SIMPLEX interface.
19740e261aaSdyoung 			 */
19890634029Sdyoung 			IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
19990634029Sdyoung 			    wh, NULL, "%s", "multicast echo");
2009280f4b4Sdyoung 			ic->ic_stats.is_rx_mcastecho++;
20140e261aaSdyoung 			goto out;
20240e261aaSdyoung 		}
20340e261aaSdyoung 		break;
2046d2a143aSmaxv 
20540e261aaSdyoung 	case IEEE80211_M_IBSS:
20640e261aaSdyoung 	case IEEE80211_M_AHDEMO:
2079280f4b4Sdyoung 		if (dir != IEEE80211_FC1_DIR_NODS) {
20887515e34Sskrll 			IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
209*b471b82fSyamt 			    wh, "data", "unknown dir 0x%x", dir);
2109280f4b4Sdyoung 			ic->ic_stats.is_rx_wrongdir++;
21140e261aaSdyoung 			goto out;
2129280f4b4Sdyoung 		}
21390634029Sdyoung 		/* XXX no power-save support */
21440e261aaSdyoung 		break;
2156d2a143aSmaxv 
21640e261aaSdyoung 	case IEEE80211_M_HOSTAP:
21774988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
2189280f4b4Sdyoung 		if (dir != IEEE80211_FC1_DIR_TODS) {
21987515e34Sskrll 			IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
220*b471b82fSyamt 			    wh, "data", "unknown dir 0x%x", dir);
2219280f4b4Sdyoung 			ic->ic_stats.is_rx_wrongdir++;
22240e261aaSdyoung 			goto out;
2239280f4b4Sdyoung 		}
22440e261aaSdyoung 		/* check if source STA is associated */
22540e261aaSdyoung 		if (ni == ic->ic_bss) {
22690634029Sdyoung 			IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
22790634029Sdyoung 			    wh, "data", "%s", "unknown src");
22880b91eeaSdyoung 			ieee80211_send_error(ic, ni, wh->i_addr2,
22940e261aaSdyoung 			    IEEE80211_FC0_SUBTYPE_DEAUTH,
23040e261aaSdyoung 			    IEEE80211_REASON_NOT_AUTHED);
2319280f4b4Sdyoung 			ic->ic_stats.is_rx_notassoc++;
23240e261aaSdyoung 			goto err;
23340e261aaSdyoung 		}
23440e261aaSdyoung 		if (ni->ni_associd == 0) {
23590634029Sdyoung 			IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
23690634029Sdyoung 			    wh, "data", "%s", "unassoc src");
23740e261aaSdyoung 			IEEE80211_SEND_MGMT(ic, ni,
23840e261aaSdyoung 			    IEEE80211_FC0_SUBTYPE_DISASSOC,
23940e261aaSdyoung 			    IEEE80211_REASON_NOT_ASSOCED);
2409280f4b4Sdyoung 			ic->ic_stats.is_rx_notassoc++;
24140e261aaSdyoung 			goto err;
24240e261aaSdyoung 		}
24390634029Sdyoung 
24490634029Sdyoung 		/*
24590634029Sdyoung 		 * Check for power save state change.
24690634029Sdyoung 		 */
24790634029Sdyoung 		if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
24890634029Sdyoung 		    (ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
24990634029Sdyoung 			ieee80211_node_pwrsave(ni,
25090634029Sdyoung 				wh->i_fc[1] & IEEE80211_FC1_PWR_MGT);
25174988b0fSdyoung #endif /* !IEEE80211_NO_HOSTAP */
25240e261aaSdyoung 		break;
2536d2a143aSmaxv 
25490634029Sdyoung 	default:
25590634029Sdyoung 		/* XXX here to keep compiler happy */
25690634029Sdyoung 		goto out;
25740e261aaSdyoung 	}
25890634029Sdyoung 
25990634029Sdyoung 	/*
26090634029Sdyoung 	 * Handle privacy requirements.  Note that we
26190634029Sdyoung 	 * must not be preempted from here until after
26290634029Sdyoung 	 * we (potentially) call ieee80211_crypto_demic;
26390634029Sdyoung 	 * otherwise we may violate assumptions in the
26490634029Sdyoung 	 * crypto cipher modules used to do delayed update
26590634029Sdyoung 	 * of replay sequence numbers.
26690634029Sdyoung 	 */
26740e261aaSdyoung 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
26890634029Sdyoung 		if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
26990634029Sdyoung 			/*
27090634029Sdyoung 			 * Discard encrypted frames when privacy is off.
27190634029Sdyoung 			 */
27290634029Sdyoung 			IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
27390634029Sdyoung 			    wh, "WEP", "%s", "PRIVACY off");
27490634029Sdyoung 			ic->ic_stats.is_rx_noprivacy++;
27590634029Sdyoung 			IEEE80211_NODE_STAT(ni, rx_noprivacy);
27690634029Sdyoung 			goto out;
27790634029Sdyoung 		}
2780b0687d8Smaxv 		key = ieee80211_crypto_decap(ic, ni, &m, hdrspace);
27990634029Sdyoung 		if (key == NULL) {
28090634029Sdyoung 			/* NB: stats+msgs handled in crypto_decap */
28190634029Sdyoung 			IEEE80211_NODE_STAT(ni, rx_wepfail);
28290634029Sdyoung 			goto out;
2839280f4b4Sdyoung 		}
28440e261aaSdyoung 		wh = mtod(m, struct ieee80211_frame *);
28590634029Sdyoung 		wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
2869280f4b4Sdyoung 	} else {
28790634029Sdyoung 		key = NULL;
28890634029Sdyoung 	}
28990634029Sdyoung 
29090634029Sdyoung 	/*
29190634029Sdyoung 	 * Next up, any fragmentation.
29290634029Sdyoung 	 */
29390634029Sdyoung 	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
2941f639d06Smaxv 		m = ieee80211_defrag(ni, m, hdrspace);
29590634029Sdyoung 		if (m == NULL) {
29690634029Sdyoung 			/* Fragment dropped or frame not complete yet */
29740e261aaSdyoung 			goto out;
29840e261aaSdyoung 		}
2999280f4b4Sdyoung 	}
30090634029Sdyoung 
30190634029Sdyoung 	/*
30201f382e3Smaxv 	 * Next, strip any MSDU crypto bits.
30390634029Sdyoung 	 */
30480b91eeaSdyoung 	if (key != NULL && !ieee80211_crypto_demic(ic, key, m, 0)) {
30590634029Sdyoung 		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
30690634029Sdyoung 		    ni->ni_macaddr, "data", "%s", "demic error");
30790634029Sdyoung 		IEEE80211_NODE_STAT(ni, rx_demicfail);
30890634029Sdyoung 		goto out;
30990634029Sdyoung 	}
31090634029Sdyoung 
31140e261aaSdyoung 	/* copy to listener after decrypt */
3123cd62456Smsaitoh 	bpf_mtap3(ic->ic_rawbpf, m, BPF_D_IN);
31390634029Sdyoung 
31490634029Sdyoung 	/*
31590634029Sdyoung 	 * Finally, strip the 802.11 header.
31690634029Sdyoung 	 */
3171f639d06Smaxv 	m = ieee80211_decap(m, hdrspace);
3184232da0fSdyoung 	if (m == NULL) {
31990634029Sdyoung 		/* don't count Null data frames as errors */
32090634029Sdyoung 		if (subtype == IEEE80211_FC0_SUBTYPE_NODATA)
32190634029Sdyoung 			goto out;
32290634029Sdyoung 		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
32390634029Sdyoung 		    ni->ni_macaddr, "data", "%s", "decap error");
3249280f4b4Sdyoung 		ic->ic_stats.is_rx_decap++;
32590634029Sdyoung 		IEEE80211_NODE_STAT(ni, rx_decap);
32640e261aaSdyoung 		goto err;
3274232da0fSdyoung 	}
3286d2a143aSmaxv 
32990634029Sdyoung 	eh = mtod(m, struct ether_header *);
33090634029Sdyoung 	if (!ieee80211_node_is_authorized(ni)) {
33190634029Sdyoung 		/*
33290634029Sdyoung 		 * Deny any non-PAE frames received prior to
33390634029Sdyoung 		 * authorization.  For open/shared-key
33490634029Sdyoung 		 * authentication the port is mark authorized
33590634029Sdyoung 		 * after authentication completes.  For 802.1x
33690634029Sdyoung 		 * the port is not marked authorized by the
33790634029Sdyoung 		 * authenticator until the handshake has completed.
33890634029Sdyoung 		 */
33990634029Sdyoung 		if (eh->ether_type != htons(ETHERTYPE_PAE)) {
34090634029Sdyoung 			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
34190634029Sdyoung 			    eh->ether_shost, "data",
34290634029Sdyoung 			    "unauthorized port: ether type 0x%x len %u",
34390634029Sdyoung 			    eh->ether_type, m->m_pkthdr.len);
34490634029Sdyoung 			ic->ic_stats.is_rx_unauth++;
34590634029Sdyoung 			IEEE80211_NODE_STAT(ni, rx_unauth);
34690634029Sdyoung 			goto err;
34790634029Sdyoung 		}
34890634029Sdyoung 	} else {
34990634029Sdyoung 		/*
35090634029Sdyoung 		 * When denying unencrypted frames, discard
35190634029Sdyoung 		 * any non-PAE frames received without encryption.
35290634029Sdyoung 		 */
35390634029Sdyoung 		if ((ic->ic_flags & IEEE80211_F_DROPUNENC) &&
354a45976c2Smaxv 		    key == NULL && eh->ether_type != htons(ETHERTYPE_PAE)) {
35590634029Sdyoung 			/*
35690634029Sdyoung 			 * Drop unencrypted frames.
35790634029Sdyoung 			 */
35890634029Sdyoung 			ic->ic_stats.is_rx_unencrypted++;
35990634029Sdyoung 			IEEE80211_NODE_STAT(ni, rx_unencrypted);
36090634029Sdyoung 			goto out;
36190634029Sdyoung 		}
36290634029Sdyoung 	}
3636d2a143aSmaxv 
3641a2cab17Sthorpej 	if_statinc(ifp, if_ipackets);
36590634029Sdyoung 	IEEE80211_NODE_STAT(ni, rx_data);
36690634029Sdyoung 	IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
36740e261aaSdyoung 
36887515e34Sskrll 	ieee80211_deliver_data(ic, ni, m);
36940e261aaSdyoung 
3701151deddSmaxv 	*mp = NULL;
3711151deddSmaxv 	return 0;
3721151deddSmaxv 
3731151deddSmaxv err:
3741a2cab17Sthorpej 	if_statinc(ifp, if_ierrors);
3751151deddSmaxv out:
3761151deddSmaxv 	*mp = m;
3771151deddSmaxv 	return -1;
3781151deddSmaxv }
3791151deddSmaxv 
3801151deddSmaxv /*
3811151deddSmaxv  * Input code for a MANAGEMENT frame.
3821151deddSmaxv  */
3831151deddSmaxv static int
ieee80211_input_management(struct ieee80211com * ic,struct mbuf ** mp,struct ieee80211_node * ni,int rssi,u_int32_t rstamp)3841151deddSmaxv ieee80211_input_management(struct ieee80211com *ic, struct mbuf **mp,
3851151deddSmaxv     struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
3861151deddSmaxv {
3871151deddSmaxv 	IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
3881151deddSmaxv 	struct ifnet *ifp = ic->ic_ifp;
3891151deddSmaxv 	struct ieee80211_key *key;
3901151deddSmaxv 	struct ieee80211_frame *wh;
3911151deddSmaxv 	u_int8_t dir, subtype;
3921151deddSmaxv 	struct mbuf *m = *mp;
3931151deddSmaxv 	int hdrspace;
3941151deddSmaxv 
3951151deddSmaxv 	wh = mtod(m, struct ieee80211_frame *);
3961151deddSmaxv 	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
3971151deddSmaxv 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3981151deddSmaxv 
39990634029Sdyoung 	IEEE80211_NODE_STAT(ni, rx_mgmt);
4009280f4b4Sdyoung 	if (dir != IEEE80211_FC1_DIR_NODS) {
40187515e34Sskrll 		IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
402*b471b82fSyamt 		    wh, "data", "unknown dir 0x%x", dir);
4039280f4b4Sdyoung 		ic->ic_stats.is_rx_wrongdir++;
40440e261aaSdyoung 		goto err;
4059280f4b4Sdyoung 	}
406a45976c2Smaxv 	if (m->m_len < sizeof(struct ieee80211_frame)) {
407a45976c2Smaxv 		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, ni->ni_macaddr,
408a45976c2Smaxv 		    "mgt", "too short: len %u", m->m_len);
40990634029Sdyoung 		ic->ic_stats.is_rx_tooshort++;
41040e261aaSdyoung 		goto out;
4119280f4b4Sdyoung 	}
41240e261aaSdyoung #ifdef IEEE80211_DEBUG
413a02b46e8Smycroft 	if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) ||
414a02b46e8Smycroft 	    ieee80211_msg_dumppkts(ic)) {
41590634029Sdyoung 		if_printf(ic->ic_ifp, "received %s from %s rssi %d\n",
41690634029Sdyoung 		    ieee80211_mgt_subtype_name[subtype >>
41790634029Sdyoung 			IEEE80211_FC0_SUBTYPE_SHIFT],
418c2ec5838Schristos 		    ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
419c2ec5838Schristos 		    rssi);
42040e261aaSdyoung 	}
421a02b46e8Smycroft #endif
4221151deddSmaxv 
42390634029Sdyoung 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
42490634029Sdyoung 		if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
42590634029Sdyoung 			/*
42690634029Sdyoung 			 * Only shared key auth frames with a challenge
42790634029Sdyoung 			 * should be encrypted, discard all others.
42890634029Sdyoung 			 */
42990634029Sdyoung 			IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
43090634029Sdyoung 			    wh, ieee80211_mgt_subtype_name[subtype >>
43190634029Sdyoung 				IEEE80211_FC0_SUBTYPE_SHIFT],
43290634029Sdyoung 			    "%s", "WEP set but not permitted");
43390634029Sdyoung 			ic->ic_stats.is_rx_mgtdiscard++; /* XXX */
43490634029Sdyoung 			goto out;
43590634029Sdyoung 		}
43690634029Sdyoung 		if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
43790634029Sdyoung 			/*
43890634029Sdyoung 			 * Discard encrypted frames when privacy is off.
43990634029Sdyoung 			 */
44090634029Sdyoung 			IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
44190634029Sdyoung 			    wh, "mgt", "%s", "WEP set but PRIVACY off");
44290634029Sdyoung 			ic->ic_stats.is_rx_noprivacy++;
44390634029Sdyoung 			goto out;
44490634029Sdyoung 		}
44580b91eeaSdyoung 		hdrspace = ieee80211_hdrspace(ic, wh);
4460b0687d8Smaxv 		key = ieee80211_crypto_decap(ic, ni, &m, hdrspace);
44790634029Sdyoung 		if (key == NULL) {
44890634029Sdyoung 			/* NB: stats+msgs handled in crypto_decap */
44990634029Sdyoung 			goto out;
45090634029Sdyoung 		}
45190634029Sdyoung 		wh = mtod(m, struct ieee80211_frame *);
45290634029Sdyoung 		wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
45390634029Sdyoung 	}
4546d2a143aSmaxv 
4553cd62456Smsaitoh 	bpf_mtap3(ic->ic_rawbpf, m, BPF_D_IN);
45640e261aaSdyoung 	(*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp);
45740e261aaSdyoung 	m_freem(m);
45840e261aaSdyoung 
4591151deddSmaxv 	*mp = NULL;
4601151deddSmaxv 	return 0;
4611151deddSmaxv 
4621151deddSmaxv err:
4631a2cab17Sthorpej 	if_statinc(ifp, if_ierrors);
4641151deddSmaxv out:
4651151deddSmaxv 	*mp = m;
4661151deddSmaxv 	return -1;
4671151deddSmaxv }
4681151deddSmaxv 
4691151deddSmaxv /*
4701151deddSmaxv  * Input code for a CONTROL frame.
4711151deddSmaxv  */
4721151deddSmaxv static void
ieee80211_input_control(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni)4731151deddSmaxv ieee80211_input_control(struct ieee80211com *ic, struct mbuf *m,
4741151deddSmaxv     struct ieee80211_node *ni)
4751151deddSmaxv {
47690634029Sdyoung 	IEEE80211_NODE_STAT(ni, rx_ctrl);
4779280f4b4Sdyoung 	ic->ic_stats.is_rx_ctl++;
4781151deddSmaxv 
47974988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
48090634029Sdyoung 	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
4811151deddSmaxv 		struct ieee80211_frame *wh;
4821151deddSmaxv 		u_int8_t subtype;
4831151deddSmaxv 
4841151deddSmaxv 		wh = mtod(m, struct ieee80211_frame *);
4851151deddSmaxv 		subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
4861151deddSmaxv 
48790634029Sdyoung 		switch (subtype) {
48890634029Sdyoung 		case IEEE80211_FC0_SUBTYPE_PS_POLL:
48990634029Sdyoung 			ieee80211_recv_pspoll(ic, ni, m);
49090634029Sdyoung 			break;
49190634029Sdyoung 		}
492b8e804e8Sdyoung 	}
4931151deddSmaxv #endif
4941151deddSmaxv }
4951151deddSmaxv 
4961151deddSmaxv /* -------------------------------------------------------------------------- */
4971151deddSmaxv 
4981151deddSmaxv /*
4991151deddSmaxv  * Process a received frame.  The node associated with the sender
5001151deddSmaxv  * should be supplied.  If nothing was found in the node table then
5011151deddSmaxv  * the caller is assumed to supply a reference to ic_bss instead.
5021151deddSmaxv  * The RSSI and a timestamp are also supplied.  The RSSI data is used
5031151deddSmaxv  * during AP scanning to select a AP to associate with; it can have
5041151deddSmaxv  * any units so long as values have consistent units and higher values
5051151deddSmaxv  * mean ``better signal''.  The receive timestamp is currently not used
5061151deddSmaxv  * by the 802.11 layer.
5071151deddSmaxv  */
5081151deddSmaxv int
ieee80211_input(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni,int rssi,u_int32_t rstamp)5091151deddSmaxv ieee80211_input(struct ieee80211com *ic, struct mbuf *m,
5101151deddSmaxv 	struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
5111151deddSmaxv {
5121151deddSmaxv #define	SEQ_LEQ(a,b)	((int)((a)-(b)) <= 0)
5131151deddSmaxv #define	HAS_SEQ(type)	((type & 0x4) == 0)
5141151deddSmaxv 	struct ifnet *ifp = ic->ic_ifp;
5151151deddSmaxv 	struct ieee80211_frame *wh;
5161151deddSmaxv 	u_int8_t dir, type;
5171151deddSmaxv 	u_int16_t rxseq;
5181151deddSmaxv 	IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
5191151deddSmaxv 	int ret;
5201151deddSmaxv 
5211151deddSmaxv 	KASSERT(!cpu_intr_p());
5221151deddSmaxv 
5231151deddSmaxv 	IASSERT(ni != NULL, ("null node"));
5241151deddSmaxv 	ni->ni_inact = ni->ni_inact_reload;
5251151deddSmaxv 
5261151deddSmaxv 	/* trim CRC here so WEP can find its own CRC at the end of packet. */
5271151deddSmaxv 	if (m->m_flags & M_HASFCS) {
5281151deddSmaxv 		m_adj(m, -IEEE80211_CRC_LEN);
5291151deddSmaxv 		m->m_flags &= ~M_HASFCS;
5301151deddSmaxv 	}
5311151deddSmaxv 	type = -1;			/* undefined */
5321151deddSmaxv 
5331151deddSmaxv 	/*
5341151deddSmaxv 	 * In monitor mode, send everything directly to bpf.
5351151deddSmaxv 	 * XXX may want to include the CRC
5361151deddSmaxv 	 */
5371151deddSmaxv 	if (ic->ic_opmode == IEEE80211_M_MONITOR)
538b8e804e8Sdyoung 		goto out;
5391151deddSmaxv 
540a45976c2Smaxv 	if (m->m_len < sizeof(struct ieee80211_frame_min)) {
5411151deddSmaxv 		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
5421151deddSmaxv 		    ni->ni_macaddr, NULL,
543a45976c2Smaxv 		    "too short (1): len %u", m->m_len);
5441151deddSmaxv 		ic->ic_stats.is_rx_tooshort++;
5451151deddSmaxv 		goto out;
5461151deddSmaxv 	}
5471151deddSmaxv 
5481151deddSmaxv 	/*
5491151deddSmaxv 	 * Bit of a cheat here, we use a pointer for a 3-address
5501151deddSmaxv 	 * frame format but don't reference fields past outside
5511151deddSmaxv 	 * ieee80211_frame_min w/o first validating the data is
5521151deddSmaxv 	 * present.
5531151deddSmaxv 	 */
5541151deddSmaxv 	wh = mtod(m, struct ieee80211_frame *);
5551151deddSmaxv 
5561151deddSmaxv 	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
5571151deddSmaxv 	    IEEE80211_FC0_VERSION_0) {
5581151deddSmaxv 		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
5591151deddSmaxv 		    ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
5601151deddSmaxv 		ic->ic_stats.is_rx_badversion++;
5611151deddSmaxv 		goto err;
5621151deddSmaxv 	}
5631151deddSmaxv 
5641151deddSmaxv 	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
5651151deddSmaxv 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
5661151deddSmaxv 
5671151deddSmaxv 	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
5681151deddSmaxv 		u_int8_t *bssid;
5691151deddSmaxv 
5701151deddSmaxv 		switch (ic->ic_opmode) {
5711151deddSmaxv 		case IEEE80211_M_STA:
5721151deddSmaxv 			bssid = wh->i_addr2;
5731151deddSmaxv 			if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
5741151deddSmaxv 				/* not interested in */
5751151deddSmaxv 				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
5761151deddSmaxv 				    bssid, NULL, "node %s, %s",
5771151deddSmaxv 				    ether_snprintf(ebuf, sizeof(ebuf),
5781151deddSmaxv 				    ni->ni_bssid), "not to bss");
5791151deddSmaxv 				ic->ic_stats.is_rx_wrongbss++;
5801151deddSmaxv 				goto out;
5811151deddSmaxv 			}
5821151deddSmaxv 
5831151deddSmaxv 			/*
5841151deddSmaxv 			 * Filter out packets not directed to us in case the
5851151deddSmaxv 			 * device is in promiscuous mode
5861151deddSmaxv 			 */
5871151deddSmaxv 			if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
5881151deddSmaxv 			    !IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
5891151deddSmaxv 				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
5901151deddSmaxv 				    bssid, NULL, "not to cur sta: lladdr=%6D, addr1=%6D",
5911151deddSmaxv 				    ic->ic_myaddr, ":", wh->i_addr1, ":");
5921151deddSmaxv 				ic->ic_stats.is_rx_wrongbss++;
5931151deddSmaxv 				goto out;
5941151deddSmaxv 			}
5951151deddSmaxv 			break;
5961151deddSmaxv 
5971151deddSmaxv 		case IEEE80211_M_IBSS:
5981151deddSmaxv 		case IEEE80211_M_AHDEMO:
5991151deddSmaxv 		case IEEE80211_M_HOSTAP:
6001151deddSmaxv 			if (dir != IEEE80211_FC1_DIR_NODS)
6011151deddSmaxv 				bssid = wh->i_addr1;
6021151deddSmaxv 			else if (type == IEEE80211_FC0_TYPE_CTL)
6031151deddSmaxv 				bssid = wh->i_addr1;
6041151deddSmaxv 			else {
605a45976c2Smaxv 				if (m->m_len < sizeof(struct ieee80211_frame)) {
6061151deddSmaxv 					IEEE80211_DISCARD_MAC(ic,
6071151deddSmaxv 					    IEEE80211_MSG_ANY, ni->ni_macaddr,
6081151deddSmaxv 					    NULL, "too short (2): len %u",
609a45976c2Smaxv 					    m->m_len);
6101151deddSmaxv 					ic->ic_stats.is_rx_tooshort++;
6111151deddSmaxv 					goto out;
6121151deddSmaxv 				}
6131151deddSmaxv 				bssid = wh->i_addr3;
6141151deddSmaxv 			}
6151151deddSmaxv 			if (type != IEEE80211_FC0_TYPE_DATA)
6161151deddSmaxv 				break;
6171151deddSmaxv 
6181151deddSmaxv 			/*
6191151deddSmaxv 			 * Data frame, validate the bssid.
6201151deddSmaxv 			 */
6211151deddSmaxv 			if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) &&
6221151deddSmaxv 			    !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
6231151deddSmaxv 				/* not interested in */
6241151deddSmaxv 				IEEE80211_DEBUGVAR(
6251151deddSmaxv 				    char bbuf[3 * ETHER_ADDR_LEN]);
6261151deddSmaxv 				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
6271151deddSmaxv 				    bssid, NULL, "bss %s, broadcast %s, %s",
6281151deddSmaxv 				    ether_snprintf(ebuf, sizeof(ebuf),
6291151deddSmaxv 				    ic->ic_bss->ni_bssid),
6301151deddSmaxv 				    ether_snprintf(bbuf, sizeof(bbuf),
6311151deddSmaxv 				    ifp->if_broadcastaddr), "not to bss");
6321151deddSmaxv 				ic->ic_stats.is_rx_wrongbss++;
6331151deddSmaxv 				goto out;
6341151deddSmaxv 			}
6351151deddSmaxv 
6361151deddSmaxv 			/*
6371151deddSmaxv 			 * For adhoc mode we cons up a node when it doesn't
6381151deddSmaxv 			 * exist. This should probably be done after an ACL
6391151deddSmaxv 			 * check.
6401151deddSmaxv 			 */
6411151deddSmaxv 			if (ni == ic->ic_bss &&
6421151deddSmaxv 			    ic->ic_opmode != IEEE80211_M_HOSTAP &&
6431151deddSmaxv 			    !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
6441151deddSmaxv 				/*
6451151deddSmaxv 				 * Fake up a node for this newly
6461151deddSmaxv 				 * discovered member of the IBSS.
6471151deddSmaxv 				 */
6481151deddSmaxv 				ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
6491151deddSmaxv 				    wh->i_addr2);
6501151deddSmaxv 				if (ni == NULL) {
6511151deddSmaxv 					/* NB: stat kept for alloc failure */
6521151deddSmaxv 					goto err;
6531151deddSmaxv 				}
6541151deddSmaxv 			}
6551151deddSmaxv 			break;
6561151deddSmaxv 
6571151deddSmaxv 		default:
6581151deddSmaxv 			goto out;
6591151deddSmaxv 		}
6601151deddSmaxv 
6611151deddSmaxv 		ni->ni_rssi = rssi;
6621151deddSmaxv 		ni->ni_rstamp = rstamp;
6631151deddSmaxv 
6641151deddSmaxv 		if (HAS_SEQ(type) && (ic->ic_opmode != IEEE80211_M_STA ||
6651151deddSmaxv 		    !IEEE80211_IS_MULTICAST(wh->i_addr1))) {
6661151deddSmaxv 			u_int8_t tid, retry;
6671151deddSmaxv 			u_int16_t rxno, orxno;
6681151deddSmaxv 
6691151deddSmaxv 			if (ieee80211_has_qos(wh)) {
6701151deddSmaxv 				struct ieee80211_qosframe *qosf;
6711151deddSmaxv 
672a45976c2Smaxv 				if (m->m_len < sizeof(struct ieee80211_qosframe)) {
673a45976c2Smaxv 					IEEE80211_DISCARD_MAC(ic,
674a45976c2Smaxv 					    IEEE80211_MSG_ANY,
675a45976c2Smaxv 					    ni->ni_macaddr, NULL,
676a45976c2Smaxv 					    "too short (1): len %u", m->m_len);
677a45976c2Smaxv 					ic->ic_stats.is_rx_tooshort++;
678a45976c2Smaxv 					goto out;
679a45976c2Smaxv 				}
6801151deddSmaxv 				qosf = mtod(m, struct ieee80211_qosframe *);
6811151deddSmaxv 
6821151deddSmaxv 				tid = qosf->i_qos[0] & IEEE80211_QOS_TID;
6831151deddSmaxv 				if (TID_TO_WME_AC(tid) >= WME_AC_VI)
6841151deddSmaxv 					ic->ic_wme.wme_hipri_traffic++;
6851151deddSmaxv 				tid++;
6861151deddSmaxv 			} else {
6871151deddSmaxv 				tid = 0;
6881151deddSmaxv 			}
6891151deddSmaxv 
6901151deddSmaxv 			rxseq = le16toh(*(u_int16_t *)wh->i_seq);
6911151deddSmaxv 			retry = wh->i_fc[1] & IEEE80211_FC1_RETRY;
6921151deddSmaxv 			rxno = rxseq >> IEEE80211_SEQ_SEQ_SHIFT;
6931151deddSmaxv 			orxno = ni->ni_rxseqs[tid] >> IEEE80211_SEQ_SEQ_SHIFT;
6941151deddSmaxv 
6951151deddSmaxv 			if (retry && (
6961151deddSmaxv 			    (orxno == 4095 && rxno == orxno) ||
6971151deddSmaxv 			    (orxno != 4095 &&
6981151deddSmaxv 			     SEQ_LEQ(rxseq, ni->ni_rxseqs[tid]))
6991151deddSmaxv 			    )) {
7001151deddSmaxv 				/* duplicate, discard */
7011151deddSmaxv 				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
7021151deddSmaxv 				    bssid, "duplicate",
7031151deddSmaxv 				    "seqno <%u,%u> fragno <%u,%u> tid %u",
7041151deddSmaxv 				    rxno,
7051151deddSmaxv 				    orxno,
7061151deddSmaxv 				    rxseq & IEEE80211_SEQ_FRAG_MASK,
7071151deddSmaxv 				    ni->ni_rxseqs[tid] &
7081151deddSmaxv 					IEEE80211_SEQ_FRAG_MASK,
7091151deddSmaxv 				    tid);
7101151deddSmaxv 				ic->ic_stats.is_rx_dup++;
7111151deddSmaxv 				IEEE80211_NODE_STAT(ni, rx_dup);
7121151deddSmaxv 				goto out;
7131151deddSmaxv 			}
7141151deddSmaxv 			ni->ni_rxseqs[tid] = rxseq;
7151151deddSmaxv 		}
7161151deddSmaxv 	}
7171151deddSmaxv 
7181151deddSmaxv 	switch (type) {
7191151deddSmaxv 	case IEEE80211_FC0_TYPE_DATA:
7201151deddSmaxv 		ret = ieee80211_input_data(ic, &m, ni);
7211151deddSmaxv 		if (ret == -1) {
7221151deddSmaxv 			goto out;
7231151deddSmaxv 		}
7241151deddSmaxv 		return IEEE80211_FC0_TYPE_DATA;
7251151deddSmaxv 
7261151deddSmaxv 	case IEEE80211_FC0_TYPE_MGT:
7271151deddSmaxv 		ret = ieee80211_input_management(ic, &m, ni, rssi, rstamp);
7281151deddSmaxv 		if (ret == -1) {
7291151deddSmaxv 			goto out;
7301151deddSmaxv 		}
7311151deddSmaxv 		return IEEE80211_FC0_TYPE_MGT;
7321151deddSmaxv 
7331151deddSmaxv 	case IEEE80211_FC0_TYPE_CTL:
7341151deddSmaxv 		ieee80211_input_control(ic, m, ni);
7351151deddSmaxv 		goto out;
7361151deddSmaxv 
73740e261aaSdyoung 	default:
73890634029Sdyoung 		IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
73990634029Sdyoung 		    wh, NULL, "bad frame type 0x%x", type);
74040e261aaSdyoung 		/* should not come here */
74140e261aaSdyoung 		break;
74240e261aaSdyoung 	}
7436d2a143aSmaxv 
74440e261aaSdyoung err:
7451a2cab17Sthorpej 	if_statinc(ifp, if_ierrors);
7466d2a143aSmaxv 
74740e261aaSdyoung out:
74840e261aaSdyoung 	if (m != NULL) {
7493cd62456Smsaitoh 		bpf_mtap3(ic->ic_rawbpf, m, BPF_D_IN);
75040e261aaSdyoung 		m_freem(m);
75140e261aaSdyoung 	}
75290634029Sdyoung 	return type;
75390634029Sdyoung #undef SEQ_LEQ
75440e261aaSdyoung }
75540e261aaSdyoung 
75690634029Sdyoung /*
7576d2a143aSmaxv  * This function reassembles fragments.
75890634029Sdyoung  */
75990634029Sdyoung static struct mbuf *
ieee80211_defrag(struct ieee80211_node * ni,struct mbuf * m,int hdrspace)7601f639d06Smaxv ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace)
76140e261aaSdyoung {
76290634029Sdyoung 	struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
76390634029Sdyoung 	struct ieee80211_frame *lwh;
7640a9aff6fSmaxv 	u_int16_t rxseq, iseq;
76590634029Sdyoung 	u_int8_t fragno;
76601f382e3Smaxv 	const u_int8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG;
76790634029Sdyoung 	struct mbuf *mfrag;
76890634029Sdyoung 
76990634029Sdyoung 	IASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?"));
77090634029Sdyoung 
7710a9aff6fSmaxv 	iseq = *(u_int16_t *)wh->i_seq;
7720a9aff6fSmaxv 	rxseq = le16toh(iseq);
77390634029Sdyoung 	fragno = rxseq & IEEE80211_SEQ_FRAG_MASK;
77490634029Sdyoung 
77590634029Sdyoung 	/* Quick way out, if there's nothing to defragment */
77690634029Sdyoung 	if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL)
77790634029Sdyoung 		return m;
77890634029Sdyoung 
77990634029Sdyoung 	/*
78090634029Sdyoung 	 * Remove frag to insure it doesn't get reaped by timer.
78190634029Sdyoung 	 */
78290634029Sdyoung 	if (ni->ni_table == NULL) {
78390634029Sdyoung 		/*
78490634029Sdyoung 		 * Should never happen.  If the node is orphaned (not in
78590634029Sdyoung 		 * the table) then input packets should not reach here.
78690634029Sdyoung 		 * Otherwise, a concurrent request that yanks the table
78790634029Sdyoung 		 * should be blocked by other interlocking and/or by first
78890634029Sdyoung 		 * shutting the driver down.  Regardless, be defensive
78990634029Sdyoung 		 * here and just bail
79090634029Sdyoung 		 */
79190634029Sdyoung 		/* XXX need msg+stat */
79290634029Sdyoung 		m_freem(m);
79390634029Sdyoung 		return NULL;
79490634029Sdyoung 	}
79590634029Sdyoung 	IEEE80211_NODE_LOCK(ni->ni_table);
79690634029Sdyoung 	mfrag = ni->ni_rxfrag[0];
79790634029Sdyoung 	ni->ni_rxfrag[0] = NULL;
79890634029Sdyoung 	IEEE80211_NODE_UNLOCK(ni->ni_table);
79990634029Sdyoung 
80090634029Sdyoung 	/*
80190634029Sdyoung 	 * Validate new fragment is in order and
80290634029Sdyoung 	 * related to the previous ones.
80390634029Sdyoung 	 */
80490634029Sdyoung 	if (mfrag != NULL) {
80590634029Sdyoung 		u_int16_t last_rxseq;
80690634029Sdyoung 
80790634029Sdyoung 		lwh = mtod(mfrag, struct ieee80211_frame *);
80890634029Sdyoung 		last_rxseq = le16toh(*(u_int16_t *)lwh->i_seq);
80990634029Sdyoung 		/* NB: check seq # and frag together */
81090634029Sdyoung 		if (rxseq != last_rxseq+1 ||
81190634029Sdyoung 		    !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) ||
81290634029Sdyoung 		    !IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) {
81390634029Sdyoung 			/*
81490634029Sdyoung 			 * Unrelated fragment or no space for it,
81590634029Sdyoung 			 * clear current fragments.
81690634029Sdyoung 			 */
81790634029Sdyoung 			m_freem(mfrag);
81890634029Sdyoung 			mfrag = NULL;
81990634029Sdyoung 		}
82090634029Sdyoung 	}
82190634029Sdyoung 
82290634029Sdyoung 	if (mfrag == NULL) {
82390634029Sdyoung 		if (fragno != 0) {		/* !first fragment, discard */
82490634029Sdyoung 			IEEE80211_NODE_STAT(ni, rx_defrag);
82590634029Sdyoung 			m_freem(m);
82690634029Sdyoung 			return NULL;
82790634029Sdyoung 		}
82890634029Sdyoung 		mfrag = m;
82901f382e3Smaxv 	} else {
8300a9aff6fSmaxv 		int mlen;
8310a9aff6fSmaxv 
83201f382e3Smaxv 		/* Strip header and concatenate */
83301f382e3Smaxv 		m_adj(m, hdrspace);
8340a9aff6fSmaxv 		mlen = m->m_pkthdr.len;
83590634029Sdyoung 		m_cat(mfrag, m);
83601f382e3Smaxv 
83790634029Sdyoung 		/* NB: m_cat doesn't update the packet header */
8380a9aff6fSmaxv 		mfrag->m_pkthdr.len += mlen;
83901f382e3Smaxv 
84090634029Sdyoung 		/* track last seqnum and fragno */
84190634029Sdyoung 		lwh = mtod(mfrag, struct ieee80211_frame *);
8420a9aff6fSmaxv 		*(u_int16_t *)lwh->i_seq = iseq;
84390634029Sdyoung 	}
84401f382e3Smaxv 
84501f382e3Smaxv 	if (more_frag) {
84601f382e3Smaxv 		/* more to come, save */
84790634029Sdyoung 		ni->ni_rxfragstamp = ticks;
84890634029Sdyoung 		ni->ni_rxfrag[0] = mfrag;
84990634029Sdyoung 		mfrag = NULL;
85090634029Sdyoung 	}
85101f382e3Smaxv 
85290634029Sdyoung 	return mfrag;
85390634029Sdyoung }
85490634029Sdyoung 
85587515e34Sskrll static void
ieee80211_deliver_data(struct ieee80211com * ic,struct ieee80211_node * ni,struct mbuf * m)85687515e34Sskrll ieee80211_deliver_data(struct ieee80211com *ic,
85787515e34Sskrll 	struct ieee80211_node *ni, struct mbuf *m)
85887515e34Sskrll {
85987515e34Sskrll 	struct ether_header *eh = mtod(m, struct ether_header *);
86087515e34Sskrll 	struct ifnet *ifp = ic->ic_ifp;
86121268b39Sjoerg 	int error;
86287515e34Sskrll 
86387515e34Sskrll 	/* perform as a bridge within the AP */
86487515e34Sskrll 	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
86587515e34Sskrll 	    (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) {
86687515e34Sskrll 		struct mbuf *m1 = NULL;
86787515e34Sskrll 
86887515e34Sskrll 		if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
86987515e34Sskrll 			m1 = m_copypacket(m, M_DONTWAIT);
87087515e34Sskrll 			if (m1 == NULL)
8711a2cab17Sthorpej 				if_statinc(ifp, if_oerrors);
87287515e34Sskrll 			else
87387515e34Sskrll 				m1->m_flags |= M_MCAST;
87487515e34Sskrll 		} else {
87587515e34Sskrll 			/*
87687515e34Sskrll 			 * Check if the destination is known; if so
87787515e34Sskrll 			 * and the port is authorized dispatch directly.
87887515e34Sskrll 			 */
87987515e34Sskrll 			struct ieee80211_node *sta =
88087515e34Sskrll 			    ieee80211_find_node(&ic->ic_sta, eh->ether_dhost);
88187515e34Sskrll 			if (sta != NULL) {
88287515e34Sskrll 				if (ieee80211_node_is_authorized(sta)) {
88387515e34Sskrll 					/*
88487515e34Sskrll 					 * Beware of sending to ourself; this
88587515e34Sskrll 					 * needs to happen via the normal
88687515e34Sskrll 					 * input path.
88787515e34Sskrll 					 */
88887515e34Sskrll 					if (sta != ic->ic_bss) {
88987515e34Sskrll 						m1 = m;
89087515e34Sskrll 						m = NULL;
89187515e34Sskrll 					}
89287515e34Sskrll 				} else {
89387515e34Sskrll 					ic->ic_stats.is_rx_unauth++;
89487515e34Sskrll 					IEEE80211_NODE_STAT(sta, rx_unauth);
89587515e34Sskrll 				}
89687515e34Sskrll 				ieee80211_free_node(sta);
89787515e34Sskrll 			}
89887515e34Sskrll 		}
899bea24f0eSmaxv 
90087515e34Sskrll 		if (m1 != NULL) {
90187515e34Sskrll 			int len;
90287515e34Sskrll #ifdef ALTQ
90387515e34Sskrll 			if (ALTQ_IS_ENABLED(&ifp->if_snd)) {
90440b1061cSknakahara 				altq_etherclassify(&ifp->if_snd, m1);
90587515e34Sskrll 			}
90687515e34Sskrll #endif
90787515e34Sskrll 			len = m1->m_pkthdr.len;
908b76ec0b0Sknakahara 			IFQ_ENQUEUE(&ifp->if_snd, m1, error);
90921268b39Sjoerg 			if (error) {
9101a2cab17Sthorpej 				if_statinc(ifp, if_oerrors);
911bea24f0eSmaxv 				m_freem(m);
91221268b39Sjoerg 				m = NULL;
91321268b39Sjoerg 			}
9141a2cab17Sthorpej 			if_statadd(ifp, if_obytes, len);
91587515e34Sskrll 		}
91687515e34Sskrll 	}
91787515e34Sskrll 
918bea24f0eSmaxv 	if (m != NULL) {
919a6e88d78Sknakahara 		if (ni->ni_vlan != 0)
920a6e88d78Sknakahara 			vlan_set_tag(m, ni->ni_vlan);
9219c4cd063Sozaki-r 
9229c4cd063Sozaki-r 		/*
9239c4cd063Sozaki-r 		 * XXX once ieee80211_input (or rxintr itself) runs in softint
9249c4cd063Sozaki-r 		 * we have to change here too to use if_input.
9259c4cd063Sozaki-r 		 */
9269c4cd063Sozaki-r 		KASSERT(ifp->if_percpuq);
9279c4cd063Sozaki-r 		if_percpuq_enqueue(ifp->if_percpuq, m);
92887515e34Sskrll 	}
929bea24f0eSmaxv 
93087515e34Sskrll 	return;
93187515e34Sskrll }
93287515e34Sskrll 
93390634029Sdyoung static struct mbuf *
ieee80211_decap(struct mbuf * m,int hdrlen)9341f639d06Smaxv ieee80211_decap(struct mbuf *m, int hdrlen)
93590634029Sdyoung {
93680b91eeaSdyoung 	struct ieee80211_qosframe_addr4 wh; /* Max size address frames */
93740e261aaSdyoung 	struct ether_header *eh;
93840e261aaSdyoung 	struct llc *llc;
93940e261aaSdyoung 
94080b91eeaSdyoung 	if (m->m_len < hdrlen + sizeof(*llc) &&
94180b91eeaSdyoung 	    (m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) {
94240e261aaSdyoung 		return NULL;
94340e261aaSdyoung 	}
94401f382e3Smaxv 
94553524e44Schristos 	memcpy(&wh, mtod(m, void *), hdrlen);
94601f382e3Smaxv 
94753524e44Schristos 	llc = (struct llc *)(mtod(m, char *) + hdrlen);
94801f382e3Smaxv 	if (llc->llc_dsap == LLC_SNAP_LSAP &&
94901f382e3Smaxv 	    llc->llc_ssap == LLC_SNAP_LSAP &&
95001f382e3Smaxv 	    llc->llc_control == LLC_UI &&
95101f382e3Smaxv 	    llc->llc_snap.org_code[0] == 0 &&
95201f382e3Smaxv 	    llc->llc_snap.org_code[1] == 0 &&
95301f382e3Smaxv 	    llc->llc_snap.org_code[2] == 0) {
95480b91eeaSdyoung 		m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh));
95540e261aaSdyoung 		llc = NULL;
95640e261aaSdyoung 	} else {
95701f382e3Smaxv 		/* Keep the LLC after the Ethernet header. */
95880b91eeaSdyoung 		m_adj(m, hdrlen - sizeof(*eh));
95940e261aaSdyoung 	}
96001f382e3Smaxv 
96140e261aaSdyoung 	eh = mtod(m, struct ether_header *);
96201f382e3Smaxv 
96340e261aaSdyoung 	switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) {
96440e261aaSdyoung 	case IEEE80211_FC1_DIR_NODS:
96540e261aaSdyoung 		IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1);
96640e261aaSdyoung 		IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2);
96740e261aaSdyoung 		break;
96840e261aaSdyoung 	case IEEE80211_FC1_DIR_TODS:
96940e261aaSdyoung 		IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3);
97040e261aaSdyoung 		IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2);
97140e261aaSdyoung 		break;
97240e261aaSdyoung 	case IEEE80211_FC1_DIR_FROMDS:
97340e261aaSdyoung 		IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1);
97440e261aaSdyoung 		IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3);
97540e261aaSdyoung 		break;
97640e261aaSdyoung 	case IEEE80211_FC1_DIR_DSTODS:
97780b91eeaSdyoung 		IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3);
97880b91eeaSdyoung 		IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr4);
97980b91eeaSdyoung 		break;
98040e261aaSdyoung 	}
98101f382e3Smaxv 
98240e261aaSdyoung #ifdef ALIGNED_POINTER
983fffc9c66Schristos 	if (!ALIGNED_POINTER(mtod(m, char *) + sizeof(*eh), u_int32_t)) {
98440e261aaSdyoung 		struct mbuf *n, *n0, **np;
98553524e44Schristos 		char *newdata;
98640e261aaSdyoung 		int off, pktlen;
98740e261aaSdyoung 
98840e261aaSdyoung 		n0 = NULL;
98940e261aaSdyoung 		np = &n0;
99040e261aaSdyoung 		off = 0;
99140e261aaSdyoung 		pktlen = m->m_pkthdr.len;
99240e261aaSdyoung 		while (pktlen > off) {
99340e261aaSdyoung 			if (n0 == NULL) {
99440e261aaSdyoung 				MGETHDR(n, M_DONTWAIT, MT_DATA);
99540e261aaSdyoung 				if (n == NULL) {
99640e261aaSdyoung 					m_freem(m);
99740e261aaSdyoung 					return NULL;
99840e261aaSdyoung 				}
999b1305a6dSmaxv 				m_move_pkthdr(n, m);
100040e261aaSdyoung 				n->m_len = MHLEN;
100140e261aaSdyoung 			} else {
100240e261aaSdyoung 				MGET(n, M_DONTWAIT, MT_DATA);
100340e261aaSdyoung 				if (n == NULL) {
100440e261aaSdyoung 					m_freem(m);
100540e261aaSdyoung 					m_freem(n0);
100640e261aaSdyoung 					return NULL;
100740e261aaSdyoung 				}
100840e261aaSdyoung 				n->m_len = MLEN;
100940e261aaSdyoung 			}
101040e261aaSdyoung 			if (pktlen - off >= MINCLSIZE) {
101140e261aaSdyoung 				MCLGET(n, M_DONTWAIT);
101240e261aaSdyoung 				if (n->m_flags & M_EXT)
101340e261aaSdyoung 					n->m_len = n->m_ext.ext_size;
101440e261aaSdyoung 			}
101540e261aaSdyoung 			if (n0 == NULL) {
101640e261aaSdyoung 				newdata =
101753524e44Schristos 				    (char *)ALIGN(n->m_data + sizeof(*eh)) -
101840e261aaSdyoung 				    sizeof(*eh);
101940e261aaSdyoung 				n->m_len -= newdata - n->m_data;
102040e261aaSdyoung 				n->m_data = newdata;
102140e261aaSdyoung 			}
102240e261aaSdyoung 			if (n->m_len > pktlen - off)
102340e261aaSdyoung 				n->m_len = pktlen - off;
102453524e44Schristos 			m_copydata(m, off, n->m_len, mtod(n, void *));
102540e261aaSdyoung 			off += n->m_len;
102640e261aaSdyoung 			*np = n;
102740e261aaSdyoung 			np = &n->m_next;
102840e261aaSdyoung 		}
102940e261aaSdyoung 		m_freem(m);
103040e261aaSdyoung 		m = n0;
103140e261aaSdyoung 	}
103240e261aaSdyoung #endif /* ALIGNED_POINTER */
103301f382e3Smaxv 
103440e261aaSdyoung 	if (llc != NULL) {
103540e261aaSdyoung 		eh = mtod(m, struct ether_header *);
103640e261aaSdyoung 		eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
103740e261aaSdyoung 	}
103801f382e3Smaxv 
103940e261aaSdyoung 	return m;
104040e261aaSdyoung }
104140e261aaSdyoung 
104240e261aaSdyoung /*
104340e261aaSdyoung  * Install received rate set information in the node's state block.
104440e261aaSdyoung  */
104587515e34Sskrll int
ieee80211_setup_rates(struct ieee80211_node * ni,const u_int8_t * rates,const u_int8_t * xrates,int flags)104611a42b5cSmaxv ieee80211_setup_rates(struct ieee80211_node *ni, const u_int8_t *rates,
104711a42b5cSmaxv     const u_int8_t *xrates, int flags)
104840e261aaSdyoung {
104987515e34Sskrll 	struct ieee80211com *ic = ni->ni_ic;
105040e261aaSdyoung 	struct ieee80211_rateset *rs = &ni->ni_rates;
105140e261aaSdyoung 
105240e261aaSdyoung 	memset(rs, 0, sizeof(*rs));
105311a42b5cSmaxv 
105440e261aaSdyoung 	rs->rs_nrates = rates[1];
105540e261aaSdyoung 	memcpy(rs->rs_rates, rates + 2, rs->rs_nrates);
105611a42b5cSmaxv 
105740e261aaSdyoung 	if (xrates != NULL) {
105840e261aaSdyoung 		u_int8_t nxrates;
105911a42b5cSmaxv 		size_t totalrate;
106011a42b5cSmaxv 
106140e261aaSdyoung 		/*
106240e261aaSdyoung 		 * Tack on 11g extended supported rate element.
106340e261aaSdyoung 		 */
106440e261aaSdyoung 		nxrates = xrates[1];
106511a42b5cSmaxv 		totalrate = (size_t)rs->rs_nrates + (size_t)nxrates;
106611a42b5cSmaxv 
106711a42b5cSmaxv 		if (totalrate > IEEE80211_RATE_MAXSIZE) {
1068c2ec5838Schristos 			IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
106940e261aaSdyoung 			nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates;
1070f0546001Smycroft 			IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE,
107190634029Sdyoung 			     "[%s] extended rate set too large;"
107240e261aaSdyoung 			     " only using %u of %u rates\n",
1073c2ec5838Schristos 			     ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr),
1074c2ec5838Schristos 			     nxrates, xrates[1]);
10759280f4b4Sdyoung 			ic->ic_stats.is_rx_rstoobig++;
107640e261aaSdyoung 		}
107711a42b5cSmaxv 
107840e261aaSdyoung 		memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
107940e261aaSdyoung 		rs->rs_nrates += nxrates;
108040e261aaSdyoung 	}
108111a42b5cSmaxv 
108287515e34Sskrll 	return ieee80211_fix_rate(ni, flags);
108340e261aaSdyoung }
108440e261aaSdyoung 
1085db0e43b2Sdyoung static void
ieee80211_auth_open(struct ieee80211com * ic,struct ieee80211_frame * wh,struct ieee80211_node * ni,int rssi,u_int32_t rstamp,u_int16_t seq,u_int16_t status)1086db0e43b2Sdyoung ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh,
1087168cd830Schristos     struct ieee80211_node *ni, int rssi, u_int32_t rstamp,
10884d595fd7Schristos     u_int16_t seq, u_int16_t status)
1089db0e43b2Sdyoung {
1090c2ec5838Schristos 	IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
109190634029Sdyoung 
109280b91eeaSdyoung 	if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
109380b91eeaSdyoung 		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
109480b91eeaSdyoung 		    ni->ni_macaddr, "open auth",
109580b91eeaSdyoung 		    "bad sta auth mode %u", ni->ni_authmode);
109680b91eeaSdyoung 		ic->ic_stats.is_rx_bad_auth++;	/* XXX */
1097a45976c2Smaxv 
109880b91eeaSdyoung 		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
109980b91eeaSdyoung 			/* XXX hack to workaround calling convention */
110080b91eeaSdyoung 			ieee80211_send_error(ic, ni, wh->i_addr2,
110180b91eeaSdyoung 			    IEEE80211_FC0_SUBTYPE_AUTH,
110280b91eeaSdyoung 			    (seq + 1) | (IEEE80211_STATUS_ALG<<16));
110380b91eeaSdyoung 		}
1104db0e43b2Sdyoung 		return;
11059280f4b4Sdyoung 	}
1106a45976c2Smaxv 
110780b91eeaSdyoung 	switch (ic->ic_opmode) {
110880b91eeaSdyoung 	case IEEE80211_M_IBSS:
1109db0e43b2Sdyoung 	case IEEE80211_M_AHDEMO:
111080b91eeaSdyoung 	case IEEE80211_M_MONITOR:
1111db0e43b2Sdyoung 		/* should not come here */
111280b91eeaSdyoung 		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
111380b91eeaSdyoung 		    ni->ni_macaddr, "open auth",
111480b91eeaSdyoung 		    "bad operating mode %u", ic->ic_opmode);
1115db0e43b2Sdyoung 		break;
1116db0e43b2Sdyoung 
1117db0e43b2Sdyoung 	case IEEE80211_M_HOSTAP:
111874988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
11193ebaf267Smycroft 		if (ic->ic_state != IEEE80211_S_RUN ||
11209280f4b4Sdyoung 		    seq != IEEE80211_AUTH_OPEN_REQUEST) {
11219280f4b4Sdyoung 			ic->ic_stats.is_rx_bad_auth++;
1122db0e43b2Sdyoung 			return;
11239280f4b4Sdyoung 		}
1124a45976c2Smaxv 
112590634029Sdyoung 		/* always accept open authentication requests */
1126db0e43b2Sdyoung 		if (ni == ic->ic_bss) {
112790634029Sdyoung 			ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
112890634029Sdyoung 			if (ni == NULL)
1129db0e43b2Sdyoung 				return;
1130a45976c2Smaxv 		} else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) {
113190634029Sdyoung 			(void)ieee80211_ref_node(ni);
1132a45976c2Smaxv 		}
1133a45976c2Smaxv 
113480b91eeaSdyoung 		/*
1135f0a7346dSsnj 		 * Mark the node as referenced to reflect that its
113680b91eeaSdyoung 		 * reference count has been bumped to insure it remains
113780b91eeaSdyoung 		 * after the transaction completes.
113880b91eeaSdyoung 		 */
113980b91eeaSdyoung 		ni->ni_flags |= IEEE80211_NODE_AREF;
114080b91eeaSdyoung 
1141a45976c2Smaxv 		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH,
1142a45976c2Smaxv 		    seq + 1);
1143a02b46e8Smycroft 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
114490634029Sdyoung 		    "[%s] station authenticated (open)\n",
1145c2ec5838Schristos 		    ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr));
1146a45976c2Smaxv 
114780b91eeaSdyoung 		/*
114880b91eeaSdyoung 		 * When 802.1x is not in use mark the port
114980b91eeaSdyoung 		 * authorized at this point so traffic can flow.
115080b91eeaSdyoung 		 */
115180b91eeaSdyoung 		if (ni->ni_authmode != IEEE80211_AUTH_8021X)
115287515e34Sskrll 			ieee80211_node_authorize(ni);
115374988b0fSdyoung #endif /* !IEEE80211_NO_HOSTAP */
1154db0e43b2Sdyoung 		break;
1155a02b46e8Smycroft 
1156db0e43b2Sdyoung 	case IEEE80211_M_STA:
1157db0e43b2Sdyoung 		if (ic->ic_state != IEEE80211_S_AUTH ||
11589280f4b4Sdyoung 		    seq != IEEE80211_AUTH_OPEN_RESPONSE) {
11599280f4b4Sdyoung 			ic->ic_stats.is_rx_bad_auth++;
1160db0e43b2Sdyoung 			return;
11619280f4b4Sdyoung 		}
1162db0e43b2Sdyoung 		if (status != 0) {
1163a02b46e8Smycroft 			IEEE80211_DPRINTF(ic,
1164a02b46e8Smycroft 			    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
116590634029Sdyoung 			    "[%s] open auth failed (reason %d)\n",
1166c2ec5838Schristos 			    ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr),
1167c2ec5838Schristos 			    status);
1168a45976c2Smaxv 
116990634029Sdyoung 			/* XXX can this happen? */
1170db0e43b2Sdyoung 			if (ni != ic->ic_bss)
1171db0e43b2Sdyoung 				ni->ni_fails++;
1172a45976c2Smaxv 
11739280f4b4Sdyoung 			ic->ic_stats.is_rx_auth_fail++;
117480b91eeaSdyoung 			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
1175a45976c2Smaxv 		} else {
1176db0e43b2Sdyoung 			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
1177db0e43b2Sdyoung 			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
1178a45976c2Smaxv 		}
1179db0e43b2Sdyoung 		break;
1180db0e43b2Sdyoung 	}
1181db0e43b2Sdyoung }
1182db0e43b2Sdyoung 
118380b91eeaSdyoung /*
118480b91eeaSdyoung  * Send a management frame error response to the specified
118580b91eeaSdyoung  * station.  If ni is associated with the station then use
118680b91eeaSdyoung  * it; otherwise allocate a temporary node suitable for
118780b91eeaSdyoung  * transmitting the frame and then free the reference so
118880b91eeaSdyoung  * it will go away as soon as the frame has been transmitted.
118980b91eeaSdyoung  */
119080b91eeaSdyoung static void
ieee80211_send_error(struct ieee80211com * ic,struct ieee80211_node * ni,const u_int8_t * mac,int subtype,int arg)119180b91eeaSdyoung ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni,
119280b91eeaSdyoung     const u_int8_t *mac, int subtype, int arg)
119380b91eeaSdyoung {
1194a45976c2Smaxv 	bool istmp;
119580b91eeaSdyoung 
119680b91eeaSdyoung 	if (ni == ic->ic_bss) {
119787515e34Sskrll 		ni = ieee80211_tmp_node(ic, mac);
119880b91eeaSdyoung 		if (ni == NULL) {
119980b91eeaSdyoung 			/* XXX msg */
120080b91eeaSdyoung 			return;
120180b91eeaSdyoung 		}
1202a45976c2Smaxv 		istmp = true;
1203a45976c2Smaxv 	} else {
1204a45976c2Smaxv 		istmp = false;
1205a45976c2Smaxv 	}
1206a45976c2Smaxv 
120780b91eeaSdyoung 	IEEE80211_SEND_MGMT(ic, ni, subtype, arg);
120880b91eeaSdyoung 	if (istmp)
120980b91eeaSdyoung 		ieee80211_free_node(ni);
121080b91eeaSdyoung }
121180b91eeaSdyoung 
121290634029Sdyoung static int
alloc_challenge(struct ieee80211com * ic,struct ieee80211_node * ni)121390634029Sdyoung alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni)
121490634029Sdyoung {
1215a45976c2Smaxv 	if (ni->ni_challenge == NULL) {
12169b87d582Scegger 		ni->ni_challenge = malloc(IEEE80211_CHALLENGE_LEN,
121790634029Sdyoung 		    M_DEVBUF, M_NOWAIT);
1218a45976c2Smaxv 	}
121990634029Sdyoung 	if (ni->ni_challenge == NULL) {
1220c2ec5838Schristos 		IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
1221c2ec5838Schristos 
122290634029Sdyoung 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
122390634029Sdyoung 		    "[%s] shared key challenge alloc failed\n",
1224c2ec5838Schristos 		    ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr));
122590634029Sdyoung 		/* XXX statistic */
122690634029Sdyoung 	}
122790634029Sdyoung 	return (ni->ni_challenge != NULL);
122890634029Sdyoung }
122990634029Sdyoung 
123090634029Sdyoung /* XXX TODO: add statistics */
1231db0e43b2Sdyoung static void
ieee80211_auth_shared(struct ieee80211com * ic,struct ieee80211_frame * wh,u_int8_t * frm,u_int8_t * efrm,struct ieee80211_node * ni,int rssi,u_int32_t rstamp,u_int16_t seq,u_int16_t status)1232db0e43b2Sdyoung ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh,
1233db0e43b2Sdyoung     u_int8_t *frm, u_int8_t *efrm, struct ieee80211_node *ni, int rssi,
1234db0e43b2Sdyoung     u_int32_t rstamp, u_int16_t seq, u_int16_t status)
1235db0e43b2Sdyoung {
123690634029Sdyoung 	u_int8_t *challenge;
123774988b0fSdyoung 	int estatus;
1238c2ec5838Schristos 	IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
1239db0e43b2Sdyoung 
124090634029Sdyoung 	/*
124190634029Sdyoung 	 * NB: this can happen as we allow pre-shared key
124290634029Sdyoung 	 * authentication to be enabled w/o wep being turned
124390634029Sdyoung 	 * on so that configuration of these can be done
124490634029Sdyoung 	 * in any order.  It may be better to enforce the
124590634029Sdyoung 	 * ordering in which case this check would just be
124690634029Sdyoung 	 * for sanity/consistency.
124790634029Sdyoung 	 */
124850d44f4fSmycroft 	if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
124990634029Sdyoung 		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
125090634029Sdyoung 		    ni->ni_macaddr, "shared key auth",
125190634029Sdyoung 		    "%s", " PRIVACY is disabled");
125290634029Sdyoung 		estatus = IEEE80211_STATUS_ALG;
125390634029Sdyoung 		goto bad;
125490634029Sdyoung 	}
1255a45976c2Smaxv 
125690634029Sdyoung 	/*
125790634029Sdyoung 	 * Pre-shared key authentication is evil; accept
125890634029Sdyoung 	 * it only if explicitly configured (it is supported
125990634029Sdyoung 	 * mainly for compatibility with clients like OS X).
126090634029Sdyoung 	 */
126190634029Sdyoung 	if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
126290634029Sdyoung 	    ni->ni_authmode != IEEE80211_AUTH_SHARED) {
126390634029Sdyoung 		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
126490634029Sdyoung 		    ni->ni_macaddr, "shared key auth",
126590634029Sdyoung 		    "bad sta auth mode %u", ni->ni_authmode);
126690634029Sdyoung 		ic->ic_stats.is_rx_bad_auth++;	/* XXX maybe a unique error? */
126790634029Sdyoung 		estatus = IEEE80211_STATUS_ALG;
126890634029Sdyoung 		goto bad;
1269db0e43b2Sdyoung 	}
1270db0e43b2Sdyoung 
127190634029Sdyoung 	challenge = NULL;
1272db0e43b2Sdyoung 	if (frm + 1 < efrm) {
127390634029Sdyoung 		if ((frm[1] + 2) > (efrm - frm)) {
127490634029Sdyoung 			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
127590634029Sdyoung 			    ni->ni_macaddr, "shared key auth",
127690634029Sdyoung 			    "ie %d/%d too long",
127790634029Sdyoung 			    frm[0], (frm[1] + 2) - (efrm - frm));
12789280f4b4Sdyoung 			ic->ic_stats.is_rx_bad_auth++;
127990634029Sdyoung 			estatus = IEEE80211_STATUS_CHALLENGE;
128090634029Sdyoung 			goto bad;
1281db0e43b2Sdyoung 		}
1282db0e43b2Sdyoung 		if (*frm == IEEE80211_ELEMID_CHALLENGE)
1283db0e43b2Sdyoung 			challenge = frm;
1284db0e43b2Sdyoung 		frm += frm[1] + 2;
1285db0e43b2Sdyoung 	}
1286a45976c2Smaxv 
1287db0e43b2Sdyoung 	switch (seq) {
1288db0e43b2Sdyoung 	case IEEE80211_AUTH_SHARED_CHALLENGE:
1289db0e43b2Sdyoung 	case IEEE80211_AUTH_SHARED_RESPONSE:
1290db0e43b2Sdyoung 		if (challenge == NULL) {
129190634029Sdyoung 			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
129290634029Sdyoung 			    ni->ni_macaddr, "shared key auth",
129390634029Sdyoung 			    "%s", "no challenge");
12949280f4b4Sdyoung 			ic->ic_stats.is_rx_bad_auth++;
129590634029Sdyoung 			estatus = IEEE80211_STATUS_CHALLENGE;
129690634029Sdyoung 			goto bad;
1297db0e43b2Sdyoung 		}
1298db0e43b2Sdyoung 		if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
129990634029Sdyoung 			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
130090634029Sdyoung 			    ni->ni_macaddr, "shared key auth",
130190634029Sdyoung 			    "bad challenge len %d", challenge[1]);
13029280f4b4Sdyoung 			ic->ic_stats.is_rx_bad_auth++;
130390634029Sdyoung 			estatus = IEEE80211_STATUS_CHALLENGE;
130490634029Sdyoung 			goto bad;
1305db0e43b2Sdyoung 		}
1306db0e43b2Sdyoung 	default:
1307db0e43b2Sdyoung 		break;
1308db0e43b2Sdyoung 	}
1309a45976c2Smaxv 
1310db0e43b2Sdyoung 	switch (ic->ic_opmode) {
1311db0e43b2Sdyoung 	case IEEE80211_M_MONITOR:
1312db0e43b2Sdyoung 	case IEEE80211_M_AHDEMO:
1313db0e43b2Sdyoung 	case IEEE80211_M_IBSS:
131490634029Sdyoung 		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
131590634029Sdyoung 		    ni->ni_macaddr, "shared key auth",
131690634029Sdyoung 		    "bad operating mode %u", ic->ic_opmode);
1317db0e43b2Sdyoung 		return;
1318a45976c2Smaxv 
1319db0e43b2Sdyoung 	case IEEE80211_M_HOSTAP:
132074988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
132174988b0fSdyoung 	{
132274988b0fSdyoung 		int allocbs;
1323db0e43b2Sdyoung 		if (ic->ic_state != IEEE80211_S_RUN) {
132490634029Sdyoung 			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
132590634029Sdyoung 			    ni->ni_macaddr, "shared key auth",
132690634029Sdyoung 			    "bad state %u", ic->ic_state);
132790634029Sdyoung 			estatus = IEEE80211_STATUS_ALG;	/* XXX */
132890634029Sdyoung 			goto bad;
1329db0e43b2Sdyoung 		}
1330db0e43b2Sdyoung 		switch (seq) {
1331db0e43b2Sdyoung 		case IEEE80211_AUTH_SHARED_REQUEST:
1332db0e43b2Sdyoung 			if (ni == ic->ic_bss) {
133390634029Sdyoung 				ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
13349280f4b4Sdyoung 				if (ni == NULL) {
133590634029Sdyoung 					/* NB: no way to return an error */
1336db0e43b2Sdyoung 					return;
13379280f4b4Sdyoung 				}
133890634029Sdyoung 				allocbs = 1;
133990634029Sdyoung 			} else {
134080b91eeaSdyoung 				if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
134190634029Sdyoung 					(void)ieee80211_ref_node(ni);
134290634029Sdyoung 				allocbs = 0;
134390634029Sdyoung 			}
1344c2ec5838Schristos 			__USE(allocbs);
1345a45976c2Smaxv 
134680b91eeaSdyoung 			/*
1347f0a7346dSsnj 			 * Mark the node as referenced to reflect that its
134880b91eeaSdyoung 			 * reference count has been bumped to insure it remains
134980b91eeaSdyoung 			 * after the transaction completes.
135080b91eeaSdyoung 			 */
135180b91eeaSdyoung 			ni->ni_flags |= IEEE80211_NODE_AREF;
1352db0e43b2Sdyoung 			ni->ni_rssi = rssi;
1353db0e43b2Sdyoung 			ni->ni_rstamp = rstamp;
135490634029Sdyoung 			if (!alloc_challenge(ic, ni)) {
135590634029Sdyoung 				/* NB: don't return error so they rexmit */
1356db0e43b2Sdyoung 				return;
1357db0e43b2Sdyoung 			}
1358a45976c2Smaxv 
135990634029Sdyoung 			get_random_bytes(ni->ni_challenge,
136090634029Sdyoung 				IEEE80211_CHALLENGE_LEN);
1361a45976c2Smaxv 
1362f0546001Smycroft 			IEEE80211_DPRINTF(ic,
1363f0546001Smycroft 				IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
136490634029Sdyoung 				"[%s] shared key %sauth request\n",
1365c2ec5838Schristos 				ether_snprintf(ebuf, sizeof(ebuf),
1366c2ec5838Schristos 				ni->ni_macaddr),
136790634029Sdyoung 				allocbs ? "" : "re");
1368db0e43b2Sdyoung 			break;
1369a45976c2Smaxv 
1370db0e43b2Sdyoung 		case IEEE80211_AUTH_SHARED_RESPONSE:
1371db0e43b2Sdyoung 			if (ni == ic->ic_bss) {
137290634029Sdyoung 				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
137390634029Sdyoung 				    ni->ni_macaddr, "shared key response",
137490634029Sdyoung 				    "%s", "unknown station");
137590634029Sdyoung 				/* NB: don't send a response */
1376db0e43b2Sdyoung 				return;
1377db0e43b2Sdyoung 			}
1378a45976c2Smaxv 
1379db0e43b2Sdyoung 			if (ni->ni_challenge == NULL) {
138090634029Sdyoung 				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
138190634029Sdyoung 				    ni->ni_macaddr, "shared key response",
138290634029Sdyoung 				    "%s", "no challenge recorded");
13839280f4b4Sdyoung 				ic->ic_stats.is_rx_bad_auth++;
138490634029Sdyoung 				estatus = IEEE80211_STATUS_CHALLENGE;
138590634029Sdyoung 				goto bad;
1386db0e43b2Sdyoung 			}
1387a45976c2Smaxv 
1388db0e43b2Sdyoung 			if (memcmp(ni->ni_challenge, &challenge[2],
1389db0e43b2Sdyoung 			    challenge[1]) != 0) {
139090634029Sdyoung 				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
139190634029Sdyoung 				    ni->ni_macaddr, "shared key response",
139290634029Sdyoung 				    "%s", "challenge mismatch");
13939280f4b4Sdyoung 				ic->ic_stats.is_rx_auth_fail++;
139490634029Sdyoung 				estatus = IEEE80211_STATUS_CHALLENGE;
139590634029Sdyoung 				goto bad;
1396db0e43b2Sdyoung 			}
1397a45976c2Smaxv 
1398f0546001Smycroft 			IEEE80211_DPRINTF(ic,
1399f0546001Smycroft 			    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
140090634029Sdyoung 			    "[%s] station authenticated (shared key)\n",
1401c2ec5838Schristos 			    ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr));
1402a45976c2Smaxv 
140387515e34Sskrll 			ieee80211_node_authorize(ni);
1404db0e43b2Sdyoung 			break;
1405a45976c2Smaxv 
1406db0e43b2Sdyoung 		default:
140790634029Sdyoung 			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
140890634029Sdyoung 			    ni->ni_macaddr, "shared key auth",
140990634029Sdyoung 			    "bad seq %d", seq);
14109280f4b4Sdyoung 			ic->ic_stats.is_rx_bad_auth++;
141190634029Sdyoung 			estatus = IEEE80211_STATUS_SEQUENCE;
141290634029Sdyoung 			goto bad;
1413db0e43b2Sdyoung 		}
1414a45976c2Smaxv 
1415a45976c2Smaxv 		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH,
1416a45976c2Smaxv 		    seq + 1);
141774988b0fSdyoung 	}
141874988b0fSdyoung #endif /* !IEEE80211_NO_HOSTAP */
1419db0e43b2Sdyoung 		break;
1420db0e43b2Sdyoung 
1421db0e43b2Sdyoung 	case IEEE80211_M_STA:
1422db0e43b2Sdyoung 		if (ic->ic_state != IEEE80211_S_AUTH)
1423db0e43b2Sdyoung 			return;
1424db0e43b2Sdyoung 		switch (seq) {
1425db0e43b2Sdyoung 		case IEEE80211_AUTH_SHARED_PASS:
1426db0e43b2Sdyoung 			if (ni->ni_challenge != NULL) {
14279b87d582Scegger 				free(ni->ni_challenge, M_DEVBUF);
1428db0e43b2Sdyoung 				ni->ni_challenge = NULL;
1429db0e43b2Sdyoung 			}
1430db0e43b2Sdyoung 			if (status != 0) {
1431f0546001Smycroft 				IEEE80211_DPRINTF(ic,
1432f0546001Smycroft 				    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
143390634029Sdyoung 				    "[%s] shared key auth failed (reason %d)\n",
1434c2ec5838Schristos 				    ether_snprintf(ebuf, sizeof(ebuf),
1435c2ec5838Schristos 				    ieee80211_getbssid(ic, wh)),
143690634029Sdyoung 				    status);
1437a45976c2Smaxv 
143890634029Sdyoung 				/* XXX can this happen? */
1439db0e43b2Sdyoung 				if (ni != ic->ic_bss)
1440db0e43b2Sdyoung 					ni->ni_fails++;
1441a45976c2Smaxv 
14429280f4b4Sdyoung 				ic->ic_stats.is_rx_auth_fail++;
1443db0e43b2Sdyoung 				return;
1444db0e43b2Sdyoung 			}
1445a45976c2Smaxv 
1446db0e43b2Sdyoung 			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
1447db0e43b2Sdyoung 			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
1448db0e43b2Sdyoung 			break;
1449a45976c2Smaxv 
1450db0e43b2Sdyoung 		case IEEE80211_AUTH_SHARED_CHALLENGE:
145190634029Sdyoung 			if (!alloc_challenge(ic, ni))
1452db0e43b2Sdyoung 				return;
145390634029Sdyoung 			/* XXX could optimize by passing recvd challenge */
1454db0e43b2Sdyoung 			memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
1455a45976c2Smaxv 
1456a45976c2Smaxv 			IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH,
1457a45976c2Smaxv 			    seq + 1);
1458db0e43b2Sdyoung 			break;
1459a45976c2Smaxv 
1460db0e43b2Sdyoung 		default:
146190634029Sdyoung 			IEEE80211_DISCARD(ic, IEEE80211_MSG_AUTH,
146290634029Sdyoung 			    wh, "shared key auth", "bad seq %d", seq);
14639280f4b4Sdyoung 			ic->ic_stats.is_rx_bad_auth++;
1464db0e43b2Sdyoung 			return;
1465db0e43b2Sdyoung 		}
1466db0e43b2Sdyoung 		break;
1467db0e43b2Sdyoung 	}
146890634029Sdyoung 	return;
1469a45976c2Smaxv 
147090634029Sdyoung bad:
147174988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
147290634029Sdyoung 	/*
147390634029Sdyoung 	 * Send an error response; but only when operating as an AP.
147490634029Sdyoung 	 */
147590634029Sdyoung 	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
147690634029Sdyoung 		/* XXX hack to workaround calling convention */
147780b91eeaSdyoung 		ieee80211_send_error(ic, ni, wh->i_addr2,
147890634029Sdyoung 		    IEEE80211_FC0_SUBTYPE_AUTH,
147990634029Sdyoung 		    (seq + 1) | (estatus<<16));
148080b91eeaSdyoung 	} else if (ic->ic_opmode == IEEE80211_M_STA) {
148180b91eeaSdyoung 		/*
148280b91eeaSdyoung 		 * Kick the state machine.  This short-circuits
148380b91eeaSdyoung 		 * using the mgt frame timeout to trigger the
148480b91eeaSdyoung 		 * state transition.
148580b91eeaSdyoung 		 */
148680b91eeaSdyoung 		if (ic->ic_state == IEEE80211_S_AUTH)
148780b91eeaSdyoung 			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
1488db0e43b2Sdyoung 	}
148974988b0fSdyoung #else
149074988b0fSdyoung 	;
149174988b0fSdyoung #endif /* !IEEE80211_NO_HOSTAP */
149290634029Sdyoung }
149390634029Sdyoung 
149490634029Sdyoung #ifdef IEEE80211_DEBUG
149590634029Sdyoung static void
ieee80211_ssid_mismatch(struct ieee80211com * ic,const char * tag,u_int8_t mac[IEEE80211_ADDR_LEN],u_int8_t * ssid)1496168cd830Schristos ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag,
149790634029Sdyoung 	u_int8_t mac[IEEE80211_ADDR_LEN], u_int8_t *ssid)
149890634029Sdyoung {
1499c2ec5838Schristos 	char ebuf[3 * ETHER_ADDR_LEN];
1500c2ec5838Schristos 
150190634029Sdyoung 	printf("[%s] discard %s frame, ssid mismatch: ",
1502c2ec5838Schristos 	    ether_snprintf(ebuf, sizeof(ebuf), mac), tag);
150390634029Sdyoung 	ieee80211_print_essid(ssid + 2, ssid[1]);
150490634029Sdyoung 	printf("\n");
150590634029Sdyoung }
150690634029Sdyoung 
150790634029Sdyoung #define	IEEE80211_VERIFY_SSID(_ni, _ssid) do {				\
150890634029Sdyoung 	if ((_ssid)[1] != 0 &&						\
150990634029Sdyoung 	    ((_ssid)[1] != (_ni)->ni_esslen ||				\
151090634029Sdyoung 	    memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) {	\
151190634029Sdyoung 		if (ieee80211_msg_input(ic))				\
151290634029Sdyoung 			ieee80211_ssid_mismatch(ic, 			\
151390634029Sdyoung 			    ieee80211_mgt_subtype_name[subtype >>	\
151490634029Sdyoung 				IEEE80211_FC0_SUBTYPE_SHIFT],		\
151590634029Sdyoung 				wh->i_addr2, _ssid);			\
151690634029Sdyoung 		ic->ic_stats.is_rx_ssidmismatch++;			\
151790634029Sdyoung 		return;							\
151890634029Sdyoung 	}								\
151990634029Sdyoung } while (0)
152090634029Sdyoung #else /* !IEEE80211_DEBUG */
152190634029Sdyoung #define	IEEE80211_VERIFY_SSID(_ni, _ssid) do {				\
152290634029Sdyoung 	if ((_ssid)[1] != 0 &&						\
152390634029Sdyoung 	    ((_ssid)[1] != (_ni)->ni_esslen ||				\
152490634029Sdyoung 	    memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) {	\
152590634029Sdyoung 		ic->ic_stats.is_rx_ssidmismatch++;			\
152690634029Sdyoung 		return;							\
152790634029Sdyoung 	}								\
152890634029Sdyoung } while (0)
152990634029Sdyoung #endif /* !IEEE80211_DEBUG */
153090634029Sdyoung 
1531bf2739eaSchristos /* unaligned little endian access */
153290634029Sdyoung #define LE_READ_2(p)					\
153390634029Sdyoung 	((u_int16_t)					\
153490634029Sdyoung 	 ((((const u_int8_t *)(p))[0]      ) |		\
153590634029Sdyoung 	  (((const u_int8_t *)(p))[1] <<  8)))
153690634029Sdyoung #define LE_READ_4(p)					\
153790634029Sdyoung 	((u_int32_t)					\
153890634029Sdyoung 	 ((((const u_int8_t *)(p))[0]      ) |		\
153990634029Sdyoung 	  (((const u_int8_t *)(p))[1] <<  8) |		\
154090634029Sdyoung 	  (((const u_int8_t *)(p))[2] << 16) |		\
154190634029Sdyoung 	  (((const u_int8_t *)(p))[3] << 24)))
154290634029Sdyoung 
1543989f66d1Schristos static __inline int
iswpaoui(const u_int8_t * frm)154490634029Sdyoung iswpaoui(const u_int8_t *frm)
154590634029Sdyoung {
154690634029Sdyoung 	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
154790634029Sdyoung }
154890634029Sdyoung 
1549989f66d1Schristos static __inline int
iswmeoui(const u_int8_t * frm)155090634029Sdyoung iswmeoui(const u_int8_t *frm)
155190634029Sdyoung {
155290634029Sdyoung 	return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
155390634029Sdyoung }
155490634029Sdyoung 
1555989f66d1Schristos static __inline int
iswmeparam(const u_int8_t * frm)155690634029Sdyoung iswmeparam(const u_int8_t *frm)
155790634029Sdyoung {
155890634029Sdyoung 	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
155990634029Sdyoung 		frm[6] == WME_PARAM_OUI_SUBTYPE;
156090634029Sdyoung }
156190634029Sdyoung 
1562989f66d1Schristos static __inline int
iswmeinfo(const u_int8_t * frm)156390634029Sdyoung iswmeinfo(const u_int8_t *frm)
156490634029Sdyoung {
156590634029Sdyoung 	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
156690634029Sdyoung 		frm[6] == WME_INFO_OUI_SUBTYPE;
156790634029Sdyoung }
156890634029Sdyoung 
156990634029Sdyoung /*
157090634029Sdyoung  * Convert a WPA cipher selector OUI to an internal
157190634029Sdyoung  * cipher algorithm.  Where appropriate we also
157290634029Sdyoung  * record any key length.
157390634029Sdyoung  */
157490634029Sdyoung static int
wpa_cipher(u_int8_t * sel,u_int8_t * keylen)157590634029Sdyoung wpa_cipher(u_int8_t *sel, u_int8_t *keylen)
157690634029Sdyoung {
157790634029Sdyoung #define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
157890634029Sdyoung 	u_int32_t w = LE_READ_4(sel);
157990634029Sdyoung 
158090634029Sdyoung 	switch (w) {
158190634029Sdyoung 	case WPA_SEL(WPA_CSE_NULL):
158290634029Sdyoung 		return IEEE80211_CIPHER_NONE;
158390634029Sdyoung 	case WPA_SEL(WPA_CSE_WEP40):
158490634029Sdyoung 		if (keylen)
158590634029Sdyoung 			*keylen = 40 / NBBY;
158690634029Sdyoung 		return IEEE80211_CIPHER_WEP;
158790634029Sdyoung 	case WPA_SEL(WPA_CSE_WEP104):
158890634029Sdyoung 		if (keylen)
158990634029Sdyoung 			*keylen = 104 / NBBY;
159090634029Sdyoung 		return IEEE80211_CIPHER_WEP;
159190634029Sdyoung 	case WPA_SEL(WPA_CSE_TKIP):
159290634029Sdyoung 		return IEEE80211_CIPHER_TKIP;
159390634029Sdyoung 	case WPA_SEL(WPA_CSE_CCMP):
159490634029Sdyoung 		return IEEE80211_CIPHER_AES_CCM;
159590634029Sdyoung 	}
159690634029Sdyoung 	return 32;		/* NB: so 1<< is discarded */
159790634029Sdyoung #undef WPA_SEL
159890634029Sdyoung }
159990634029Sdyoung 
160090634029Sdyoung /*
160190634029Sdyoung  * Convert a WPA key management/authentication algorithm
160290634029Sdyoung  * to an internal code.
160390634029Sdyoung  */
160490634029Sdyoung static int
wpa_keymgmt(u_int8_t * sel)160590634029Sdyoung wpa_keymgmt(u_int8_t *sel)
160690634029Sdyoung {
160790634029Sdyoung #define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
160890634029Sdyoung 	u_int32_t w = LE_READ_4(sel);
160990634029Sdyoung 
161090634029Sdyoung 	switch (w) {
161190634029Sdyoung 	case WPA_SEL(WPA_ASE_8021X_UNSPEC):
161290634029Sdyoung 		return WPA_ASE_8021X_UNSPEC;
161390634029Sdyoung 	case WPA_SEL(WPA_ASE_8021X_PSK):
161490634029Sdyoung 		return WPA_ASE_8021X_PSK;
161590634029Sdyoung 	case WPA_SEL(WPA_ASE_NONE):
161690634029Sdyoung 		return WPA_ASE_NONE;
161790634029Sdyoung 	}
161890634029Sdyoung 	return 0;		/* NB: so is discarded */
161990634029Sdyoung #undef WPA_SEL
162090634029Sdyoung }
162190634029Sdyoung 
162290634029Sdyoung /*
162390634029Sdyoung  * Parse a WPA information element to collect parameters
162490634029Sdyoung  * and validate the parameters against what has been
162590634029Sdyoung  * configured for the system.
162690634029Sdyoung  */
162790634029Sdyoung static int
ieee80211_parse_wpa(struct ieee80211com * ic,u_int8_t * frm,struct ieee80211_rsnparms * rsn,const struct ieee80211_frame * wh)162890634029Sdyoung ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm,
162990634029Sdyoung     struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
163090634029Sdyoung {
163190634029Sdyoung 	u_int8_t len = frm[1];
163290634029Sdyoung 	u_int32_t w;
163390634029Sdyoung 	int n;
163490634029Sdyoung 
163587515e34Sskrll 	if ((ic->ic_flags & IEEE80211_F_WPA1) == 0) {
163687515e34Sskrll 		IEEE80211_DISCARD_IE(ic,
163787515e34Sskrll 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
163887515e34Sskrll 		    wh, "WPA", "not WPA, flags 0x%x", ic->ic_flags);
163987515e34Sskrll 		return IEEE80211_REASON_IE_INVALID;
164087515e34Sskrll 	}
1641a45976c2Smaxv 
1642a45976c2Smaxv 	/*
1643a45976c2Smaxv 	 * Check the length once for fixed parts: OUI, type,
1644a45976c2Smaxv 	 * version, mcast cipher, and 2 selector counts.
1645a45976c2Smaxv 	 * Other, variable-length data, must be checked separately.
1646a45976c2Smaxv 	 */
164790634029Sdyoung 	if (len < 14) {
164890634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
164990634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
165090634029Sdyoung 		    wh, "WPA", "too short, len %u", len);
165190634029Sdyoung 		return IEEE80211_REASON_IE_INVALID;
165290634029Sdyoung 	}
1653a45976c2Smaxv 
1654a45976c2Smaxv 	frm += 2; /* beginning of payload */
1655a45976c2Smaxv 	frm += 4, len -= 4;
1656a45976c2Smaxv 
165790634029Sdyoung 	/* NB: iswapoui already validated the OUI and type */
165890634029Sdyoung 	w = LE_READ_2(frm);
165990634029Sdyoung 	if (w != WPA_VERSION) {
166090634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
166190634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
166290634029Sdyoung 		    wh, "WPA", "bad version %u", w);
166390634029Sdyoung 		return IEEE80211_REASON_IE_INVALID;
166490634029Sdyoung 	}
166590634029Sdyoung 	frm += 2, len -= 2;
166690634029Sdyoung 
166790634029Sdyoung 	/* multicast/group cipher */
166890634029Sdyoung 	w = wpa_cipher(frm, &rsn->rsn_mcastkeylen);
166990634029Sdyoung 	if (w != rsn->rsn_mcastcipher) {
167090634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
167190634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
167290634029Sdyoung 		    wh, "WPA", "mcast cipher mismatch; got %u, expected %u",
167390634029Sdyoung 		    w, rsn->rsn_mcastcipher);
167490634029Sdyoung 		return IEEE80211_REASON_IE_INVALID;
167590634029Sdyoung 	}
167690634029Sdyoung 	frm += 4, len -= 4;
167790634029Sdyoung 
167890634029Sdyoung 	/* unicast ciphers */
167990634029Sdyoung 	n = LE_READ_2(frm);
168090634029Sdyoung 	frm += 2, len -= 2;
168190634029Sdyoung 	if (len < n*4+2) {
168290634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
168390634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
168490634029Sdyoung 		    wh, "WPA", "ucast cipher data too short; len %u, n %u",
168590634029Sdyoung 		    len, n);
168690634029Sdyoung 		return IEEE80211_REASON_IE_INVALID;
168790634029Sdyoung 	}
168890634029Sdyoung 	w = 0;
168990634029Sdyoung 	for (; n > 0; n--) {
169090634029Sdyoung 		w |= 1 << wpa_cipher(frm, &rsn->rsn_ucastkeylen);
169190634029Sdyoung 		frm += 4, len -= 4;
169290634029Sdyoung 	}
169390634029Sdyoung 	w &= rsn->rsn_ucastcipherset;
169490634029Sdyoung 	if (w == 0) {
169590634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
169690634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
169790634029Sdyoung 		    wh, "WPA", "%s", "ucast cipher set empty");
169890634029Sdyoung 		return IEEE80211_REASON_IE_INVALID;
169990634029Sdyoung 	}
170090634029Sdyoung 	if (w & (1 << IEEE80211_CIPHER_TKIP))
170190634029Sdyoung 		rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
170290634029Sdyoung 	else
170390634029Sdyoung 		rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
170490634029Sdyoung 
170590634029Sdyoung 	/* key management algorithms */
170690634029Sdyoung 	n = LE_READ_2(frm);
170790634029Sdyoung 	frm += 2, len -= 2;
170890634029Sdyoung 	if (len < n*4) {
170990634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
171090634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
171190634029Sdyoung 		    wh, "WPA", "key mgmt alg data too short; len %u, n %u",
171290634029Sdyoung 		    len, n);
171390634029Sdyoung 		return IEEE80211_REASON_IE_INVALID;
171490634029Sdyoung 	}
171590634029Sdyoung 	w = 0;
171690634029Sdyoung 	for (; n > 0; n--) {
171790634029Sdyoung 		w |= wpa_keymgmt(frm);
171890634029Sdyoung 		frm += 4, len -= 4;
171990634029Sdyoung 	}
172090634029Sdyoung 	w &= rsn->rsn_keymgmtset;
172190634029Sdyoung 	if (w == 0) {
172290634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
172390634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
172490634029Sdyoung 		    wh, "WPA", "%s", "no acceptable key mgmt alg");
172590634029Sdyoung 		return IEEE80211_REASON_IE_INVALID;
172690634029Sdyoung 	}
172790634029Sdyoung 	if (w & WPA_ASE_8021X_UNSPEC)
172890634029Sdyoung 		rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC;
172990634029Sdyoung 	else
173090634029Sdyoung 		rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
173190634029Sdyoung 
173290634029Sdyoung 	if (len > 2)		/* optional capabilities */
173390634029Sdyoung 		rsn->rsn_caps = LE_READ_2(frm);
173490634029Sdyoung 
173590634029Sdyoung 	return 0;
173690634029Sdyoung }
173790634029Sdyoung 
173890634029Sdyoung /*
173990634029Sdyoung  * Convert an RSN cipher selector OUI to an internal
174090634029Sdyoung  * cipher algorithm.  Where appropriate we also
174190634029Sdyoung  * record any key length.
174290634029Sdyoung  */
174390634029Sdyoung static int
rsn_cipher(u_int8_t * sel,u_int8_t * keylen)174490634029Sdyoung rsn_cipher(u_int8_t *sel, u_int8_t *keylen)
174590634029Sdyoung {
174690634029Sdyoung #define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
174790634029Sdyoung 	u_int32_t w = LE_READ_4(sel);
174890634029Sdyoung 
174990634029Sdyoung 	switch (w) {
175090634029Sdyoung 	case RSN_SEL(RSN_CSE_NULL):
175190634029Sdyoung 		return IEEE80211_CIPHER_NONE;
175290634029Sdyoung 	case RSN_SEL(RSN_CSE_WEP40):
175390634029Sdyoung 		if (keylen)
175490634029Sdyoung 			*keylen = 40 / NBBY;
175590634029Sdyoung 		return IEEE80211_CIPHER_WEP;
175690634029Sdyoung 	case RSN_SEL(RSN_CSE_WEP104):
175790634029Sdyoung 		if (keylen)
175890634029Sdyoung 			*keylen = 104 / NBBY;
175990634029Sdyoung 		return IEEE80211_CIPHER_WEP;
176090634029Sdyoung 	case RSN_SEL(RSN_CSE_TKIP):
176190634029Sdyoung 		return IEEE80211_CIPHER_TKIP;
176290634029Sdyoung 	case RSN_SEL(RSN_CSE_CCMP):
176390634029Sdyoung 		return IEEE80211_CIPHER_AES_CCM;
176490634029Sdyoung 	case RSN_SEL(RSN_CSE_WRAP):
176590634029Sdyoung 		return IEEE80211_CIPHER_AES_OCB;
176690634029Sdyoung 	}
176790634029Sdyoung 	return 32;		/* NB: so 1<< is discarded */
176890634029Sdyoung #undef WPA_SEL
176990634029Sdyoung }
177090634029Sdyoung 
177190634029Sdyoung /*
177290634029Sdyoung  * Convert an RSN key management/authentication algorithm
177390634029Sdyoung  * to an internal code.
177490634029Sdyoung  */
177590634029Sdyoung static int
rsn_keymgmt(u_int8_t * sel)177690634029Sdyoung rsn_keymgmt(u_int8_t *sel)
177790634029Sdyoung {
177890634029Sdyoung #define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
177990634029Sdyoung 	u_int32_t w = LE_READ_4(sel);
178090634029Sdyoung 
178190634029Sdyoung 	switch (w) {
178290634029Sdyoung 	case RSN_SEL(RSN_ASE_8021X_UNSPEC):
178390634029Sdyoung 		return RSN_ASE_8021X_UNSPEC;
178490634029Sdyoung 	case RSN_SEL(RSN_ASE_8021X_PSK):
178590634029Sdyoung 		return RSN_ASE_8021X_PSK;
178690634029Sdyoung 	case RSN_SEL(RSN_ASE_NONE):
178790634029Sdyoung 		return RSN_ASE_NONE;
178890634029Sdyoung 	}
178990634029Sdyoung 	return 0;		/* NB: so is discarded */
179090634029Sdyoung #undef RSN_SEL
179190634029Sdyoung }
179290634029Sdyoung 
179390634029Sdyoung /*
179490634029Sdyoung  * Parse a WPA/RSN information element to collect parameters
179590634029Sdyoung  * and validate the parameters against what has been
179690634029Sdyoung  * configured for the system.
179790634029Sdyoung  */
179890634029Sdyoung static int
ieee80211_parse_rsn(struct ieee80211com * ic,u_int8_t * frm,struct ieee80211_rsnparms * rsn,const struct ieee80211_frame * wh)179990634029Sdyoung ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm,
180090634029Sdyoung     struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
180190634029Sdyoung {
180290634029Sdyoung 	u_int8_t len = frm[1];
180390634029Sdyoung 	u_int32_t w;
180490634029Sdyoung 	int n;
180590634029Sdyoung 
180687515e34Sskrll 	if ((ic->ic_flags & IEEE80211_F_WPA2) == 0) {
180787515e34Sskrll 		IEEE80211_DISCARD_IE(ic,
180887515e34Sskrll 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
180987515e34Sskrll 		    wh, "WPA", "not RSN, flags 0x%x", ic->ic_flags);
181087515e34Sskrll 		return IEEE80211_REASON_IE_INVALID;
181187515e34Sskrll 	}
1812a45976c2Smaxv 
1813a45976c2Smaxv 	/*
1814a45976c2Smaxv 	 * Check the length once for fixed parts:
1815a45976c2Smaxv 	 * version, mcast cipher, and 2 selector counts.
1816a45976c2Smaxv 	 * Other, variable-length data, must be checked separately.
1817a45976c2Smaxv 	 */
181890634029Sdyoung 	if (len < 10) {
181990634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
182090634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
182190634029Sdyoung 		    wh, "RSN", "too short, len %u", len);
182290634029Sdyoung 		return IEEE80211_REASON_IE_INVALID;
182390634029Sdyoung 	}
1824a45976c2Smaxv 
1825a45976c2Smaxv 	frm += 2; /* beginning of payload */
182690634029Sdyoung 	w = LE_READ_2(frm);
182790634029Sdyoung 	if (w != RSN_VERSION) {
182890634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
182990634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
183090634029Sdyoung 		    wh, "RSN", "bad version %u", w);
183190634029Sdyoung 		return IEEE80211_REASON_IE_INVALID;
183290634029Sdyoung 	}
183390634029Sdyoung 	frm += 2, len -= 2;
183490634029Sdyoung 
183590634029Sdyoung 	/* multicast/group cipher */
183690634029Sdyoung 	w = rsn_cipher(frm, &rsn->rsn_mcastkeylen);
183790634029Sdyoung 	if (w != rsn->rsn_mcastcipher) {
183890634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
183990634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
184090634029Sdyoung 		    wh, "RSN", "mcast cipher mismatch; got %u, expected %u",
184190634029Sdyoung 		    w, rsn->rsn_mcastcipher);
184290634029Sdyoung 		return IEEE80211_REASON_IE_INVALID;
184390634029Sdyoung 	}
184490634029Sdyoung 	frm += 4, len -= 4;
184590634029Sdyoung 
184690634029Sdyoung 	/* unicast ciphers */
184790634029Sdyoung 	n = LE_READ_2(frm);
184890634029Sdyoung 	frm += 2, len -= 2;
184990634029Sdyoung 	if (len < n*4+2) {
185090634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
185190634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
185290634029Sdyoung 		    wh, "RSN", "ucast cipher data too short; len %u, n %u",
185390634029Sdyoung 		    len, n);
185490634029Sdyoung 		return IEEE80211_REASON_IE_INVALID;
185590634029Sdyoung 	}
185690634029Sdyoung 	w = 0;
185790634029Sdyoung 	for (; n > 0; n--) {
185890634029Sdyoung 		w |= 1 << rsn_cipher(frm, &rsn->rsn_ucastkeylen);
185990634029Sdyoung 		frm += 4, len -= 4;
186090634029Sdyoung 	}
186190634029Sdyoung 	w &= rsn->rsn_ucastcipherset;
186290634029Sdyoung 	if (w == 0) {
186390634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
186490634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
186590634029Sdyoung 		    wh, "RSN", "%s", "ucast cipher set empty");
186690634029Sdyoung 		return IEEE80211_REASON_IE_INVALID;
186790634029Sdyoung 	}
186890634029Sdyoung 	if (w & (1 << IEEE80211_CIPHER_TKIP))
186990634029Sdyoung 		rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
187090634029Sdyoung 	else
187190634029Sdyoung 		rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
187290634029Sdyoung 
187390634029Sdyoung 	/* key management algorithms */
187490634029Sdyoung 	n = LE_READ_2(frm);
187590634029Sdyoung 	frm += 2, len -= 2;
187690634029Sdyoung 	if (len < n*4) {
187790634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
187890634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
187990634029Sdyoung 		    wh, "RSN", "key mgmt alg data too short; len %u, n %u",
188090634029Sdyoung 		    len, n);
188190634029Sdyoung 		return IEEE80211_REASON_IE_INVALID;
188290634029Sdyoung 	}
188390634029Sdyoung 	w = 0;
188490634029Sdyoung 	for (; n > 0; n--) {
188590634029Sdyoung 		w |= rsn_keymgmt(frm);
188690634029Sdyoung 		frm += 4, len -= 4;
188790634029Sdyoung 	}
188890634029Sdyoung 	w &= rsn->rsn_keymgmtset;
188990634029Sdyoung 	if (w == 0) {
189090634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
189190634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
189290634029Sdyoung 		    wh, "RSN", "%s", "no acceptable key mgmt alg");
189390634029Sdyoung 		return IEEE80211_REASON_IE_INVALID;
189490634029Sdyoung 	}
189590634029Sdyoung 	if (w & RSN_ASE_8021X_UNSPEC)
189690634029Sdyoung 		rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC;
189790634029Sdyoung 	else
189890634029Sdyoung 		rsn->rsn_keymgmt = RSN_ASE_8021X_PSK;
189990634029Sdyoung 
190090634029Sdyoung 	/* optional RSN capabilities */
190190634029Sdyoung 	if (len > 2)
190290634029Sdyoung 		rsn->rsn_caps = LE_READ_2(frm);
190390634029Sdyoung 	/* XXXPMKID */
190490634029Sdyoung 
190590634029Sdyoung 	return 0;
190690634029Sdyoung }
190790634029Sdyoung 
190890634029Sdyoung static int
ieee80211_parse_wmeparams(struct ieee80211com * ic,u_int8_t * frm,const struct ieee80211_frame * wh)190990634029Sdyoung ieee80211_parse_wmeparams(struct ieee80211com *ic, u_int8_t *frm,
191090634029Sdyoung     const struct ieee80211_frame *wh)
191190634029Sdyoung {
191290634029Sdyoung #define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
191390634029Sdyoung 	struct ieee80211_wme_state *wme = &ic->ic_wme;
191490634029Sdyoung 	u_int len = frm[1], qosinfo;
191590634029Sdyoung 	int i;
191690634029Sdyoung 
191790634029Sdyoung 	if (len < sizeof(struct ieee80211_wme_param)-2) {
191890634029Sdyoung 		IEEE80211_DISCARD_IE(ic,
191990634029Sdyoung 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
192090634029Sdyoung 		    wh, "WME", "too short, len %u", len);
192190634029Sdyoung 		return -1;
192290634029Sdyoung 	}
192311a42b5cSmaxv 
192442c42085Schristos 	qosinfo = frm[offsetof(struct ieee80211_wme_param, param_qosInfo)];
192590634029Sdyoung 	qosinfo &= WME_QOSINFO_COUNT;
192611a42b5cSmaxv 
192790634029Sdyoung 	/* XXX do proper check for wraparound */
192890634029Sdyoung 	if (qosinfo == wme->wme_wmeChanParams.cap_info)
192990634029Sdyoung 		return 0;
193011a42b5cSmaxv 
193142c42085Schristos 	frm += offsetof(struct ieee80211_wme_param, params_acParams);
193290634029Sdyoung 	for (i = 0; i < WME_NUM_AC; i++) {
193390634029Sdyoung 		struct wmeParams *wmep =
193490634029Sdyoung 			&wme->wme_wmeChanParams.cap_wmeParams[i];
193590634029Sdyoung 		/* NB: ACI not used */
193690634029Sdyoung 		wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
193790634029Sdyoung 		wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
193890634029Sdyoung 		wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
193990634029Sdyoung 		wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
194090634029Sdyoung 		wmep->wmep_txopLimit = LE_READ_2(frm+2);
194190634029Sdyoung 		frm += 4;
194290634029Sdyoung 	}
194311a42b5cSmaxv 
194490634029Sdyoung 	wme->wme_wmeChanParams.cap_info = qosinfo;
194590634029Sdyoung 	return 1;
194690634029Sdyoung #undef MS
194790634029Sdyoung }
194890634029Sdyoung 
194987515e34Sskrll void
ieee80211_saveie(u_int8_t ** iep,const u_int8_t * ie)195090634029Sdyoung ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie)
195190634029Sdyoung {
195290634029Sdyoung 	u_int ielen = ie[1]+2;
1953a45976c2Smaxv 
1954a45976c2Smaxv 	/* Record information element for later use. */
195590634029Sdyoung 	if (*iep == NULL || (*iep)[1] != ie[1]) {
195690634029Sdyoung 		if (*iep != NULL)
19579b87d582Scegger 			free(*iep, M_DEVBUF);
19585a57baa4Schristos 		*iep = malloc(ielen, M_DEVBUF, M_NOWAIT);
195990634029Sdyoung 	}
196090634029Sdyoung 	if (*iep != NULL)
196190634029Sdyoung 		memcpy(*iep, ie, ielen);
196290634029Sdyoung 	/* XXX note failure */
196390634029Sdyoung }
196490634029Sdyoung 
19657b6a25edSdyoung static void
ieee80211_update_adhoc_node(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_frame * wh,struct ieee80211_scanparams * scan,int rssi,u_int32_t rstamp)19667b6a25edSdyoung ieee80211_update_adhoc_node(struct ieee80211com *ic, struct ieee80211_node *ni,
19677b6a25edSdyoung     struct ieee80211_frame *wh, struct ieee80211_scanparams *scan, int rssi,
19687b6a25edSdyoung     u_int32_t rstamp)
19697b6a25edSdyoung {
19707b6a25edSdyoung 	if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
19717b6a25edSdyoung 		/*
19727b6a25edSdyoung 		 * Create a new entry in the neighbor table.
19737b6a25edSdyoung 		 * Records the TSF.
19747b6a25edSdyoung 		 */
19757b6a25edSdyoung 		if ((ni = ieee80211_add_neighbor(ic, wh, scan)) == NULL)
19767b6a25edSdyoung 			return;
19777b6a25edSdyoung 	} else if (ni->ni_capinfo == 0) {
19787b6a25edSdyoung 		/*
19797b6a25edSdyoung 		 * Initialize a node that was "faked up."  Records
19807b6a25edSdyoung 		 * the TSF.
19817b6a25edSdyoung 		 *
19827b6a25edSdyoung 		 * No need to check for a change of BSSID: ni could
19837b6a25edSdyoung 		 * not have been the IBSS (ic_bss)
19847b6a25edSdyoung 		 */
19857b6a25edSdyoung 		ieee80211_init_neighbor(ic, ni, wh, scan, 0);
19867b6a25edSdyoung 	} else {
19877b6a25edSdyoung 		/* Record TSF for potential resync. */
19881df75eefSmaxv 		memcpy(ni->ni_tstamp.data, scan->sp_tstamp, sizeof(ni->ni_tstamp));
19897b6a25edSdyoung 	}
19907b6a25edSdyoung 
19917b6a25edSdyoung 	ni->ni_rssi = rssi;
19927b6a25edSdyoung 	ni->ni_rstamp = rstamp;
19937b6a25edSdyoung 
19947b6a25edSdyoung 	/* Mark a neighbor's change of BSSID. */
19957b6a25edSdyoung 	if (IEEE80211_ADDR_EQ(wh->i_addr3, ni->ni_bssid))
19967b6a25edSdyoung 		return;
19977b6a25edSdyoung 
19987b6a25edSdyoung 	IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
19997b6a25edSdyoung 
20007b6a25edSdyoung 	if (ni != ic->ic_bss)
20017b6a25edSdyoung 		return;
20027b6a25edSdyoung 	else if (ic->ic_flags & IEEE80211_F_DESBSSID) {
20037b6a25edSdyoung 		/*
20047b6a25edSdyoung 		 * Now, ni does not represent a network we
20057b6a25edSdyoung 		 * want to belong to, so start a scan.
20067b6a25edSdyoung 		 */
20077b6a25edSdyoung 		ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
20087b6a25edSdyoung 		return;
20097b6a25edSdyoung 	} else {
20107b6a25edSdyoung 		/*
20117b6a25edSdyoung 		 * A RUN->RUN transition lets the driver
20127b6a25edSdyoung 		 * reprogram its BSSID filter.
20137b6a25edSdyoung 		 *
20147b6a25edSdyoung 		 * No need to SCAN, we already belong to
20157b6a25edSdyoung 		 * an IBSS that meets our criteria: channel,
20167b6a25edSdyoung 		 * SSID, etc.  It could be harmful to scan,
20177b6a25edSdyoung 		 * too: if a scan does not detect nodes
20187b6a25edSdyoung 		 * belonging to my current IBSS, then we
20197b6a25edSdyoung 		 * will create a new IBSS at the end of
20207b6a25edSdyoung 		 * the scan, needlessly splitting the
20217b6a25edSdyoung 		 * network.
20227b6a25edSdyoung 		 */
20237b6a25edSdyoung 		ieee80211_new_state(ic, IEEE80211_S_RUN, 0);
20247b6a25edSdyoung 	}
20257b6a25edSdyoung }
20267b6a25edSdyoung 
202785acfa9cSmaxv /* -------------------------------------------------------------------------- */
202885acfa9cSmaxv 
20291551d983Smaxv #define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do {			\
20301551d983Smaxv 	if ((__elem) == NULL) {						\
20311551d983Smaxv 		IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,		\
20321551d983Smaxv 		    wh, ieee80211_mgt_subtype_name[subtype >>		\
20331551d983Smaxv 			IEEE80211_FC0_SUBTYPE_SHIFT],			\
20341551d983Smaxv 		    "%s", "no " #__elem );				\
20351551d983Smaxv 		ic->ic_stats.is_rx_elem_missing++;			\
20361551d983Smaxv 		return;							\
20371551d983Smaxv 	}								\
20381551d983Smaxv 	if ((__elem)[1] > (__maxlen)) {					\
20391551d983Smaxv 		IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,		\
20401551d983Smaxv 		    wh, ieee80211_mgt_subtype_name[subtype >>		\
20411551d983Smaxv 			IEEE80211_FC0_SUBTYPE_SHIFT],			\
20421551d983Smaxv 		    "bad " #__elem " len %d", (__elem)[1]);		\
20431551d983Smaxv 		ic->ic_stats.is_rx_elem_toobig++;			\
20441551d983Smaxv 		return;							\
20451551d983Smaxv 	}								\
20461551d983Smaxv } while (0)
20471551d983Smaxv 
20481551d983Smaxv #define	IEEE80211_VERIFY_LENGTH(_len, _minlen) do {			\
2049a45976c2Smaxv 	if ((size_t)(_len) < (size_t)(_minlen)) {			\
20501551d983Smaxv 		IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,		\
20511551d983Smaxv 		    wh, ieee80211_mgt_subtype_name[subtype >>		\
20521551d983Smaxv 			IEEE80211_FC0_SUBTYPE_SHIFT],			\
20531551d983Smaxv 		    "%s", "ie too short");				\
20541551d983Smaxv 		ic->ic_stats.is_rx_elem_toosmall++;			\
20551551d983Smaxv 		return;							\
20561551d983Smaxv 	}								\
20571551d983Smaxv } while (0)
20581551d983Smaxv 
205985acfa9cSmaxv static void
ieee80211_recv_mgmt_beacon(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)206085acfa9cSmaxv ieee80211_recv_mgmt_beacon(struct ieee80211com *ic, struct mbuf *m0,
206124fb50b3Smaxv     struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
206240e261aaSdyoung {
206340e261aaSdyoung 	struct ieee80211_frame *wh;
206440e261aaSdyoung 	u_int8_t *frm, *efrm;
2065c2ec5838Schristos 	IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
206685acfa9cSmaxv 	struct ieee80211_scanparams scan;
206740e261aaSdyoung 
206840e261aaSdyoung 	wh = mtod(m0, struct ieee80211_frame *);
206924fb50b3Smaxv 	frm = (u_int8_t *)(wh + 1);
207040e261aaSdyoung 	efrm = mtod(m0, u_int8_t *) + m0->m_len;
207124fb50b3Smaxv 
207288a8480eSmycroft 	/*
207390634029Sdyoung 	 * We process beacon/probe response frames:
207490634029Sdyoung 	 *    o when scanning, or
207590634029Sdyoung 	 *    o station mode when associated (to collect state
207690634029Sdyoung 	 *      updates such as 802.11g slot time), or
207790634029Sdyoung 	 *    o adhoc mode (to discover neighbors)
207890634029Sdyoung 	 * Frames otherwise received are discarded.
207988a8480eSmycroft 	 */
208090634029Sdyoung 	if (!((ic->ic_flags & IEEE80211_F_SCAN) ||
208190634029Sdyoung 	      (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd) ||
208290634029Sdyoung 	       ic->ic_opmode == IEEE80211_M_IBSS)) {
208390634029Sdyoung 		ic->ic_stats.is_rx_mgtdiscard++;
208490634029Sdyoung 		return;
208540e261aaSdyoung 	}
208624fb50b3Smaxv 
208740e261aaSdyoung 	/*
208840e261aaSdyoung 	 * beacon/probe response frame format
208940e261aaSdyoung 	 *	[8] time stamp
209040e261aaSdyoung 	 *	[2] beacon interval
209140e261aaSdyoung 	 *	[2] capability information
209240e261aaSdyoung 	 *	[tlv] ssid
209340e261aaSdyoung 	 *	[tlv] supported rates
209440e261aaSdyoung 	 *	[tlv] country information
209540e261aaSdyoung 	 *	[tlv] parameter set (FH/DS)
209640e261aaSdyoung 	 *	[tlv] erp information
209740e261aaSdyoung 	 *	[tlv] extended supported rates
209890634029Sdyoung 	 *	[tlv] WME
209990634029Sdyoung 	 *	[tlv] WPA or RSN
210040e261aaSdyoung 	 */
210140e261aaSdyoung 	IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
210287515e34Sskrll 	memset(&scan, 0, sizeof(scan));
21031df75eefSmaxv 	scan.sp_tstamp  = frm;				frm += 8;
21041df75eefSmaxv 	scan.sp_bintval = le16toh(*(u_int16_t *)frm);	frm += 2;
21051df75eefSmaxv 	scan.sp_capinfo = le16toh(*(u_int16_t *)frm);	frm += 2;
21061df75eefSmaxv 	scan.sp_bchan = ieee80211_chan2ieee(ic, ic->ic_curchan);
21071df75eefSmaxv 	scan.sp_chan = scan.sp_bchan;
210887515e34Sskrll 
210924fb50b3Smaxv 	while (frm + 1 < efrm) {
211024fb50b3Smaxv 		IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2);
211124fb50b3Smaxv 
211240e261aaSdyoung 		switch (*frm) {
211340e261aaSdyoung 		case IEEE80211_ELEMID_SSID:
21143b005d6bSmaxv 			/* no length check needed */
21151df75eefSmaxv 			scan.sp_ssid = frm;
211640e261aaSdyoung 			break;
211740e261aaSdyoung 		case IEEE80211_ELEMID_RATES:
21183b005d6bSmaxv 			/* no length check needed */
21191df75eefSmaxv 			scan.sp_rates = frm;
212040e261aaSdyoung 			break;
212140e261aaSdyoung 		case IEEE80211_ELEMID_COUNTRY:
21223b005d6bSmaxv 			/* XXX: we don't do anything with this? */
21231df75eefSmaxv 			scan.sp_country = frm;
212440e261aaSdyoung 			break;
212540e261aaSdyoung 		case IEEE80211_ELEMID_FHPARMS:
2126e54b750fSmaxv 			IEEE80211_VERIFY_LENGTH(frm[1], 5);
212740e261aaSdyoung 			if (ic->ic_phytype == IEEE80211_T_FH) {
21281df75eefSmaxv 				scan.sp_fhdwell = LE_READ_2(&frm[2]);
21291df75eefSmaxv 				scan.sp_chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
21301df75eefSmaxv 				scan.sp_fhindex = frm[6];
213140e261aaSdyoung 			}
213240e261aaSdyoung 			break;
213340e261aaSdyoung 		case IEEE80211_ELEMID_DSPARMS:
213440e261aaSdyoung 			/*
213540e261aaSdyoung 			 * XXX hack this since depending on phytype
213640e261aaSdyoung 			 * is problematic for multi-mode devices.
213740e261aaSdyoung 			 */
2138e54b750fSmaxv 			IEEE80211_VERIFY_LENGTH(frm[1], 1);
213940e261aaSdyoung 			if (ic->ic_phytype != IEEE80211_T_FH)
21401df75eefSmaxv 				scan.sp_chan = frm[2];
214140e261aaSdyoung 			break;
214240e261aaSdyoung 		case IEEE80211_ELEMID_TIM:
214390634029Sdyoung 			/* XXX ATIM? */
214403cb4450Smaxv 			IEEE80211_VERIFY_LENGTH(frm[1], 4);
21451df75eefSmaxv 			scan.sp_tim = frm;
21461df75eefSmaxv 			scan.sp_timoff = frm - mtod(m0, u_int8_t *);
214740e261aaSdyoung 			break;
21481d6dd12bSdyoung 		case IEEE80211_ELEMID_IBSSPARMS:
21491d6dd12bSdyoung 			break;
215040e261aaSdyoung 		case IEEE80211_ELEMID_XRATES:
21511df75eefSmaxv 			scan.sp_xrates = frm;
215240e261aaSdyoung 			break;
215340e261aaSdyoung 		case IEEE80211_ELEMID_ERP:
215440e261aaSdyoung 			if (frm[1] != 1) {
21550170309aSmaxv 				IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID,
21560170309aSmaxv 				    wh, "ERP", "bad len %u", frm[1]);
21579280f4b4Sdyoung 				ic->ic_stats.is_rx_elem_toobig++;
215840e261aaSdyoung 				break;
215940e261aaSdyoung 			}
21601df75eefSmaxv 			scan.sp_erp = frm[2];
216140e261aaSdyoung 			break;
216290634029Sdyoung 		case IEEE80211_ELEMID_RSN:
21633b005d6bSmaxv 			/* no length check needed */
21641df75eefSmaxv 			scan.sp_wpa = frm;
216590634029Sdyoung 			break;
216690634029Sdyoung 		case IEEE80211_ELEMID_VENDOR:
21673b005d6bSmaxv 			/* no length check needed */
216890634029Sdyoung 			if (iswpaoui(frm))
21691df75eefSmaxv 				scan.sp_wpa = frm;
217090634029Sdyoung 			else if (iswmeparam(frm) || iswmeinfo(frm))
21711df75eefSmaxv 				scan.sp_wme = frm;
217290634029Sdyoung 			/* XXX Atheros OUI support */
217390634029Sdyoung 			break;
217440e261aaSdyoung 		default:
217590634029Sdyoung 			IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID,
21760170309aSmaxv 			    wh, "unhandled", "id %u, len %u", *frm, frm[1]);
21779280f4b4Sdyoung 			ic->ic_stats.is_rx_elem_unknown++;
217840e261aaSdyoung 			break;
217940e261aaSdyoung 		}
218024fb50b3Smaxv 
218140e261aaSdyoung 		frm += frm[1] + 2;
218240e261aaSdyoung 	}
218324fb50b3Smaxv 
21841df75eefSmaxv 	IEEE80211_VERIFY_ELEMENT(scan.sp_rates, IEEE80211_RATE_MAXSIZE);
21851df75eefSmaxv 	IEEE80211_VERIFY_ELEMENT(scan.sp_ssid, IEEE80211_NWID_LEN);
218624fb50b3Smaxv 
218740e261aaSdyoung 	if (
218840e261aaSdyoung #if IEEE80211_CHAN_MAX < 255
21891df75eefSmaxv 	    scan.sp_chan > IEEE80211_CHAN_MAX ||
219040e261aaSdyoung #endif
21911df75eefSmaxv 	    isclr(ic->ic_chan_active, scan.sp_chan)) {
219287515e34Sskrll 		IEEE80211_DISCARD(ic,
219387515e34Sskrll 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
219490634029Sdyoung 		    wh, ieee80211_mgt_subtype_name[subtype >>
219590634029Sdyoung 			IEEE80211_FC0_SUBTYPE_SHIFT],
21961df75eefSmaxv 		    "invalid channel %u", scan.sp_chan);
21979280f4b4Sdyoung 		ic->ic_stats.is_rx_badchan++;
219840e261aaSdyoung 		return;
219940e261aaSdyoung 	}
22000170309aSmaxv 
22011df75eefSmaxv 	if (scan.sp_chan != scan.sp_bchan &&
220287515e34Sskrll 	    ic->ic_phytype != IEEE80211_T_FH) {
220340e261aaSdyoung 		/*
220440e261aaSdyoung 		 * Frame was received on a channel different from the
2205b001a515Sonoe 		 * one indicated in the DS params element id;
220640e261aaSdyoung 		 * silently discard it.
220740e261aaSdyoung 		 *
220840e261aaSdyoung 		 * NB: this can happen due to signal leakage.
2209b001a515Sonoe 		 *     But we should take it for FH phy because
2210b001a515Sonoe 		 *     the rssi value should be correct even for
2211b001a515Sonoe 		 *     different hop pattern in FH.
221240e261aaSdyoung 		 */
221387515e34Sskrll 		IEEE80211_DISCARD(ic,
221487515e34Sskrll 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
221590634029Sdyoung 		    wh, ieee80211_mgt_subtype_name[subtype >>
221690634029Sdyoung 			IEEE80211_FC0_SUBTYPE_SHIFT],
22171df75eefSmaxv 		    "for off-channel %u", scan.sp_chan);
22189280f4b4Sdyoung 		ic->ic_stats.is_rx_chanmismatch++;
221940e261aaSdyoung 		return;
222040e261aaSdyoung 	}
22210170309aSmaxv 
22221df75eefSmaxv 	if (!(IEEE80211_BINTVAL_MIN <= scan.sp_bintval &&
22231df75eefSmaxv 	      scan.sp_bintval <= IEEE80211_BINTVAL_MAX)) {
222487515e34Sskrll 		IEEE80211_DISCARD(ic,
222587515e34Sskrll 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
222687515e34Sskrll 		    wh, ieee80211_mgt_subtype_name[subtype >>
222787515e34Sskrll 			IEEE80211_FC0_SUBTYPE_SHIFT],
22281df75eefSmaxv 		    "bogus beacon interval", scan.sp_bintval);
222987515e34Sskrll 		ic->ic_stats.is_rx_badbintval++;
223087515e34Sskrll 		return;
223187515e34Sskrll 	}
223240e261aaSdyoung 
2233fdf8c16fSdyoung 	if (ni != ic->ic_bss) {
2234fdf8c16fSdyoung 		ni = ieee80211_refine_node_for_beacon(ic, ni,
22351df75eefSmaxv 		    &ic->ic_channels[scan.sp_chan], scan.sp_ssid);
2236fdf8c16fSdyoung 	}
22370170309aSmaxv 
223840e261aaSdyoung 	/*
223990634029Sdyoung 	 * Count frame now that we know it's to be processed.
224040e261aaSdyoung 	 */
224190634029Sdyoung 	if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
224211a42b5cSmaxv 		ic->ic_stats.is_rx_beacon++;
224390634029Sdyoung 		IEEE80211_NODE_STAT(ni, rx_beacons);
22440170309aSmaxv 	} else {
224590634029Sdyoung 		IEEE80211_NODE_STAT(ni, rx_proberesp);
22460170309aSmaxv 	}
224790634029Sdyoung 
224890634029Sdyoung 	/*
224990634029Sdyoung 	 * When operating in station mode, check for state updates.
225090634029Sdyoung 	 * Be careful to ignore beacons received while doing a
225190634029Sdyoung 	 * background scan.  We consider only 11g/WMM stuff right now.
225290634029Sdyoung 	 */
22530170309aSmaxv 	if (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd != 0 &&
225490634029Sdyoung 	    ((ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
225590634029Sdyoung 	     IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) {
225680b91eeaSdyoung 		/* record tsf of last beacon */
22571df75eefSmaxv 		memcpy(ni->ni_tstamp.data, scan.sp_tstamp, sizeof(ni->ni_tstamp));
22580170309aSmaxv 
22591df75eefSmaxv 		if (ni->ni_erp != scan.sp_erp) {
226090634029Sdyoung 			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
226190634029Sdyoung 			    "[%s] erp change: was 0x%x, now 0x%x\n",
2262c2ec5838Schristos 			    ether_snprintf(ebuf, sizeof(ebuf),
22631df75eefSmaxv 			    wh->i_addr2), ni->ni_erp, scan.sp_erp);
2264a45976c2Smaxv 
226580b91eeaSdyoung 			if (ic->ic_curmode == IEEE80211_MODE_11G &&
2266a45976c2Smaxv 			    (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) {
226790634029Sdyoung 				ic->ic_flags |= IEEE80211_F_USEPROT;
2268a45976c2Smaxv 			} else {
226990634029Sdyoung 				ic->ic_flags &= ~IEEE80211_F_USEPROT;
2270a45976c2Smaxv 			}
22711df75eefSmaxv 			ni->ni_erp = scan.sp_erp;
227290634029Sdyoung 		}
22730170309aSmaxv 
22741df75eefSmaxv 		if ((ni->ni_capinfo ^ scan.sp_capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
227590634029Sdyoung 			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
227690634029Sdyoung 			    "[%s] capabilities change: before 0x%x,"
227790634029Sdyoung 			     " now 0x%x\n",
2278c2ec5838Schristos 			     ether_snprintf(ebuf, sizeof(ebuf),
2279c2ec5838Schristos 			     wh->i_addr2),
22801df75eefSmaxv 			     ni->ni_capinfo, scan.sp_capinfo);
228190634029Sdyoung 			/*
228290634029Sdyoung 			 * NB: we assume short preamble doesn't
228390634029Sdyoung 			 *     change dynamically
228490634029Sdyoung 			 */
228590634029Sdyoung 			ieee80211_set_shortslottime(ic,
228690634029Sdyoung 			    ic->ic_curmode == IEEE80211_MODE_11A ||
228790634029Sdyoung 			    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
22881df75eefSmaxv 			ni->ni_capinfo = scan.sp_capinfo;
228990634029Sdyoung 		}
22900170309aSmaxv 
22911df75eefSmaxv 		if (scan.sp_wme != NULL && (ni->ni_flags & IEEE80211_NODE_QOS) &&
22921df75eefSmaxv 		    ieee80211_parse_wmeparams(ic, scan.sp_wme, wh) > 0) {
229390634029Sdyoung 			ieee80211_wme_updateparams(ic);
22940170309aSmaxv 		}
22950170309aSmaxv 
22961df75eefSmaxv 		if (scan.sp_tim != NULL) {
229780b91eeaSdyoung 			struct ieee80211_tim_ie *ie =
22981df75eefSmaxv 			    (struct ieee80211_tim_ie *)scan.sp_tim;
229980b91eeaSdyoung 
230080b91eeaSdyoung 			ni->ni_dtim_count = ie->tim_count;
230180b91eeaSdyoung 			ni->ni_dtim_period = ie->tim_period;
230280b91eeaSdyoung 		}
23030170309aSmaxv 
23040170309aSmaxv 		if (ic->ic_flags & IEEE80211_F_SCAN) {
23050170309aSmaxv 			ieee80211_add_scan(ic, &scan, wh, subtype, rssi,
23060170309aSmaxv 			    rstamp);
23070170309aSmaxv 		}
23080170309aSmaxv 
230925e9e914Sdyoung 		ic->ic_bmiss_count = 0;
231090634029Sdyoung 		return;
231190634029Sdyoung 	}
231285acfa9cSmaxv 
231390634029Sdyoung 	/*
231487515e34Sskrll 	 * If scanning, just pass information to the scan module.
231590634029Sdyoung 	 */
231687515e34Sskrll 	if (ic->ic_flags & IEEE80211_F_SCAN) {
23173fb751b1Stacha 		if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
23183fb751b1Stacha 			/*
23193fb751b1Stacha 			 * Actively scanning a channel marked passive;
23203fb751b1Stacha 			 * send a probe request now that we know there
23213fb751b1Stacha 			 * is 802.11 traffic present.
23223fb751b1Stacha 			 *
23233fb751b1Stacha 			 * XXX check if the beacon we recv'd gives
23243fb751b1Stacha 			 * us what we need and suppress the probe req
23253fb751b1Stacha 			 */
23263fb751b1Stacha 			ieee80211_probe_curchan(ic, 1);
23273fb751b1Stacha 			ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
23283fb751b1Stacha 		}
23290170309aSmaxv 		ieee80211_add_scan(ic, &scan, wh, subtype, rssi, rstamp);
233090634029Sdyoung 		return;
233188a8480eSmycroft 	}
23320170309aSmaxv 
23331df75eefSmaxv 	if (scan.sp_capinfo & IEEE80211_CAPINFO_IBSS) {
233485acfa9cSmaxv 		ieee80211_update_adhoc_node(ic, ni, wh, &scan, rssi, rstamp);
233540e261aaSdyoung 	}
23360170309aSmaxv }
233740e261aaSdyoung 
233885acfa9cSmaxv static void
ieee80211_recv_mgmt_probe_req(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)233985acfa9cSmaxv ieee80211_recv_mgmt_probe_req(struct ieee80211com *ic, struct mbuf *m0,
234085acfa9cSmaxv     struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
234185acfa9cSmaxv {
234285acfa9cSmaxv 	struct ieee80211_frame *wh;
234385acfa9cSmaxv 	u_int8_t *frm, *efrm;
234485acfa9cSmaxv 	u_int8_t *ssid, *rates, *xrates;
2345a45976c2Smaxv 	bool need_free = false;
234685acfa9cSmaxv 	u_int8_t rate;
234785acfa9cSmaxv 	IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
234885acfa9cSmaxv 
234985acfa9cSmaxv 	wh = mtod(m0, struct ieee80211_frame *);
235085acfa9cSmaxv 	frm = (u_int8_t *)(wh + 1);
235185acfa9cSmaxv 	efrm = mtod(m0, u_int8_t *) + m0->m_len;
235285acfa9cSmaxv 
235390634029Sdyoung 	if (ic->ic_opmode == IEEE80211_M_STA ||
235490634029Sdyoung 	    ic->ic_state != IEEE80211_S_RUN) {
235590634029Sdyoung 		ic->ic_stats.is_rx_mgtdiscard++;
235640e261aaSdyoung 		return;
235790634029Sdyoung 	}
235890634029Sdyoung 	if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
235990634029Sdyoung 		/* frame must be directed */
2360a45976c2Smaxv 		ic->ic_stats.is_rx_mgtdiscard++;
236140e261aaSdyoung 		return;
236290634029Sdyoung 	}
236340e261aaSdyoung 
236440e261aaSdyoung 	/*
236540e261aaSdyoung 	 * prreq frame format
236640e261aaSdyoung 	 *	[tlv] ssid
236740e261aaSdyoung 	 *	[tlv] supported rates
236840e261aaSdyoung 	 *	[tlv] extended supported rates
236940e261aaSdyoung 	 */
237040e261aaSdyoung 	ssid = rates = xrates = NULL;
2371a45976c2Smaxv 	while (frm + 1 < efrm) {
2372a45976c2Smaxv 		IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2);
2373a45976c2Smaxv 
237440e261aaSdyoung 		switch (*frm) {
237540e261aaSdyoung 		case IEEE80211_ELEMID_SSID:
237640e261aaSdyoung 			ssid = frm;
237740e261aaSdyoung 			break;
237840e261aaSdyoung 		case IEEE80211_ELEMID_RATES:
237940e261aaSdyoung 			rates = frm;
238040e261aaSdyoung 			break;
238140e261aaSdyoung 		case IEEE80211_ELEMID_XRATES:
238240e261aaSdyoung 			xrates = frm;
238340e261aaSdyoung 			break;
238440e261aaSdyoung 		}
2385a45976c2Smaxv 
238640e261aaSdyoung 		frm += frm[1] + 2;
238740e261aaSdyoung 	}
23880170309aSmaxv 
238940e261aaSdyoung 	IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
239040e261aaSdyoung 	IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
239190634029Sdyoung 	IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
23920170309aSmaxv 
239390634029Sdyoung 	if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
239490634029Sdyoung 		IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
239590634029Sdyoung 		    wh, ieee80211_mgt_subtype_name[subtype >>
239690634029Sdyoung 		    IEEE80211_FC0_SUBTYPE_SHIFT],
239790634029Sdyoung 		    "%s", "no ssid with ssid suppression enabled");
239890634029Sdyoung 		ic->ic_stats.is_rx_ssidmismatch++; /*XXX*/
239990634029Sdyoung 		return;
240090634029Sdyoung 	}
240140e261aaSdyoung 
240240e261aaSdyoung 	if (ni == ic->ic_bss) {
24030170309aSmaxv 		if (ic->ic_opmode != IEEE80211_M_IBSS) {
2404461cd4dbSdyoung 			ni = ieee80211_tmp_node(ic, wh->i_addr2);
2405a45976c2Smaxv 			need_free = true;
24060170309aSmaxv 		} else if (IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
2407461cd4dbSdyoung 			;
24080170309aSmaxv 		} else {
240990634029Sdyoung 			/*
241090634029Sdyoung 			 * XXX Cannot tell if the sender is operating
241190634029Sdyoung 			 * in ibss mode.  But we need a new node to
241290634029Sdyoung 			 * send the response so blindly add them to the
241390634029Sdyoung 			 * neighbor table.
241490634029Sdyoung 			 */
241590634029Sdyoung 			ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
241690634029Sdyoung 				wh->i_addr2);
2417461cd4dbSdyoung 		}
24181d6dd12bSdyoung 		if (ni == NULL)
241940e261aaSdyoung 			return;
24200170309aSmaxv 	}
24210170309aSmaxv 
24220170309aSmaxv 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, "[%s] recv probe req\n",
24230170309aSmaxv 	    ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2));
24240170309aSmaxv 
242540e261aaSdyoung 	ni->ni_rssi = rssi;
242640e261aaSdyoung 	ni->ni_rstamp = rstamp;
242787515e34Sskrll 	rate = ieee80211_setup_rates(ni, rates, xrates,
2428a45976c2Smaxv 	    IEEE80211_R_DOSORT | IEEE80211_R_DOFRATE |
2429a45976c2Smaxv 	    IEEE80211_R_DONEGO | IEEE80211_R_DODEL);
24300170309aSmaxv 
243140e261aaSdyoung 	if (rate & IEEE80211_RATE_BASIC) {
243290634029Sdyoung 		IEEE80211_DISCARD(ic, IEEE80211_MSG_XRATE,
243390634029Sdyoung 		    wh, ieee80211_mgt_subtype_name[subtype >>
243490634029Sdyoung 		    IEEE80211_FC0_SUBTYPE_SHIFT],
243590634029Sdyoung 		    "%s", "recv'd rate set invalid");
243640e261aaSdyoung 	} else {
243740e261aaSdyoung 		IEEE80211_SEND_MGMT(ic, ni,
243840e261aaSdyoung 		    IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
243940e261aaSdyoung 	}
24400170309aSmaxv 
2441a45976c2Smaxv 	if (need_free) {
244290634029Sdyoung 		/* reclaim immediately */
244390634029Sdyoung 		ieee80211_free_node(ni);
244490634029Sdyoung 	}
244585acfa9cSmaxv }
244685acfa9cSmaxv 
2447f940273bSmaxv static void
ieee80211_recv_mgmt_auth(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)2448f940273bSmaxv ieee80211_recv_mgmt_auth(struct ieee80211com *ic, struct mbuf *m0,
244985acfa9cSmaxv     struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
245085acfa9cSmaxv {
245185acfa9cSmaxv 	struct ieee80211_frame *wh;
245285acfa9cSmaxv 	u_int8_t *frm, *efrm;
245385acfa9cSmaxv 	IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
24540170309aSmaxv 	u_int16_t algo, seq, status;
245585acfa9cSmaxv 
245685acfa9cSmaxv 	wh = mtod(m0, struct ieee80211_frame *);
245785acfa9cSmaxv 	frm = (u_int8_t *)(wh + 1);
245885acfa9cSmaxv 	efrm = mtod(m0, u_int8_t *) + m0->m_len;
245985acfa9cSmaxv 
246040e261aaSdyoung 	/*
246140e261aaSdyoung 	 * auth frame format
246240e261aaSdyoung 	 *	[2] algorithm
246340e261aaSdyoung 	 *	[2] sequence
246440e261aaSdyoung 	 *	[2] status
246540e261aaSdyoung 	 *	[tlv*] challenge
246640e261aaSdyoung 	 */
246740e261aaSdyoung 	IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
246840e261aaSdyoung 	algo   = le16toh(*(u_int16_t *)frm);
246940e261aaSdyoung 	seq    = le16toh(*(u_int16_t *)(frm + 2));
247040e261aaSdyoung 	status = le16toh(*(u_int16_t *)(frm + 4));
24710170309aSmaxv 
2472f0546001Smycroft 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
247390634029Sdyoung 	    "[%s] recv auth frame with algorithm %d seq %d\n",
2474c2ec5838Schristos 	    ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2), algo, seq);
24750170309aSmaxv 
247690634029Sdyoung 	/*
247790634029Sdyoung 	 * Consult the ACL policy module if setup.
247890634029Sdyoung 	 */
24790170309aSmaxv 	if (ic->ic_acl != NULL && !ic->ic_acl->iac_check(ic, wh->i_addr2)) {
248090634029Sdyoung 		IEEE80211_DISCARD(ic, IEEE80211_MSG_ACL,
248190634029Sdyoung 		    wh, "auth", "%s", "disallowed by ACL");
248290634029Sdyoung 		ic->ic_stats.is_rx_acl++;
248387515e34Sskrll 		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
248487515e34Sskrll 			IEEE80211_SEND_MGMT(ic, ni,
248587515e34Sskrll 			    IEEE80211_FC0_SUBTYPE_AUTH,
248687515e34Sskrll 			    (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16));
248787515e34Sskrll 		}
248890634029Sdyoung 		return;
248990634029Sdyoung 	}
24900170309aSmaxv 
249190634029Sdyoung 	if (ic->ic_flags & IEEE80211_F_COUNTERM) {
249290634029Sdyoung 		IEEE80211_DISCARD(ic,
249390634029Sdyoung 		    IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
249490634029Sdyoung 		    wh, "auth", "%s", "TKIP countermeasures enabled");
249590634029Sdyoung 		ic->ic_stats.is_rx_auth_countermeasures++;
249674988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
249790634029Sdyoung 		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
249890634029Sdyoung 			IEEE80211_SEND_MGMT(ic, ni,
249990634029Sdyoung 				IEEE80211_FC0_SUBTYPE_AUTH,
250090634029Sdyoung 				IEEE80211_REASON_MIC_FAILURE);
250190634029Sdyoung 		}
25020170309aSmaxv #endif
250390634029Sdyoung 		return;
250490634029Sdyoung 	}
25050170309aSmaxv 
2506a45976c2Smaxv 	if (algo == IEEE80211_AUTH_ALG_SHARED) {
2507db0e43b2Sdyoung 		ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi,
2508db0e43b2Sdyoung 		    rstamp, seq, status);
2509a45976c2Smaxv 	} else if (algo == IEEE80211_AUTH_ALG_OPEN) {
2510a45976c2Smaxv 		ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq, status);
2511a45976c2Smaxv 	} else {
251290634029Sdyoung 		IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
251390634029Sdyoung 		    wh, "auth", "unsupported alg %d", algo);
25149280f4b4Sdyoung 		ic->ic_stats.is_rx_auth_unsupported++;
251574988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
251690634029Sdyoung 		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
251790634029Sdyoung 			/* XXX not right */
251890634029Sdyoung 			IEEE80211_SEND_MGMT(ic, ni,
251990634029Sdyoung 				IEEE80211_FC0_SUBTYPE_AUTH,
252090634029Sdyoung 				(seq+1) | (IEEE80211_STATUS_ALG<<16));
252190634029Sdyoung 		}
25220170309aSmaxv #endif
252340e261aaSdyoung 	}
252440e261aaSdyoung }
252540e261aaSdyoung 
25266d9e139fSmaxv static void
ieee80211_recv_mgmt_assoc_req(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)25276d9e139fSmaxv ieee80211_recv_mgmt_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
2528f940273bSmaxv     struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
2529f940273bSmaxv {
2530f940273bSmaxv 	struct ieee80211_frame *wh;
2531f940273bSmaxv 	u_int8_t *frm, *efrm;
2532f940273bSmaxv 	u_int8_t *ssid, *rates, *xrates, *wpa, *wme;
2533f940273bSmaxv 	int reassoc, resp;
2534f940273bSmaxv 	u_int8_t rate;
2535f940273bSmaxv 	IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
25360170309aSmaxv 	u_int16_t capinfo, lintval;
25370170309aSmaxv 	struct ieee80211_rsnparms rsn;
25380170309aSmaxv 	u_int8_t reason;
2539f940273bSmaxv 
2540f940273bSmaxv 	wh = mtod(m0, struct ieee80211_frame *);
2541f940273bSmaxv 	frm = (u_int8_t *)(wh + 1);
2542f940273bSmaxv 	efrm = mtod(m0, u_int8_t *) + m0->m_len;
2543f940273bSmaxv 
254440e261aaSdyoung 	if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
254590634029Sdyoung 	    ic->ic_state != IEEE80211_S_RUN) {
254690634029Sdyoung 		ic->ic_stats.is_rx_mgtdiscard++;
254740e261aaSdyoung 		return;
254890634029Sdyoung 	}
254940e261aaSdyoung 
255040e261aaSdyoung 	if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
255140e261aaSdyoung 		reassoc = 1;
255240e261aaSdyoung 		resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
255340e261aaSdyoung 	} else {
255440e261aaSdyoung 		reassoc = 0;
255540e261aaSdyoung 		resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
255640e261aaSdyoung 	}
25570170309aSmaxv 
255840e261aaSdyoung 	/*
255940e261aaSdyoung 	 * asreq frame format
256040e261aaSdyoung 	 *	[2] capability information
256140e261aaSdyoung 	 *	[2] listen interval
256240e261aaSdyoung 	 *	[6*] current AP address (reassoc only)
256340e261aaSdyoung 	 *	[tlv] ssid
256440e261aaSdyoung 	 *	[tlv] supported rates
256540e261aaSdyoung 	 *	[tlv] extended supported rates
256690634029Sdyoung 	 *	[tlv] WPA or RSN
256740e261aaSdyoung 	 */
256840e261aaSdyoung 	IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4));
2569a45976c2Smaxv 
257040e261aaSdyoung 	if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {
257190634029Sdyoung 		IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
257290634029Sdyoung 		    wh, ieee80211_mgt_subtype_name[subtype >>
257390634029Sdyoung 			IEEE80211_FC0_SUBTYPE_SHIFT],
257490634029Sdyoung 		    "%s", "wrong bssid");
25759280f4b4Sdyoung 		ic->ic_stats.is_rx_assoc_bss++;
257640e261aaSdyoung 		return;
257740e261aaSdyoung 	}
25780170309aSmaxv 
257940e261aaSdyoung 	capinfo = le16toh(*(u_int16_t *)frm);	frm += 2;
258087515e34Sskrll 	lintval = le16toh(*(u_int16_t *)frm);	frm += 2;
258140e261aaSdyoung 	if (reassoc)
258240e261aaSdyoung 		frm += 6;	/* ignore current AP info */
25830170309aSmaxv 
258490634029Sdyoung 	ssid = rates = xrates = wpa = wme = NULL;
2585a45976c2Smaxv 	while (frm + 1 < efrm) {
2586a45976c2Smaxv 		IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2);
2587a45976c2Smaxv 
258840e261aaSdyoung 		switch (*frm) {
258940e261aaSdyoung 		case IEEE80211_ELEMID_SSID:
259040e261aaSdyoung 			ssid = frm;
259140e261aaSdyoung 			break;
259240e261aaSdyoung 		case IEEE80211_ELEMID_RATES:
259340e261aaSdyoung 			rates = frm;
259440e261aaSdyoung 			break;
259540e261aaSdyoung 		case IEEE80211_ELEMID_XRATES:
259640e261aaSdyoung 			xrates = frm;
259740e261aaSdyoung 			break;
259890634029Sdyoung 		/* XXX verify only one of RSN and WPA ie's? */
259990634029Sdyoung 		case IEEE80211_ELEMID_RSN:
260090634029Sdyoung 			wpa = frm;
260190634029Sdyoung 			break;
260290634029Sdyoung 		case IEEE80211_ELEMID_VENDOR:
260387515e34Sskrll 			if (iswpaoui(frm))
260490634029Sdyoung 				wpa = frm;
260587515e34Sskrll 			else if (iswmeinfo(frm))
260690634029Sdyoung 				wme = frm;
260790634029Sdyoung 			/* XXX Atheros OUI support */
260890634029Sdyoung 			break;
260940e261aaSdyoung 		}
2610a45976c2Smaxv 
261140e261aaSdyoung 		frm += frm[1] + 2;
261240e261aaSdyoung 	}
26130170309aSmaxv 
261440e261aaSdyoung 	IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
261540e261aaSdyoung 	IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
261690634029Sdyoung 	IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
261775f8366dSmycroft 
261890634029Sdyoung 	if (ni == ic->ic_bss) {
2619f0546001Smycroft 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
262090634029Sdyoung 		    "[%s] deny %s request, sta not authenticated\n",
2621c2ec5838Schristos 		    ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
262290634029Sdyoung 		    reassoc ? "reassoc" : "assoc");
262380b91eeaSdyoung 		ieee80211_send_error(ic, ni, wh->i_addr2,
262440e261aaSdyoung 		    IEEE80211_FC0_SUBTYPE_DEAUTH,
262540e261aaSdyoung 		    IEEE80211_REASON_ASSOC_NOT_AUTHED);
26269280f4b4Sdyoung 		ic->ic_stats.is_rx_assoc_notauth++;
262740e261aaSdyoung 		return;
262840e261aaSdyoung 	}
26290170309aSmaxv 
2630a45976c2Smaxv 	/* assert right association security credentials */
263190634029Sdyoung 	if (wpa == NULL && (ic->ic_flags & IEEE80211_F_WPA)) {
263290634029Sdyoung 		IEEE80211_DPRINTF(ic,
263390634029Sdyoung 		    IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
263490634029Sdyoung 		    "[%s] no WPA/RSN IE in association request\n",
2635c2ec5838Schristos 		    ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2));
2636a45976c2Smaxv 
263790634029Sdyoung 		IEEE80211_SEND_MGMT(ic, ni,
263890634029Sdyoung 		    IEEE80211_FC0_SUBTYPE_DEAUTH,
263990634029Sdyoung 		    IEEE80211_REASON_RSN_REQUIRED);
264090634029Sdyoung 		ieee80211_node_leave(ic, ni);
2641a45976c2Smaxv 
264290634029Sdyoung 		/* XXX distinguish WPA/RSN? */
264390634029Sdyoung 		ic->ic_stats.is_rx_assoc_badwpaie++;
264490634029Sdyoung 		return;
264590634029Sdyoung 	}
26460170309aSmaxv 
264790634029Sdyoung 	if (wpa != NULL) {
264890634029Sdyoung 		/*
264990634029Sdyoung 		 * Parse WPA information element.  Note that
265090634029Sdyoung 		 * we initialize the param block from the node
265190634029Sdyoung 		 * state so that information in the IE overrides
265290634029Sdyoung 		 * our defaults.  The resulting parameters are
265390634029Sdyoung 		 * installed below after the association is assured.
265490634029Sdyoung 		 */
265590634029Sdyoung 		rsn = ni->ni_rsn;
265690634029Sdyoung 		if (wpa[0] != IEEE80211_ELEMID_RSN)
265790634029Sdyoung 			reason = ieee80211_parse_wpa(ic, wpa, &rsn, wh);
265890634029Sdyoung 		else
265990634029Sdyoung 			reason = ieee80211_parse_rsn(ic, wpa, &rsn, wh);
2660a45976c2Smaxv 
266190634029Sdyoung 		if (reason != 0) {
266290634029Sdyoung 			IEEE80211_SEND_MGMT(ic, ni,
266390634029Sdyoung 			    IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
266490634029Sdyoung 			ieee80211_node_leave(ic, ni);
2665a45976c2Smaxv 
266690634029Sdyoung 			/* XXX distinguish WPA/RSN? */
266790634029Sdyoung 			ic->ic_stats.is_rx_assoc_badwpaie++;
266890634029Sdyoung 			return;
266990634029Sdyoung 		}
2670a45976c2Smaxv 
267190634029Sdyoung 		IEEE80211_DPRINTF(ic,
267290634029Sdyoung 		    IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
267390634029Sdyoung 		    "[%s] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n",
2674c2ec5838Schristos 		    ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
267590634029Sdyoung 		    wpa[0] != IEEE80211_ELEMID_RSN ?  "WPA" : "RSN",
267690634029Sdyoung 		    rsn.rsn_mcastcipher, rsn.rsn_mcastkeylen,
267790634029Sdyoung 		    rsn.rsn_ucastcipher, rsn.rsn_ucastkeylen,
267890634029Sdyoung 		    rsn.rsn_keymgmt, rsn.rsn_caps);
267990634029Sdyoung 	}
26800170309aSmaxv 
2681db0e43b2Sdyoung 	/* discard challenge after association */
2682db0e43b2Sdyoung 	if (ni->ni_challenge != NULL) {
26839b87d582Scegger 		free(ni->ni_challenge, M_DEVBUF);
2684db0e43b2Sdyoung 		ni->ni_challenge = NULL;
2685db0e43b2Sdyoung 	}
26860170309aSmaxv 
268780b91eeaSdyoung 	/* NB: 802.11 spec says to ignore station's privacy bit */
268880b91eeaSdyoung 	if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) {
2689f0546001Smycroft 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
269090634029Sdyoung 		    "[%s] deny %s request, capability mismatch 0x%x\n",
2691c2ec5838Schristos 		    ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
269290634029Sdyoung 		    reassoc ? "reassoc" : "assoc", capinfo);
269340e261aaSdyoung 		IEEE80211_SEND_MGMT(ic, ni, resp,
269440e261aaSdyoung 			IEEE80211_STATUS_CAPINFO);
269535515831Smycroft 		ieee80211_node_leave(ic, ni);
26969280f4b4Sdyoung 		ic->ic_stats.is_rx_assoc_capmismatch++;
269740e261aaSdyoung 		return;
269840e261aaSdyoung 	}
26990170309aSmaxv 
270087515e34Sskrll 	rate = ieee80211_setup_rates(ni, rates, xrates,
270197827674Schristos 	    IEEE80211_R_DOSORT | IEEE80211_R_DOFRATE |
270297827674Schristos 	    IEEE80211_R_DONEGO | IEEE80211_R_DODEL);
27030170309aSmaxv 
270480b91eeaSdyoung 	/*
270580b91eeaSdyoung 	 * If constrained to 11g-only stations reject an
270680b91eeaSdyoung 	 * 11b-only station.  We cheat a bit here by looking
270780b91eeaSdyoung 	 * at the max negotiated xmit rate and assuming anyone
270880b91eeaSdyoung 	 * with a best rate <24Mb/s is an 11b station.
270980b91eeaSdyoung 	 */
271080b91eeaSdyoung 	if ((rate & IEEE80211_RATE_BASIC) ||
271180b91eeaSdyoung 	    ((ic->ic_flags & IEEE80211_F_PUREG) && rate < 48)) {
2712f0546001Smycroft 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
271390634029Sdyoung 		    "[%s] deny %s request, rate set mismatch\n",
2714c2ec5838Schristos 		    ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
271590634029Sdyoung 		    reassoc ? "reassoc" : "assoc");
271640e261aaSdyoung 		IEEE80211_SEND_MGMT(ic, ni, resp,
271740e261aaSdyoung 			IEEE80211_STATUS_BASIC_RATE);
271835515831Smycroft 		ieee80211_node_leave(ic, ni);
27199280f4b4Sdyoung 		ic->ic_stats.is_rx_assoc_norate++;
272040e261aaSdyoung 		return;
272140e261aaSdyoung 	}
27220170309aSmaxv 
272340e261aaSdyoung 	ni->ni_rssi = rssi;
272440e261aaSdyoung 	ni->ni_rstamp = rstamp;
272587515e34Sskrll 	ni->ni_intval = lintval;
272640e261aaSdyoung 	ni->ni_capinfo = capinfo;
272740e261aaSdyoung 	ni->ni_chan = ic->ic_bss->ni_chan;
272840e261aaSdyoung 	ni->ni_fhdwell = ic->ic_bss->ni_fhdwell;
272940e261aaSdyoung 	ni->ni_fhindex = ic->ic_bss->ni_fhindex;
27300170309aSmaxv 
273190634029Sdyoung 	if (wpa != NULL) {
273290634029Sdyoung 		/*
273390634029Sdyoung 		 * Record WPA/RSN parameters for station, mark
273490634029Sdyoung 		 * node as using WPA and record information element
273590634029Sdyoung 		 * for applications that require it.
273690634029Sdyoung 		 */
273790634029Sdyoung 		ni->ni_rsn = rsn;
273890634029Sdyoung 		ieee80211_saveie(&ni->ni_wpa_ie, wpa);
273990634029Sdyoung 	} else if (ni->ni_wpa_ie != NULL) {
274090634029Sdyoung 		/*
274190634029Sdyoung 		 * Flush any state from a previous association.
274290634029Sdyoung 		 */
27439b87d582Scegger 		free(ni->ni_wpa_ie, M_DEVBUF);
274490634029Sdyoung 		ni->ni_wpa_ie = NULL;
274590634029Sdyoung 	}
27460170309aSmaxv 
274790634029Sdyoung 	if (wme != NULL) {
274890634029Sdyoung 		/*
274990634029Sdyoung 		 * Record WME parameters for station, mark node
275090634029Sdyoung 		 * as capable of QoS and record information
275190634029Sdyoung 		 * element for applications that require it.
275290634029Sdyoung 		 */
275390634029Sdyoung 		ieee80211_saveie(&ni->ni_wme_ie, wme);
275490634029Sdyoung 		ni->ni_flags |= IEEE80211_NODE_QOS;
275590634029Sdyoung 	} else if (ni->ni_wme_ie != NULL) {
275690634029Sdyoung 		/*
275790634029Sdyoung 		 * Flush any state from a previous association.
275890634029Sdyoung 		 */
27599b87d582Scegger 		free(ni->ni_wme_ie, M_DEVBUF);
276090634029Sdyoung 		ni->ni_wme_ie = NULL;
276190634029Sdyoung 		ni->ni_flags &= ~IEEE80211_NODE_QOS;
276290634029Sdyoung 	}
27630170309aSmaxv 
276435515831Smycroft 	ieee80211_node_join(ic, ni, resp);
276540e261aaSdyoung }
276640e261aaSdyoung 
276724023c75Smaxv #define	ISREASSOC(_st)	((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
27686d9e139fSmaxv 
276924023c75Smaxv static void
ieee80211_recv_mgmt_assoc_resp(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)277024023c75Smaxv ieee80211_recv_mgmt_assoc_resp(struct ieee80211com *ic, struct mbuf *m0,
27716d9e139fSmaxv     struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
27726d9e139fSmaxv {
27736d9e139fSmaxv 	struct ieee80211_frame *wh;
27746d9e139fSmaxv 	u_int8_t *frm, *efrm;
2775a45976c2Smaxv 	u_int8_t *rates, *xrates, *wme;
27766d9e139fSmaxv 	u_int8_t rate;
27776d9e139fSmaxv 	IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
27780170309aSmaxv 	u_int16_t capinfo, associd;
27790170309aSmaxv 	u_int16_t status;
27806d9e139fSmaxv 
27816d9e139fSmaxv 	wh = mtod(m0, struct ieee80211_frame *);
27826d9e139fSmaxv 	frm = (u_int8_t *)(wh + 1);
27836d9e139fSmaxv 	efrm = mtod(m0, u_int8_t *) + m0->m_len;
27846d9e139fSmaxv 
278540e261aaSdyoung 	if (ic->ic_opmode != IEEE80211_M_STA ||
2786a02b46e8Smycroft 	    ic->ic_state != IEEE80211_S_ASSOC) {
2787a02b46e8Smycroft 		ic->ic_stats.is_rx_mgtdiscard++;
278840e261aaSdyoung 		return;
2789a02b46e8Smycroft 	}
279040e261aaSdyoung 
279140e261aaSdyoung 	/*
279240e261aaSdyoung 	 * asresp frame format
279340e261aaSdyoung 	 *	[2] capability information
279440e261aaSdyoung 	 *	[2] status
279540e261aaSdyoung 	 *	[2] association ID
279640e261aaSdyoung 	 *	[tlv] supported rates
279740e261aaSdyoung 	 *	[tlv] extended supported rates
279890634029Sdyoung 	 *	[tlv] WME
279940e261aaSdyoung 	 */
280040e261aaSdyoung 	IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
280140e261aaSdyoung 	ni = ic->ic_bss;
280290634029Sdyoung 	capinfo = le16toh(*(u_int16_t *)frm);
280340e261aaSdyoung 	frm += 2;
280440e261aaSdyoung 	status = le16toh(*(u_int16_t *)frm);
280540e261aaSdyoung 	frm += 2;
280640e261aaSdyoung 	if (status != 0) {
2807a02b46e8Smycroft 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
280890634029Sdyoung 		    "[%s] %sassoc failed (reason %d)\n",
2809c2ec5838Schristos 		    ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
281090634029Sdyoung 		    ISREASSOC(subtype) ?  "re" : "", status);
281190634029Sdyoung 		if (ni != ic->ic_bss)	/* XXX never true? */
281240e261aaSdyoung 			ni->ni_fails++;
281390634029Sdyoung 		ic->ic_stats.is_rx_auth_fail++;	/* XXX */
281440e261aaSdyoung 		return;
281540e261aaSdyoung 	}
281690634029Sdyoung 	associd = le16toh(*(u_int16_t *)frm);
281740e261aaSdyoung 	frm += 2;
281840e261aaSdyoung 
2819a45976c2Smaxv 	rates = xrates = wme = NULL;
2820a45976c2Smaxv 	while (frm + 1 < efrm) {
2821a45976c2Smaxv 		IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2);
2822a45976c2Smaxv 
282340e261aaSdyoung 		switch (*frm) {
282440e261aaSdyoung 		case IEEE80211_ELEMID_RATES:
282540e261aaSdyoung 			rates = frm;
282640e261aaSdyoung 			break;
282740e261aaSdyoung 		case IEEE80211_ELEMID_XRATES:
282840e261aaSdyoung 			xrates = frm;
282940e261aaSdyoung 			break;
283090634029Sdyoung 		case IEEE80211_ELEMID_VENDOR:
283190634029Sdyoung 			if (iswmeoui(frm))
283290634029Sdyoung 				wme = frm;
283390634029Sdyoung 			/* XXX Atheros OUI support */
283490634029Sdyoung 			break;
283540e261aaSdyoung 		}
2836a45976c2Smaxv 
283740e261aaSdyoung 		frm += frm[1] + 2;
283840e261aaSdyoung 	}
283940e261aaSdyoung 
284040e261aaSdyoung 	IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
2841a45976c2Smaxv 
284287515e34Sskrll 	rate = ieee80211_setup_rates(ni, rates, xrates,
284397827674Schristos 	    IEEE80211_R_DOSORT | IEEE80211_R_DOFRATE |
284497827674Schristos 	    IEEE80211_R_DONEGO | IEEE80211_R_DODEL);
2845a45976c2Smaxv 
284680b91eeaSdyoung 	if (rate & IEEE80211_RATE_BASIC) {
284790634029Sdyoung 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
284890634029Sdyoung 		    "[%s] %sassoc failed (rate set mismatch)\n",
2849c2ec5838Schristos 		    ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
285090634029Sdyoung 		    ISREASSOC(subtype) ?  "re" : "");
285190634029Sdyoung 		if (ni != ic->ic_bss)	/* XXX never true? */
285290634029Sdyoung 			ni->ni_fails++;
285390634029Sdyoung 		ic->ic_stats.is_rx_assoc_norate++;
285480b91eeaSdyoung 		ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
285590634029Sdyoung 		return;
285690634029Sdyoung 	}
285790634029Sdyoung 
285890634029Sdyoung 	ni->ni_capinfo = capinfo;
285990634029Sdyoung 	ni->ni_associd = associd;
2860a45976c2Smaxv 	if (wme != NULL && ieee80211_parse_wmeparams(ic, wme, wh) >= 0) {
286190634029Sdyoung 		ni->ni_flags |= IEEE80211_NODE_QOS;
286290634029Sdyoung 		ieee80211_wme_updateparams(ic);
28630170309aSmaxv 	} else {
286490634029Sdyoung 		ni->ni_flags &= ~IEEE80211_NODE_QOS;
28650170309aSmaxv 	}
28660170309aSmaxv 
286790634029Sdyoung 	/*
286890634029Sdyoung 	 * Configure state now that we are associated.
286990634029Sdyoung 	 *
287090634029Sdyoung 	 * XXX may need different/additional driver callbacks?
287190634029Sdyoung 	 */
287290634029Sdyoung 	if (ic->ic_curmode == IEEE80211_MODE_11A ||
287390634029Sdyoung 	    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
287490634029Sdyoung 		ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
287590634029Sdyoung 		ic->ic_flags &= ~IEEE80211_F_USEBARKER;
287690634029Sdyoung 	} else {
287790634029Sdyoung 		ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
287890634029Sdyoung 		ic->ic_flags |= IEEE80211_F_USEBARKER;
287990634029Sdyoung 	}
288090634029Sdyoung 	ieee80211_set_shortslottime(ic,
288190634029Sdyoung 	    ic->ic_curmode == IEEE80211_MODE_11A ||
288290634029Sdyoung 	    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
28830170309aSmaxv 
288490634029Sdyoung 	/*
288590634029Sdyoung 	 * Honor ERP protection.
288690634029Sdyoung 	 *
288790634029Sdyoung 	 * NB: ni_erp should zero for non-11g operation.
288890634029Sdyoung 	 * XXX check ic_curmode anyway?
288990634029Sdyoung 	 */
289080b91eeaSdyoung 	if (ic->ic_curmode == IEEE80211_MODE_11G &&
2891a45976c2Smaxv 	    (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) {
289290634029Sdyoung 		ic->ic_flags |= IEEE80211_F_USEPROT;
2893a45976c2Smaxv 	} else {
289490634029Sdyoung 		ic->ic_flags &= ~IEEE80211_F_USEPROT;
2895a45976c2Smaxv 	}
28960170309aSmaxv 
289790634029Sdyoung 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
289890634029Sdyoung 	    "[%s] %sassoc success: %s preamble, %s slot time%s%s\n",
2899c2ec5838Schristos 	    ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
290090634029Sdyoung 	    ISREASSOC(subtype) ? "re" : "",
290190634029Sdyoung 	    ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
290290634029Sdyoung 	    ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
290390634029Sdyoung 	    ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
290490634029Sdyoung 	    ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : ""
290590634029Sdyoung 	);
29060170309aSmaxv 
290790634029Sdyoung 	ieee80211_new_state(ic, IEEE80211_S_RUN, subtype);
290840e261aaSdyoung }
290940e261aaSdyoung 
2910e51554e1Smaxv static void
ieee80211_recv_mgmt_deauth(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)2911e51554e1Smaxv ieee80211_recv_mgmt_deauth(struct ieee80211com *ic, struct mbuf *m0,
2912e51554e1Smaxv     struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
2913e51554e1Smaxv {
2914e51554e1Smaxv 	struct ieee80211_frame *wh;
2915e51554e1Smaxv 	u_int8_t *frm, *efrm;
2916e51554e1Smaxv 	IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
29170170309aSmaxv 	u_int16_t reason;
2918e51554e1Smaxv 
2919e51554e1Smaxv 	wh = mtod(m0, struct ieee80211_frame *);
2920e51554e1Smaxv 	frm = (u_int8_t *)(wh + 1);
2921e51554e1Smaxv 	efrm = mtod(m0, u_int8_t *) + m0->m_len;
2922e51554e1Smaxv 
2923e51554e1Smaxv 	if (ic->ic_state == IEEE80211_S_SCAN) {
2924e51554e1Smaxv 		ic->ic_stats.is_rx_mgtdiscard++;
2925e51554e1Smaxv 		return;
2926e51554e1Smaxv 	}
29270170309aSmaxv 
2928e51554e1Smaxv 	/*
2929e51554e1Smaxv 	 * deauth frame format
2930e51554e1Smaxv 	 *	[2] reason
2931e51554e1Smaxv 	 */
2932e51554e1Smaxv 	IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
2933e51554e1Smaxv 	reason = le16toh(*(u_int16_t *)frm);
2934e51554e1Smaxv 	__USE(reason);
2935e51554e1Smaxv 	ic->ic_stats.is_rx_deauth++;
2936e51554e1Smaxv 	IEEE80211_NODE_STAT(ni, rx_deauth);
2937e51554e1Smaxv 
2938e51554e1Smaxv 	if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
2939e51554e1Smaxv 		/* Not intended for this station. */
2940e51554e1Smaxv 		ic->ic_stats.is_rx_mgtdiscard++;
2941e51554e1Smaxv 		return;
2942e51554e1Smaxv 	}
29430170309aSmaxv 
2944e51554e1Smaxv 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
2945e51554e1Smaxv 	    "[%s] recv deauthenticate (reason %d)\n",
2946e51554e1Smaxv 	    ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr), reason);
29470170309aSmaxv 
2948e51554e1Smaxv 	switch (ic->ic_opmode) {
2949e51554e1Smaxv 	case IEEE80211_M_STA:
2950e51554e1Smaxv 		ieee80211_new_state(ic, IEEE80211_S_AUTH,
2951e51554e1Smaxv 		    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
2952e51554e1Smaxv 		break;
2953e51554e1Smaxv 	case IEEE80211_M_HOSTAP:
2954e51554e1Smaxv #ifndef IEEE80211_NO_HOSTAP
2955e51554e1Smaxv 		if (ni != ic->ic_bss)
2956e51554e1Smaxv 			ieee80211_node_leave(ic, ni);
29570170309aSmaxv #endif
2958e51554e1Smaxv 		break;
2959e51554e1Smaxv 	default:
2960e51554e1Smaxv 		ic->ic_stats.is_rx_mgtdiscard++;
2961e51554e1Smaxv 		break;
2962e51554e1Smaxv 	}
2963e51554e1Smaxv }
2964e51554e1Smaxv 
296582e96ed7Smaxv static void
ieee80211_recv_mgmt_disassoc(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)296682e96ed7Smaxv ieee80211_recv_mgmt_disassoc(struct ieee80211com *ic, struct mbuf *m0,
296724023c75Smaxv     struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
296824023c75Smaxv {
296924023c75Smaxv 	struct ieee80211_frame *wh;
297024023c75Smaxv 	u_int8_t *frm, *efrm;
297124023c75Smaxv 	IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
29720170309aSmaxv 	u_int16_t reason;
297324023c75Smaxv 
297424023c75Smaxv 	wh = mtod(m0, struct ieee80211_frame *);
297524023c75Smaxv 	frm = (u_int8_t *)(wh + 1);
297624023c75Smaxv 	efrm = mtod(m0, u_int8_t *) + m0->m_len;
297724023c75Smaxv 
297882e96ed7Smaxv 	if (ic->ic_state != IEEE80211_S_RUN &&
297982e96ed7Smaxv 	    ic->ic_state != IEEE80211_S_ASSOC &&
298082e96ed7Smaxv 	    ic->ic_state != IEEE80211_S_AUTH) {
298182e96ed7Smaxv 		ic->ic_stats.is_rx_mgtdiscard++;
298282e96ed7Smaxv 		return;
298382e96ed7Smaxv 	}
29840170309aSmaxv 
298582e96ed7Smaxv 	/*
298682e96ed7Smaxv 	 * disassoc frame format
298782e96ed7Smaxv 	 *	[2] reason
298882e96ed7Smaxv 	 */
298982e96ed7Smaxv 	IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
299082e96ed7Smaxv 	reason = le16toh(*(u_int16_t *)frm);
299182e96ed7Smaxv 	__USE(reason);
299282e96ed7Smaxv 	ic->ic_stats.is_rx_disassoc++;
299382e96ed7Smaxv 	IEEE80211_NODE_STAT(ni, rx_disassoc);
299482e96ed7Smaxv 
299582e96ed7Smaxv 	if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
299682e96ed7Smaxv 		/* Not intended for this station. */
299782e96ed7Smaxv 		ic->ic_stats.is_rx_mgtdiscard++;
299882e96ed7Smaxv 		return;
299982e96ed7Smaxv 	}
30000170309aSmaxv 
300182e96ed7Smaxv 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
300282e96ed7Smaxv 	    "[%s] recv disassociate (reason %d)\n",
300382e96ed7Smaxv 	    ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr), reason);
30040170309aSmaxv 
300582e96ed7Smaxv 	switch (ic->ic_opmode) {
300682e96ed7Smaxv 	case IEEE80211_M_STA:
300782e96ed7Smaxv 		ieee80211_new_state(ic, IEEE80211_S_ASSOC,
300882e96ed7Smaxv 		    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
300982e96ed7Smaxv 		break;
301082e96ed7Smaxv 	case IEEE80211_M_HOSTAP:
301182e96ed7Smaxv #ifndef IEEE80211_NO_HOSTAP
301282e96ed7Smaxv 		if (ni != ic->ic_bss)
301382e96ed7Smaxv 			ieee80211_node_leave(ic, ni);
30140170309aSmaxv #endif
301582e96ed7Smaxv 		break;
301682e96ed7Smaxv 	default:
301782e96ed7Smaxv 		ic->ic_stats.is_rx_mgtdiscard++;
301882e96ed7Smaxv 		break;
301982e96ed7Smaxv 	}
302082e96ed7Smaxv }
302182e96ed7Smaxv 
30221551d983Smaxv #undef ISREASSOC
30231551d983Smaxv #undef IEEE80211_VERIFY_LENGTH
30241551d983Smaxv #undef IEEE80211_VERIFY_ELEMENT
30251551d983Smaxv 
302682e96ed7Smaxv /* -------------------------------------------------------------------------- */
302782e96ed7Smaxv 
302882e96ed7Smaxv void
ieee80211_recv_mgmt(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)302982e96ed7Smaxv ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
303082e96ed7Smaxv     struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
303182e96ed7Smaxv {
303282e96ed7Smaxv 	struct ieee80211_frame *wh;
303382e96ed7Smaxv 
303482e96ed7Smaxv 	wh = mtod(m0, struct ieee80211_frame *);
303582e96ed7Smaxv 
303624023c75Smaxv 	switch (subtype) {
303724023c75Smaxv 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
303824023c75Smaxv 	case IEEE80211_FC0_SUBTYPE_BEACON:
303924023c75Smaxv 		ieee80211_recv_mgmt_beacon(ic, m0, ni, subtype, rssi, rstamp);
304024023c75Smaxv 		return;
304124023c75Smaxv 
304224023c75Smaxv 	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
304324023c75Smaxv 		ieee80211_recv_mgmt_probe_req(ic, m0, ni, subtype, rssi, rstamp);
304424023c75Smaxv 		return;
304524023c75Smaxv 
304624023c75Smaxv 	case IEEE80211_FC0_SUBTYPE_AUTH:
304724023c75Smaxv 		ieee80211_recv_mgmt_auth(ic, m0, ni, subtype, rssi, rstamp);
304824023c75Smaxv 		return;
304924023c75Smaxv 
305024023c75Smaxv 	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
305124023c75Smaxv 	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
305224023c75Smaxv 		ieee80211_recv_mgmt_assoc_req(ic, m0, ni, subtype, rssi, rstamp);
305324023c75Smaxv 		return;
305424023c75Smaxv 
305524023c75Smaxv 	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
305624023c75Smaxv 	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
305724023c75Smaxv 		ieee80211_recv_mgmt_assoc_resp(ic, m0, ni, subtype, rssi, rstamp);
305824023c75Smaxv 		return;
305924023c75Smaxv 
3060e51554e1Smaxv 	case IEEE80211_FC0_SUBTYPE_DEAUTH:
3061e51554e1Smaxv 		ieee80211_recv_mgmt_deauth(ic, m0, ni, subtype, rssi, rstamp);
306290634029Sdyoung 		return;
306340e261aaSdyoung 
306482e96ed7Smaxv 	case IEEE80211_FC0_SUBTYPE_DISASSOC:
306582e96ed7Smaxv 		ieee80211_recv_mgmt_disassoc(ic, m0, ni, subtype, rssi, rstamp);
306690634029Sdyoung 		return;
306780b91eeaSdyoung 
306840e261aaSdyoung 	default:
306990634029Sdyoung 		IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
307090634029Sdyoung 		     wh, "mgt", "subtype 0x%x not handled", subtype);
30719280f4b4Sdyoung 		ic->ic_stats.is_rx_badsubtype++;
307240e261aaSdyoung 		break;
307340e261aaSdyoung 	}
307490634029Sdyoung }
307582e96ed7Smaxv 
307674988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
307790634029Sdyoung /*
307890634029Sdyoung  * Handle station power-save state change.
307990634029Sdyoung  */
308090634029Sdyoung static void
ieee80211_node_pwrsave(struct ieee80211_node * ni,int enable)308190634029Sdyoung ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
308290634029Sdyoung {
308390634029Sdyoung 	struct ieee80211com *ic = ni->ni_ic;
308490634029Sdyoung 	struct mbuf *m;
3085c2ec5838Schristos 	IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
308690634029Sdyoung 
308790634029Sdyoung 	if (enable) {
308890634029Sdyoung 		if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
308990634029Sdyoung 			ic->ic_ps_sta++;
309090634029Sdyoung 		ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
309190634029Sdyoung 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
309290634029Sdyoung 		    "[%s] power save mode on, %u sta's in ps mode\n",
3093c2ec5838Schristos 		    ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr),
3094c2ec5838Schristos 		    ic->ic_ps_sta);
309590634029Sdyoung 		return;
309640e261aaSdyoung 	}
3097b8e804e8Sdyoung 
309890634029Sdyoung 	if (ni->ni_flags & IEEE80211_NODE_PWR_MGT)
309990634029Sdyoung 		ic->ic_ps_sta--;
310090634029Sdyoung 	ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
310190634029Sdyoung 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
310290634029Sdyoung 	    "[%s] power save mode off, %u sta's in ps mode\n",
3103c2ec5838Schristos 	    ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr), ic->ic_ps_sta);
310490634029Sdyoung 	/* XXX if no stations in ps mode, flush mc frames */
310590634029Sdyoung 
310690634029Sdyoung 	/*
310790634029Sdyoung 	 * Flush queued unicast frames.
310890634029Sdyoung 	 */
310990634029Sdyoung 	if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) {
311080b91eeaSdyoung 		if (ic->ic_set_tim != NULL)
311187515e34Sskrll 			ic->ic_set_tim(ni, 0);		/* just in case */
311290634029Sdyoung 		return;
311390634029Sdyoung 	}
3114a45976c2Smaxv 
311590634029Sdyoung 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
311690634029Sdyoung 	    "[%s] flush ps queue, %u packets queued\n",
3117c2ec5838Schristos 	    ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr),
3118c2ec5838Schristos 	    IEEE80211_NODE_SAVEQ_QLEN(ni));
3119a45976c2Smaxv 
312090634029Sdyoung 	for (;;) {
312190634029Sdyoung 		int qlen;
312290634029Sdyoung 
312390634029Sdyoung 		IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
312490634029Sdyoung 		if (m == NULL)
312590634029Sdyoung 			break;
3126a45976c2Smaxv 
312790634029Sdyoung 		/*
312890634029Sdyoung 		 * If this is the last packet, turn off the TIM bit.
312990634029Sdyoung 		 * If there are more packets, set the more packets bit
313080b91eeaSdyoung 		 * in the mbuf so ieee80211_encap will mark the 802.11
313180b91eeaSdyoung 		 * head to indicate more data frames will follow.
313290634029Sdyoung 		 */
313380b91eeaSdyoung 		if (qlen != 0)
313480b91eeaSdyoung 			m->m_flags |= M_MORE_DATA;
3135a45976c2Smaxv 
313690634029Sdyoung 		/* XXX need different driver interface */
313790634029Sdyoung 		/* XXX bypasses q max */
313890634029Sdyoung 		IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
313990634029Sdyoung 	}
3140a45976c2Smaxv 
314180b91eeaSdyoung 	if (ic->ic_set_tim != NULL)
314287515e34Sskrll 		ic->ic_set_tim(ni, 0);
314390634029Sdyoung }
314490634029Sdyoung 
314590634029Sdyoung /*
314690634029Sdyoung  * Process a received ps-poll frame.
314790634029Sdyoung  */
3148b8e804e8Sdyoung static void
ieee80211_recv_pspoll(struct ieee80211com * ic,struct ieee80211_node * ni,struct mbuf * m0)3149a45976c2Smaxv ieee80211_recv_pspoll(struct ieee80211com *ic, struct ieee80211_node *ni,
3150a45976c2Smaxv     struct mbuf *m0)
3151b8e804e8Sdyoung {
315290634029Sdyoung 	struct ieee80211_frame_min *wh;
3153b8e804e8Sdyoung 	struct mbuf *m;
3154b8e804e8Sdyoung 	u_int16_t aid;
315590634029Sdyoung 	int qlen;
3156c2ec5838Schristos 	IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
3157b8e804e8Sdyoung 
315890634029Sdyoung 	wh = mtod(m0, struct ieee80211_frame_min *);
315990634029Sdyoung 	if (ni->ni_associd == 0) {
316090634029Sdyoung 		IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
316190634029Sdyoung 		    (struct ieee80211_frame *)wh, "ps-poll",
316290634029Sdyoung 		    "%s", "unassociated station");
316390634029Sdyoung 		ic->ic_stats.is_ps_unassoc++;
316490634029Sdyoung 		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
316590634029Sdyoung 			IEEE80211_REASON_NOT_ASSOCED);
3166b8e804e8Sdyoung 		return;
3167b8e804e8Sdyoung 	}
3168b8e804e8Sdyoung 
316990634029Sdyoung 	aid = le16toh(*(u_int16_t *)wh->i_dur);
3170b8e804e8Sdyoung 	if (aid != ni->ni_associd) {
317190634029Sdyoung 		IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
317290634029Sdyoung 		    (struct ieee80211_frame *)wh, "ps-poll",
317390634029Sdyoung 		    "aid mismatch: sta aid 0x%x poll aid 0x%x",
3174b8e804e8Sdyoung 		    ni->ni_associd, aid);
317590634029Sdyoung 		ic->ic_stats.is_ps_badaid++;
317690634029Sdyoung 		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
317790634029Sdyoung 			IEEE80211_REASON_NOT_ASSOCED);
3178b8e804e8Sdyoung 		return;
3179b8e804e8Sdyoung 	}
3180b8e804e8Sdyoung 
3181b8e804e8Sdyoung 	/* Okay, take the first queued packet and put it out... */
318290634029Sdyoung 	IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
3183b8e804e8Sdyoung 	if (m == NULL) {
318490634029Sdyoung 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
318590634029Sdyoung 		    "[%s] recv ps-poll, but queue empty\n",
3186c2ec5838Schristos 		    ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2));
318787515e34Sskrll 		ieee80211_send_nulldata(ieee80211_ref_node(ni));
318890634029Sdyoung 		ic->ic_stats.is_ps_qempty++;	/* XXX node stat */
318980b91eeaSdyoung 		if (ic->ic_set_tim != NULL)
319087515e34Sskrll 			ic->ic_set_tim(ni, 0);	/* just in case */
3191b8e804e8Sdyoung 		return;
3192b8e804e8Sdyoung 	}
3193a45976c2Smaxv 
319490634029Sdyoung 	/*
319590634029Sdyoung 	 * If there are more packets, set the more packets bit
319690634029Sdyoung 	 * in the packet dispatched to the station; otherwise
319790634029Sdyoung 	 * turn off the TIM bit.
319890634029Sdyoung 	 */
319990634029Sdyoung 	if (qlen != 0) {
320090634029Sdyoung 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
320190634029Sdyoung 		    "[%s] recv ps-poll, send packet, %u still queued\n",
3202c2ec5838Schristos 		    ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr), qlen);
320380b91eeaSdyoung 		m->m_flags |= M_MORE_DATA;
320490634029Sdyoung 	} else {
320590634029Sdyoung 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
320690634029Sdyoung 		    "[%s] recv ps-poll, send packet, queue empty\n",
3207c2ec5838Schristos 		    ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr));
320880b91eeaSdyoung 		if (ic->ic_set_tim != NULL)
320987515e34Sskrll 			ic->ic_set_tim(ni, 0);
321090634029Sdyoung 	}
3211a45976c2Smaxv 
321290634029Sdyoung 	m->m_flags |= M_PWR_SAV;		/* bypass PS handling */
321390634029Sdyoung 	IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
321490634029Sdyoung }
321574988b0fSdyoung #endif /* !IEEE80211_NO_HOSTAP */
321690634029Sdyoung 
321790634029Sdyoung #ifdef IEEE80211_DEBUG
321890634029Sdyoung /*
321990634029Sdyoung  * Debugging support.
322090634029Sdyoung  */
3221b8e804e8Sdyoung 
3222b8e804e8Sdyoung /*
322390634029Sdyoung  * Return the bssid of a frame.
3224b8e804e8Sdyoung  */
322590634029Sdyoung static const u_int8_t *
ieee80211_getbssid(struct ieee80211com * ic,const struct ieee80211_frame * wh)322690634029Sdyoung ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh)
3227ce488d2bSdyoung {
322890634029Sdyoung 	if (ic->ic_opmode == IEEE80211_M_STA)
322990634029Sdyoung 		return wh->i_addr2;
323090634029Sdyoung 	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS)
323190634029Sdyoung 		return wh->i_addr1;
323290634029Sdyoung 	if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
323390634029Sdyoung 		return wh->i_addr1;
323490634029Sdyoung 	return wh->i_addr3;
3235ce488d2bSdyoung }
3236ce488d2bSdyoung 
323787515e34Sskrll void
ieee80211_note(struct ieee80211com * ic,const char * fmt,...)323887515e34Sskrll ieee80211_note(struct ieee80211com *ic, const char *fmt, ...)
323987515e34Sskrll {
324087515e34Sskrll 	char buf[128];		/* XXX */
324187515e34Sskrll 	va_list ap;
324287515e34Sskrll 
324387515e34Sskrll 	va_start(ap, fmt);
324487515e34Sskrll 	vsnprintf(buf, sizeof(buf), fmt, ap);
324587515e34Sskrll 	va_end(ap);
324687515e34Sskrll 
324787515e34Sskrll 	if_printf(ic->ic_ifp, "%s", buf);	/* NB: no \n */
324887515e34Sskrll }
324987515e34Sskrll 
325087515e34Sskrll void
ieee80211_note_mac(struct ieee80211com * ic,const u_int8_t mac[IEEE80211_ADDR_LEN],const char * fmt,...)325187515e34Sskrll ieee80211_note_mac(struct ieee80211com *ic,
325287515e34Sskrll 	const u_int8_t mac[IEEE80211_ADDR_LEN],
325387515e34Sskrll 	const char *fmt, ...)
325487515e34Sskrll {
325587515e34Sskrll 	char buf[128];		/* XXX */
325687515e34Sskrll 	va_list ap;
3257c2ec5838Schristos 	char ebuf[3 * ETHER_ADDR_LEN];
325887515e34Sskrll 
325987515e34Sskrll 	va_start(ap, fmt);
326087515e34Sskrll 	vsnprintf(buf, sizeof(buf), fmt, ap);
326187515e34Sskrll 	va_end(ap);
3262c2ec5838Schristos 	if_printf(ic->ic_ifp, "[%s] %s\n", ether_snprintf(ebuf, sizeof(ebuf),
3263c2ec5838Schristos 	    mac), buf);
326487515e34Sskrll }
326587515e34Sskrll 
326690634029Sdyoung static void
ieee80211_discard_frame(struct ieee80211com * ic,const struct ieee80211_frame * wh,const char * type,const char * fmt,...)326790634029Sdyoung ieee80211_discard_frame(struct ieee80211com *ic,
326890634029Sdyoung 	const struct ieee80211_frame *wh,
326990634029Sdyoung 	const char *type, const char *fmt, ...)
3270ce488d2bSdyoung {
327190634029Sdyoung 	va_list ap;
3272c2ec5838Schristos 	char ebuf[3 * ETHER_ADDR_LEN];
3273ce488d2bSdyoung 
327487515e34Sskrll 	printf("[%s:%s] discard ", ic->ic_ifp->if_xname,
3275c2ec5838Schristos 		ether_snprintf(ebuf, sizeof(ebuf), ieee80211_getbssid(ic, wh)));
327690634029Sdyoung 	if (type != NULL)
327790634029Sdyoung 		printf("%s frame, ", type);
327890634029Sdyoung 	else
327990634029Sdyoung 		printf("frame, ");
328090634029Sdyoung 	va_start(ap, fmt);
328190634029Sdyoung 	vprintf(fmt, ap);
328290634029Sdyoung 	va_end(ap);
328390634029Sdyoung 	printf("\n");
3284ce488d2bSdyoung }
3285ce488d2bSdyoung 
328690634029Sdyoung static void
ieee80211_discard_ie(struct ieee80211com * ic,const struct ieee80211_frame * wh,const char * type,const char * fmt,...)328790634029Sdyoung ieee80211_discard_ie(struct ieee80211com *ic,
328890634029Sdyoung 	const struct ieee80211_frame *wh,
328990634029Sdyoung 	const char *type, const char *fmt, ...)
329090634029Sdyoung {
329190634029Sdyoung 	va_list ap;
3292c2ec5838Schristos 	char ebuf[3 * ETHER_ADDR_LEN];
3293ce488d2bSdyoung 
329487515e34Sskrll 	printf("[%s:%s] discard ", ic->ic_ifp->if_xname,
3295c2ec5838Schristos 	    ether_snprintf(ebuf, sizeof(ebuf), ieee80211_getbssid(ic, wh)));
329690634029Sdyoung 	if (type != NULL)
329790634029Sdyoung 		printf("%s information element, ", type);
329890634029Sdyoung 	else
329990634029Sdyoung 		printf("information element, ");
330090634029Sdyoung 	va_start(ap, fmt);
330190634029Sdyoung 	vprintf(fmt, ap);
330290634029Sdyoung 	va_end(ap);
330390634029Sdyoung 	printf("\n");
3304ce488d2bSdyoung }
330590634029Sdyoung 
330690634029Sdyoung static void
ieee80211_discard_mac(struct ieee80211com * ic,const u_int8_t mac[IEEE80211_ADDR_LEN],const char * type,const char * fmt,...)330790634029Sdyoung ieee80211_discard_mac(struct ieee80211com *ic,
330890634029Sdyoung 	const u_int8_t mac[IEEE80211_ADDR_LEN],
330990634029Sdyoung 	const char *type, const char *fmt, ...)
331090634029Sdyoung {
331190634029Sdyoung 	va_list ap;
3312c2ec5838Schristos 	char ebuf[3 * ETHER_ADDR_LEN];
331390634029Sdyoung 
3314c2ec5838Schristos 	printf("[%s:%s] discard ", ic->ic_ifp->if_xname,
3315c2ec5838Schristos 	    ether_snprintf(ebuf, sizeof(ebuf), mac));
331690634029Sdyoung 	if (type != NULL)
331790634029Sdyoung 		printf("%s frame, ", type);
331890634029Sdyoung 	else
331990634029Sdyoung 		printf("frame, ");
332090634029Sdyoung 	va_start(ap, fmt);
332190634029Sdyoung 	vprintf(fmt, ap);
332290634029Sdyoung 	va_end(ap);
332390634029Sdyoung 	printf("\n");
332490634029Sdyoung }
332590634029Sdyoung #endif /* IEEE80211_DEBUG */
3326