ieee80211_sta.c revision 183247
146492Swpaul/*-
246492Swpaul * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
346492Swpaul * All rights reserved.
446492Swpaul *
546492Swpaul * Redistribution and use in source and binary forms, with or without
646492Swpaul * modification, are permitted provided that the following conditions
746492Swpaul * are met:
846492Swpaul * 1. Redistributions of source code must retain the above copyright
946492Swpaul *    notice, this list of conditions and the following disclaimer.
1046492Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1146492Swpaul *    notice, this list of conditions and the following disclaimer in the
1246492Swpaul *    documentation and/or other materials provided with the distribution.
1346492Swpaul *
1446492Swpaul * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1546492Swpaul * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1646492Swpaul * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1746492Swpaul * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1846492Swpaul * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1946492Swpaul * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2046492Swpaul * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2146492Swpaul * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2246492Swpaul * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2346492Swpaul * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2446492Swpaul */
2546492Swpaul
2646492Swpaul#include <sys/cdefs.h>
2746492Swpaul#ifdef __FreeBSD__
2846492Swpaul__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_sta.c 183247 2008-09-21 23:00:19Z sam $");
2946492Swpaul#endif
3046492Swpaul
3146492Swpaul/*
3250477Speter * IEEE 802.11 Station mode support.
3346492Swpaul */
3446492Swpaul#include "opt_inet.h"
3546492Swpaul#include "opt_wlan.h"
3646492Swpaul
3746492Swpaul#include <sys/param.h>
3846492Swpaul#include <sys/systm.h>
3946492Swpaul#include <sys/mbuf.h>
4046492Swpaul#include <sys/malloc.h>
4146492Swpaul#include <sys/kernel.h>
4246492Swpaul
4346492Swpaul#include <sys/socket.h>
4447401Swpaul#include <sys/sockio.h>
4546492Swpaul#include <sys/endian.h>
4646492Swpaul#include <sys/errno.h>
4746492Swpaul#include <sys/proc.h>
4846492Swpaul#include <sys/sysctl.h>
4946492Swpaul
5046492Swpaul#include <net/if.h>
5146492Swpaul#include <net/if_media.h>
5247401Swpaul#include <net/if_llc.h>
5346492Swpaul#include <net/ethernet.h>
5446492Swpaul
5546492Swpaul#include <net/bpf.h>
5646492Swpaul
5746492Swpaul#include <net80211/ieee80211_var.h>
5846492Swpaul#include <net80211/ieee80211_sta.h>
5946492Swpaul#include <net80211/ieee80211_input.h>
6046492Swpaul
6146492Swpaul#define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
6246492Swpaul
6346492Swpaulstatic	void sta_vattach(struct ieee80211vap *);
6446492Swpaulstatic	void sta_beacon_miss(struct ieee80211vap *);
6546492Swpaulstatic	int sta_newstate(struct ieee80211vap *, enum ieee80211_state, int);
6646492Swpaulstatic	int sta_input(struct ieee80211_node *, struct mbuf *,
6746492Swpaul	    int rssi, int noise, uint32_t rstamp);
6846492Swpaulstatic void sta_recv_mgmt(struct ieee80211_node *, struct mbuf *,
6953702Swpaul	    int subtype, int rssi, int noise, uint32_t rstamp);
7046492Swpaul
7146492Swpaulvoid
7246492Swpaulieee80211_sta_attach(struct ieee80211com *ic)
7346492Swpaul{
7446492Swpaul	ic->ic_vattach[IEEE80211_M_STA] = sta_vattach;
7546492Swpaul}
7661818Sroberto
7761818Srobertovoid
7846492Swpaulieee80211_sta_detach(struct ieee80211com *ic)
7953702Swpaul{
8053702Swpaul}
8153702Swpaul
8253702Swpaulstatic void
8346492Swpaulsta_vdetach(struct ieee80211vap *vap)
8453702Swpaul{
8553702Swpaul}
8653702Swpaul
8753702Swpaulstatic void
8853702Swpaulsta_vattach(struct ieee80211vap *vap)
8953702Swpaul{
9053702Swpaul	vap->iv_newstate = sta_newstate;
9146492Swpaul	vap->iv_input = sta_input;
9246492Swpaul	vap->iv_recv_mgmt = sta_recv_mgmt;
9346492Swpaul	vap->iv_opdetach = sta_vdetach;
9446492Swpaul	vap->iv_bmiss = sta_beacon_miss;
9546492Swpaul}
9646492Swpaul
9746492Swpaul/*
9846492Swpaul * Handle a beacon miss event.  The common code filters out
9946492Swpaul * spurious events that can happen when scanning and/or before
10046492Swpaul * reaching RUN state.
10146492Swpaul */
10246492Swpaulstatic void
10346492Swpaulsta_beacon_miss(struct ieee80211vap *vap)
10446492Swpaul{
10546492Swpaul	struct ieee80211com *ic = vap->iv_ic;
10653702Swpaul
10746492Swpaul	KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning"));
10846492Swpaul	KASSERT(vap->iv_state == IEEE80211_S_RUN,
10946492Swpaul	    ("wrong state %d", vap->iv_state));
11046492Swpaul
11150477Speter	IEEE80211_DPRINTF(vap,
11246492Swpaul		IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
11346492Swpaul		"beacon miss, mode %u state %s\n",
11446492Swpaul		vap->iv_opmode, ieee80211_state_name[vap->iv_state]);
11547401Swpaul
11646492Swpaul	if (++vap->iv_bmiss_count < vap->iv_bmiss_max) {
11746492Swpaul		/*
11853702Swpaul		 * Send a directed probe req before falling back to a
11946492Swpaul		 * scan; if we receive a response ic_bmiss_count will
12046492Swpaul		 * be reset.  Some cards mistakenly report beacon miss
12146492Swpaul		 * so this avoids the expensive scan if the ap is
12246492Swpaul		 * still there.
12346492Swpaul		 */
12446492Swpaul		ieee80211_send_probereq(vap->iv_bss, vap->iv_myaddr,
12546492Swpaul			vap->iv_bss->ni_bssid, vap->iv_bss->ni_bssid,
12646492Swpaul			vap->iv_bss->ni_essid, vap->iv_bss->ni_esslen);
12746492Swpaul		return;
12846492Swpaul	}
12946492Swpaul	vap->iv_bmiss_count = 0;
13046492Swpaul	vap->iv_stats.is_beacon_miss++;
13146492Swpaul	if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
13246492Swpaul		/*
13346492Swpaul		 * If we receive a beacon miss interrupt when using
13446492Swpaul		 * dynamic turbo, attempt to switch modes before
13546492Swpaul		 * reassociating.
13646492Swpaul		 */
13746492Swpaul		if (IEEE80211_ATH_CAP(vap, vap->iv_bss, IEEE80211_NODE_TURBOP))
13846492Swpaul			ieee80211_dturbo_switch(vap,
13946492Swpaul			    ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO);
14046492Swpaul		/*
14146492Swpaul		 * Try to reassociate before scanning for a new ap.
14246492Swpaul		 */
14353702Swpaul		ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1);
14453702Swpaul	} else {
14553702Swpaul		/*
14653702Swpaul		 * Somebody else is controlling state changes (e.g.
14753702Swpaul		 * a user-mode app) don't do anything that would
14853702Swpaul		 * confuse them; just drop into scan mode so they'll
14953702Swpaul		 * notified of the state change and given control.
15053702Swpaul		 */
15153702Swpaul		ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
15253702Swpaul	}
15353702Swpaul}
15453702Swpaul
15553702Swpaul/*
15653702Swpaul * Handle deauth with reason.  We retry only for
15753702Swpaul * the cases where we might succeed.  Otherwise
15853702Swpaul * we downgrade the ap and scan.
15953702Swpaul */
16053702Swpaulstatic void
16153702Swpaulsta_authretry(struct ieee80211vap *vap, struct ieee80211_node *ni, int reason)
16253702Swpaul{
16353702Swpaul	switch (reason) {
16453702Swpaul	case IEEE80211_STATUS_SUCCESS:		/* NB: MLME assoc */
16546492Swpaul	case IEEE80211_STATUS_TIMEOUT:
16646492Swpaul	case IEEE80211_REASON_ASSOC_EXPIRE:
16753702Swpaul	case IEEE80211_REASON_NOT_AUTHED:
16846492Swpaul	case IEEE80211_REASON_NOT_ASSOCED:
16953702Swpaul	case IEEE80211_REASON_ASSOC_LEAVE:
17053702Swpaul	case IEEE80211_REASON_ASSOC_NOT_AUTHED:
17146492Swpaul		IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, 1);
17246492Swpaul		break;
17353702Swpaul	default:
17446492Swpaul		ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, reason);
17553702Swpaul		if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
17653702Swpaul			ieee80211_check_scan_current(vap);
17753702Swpaul		break;
17853702Swpaul	}
17946492Swpaul}
18053702Swpaul
18153702Swpaul/*
18246492Swpaul * IEEE80211_M_STA vap state machine handler.
18353702Swpaul * This routine handles the main states in the 802.11 protocol.
18446492Swpaul */
18546492Swpaulstatic int
18653702Swpaulsta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
18753702Swpaul{
18853702Swpaul	struct ieee80211com *ic = vap->iv_ic;
18953702Swpaul	struct ieee80211_node *ni;
19053702Swpaul	enum ieee80211_state ostate;
19153702Swpaul
19253702Swpaul	IEEE80211_LOCK_ASSERT(ic);
19346492Swpaul
19446492Swpaul	ostate = vap->iv_state;
19546492Swpaul	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
19646492Swpaul	    __func__, ieee80211_state_name[ostate],
19753702Swpaul	    ieee80211_state_name[nstate], arg);
19846492Swpaul	vap->iv_state = nstate;			/* state transition */
19946492Swpaul	callout_stop(&vap->iv_mgtsend);		/* XXX callout_drain */
20053702Swpaul	if (ostate != IEEE80211_S_SCAN)
20153702Swpaul		ieee80211_cancel_scan(vap);	/* background scan */
20246492Swpaul	ni = vap->iv_bss;			/* NB: no reference held */
20346492Swpaul	if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
20446492Swpaul		callout_stop(&vap->iv_swbmiss);
20546492Swpaul	switch (nstate) {
20653702Swpaul	case IEEE80211_S_INIT:
20767092Swpaul		switch (ostate) {
20846492Swpaul		case IEEE80211_S_SLEEP:
20946492Swpaul			/* XXX wakeup */
21046492Swpaul		case IEEE80211_S_RUN:
21153702Swpaul			IEEE80211_SEND_MGMT(ni,
21267092Swpaul			    IEEE80211_FC0_SUBTYPE_DISASSOC,
21353702Swpaul			    IEEE80211_REASON_ASSOC_LEAVE);
21446492Swpaul			ieee80211_sta_leave(ni);
21546492Swpaul			break;
21653702Swpaul		case IEEE80211_S_ASSOC:
21758274Srwatson			IEEE80211_SEND_MGMT(ni,
21863090Sarchie			    IEEE80211_FC0_SUBTYPE_DEAUTH,
21954277Swpaul			    IEEE80211_REASON_AUTH_LEAVE);
22053702Swpaul			break;
22146492Swpaul		case IEEE80211_S_SCAN:
22246492Swpaul			ieee80211_cancel_scan(vap);
22353702Swpaul			break;
22467092Swpaul		default:
22567092Swpaul			goto invalid;
22646492Swpaul		}
22746492Swpaul		if (ostate != IEEE80211_S_INIT) {
22846492Swpaul			/* NB: optimize INIT -> INIT case */
22946492Swpaul			ieee80211_reset_bss(vap);
23053702Swpaul		}
23146492Swpaul		if (vap->iv_auth->ia_detach != NULL)
23246492Swpaul			vap->iv_auth->ia_detach(vap);
23346492Swpaul		break;
23446563Swpaul	case IEEE80211_S_SCAN:
23546492Swpaul		switch (ostate) {
23653702Swpaul		case IEEE80211_S_INIT:
23746492Swpaul			/*
23853702Swpaul			 * Initiate a scan.  We can come here as a result
23946492Swpaul			 * of an IEEE80211_IOC_SCAN_REQ too in which case
24046492Swpaul			 * the vap will be marked with IEEE80211_FEXT_SCANREQ
24153702Swpaul			 * and the scan request parameters will be present
24253702Swpaul			 * in iv_scanreq.  Otherwise we do the default.
24353702Swpaul			 */
24453702Swpaul			if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
24553702Swpaul				ieee80211_check_scan(vap,
24653702Swpaul				    vap->iv_scanreq_flags,
24753702Swpaul				    vap->iv_scanreq_duration,
24853702Swpaul				    vap->iv_scanreq_mindwell,
24953702Swpaul				    vap->iv_scanreq_maxdwell,
25053702Swpaul				    vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
25153702Swpaul				vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
25253702Swpaul			} else
25353702Swpaul				ieee80211_check_scan_current(vap);
25453702Swpaul			break;
25553702Swpaul		case IEEE80211_S_SCAN:
25667092Swpaul		case IEEE80211_S_AUTH:
25767092Swpaul		case IEEE80211_S_ASSOC:
25867092Swpaul			/*
25946492Swpaul			 * These can happen either because of a timeout
26046492Swpaul			 * on an assoc/auth response or because of a
26146492Swpaul			 * change in state that requires a reset.  For
26246492Swpaul			 * the former we're called with a non-zero arg
26346492Swpaul			 * that is the cause for the failure; pass this
26446492Swpaul			 * to the scan code so it can update state.
26546492Swpaul			 * Otherwise trigger a new scan unless we're in
26646492Swpaul			 * manual roaming mode in which case an application
26746492Swpaul			 * must issue an explicit scan request.
26846492Swpaul			 */
26953702Swpaul			if (arg != 0)
27046492Swpaul				ieee80211_scan_assoc_fail(vap,
27146492Swpaul					vap->iv_bss->ni_macaddr, arg);
27246492Swpaul			if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
27346492Swpaul				ieee80211_check_scan_current(vap);
27446492Swpaul			break;
27546492Swpaul		case IEEE80211_S_RUN:		/* beacon miss */
27646492Swpaul			/*
27746492Swpaul			 * Beacon miss.  Notify user space and if not
27846492Swpaul			 * under control of a user application (roaming
27946492Swpaul			 * manual) kick off a scan to re-connect.
28046492Swpaul			 */
28146492Swpaul			ieee80211_sta_leave(ni);
28246492Swpaul			if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
28346492Swpaul				ieee80211_check_scan_current(vap);
28446492Swpaul			break;
28546492Swpaul		default:
28646492Swpaul			goto invalid;
28746492Swpaul		}
28846492Swpaul		break;
28946492Swpaul	case IEEE80211_S_AUTH:
29046492Swpaul		switch (ostate) {
29146492Swpaul		case IEEE80211_S_INIT:
29246492Swpaul		case IEEE80211_S_SCAN:
29346492Swpaul			IEEE80211_SEND_MGMT(ni,
29446492Swpaul			    IEEE80211_FC0_SUBTYPE_AUTH, 1);
29546492Swpaul			break;
29646492Swpaul		case IEEE80211_S_AUTH:
29746492Swpaul		case IEEE80211_S_ASSOC:
29846492Swpaul			switch (arg & 0xff) {
29946492Swpaul			case IEEE80211_FC0_SUBTYPE_AUTH:
30046492Swpaul				/* ??? */
30146492Swpaul				IEEE80211_SEND_MGMT(ni,
30246492Swpaul				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
30346492Swpaul				break;
30446611Swpaul			case IEEE80211_FC0_SUBTYPE_DEAUTH:
30546611Swpaul				sta_authretry(vap, ni, arg>>8);
30646492Swpaul				break;
30746563Swpaul			}
30846563Swpaul			break;
30946563Swpaul		case IEEE80211_S_RUN:
31046563Swpaul			switch (arg & 0xff) {
31146563Swpaul			case IEEE80211_FC0_SUBTYPE_AUTH:
31246563Swpaul				IEEE80211_SEND_MGMT(ni,
31346563Swpaul				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
31446563Swpaul				vap->iv_state = ostate;	/* stay RUN */
31546563Swpaul				break;
31646563Swpaul			case IEEE80211_FC0_SUBTYPE_DEAUTH:
31746563Swpaul				ieee80211_sta_leave(ni);
31856965Swpaul				if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
31956965Swpaul					/* try to reauth */
32056965Swpaul					IEEE80211_SEND_MGMT(ni,
32156965Swpaul					    IEEE80211_FC0_SUBTYPE_AUTH, 1);
32256965Swpaul				}
32356965Swpaul				break;
32456965Swpaul			}
32556965Swpaul			break;
32646492Swpaul		default:
32746492Swpaul			goto invalid;
32846492Swpaul		}
32946492Swpaul		break;
33046492Swpaul	case IEEE80211_S_ASSOC:
33146492Swpaul		switch (ostate) {
33263090Sarchie		case IEEE80211_S_AUTH:
33346492Swpaul		case IEEE80211_S_ASSOC:
33463090Sarchie			IEEE80211_SEND_MGMT(ni,
33553702Swpaul			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
33667092Swpaul			break;
33746492Swpaul		case IEEE80211_S_SLEEP:		/* cannot happen */
33846492Swpaul		case IEEE80211_S_RUN:
33946492Swpaul			ieee80211_sta_leave(ni);
34046492Swpaul			if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
34146492Swpaul				IEEE80211_SEND_MGMT(ni, arg ?
34246492Swpaul				    IEEE80211_FC0_SUBTYPE_REASSOC_REQ :
34346492Swpaul				    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
34446492Swpaul			}
34546492Swpaul			break;
34646492Swpaul		default:
34746492Swpaul			goto invalid;
34846492Swpaul		}
34946492Swpaul		break;
35046492Swpaul	case IEEE80211_S_RUN:
35146492Swpaul		if (vap->iv_flags & IEEE80211_F_WPA) {
35246492Swpaul			/* XXX validate prerequisites */
35346492Swpaul		}
35446492Swpaul		switch (ostate) {
35546492Swpaul		case IEEE80211_S_RUN:
35646492Swpaul			break;
35746492Swpaul		case IEEE80211_S_AUTH:		/* when join is done in fw */
35846492Swpaul		case IEEE80211_S_ASSOC:
35946492Swpaul#ifdef IEEE80211_DEBUG
36046492Swpaul			if (ieee80211_msg_debug(vap)) {
36146492Swpaul				ieee80211_note(vap, "%s with %s ssid ",
36246492Swpaul				    (vap->iv_opmode == IEEE80211_M_STA ?
36346492Swpaul				    "associated" : "synchronized"),
36446492Swpaul				    ether_sprintf(ni->ni_bssid));
36546492Swpaul				ieee80211_print_essid(vap->iv_bss->ni_essid,
36646492Swpaul				    ni->ni_esslen);
36746492Swpaul				/* XXX MCS/HT */
36846492Swpaul				printf(" channel %d start %uMb\n",
36946492Swpaul				    ieee80211_chan2ieee(ic, ic->ic_curchan),
37046492Swpaul				    IEEE80211_RATE2MBS(ni->ni_txrate));
37146492Swpaul			}
37246492Swpaul#endif
37346492Swpaul			ieee80211_scan_assoc_success(vap, ni->ni_macaddr);
37446492Swpaul			ieee80211_notify_node_join(ni,
37546492Swpaul			    arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
37646492Swpaul			break;
37746492Swpaul		case IEEE80211_S_SLEEP:
37846492Swpaul			ieee80211_sta_pwrsave(vap, 0);
37946492Swpaul			break;
38046492Swpaul		default:
38146492Swpaul			goto invalid;
38246492Swpaul		}
38348553Swpaul		ieee80211_sync_curchan(ic);
38453702Swpaul		if (ostate != IEEE80211_S_RUN &&
38553702Swpaul		    (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)) {
38648553Swpaul			/*
38748553Swpaul			 * Start s/w beacon miss timer for devices w/o
38848553Swpaul			 * hardware support.  We fudge a bit here since
38948553Swpaul			 * we're doing this in software.
39048553Swpaul			 */
39146492Swpaul			vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS(
39246492Swpaul				2 * vap->iv_bmissthreshold * ni->ni_intval);
39346492Swpaul			vap->iv_swbmiss_count = 0;
39446492Swpaul			callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
39546492Swpaul				ieee80211_swbmiss, vap);
39659328Swpaul		}
39759328Swpaul		/*
39859328Swpaul		 * When 802.1x is not in use mark the port authorized
39959328Swpaul		 * at this point so traffic can flow.
40059328Swpaul		 */
40159328Swpaul		if (ni->ni_authmode != IEEE80211_AUTH_8021X)
40259328Swpaul			ieee80211_node_authorize(ni);
40346492Swpaul		break;
40446492Swpaul	case IEEE80211_S_SLEEP:
40546492Swpaul		ieee80211_sta_pwrsave(vap, 0);
40646492Swpaul		break;
40746492Swpaul	default:
40846492Swpaul	invalid:
40946492Swpaul		IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
41046492Swpaul		    "%s: invalid state transition %s -> %s\n", __func__,
41146492Swpaul		    ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
41246492Swpaul		break;
41346492Swpaul	}
41448553Swpaul	return 0;
41548553Swpaul}
41653702Swpaul
41753702Swpaul/*
41848553Swpaul * Return non-zero if the frame is an echo of a multicast
41948553Swpaul * frame sent by ourself.  The dir is known to be DSTODS.
42048553Swpaul */
42148553Swpaulstatic __inline int
42248553Swpaulisdstods_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
42346492Swpaul{
42446492Swpaul#define	QWH4(wh)	((const struct ieee80211_qosframe_addr4 *)wh)
42546492Swpaul#define	WH4(wh)		((const struct ieee80211_frame_addr4 *)wh)
42646492Swpaul	const uint8_t *sa;
42746492Swpaul
42846492Swpaul	KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode"));
42946492Swpaul
43046492Swpaul	if (!IEEE80211_IS_MULTICAST(wh->i_addr3))
43146492Swpaul		return 0;
43246492Swpaul	sa = IEEE80211_QOS_HAS_SEQ(wh) ? QWH4(wh)->i_addr4 : WH4(wh)->i_addr4;
43346492Swpaul	return IEEE80211_ADDR_EQ(sa, vap->iv_myaddr);
43446492Swpaul#undef WH4
43546492Swpaul#undef QWH4
43646492Swpaul}
43746492Swpaul
43853702Swpaul/*
43953702Swpaul * Return non-zero if the frame is an echo of a multicast
44053702Swpaul * frame sent by ourself.  The dir is known to be FROMDS.
44146492Swpaul */
44246492Swpaulstatic __inline int
44346492Swpaulisfromds_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
44446492Swpaul{
44546492Swpaul	KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode"));
44646492Swpaul
44746492Swpaul	if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
44846492Swpaul		return 0;
44946492Swpaul	return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr);
45046492Swpaul}
45146492Swpaul
45246492Swpaul/*
45346492Swpaul * Decide if a received management frame should be
45446492Swpaul * printed when debugging is enabled.  This filters some
45546492Swpaul * of the less interesting frames that come frequently
45646492Swpaul * (e.g. beacons).
45746492Swpaul */
45846492Swpaulstatic __inline int
45946492Swpauldoprint(struct ieee80211vap *vap, int subtype)
46046492Swpaul{
46146492Swpaul	switch (subtype) {
46246492Swpaul	case IEEE80211_FC0_SUBTYPE_BEACON:
46346492Swpaul		return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
46446492Swpaul	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
46546492Swpaul		return 0;
46646492Swpaul	}
46746492Swpaul	return 1;
46846492Swpaul}
46946492Swpaul
47046492Swpaul/*
47146492Swpaul * Process a received frame.  The node associated with the sender
47246492Swpaul * should be supplied.  If nothing was found in the node table then
47346492Swpaul * the caller is assumed to supply a reference to iv_bss instead.
47446492Swpaul * The RSSI and a timestamp are also supplied.  The RSSI data is used
47546492Swpaul * during AP scanning to select a AP to associate with; it can have
47646492Swpaul * any units so long as values have consistent units and higher values
47746492Swpaul * mean ``better signal''.  The receive timestamp is currently not used
47846492Swpaul * by the 802.11 layer.
47946492Swpaul */
48046492Swpaulstatic int
48146492Swpaulsta_input(struct ieee80211_node *ni, struct mbuf *m,
48246492Swpaul	int rssi, int noise, uint32_t rstamp)
48346492Swpaul{
48446492Swpaul#define	SEQ_LEQ(a,b)	((int)((a)-(b)) <= 0)
48546492Swpaul#define	HAS_SEQ(type)	((type & 0x4) == 0)
48646492Swpaul	struct ieee80211vap *vap = ni->ni_vap;
48746492Swpaul	struct ieee80211com *ic = ni->ni_ic;
48846492Swpaul	struct ifnet *ifp = vap->iv_ifp;
48946492Swpaul	struct ieee80211_frame *wh;
49046492Swpaul	struct ieee80211_key *key;
49146492Swpaul	struct ether_header *eh;
49246492Swpaul	int hdrspace, need_tap;
49346492Swpaul	uint8_t dir, type, subtype, qos;
49446492Swpaul	uint8_t *bssid;
49546492Swpaul	uint16_t rxseq;
49646492Swpaul
49746492Swpaul	if (m->m_flags & M_AMPDU_MPDU) {
49846492Swpaul		/*
49946492Swpaul		 * Fastpath for A-MPDU reorder q resubmission.  Frames
50046492Swpaul		 * w/ M_AMPDU_MPDU marked have already passed through
50146492Swpaul		 * here but were received out of order and been held on
50246492Swpaul		 * the reorder queue.  When resubmitted they are marked
50346492Swpaul		 * with the M_AMPDU_MPDU flag and we can bypass most of
50446492Swpaul		 * the normal processing.
50546492Swpaul		 */
50646492Swpaul		wh = mtod(m, struct ieee80211_frame *);
50746492Swpaul		type = IEEE80211_FC0_TYPE_DATA;
50846492Swpaul		dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
50946492Swpaul		subtype = IEEE80211_FC0_SUBTYPE_QOS;
51046492Swpaul		hdrspace = ieee80211_hdrspace(ic, wh);	/* XXX optimize? */
51146492Swpaul		goto resubmit_ampdu;
51246492Swpaul	}
51346492Swpaul
51446492Swpaul	KASSERT(ni != NULL, ("null node"));
51546492Swpaul	ni->ni_inact = ni->ni_inact_reload;
51646492Swpaul
51746492Swpaul	need_tap = 1;			/* mbuf need to be tapped. */
51846492Swpaul	type = -1;			/* undefined */
51946492Swpaul
52046492Swpaul	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
52153702Swpaul		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
52253702Swpaul		    ni->ni_macaddr, NULL,
52346492Swpaul		    "too short (1): len %u", m->m_pkthdr.len);
52453702Swpaul		vap->iv_stats.is_rx_tooshort++;
52546492Swpaul		goto out;
52646492Swpaul	}
52746492Swpaul	/*
52867092Swpaul	 * Bit of a cheat here, we use a pointer for a 3-address
52967092Swpaul	 * frame format but don't reference fields past outside
53046492Swpaul	 * ieee80211_frame_min w/o first validating the data is
53146492Swpaul	 * present.
53246492Swpaul	 */
53346492Swpaul	wh = mtod(m, struct ieee80211_frame *);
53446492Swpaul
53567092Swpaul	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
53646492Swpaul	    IEEE80211_FC0_VERSION_0) {
53746492Swpaul		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
53846492Swpaul		    ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
53946492Swpaul		vap->iv_stats.is_rx_badversion++;
54046492Swpaul		goto err;
54146492Swpaul	}
54246492Swpaul
54346492Swpaul	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
54446492Swpaul	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
54546492Swpaul	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
54646492Swpaul	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
54746492Swpaul		bssid = wh->i_addr2;
54846492Swpaul		if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
54946492Swpaul			/* not interested in */
55046492Swpaul			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
55146492Swpaul			    bssid, NULL, "%s", "not to bss");
55246492Swpaul			vap->iv_stats.is_rx_wrongbss++;
55346492Swpaul			goto out;
55446492Swpaul		}
55546492Swpaul		IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
55646492Swpaul		ni->ni_noise = noise;
55746492Swpaul		ni->ni_rstamp = rstamp;
55846492Swpaul		if (HAS_SEQ(type)) {
55946492Swpaul			uint8_t tid = ieee80211_gettid(wh);
56046492Swpaul			if (IEEE80211_QOS_HAS_SEQ(wh) &&
56146492Swpaul			    TID_TO_WME_AC(tid) >= WME_AC_VI)
56246492Swpaul				ic->ic_wme.wme_hipri_traffic++;
56346492Swpaul			rxseq = le16toh(*(uint16_t *)wh->i_seq);
56446492Swpaul			if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
56546492Swpaul			    (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
56646492Swpaul			    SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
56746492Swpaul				/* duplicate, discard */
56846492Swpaul				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
56946492Swpaul				    bssid, "duplicate",
57046492Swpaul				    "seqno <%u,%u> fragno <%u,%u> tid %u",
57146492Swpaul				    rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
57246492Swpaul				    ni->ni_rxseqs[tid] >>
57346492Swpaul					IEEE80211_SEQ_SEQ_SHIFT,
57446492Swpaul				    rxseq & IEEE80211_SEQ_FRAG_MASK,
57546492Swpaul				    ni->ni_rxseqs[tid] &
57646492Swpaul					IEEE80211_SEQ_FRAG_MASK,
57746492Swpaul				    tid);
57846492Swpaul				vap->iv_stats.is_rx_dup++;
57946492Swpaul				IEEE80211_NODE_STAT(ni, rx_dup);
58046492Swpaul				goto out;
58146492Swpaul			}
58246492Swpaul			ni->ni_rxseqs[tid] = rxseq;
58367092Swpaul		}
58467092Swpaul	}
58546492Swpaul
58646492Swpaul	switch (type) {
58746492Swpaul	case IEEE80211_FC0_TYPE_DATA:
58846492Swpaul		hdrspace = ieee80211_hdrspace(ic, wh);
58946492Swpaul		if (m->m_len < hdrspace &&
59046492Swpaul		    (m = m_pullup(m, hdrspace)) == NULL) {
59146492Swpaul			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
59246492Swpaul			    ni->ni_macaddr, NULL,
59346492Swpaul			    "data too short: expecting %u", hdrspace);
59446492Swpaul			vap->iv_stats.is_rx_tooshort++;
59546492Swpaul			goto out;		/* XXX */
59646492Swpaul		}
59746492Swpaul		/*
59846492Swpaul		 * Handle A-MPDU re-ordering.  If the frame is to be
59946492Swpaul		 * processed directly then ieee80211_ampdu_reorder
60046492Swpaul		 * will return 0; otherwise it has consumed the mbuf
60146492Swpaul		 * and we should do nothing more with it.
60246492Swpaul		 */
60346492Swpaul		if ((m->m_flags & M_AMPDU) &&
60446492Swpaul		    (dir == IEEE80211_FC1_DIR_FROMDS ||
60546492Swpaul		     dir == IEEE80211_FC1_DIR_DSTODS) &&
60646492Swpaul		    ieee80211_ampdu_reorder(ni, m) != 0) {
60746492Swpaul			m = NULL;
60846492Swpaul			goto out;
60946492Swpaul		}
61046492Swpaul	resubmit_ampdu:
61146492Swpaul		if (dir == IEEE80211_FC1_DIR_FROMDS) {
61246492Swpaul			if ((ifp->if_flags & IFF_SIMPLEX) &&
61346492Swpaul			    isfromds_mcastecho(vap, wh)) {
61446492Swpaul				/*
61546492Swpaul				 * In IEEE802.11 network, multicast
61646492Swpaul				 * packets sent from "me" are broadcast
61746492Swpaul				 * from the AP; silently discard for
61846492Swpaul				 * SIMPLEX interface.
61946492Swpaul				 */
62046492Swpaul				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
62146492Swpaul				    wh, "data", "%s", "multicast echo");
62246492Swpaul				vap->iv_stats.is_rx_mcastecho++;
62346492Swpaul				goto out;
62446492Swpaul			}
62546492Swpaul			if ((vap->iv_flags & IEEE80211_F_DWDS) &&
62646492Swpaul			    IEEE80211_IS_MULTICAST(wh->i_addr1)) {
62753702Swpaul				/*
62853702Swpaul				 * DWDS sta's must drop 3-address mcast frames
62953702Swpaul				 * as they will be sent separately as a 4-addr
63053702Swpaul				 * frame.  Accepting the 3-addr frame will
63153702Swpaul				 * confuse the bridge into thinking the sending
63246492Swpaul				 * sta is located at the end of WDS link.
63353702Swpaul				 */
63446492Swpaul				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
63546492Swpaul				    "3-address data", "%s", "DWDS enabled");
63646492Swpaul				vap->iv_stats.is_rx_mcastecho++;
63746492Swpaul				goto out;
63846492Swpaul			}
63953702Swpaul		} else if (dir == IEEE80211_FC1_DIR_DSTODS) {
64046492Swpaul			if ((vap->iv_flags & IEEE80211_F_DWDS) == 0) {
64146492Swpaul				IEEE80211_DISCARD(vap,
64246492Swpaul				    IEEE80211_MSG_INPUT, wh, "4-address data",
64346492Swpaul				    "%s", "DWDS not enabled");
64446492Swpaul				vap->iv_stats.is_rx_wrongdir++;
64546492Swpaul				goto out;
64646492Swpaul			}
64746492Swpaul			if ((ifp->if_flags & IFF_SIMPLEX) &&
64846492Swpaul			    isdstods_mcastecho(vap, wh)) {
64946492Swpaul				/*
65046492Swpaul				 * In IEEE802.11 network, multicast
65146492Swpaul				 * packets sent from "me" are broadcast
65246492Swpaul				 * from the AP; silently discard for
65346492Swpaul				 * SIMPLEX interface.
65446492Swpaul				 */
65546492Swpaul				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
65646492Swpaul				    "4-address data", "%s", "multicast echo");
65747789Swpaul				vap->iv_stats.is_rx_mcastecho++;
65847789Swpaul				goto out;
65947789Swpaul			}
66046492Swpaul		} else {
66146492Swpaul			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
66246492Swpaul			    "data", "incorrect dir 0x%x", dir);
66346492Swpaul			vap->iv_stats.is_rx_wrongdir++;
66447401Swpaul			goto out;
66546492Swpaul		}
66646492Swpaul
66746492Swpaul		/*
66846492Swpaul		 * Handle privacy requirements.  Note that we
66946492Swpaul		 * must not be preempted from here until after
67046492Swpaul		 * we (potentially) call ieee80211_crypto_demic;
67146492Swpaul		 * otherwise we may violate assumptions in the
67246492Swpaul		 * crypto cipher modules used to do delayed update
67346492Swpaul		 * of replay sequence numbers.
67446492Swpaul		 */
67546492Swpaul		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
67646492Swpaul			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
67746492Swpaul				/*
67846492Swpaul				 * Discard encrypted frames when privacy is off.
67946492Swpaul				 */
68046492Swpaul				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
68146492Swpaul				    wh, "WEP", "%s", "PRIVACY off");
68246492Swpaul				vap->iv_stats.is_rx_noprivacy++;
68346492Swpaul				IEEE80211_NODE_STAT(ni, rx_noprivacy);
68446492Swpaul				goto out;
68546492Swpaul			}
68646492Swpaul			key = ieee80211_crypto_decap(ni, m, hdrspace);
68746492Swpaul			if (key == NULL) {
68846492Swpaul				/* NB: stats+msgs handled in crypto_decap */
68946492Swpaul				IEEE80211_NODE_STAT(ni, rx_wepfail);
69046492Swpaul				goto out;
69146492Swpaul			}
69246492Swpaul			wh = mtod(m, struct ieee80211_frame *);
69346492Swpaul			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
69447789Swpaul		} else {
69547789Swpaul			/* XXX M_WEP and IEEE80211_F_PRIVACY */
69646492Swpaul			key = NULL;
69746492Swpaul		}
69846492Swpaul
69946492Swpaul		/*
70046492Swpaul		 * Save QoS bits for use below--before we strip the header.
70146492Swpaul		 */
70246492Swpaul		if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
70346492Swpaul			qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
70446492Swpaul			    ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
70546492Swpaul			    ((struct ieee80211_qosframe *)wh)->i_qos[0];
70646492Swpaul		} else
70746492Swpaul			qos = 0;
70846492Swpaul
70946492Swpaul		/*
71046492Swpaul		 * Next up, any fragmentation.
71146492Swpaul		 */
71246492Swpaul		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
71346492Swpaul			m = ieee80211_defrag(ni, m, hdrspace);
71446492Swpaul			if (m == NULL) {
71546492Swpaul				/* Fragment dropped or frame not complete yet */
71646492Swpaul				goto out;
71746492Swpaul			}
71846492Swpaul		}
71946492Swpaul		wh = NULL;		/* no longer valid, catch any uses */
72046492Swpaul
72146492Swpaul		/*
72246492Swpaul		 * Next strip any MSDU crypto bits.
72346492Swpaul		 */
72446492Swpaul		if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
72546492Swpaul			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
72646492Swpaul			    ni->ni_macaddr, "data", "%s", "demic error");
72753702Swpaul			vap->iv_stats.is_rx_demicfail++;
72846492Swpaul			IEEE80211_NODE_STAT(ni, rx_demicfail);
72946492Swpaul			goto out;
73046492Swpaul		}
73146492Swpaul
73246492Swpaul		/* copy to listener after decrypt */
73346492Swpaul		if (bpf_peers_present(vap->iv_rawbpf))
73446492Swpaul			bpf_mtap(vap->iv_rawbpf, m);
73546492Swpaul		need_tap = 0;
73646492Swpaul
73746492Swpaul		/*
73846492Swpaul		 * Finally, strip the 802.11 header.
73946492Swpaul		 */
74046492Swpaul		m = ieee80211_decap(vap, m, hdrspace);
74146492Swpaul		if (m == NULL) {
74246492Swpaul			/* XXX mask bit to check for both */
74346492Swpaul			/* don't count Null data frames as errors */
74446492Swpaul			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
74546492Swpaul			    subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
74646492Swpaul				goto out;
74746492Swpaul			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
74846492Swpaul			    ni->ni_macaddr, "data", "%s", "decap error");
74946492Swpaul			vap->iv_stats.is_rx_decap++;
75046492Swpaul			IEEE80211_NODE_STAT(ni, rx_decap);
75146492Swpaul			goto err;
75246492Swpaul		}
75346492Swpaul		eh = mtod(m, struct ether_header *);
75446492Swpaul		if (!ieee80211_node_is_authorized(ni)) {
75546492Swpaul			/*
75646492Swpaul			 * Deny any non-PAE frames received prior to
75746492Swpaul			 * authorization.  For open/shared-key
75846492Swpaul			 * authentication the port is mark authorized
75946492Swpaul			 * after authentication completes.  For 802.1x
76046492Swpaul			 * the port is not marked authorized by the
76146492Swpaul			 * authenticator until the handshake has completed.
76246492Swpaul			 */
76346492Swpaul			if (eh->ether_type != htons(ETHERTYPE_PAE)) {
76446492Swpaul				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
76546492Swpaul				    eh->ether_shost, "data",
76646492Swpaul				    "unauthorized port: ether type 0x%x len %u",
76746492Swpaul				    eh->ether_type, m->m_pkthdr.len);
76846492Swpaul				vap->iv_stats.is_rx_unauth++;
76946492Swpaul				IEEE80211_NODE_STAT(ni, rx_unauth);
77046492Swpaul				goto err;
77146492Swpaul			}
77246492Swpaul		} else {
77346492Swpaul			/*
77446492Swpaul			 * When denying unencrypted frames, discard
77546492Swpaul			 * any non-PAE frames received without encryption.
77646492Swpaul			 */
77746492Swpaul			if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
77846492Swpaul			    (key == NULL && (m->m_flags & M_WEP) == 0) &&
77946492Swpaul			    eh->ether_type != htons(ETHERTYPE_PAE)) {
78046492Swpaul				/*
78146492Swpaul				 * Drop unencrypted frames.
78246492Swpaul				 */
78346492Swpaul				vap->iv_stats.is_rx_unencrypted++;
78446492Swpaul				IEEE80211_NODE_STAT(ni, rx_unencrypted);
78546492Swpaul				goto out;
78646492Swpaul			}
78746492Swpaul		}
78846492Swpaul		/* XXX require HT? */
78946492Swpaul		if (qos & IEEE80211_QOS_AMSDU) {
79046492Swpaul			m = ieee80211_decap_amsdu(ni, m);
79146492Swpaul			if (m == NULL)
79246492Swpaul				return IEEE80211_FC0_TYPE_DATA;
79346492Swpaul		} else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
79446492Swpaul#define	FF_LLC_SIZE	(sizeof(struct ether_header) + sizeof(struct llc))
79546492Swpaul		    m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
79646492Swpaul			struct llc *llc;
79746492Swpaul
79846492Swpaul			/*
79946492Swpaul			 * Check for fast-frame tunnel encapsulation.
80046492Swpaul			 */
80146492Swpaul			if (m->m_len < FF_LLC_SIZE &&
80246492Swpaul			    (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
80346492Swpaul				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
80446492Swpaul				    ni->ni_macaddr, "fast-frame",
80546492Swpaul				    "%s", "m_pullup(llc) failed");
80646492Swpaul				vap->iv_stats.is_rx_tooshort++;
80746492Swpaul				return IEEE80211_FC0_TYPE_DATA;
80846492Swpaul			}
80946492Swpaul			llc = (struct llc *)(mtod(m, uint8_t *) +
81046492Swpaul				sizeof(struct ether_header));
81146492Swpaul			if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
81246492Swpaul				m_adj(m, FF_LLC_SIZE);
81346492Swpaul				m = ieee80211_decap_fastframe(ni, m);
81446492Swpaul				if (m == NULL)
81546492Swpaul					return IEEE80211_FC0_TYPE_DATA;
81646492Swpaul			}
81746492Swpaul		}
81846492Swpaul#undef FF_LLC_SIZE
81946492Swpaul		ieee80211_deliver_data(vap, ni, m);
82046492Swpaul		return IEEE80211_FC0_TYPE_DATA;
82146492Swpaul
82246492Swpaul	case IEEE80211_FC0_TYPE_MGT:
82353702Swpaul		vap->iv_stats.is_rx_mgmt++;
82446492Swpaul		IEEE80211_NODE_STAT(ni, rx_mgmt);
82546492Swpaul		if (dir != IEEE80211_FC1_DIR_NODS) {
82646492Swpaul			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
82746492Swpaul			    wh, "data", "incorrect dir 0x%x", dir);
82846492Swpaul			vap->iv_stats.is_rx_wrongdir++;
82946492Swpaul			goto err;
83046492Swpaul		}
83146492Swpaul		if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
83246492Swpaul			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
83346492Swpaul			    ni->ni_macaddr, "mgt", "too short: len %u",
83446492Swpaul			    m->m_pkthdr.len);
83546492Swpaul			vap->iv_stats.is_rx_tooshort++;
83646492Swpaul			goto out;
83746492Swpaul		}
83847789Swpaul#ifdef IEEE80211_DEBUG
83947789Swpaul		if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
84046492Swpaul		    ieee80211_msg_dumppkts(vap)) {
84146492Swpaul			if_printf(ifp, "received %s from %s rssi %d\n",
84246492Swpaul			    ieee80211_mgt_subtype_name[subtype >>
84346492Swpaul				IEEE80211_FC0_SUBTYPE_SHIFT],
84446492Swpaul			    ether_sprintf(wh->i_addr2), rssi);
84546492Swpaul		}
84646492Swpaul#endif
84746492Swpaul		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
84846492Swpaul			if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
84946492Swpaul				/*
85046492Swpaul				 * Only shared key auth frames with a challenge
85146492Swpaul				 * should be encrypted, discard all others.
85246492Swpaul				 */
85346492Swpaul				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
85446492Swpaul				    wh, ieee80211_mgt_subtype_name[subtype >>
85546492Swpaul					IEEE80211_FC0_SUBTYPE_SHIFT],
85646492Swpaul				    "%s", "WEP set but not permitted");
85746492Swpaul				vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
85846492Swpaul				goto out;
85946492Swpaul			}
86046492Swpaul			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
86146492Swpaul				/*
86246492Swpaul				 * Discard encrypted frames when privacy is off.
86346492Swpaul				 */
86446492Swpaul				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
86546492Swpaul				    wh, "mgt", "%s", "WEP set but PRIVACY off");
86646492Swpaul				vap->iv_stats.is_rx_noprivacy++;
86746492Swpaul				goto out;
86846492Swpaul			}
86946492Swpaul			hdrspace = ieee80211_hdrspace(ic, wh);
87046492Swpaul			key = ieee80211_crypto_decap(ni, m, hdrspace);
87146492Swpaul			if (key == NULL) {
87246492Swpaul				/* NB: stats+msgs handled in crypto_decap */
87346492Swpaul				goto out;
87446492Swpaul			}
87546492Swpaul			wh = mtod(m, struct ieee80211_frame *);
87646492Swpaul			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
87746492Swpaul		}
87846492Swpaul		if (bpf_peers_present(vap->iv_rawbpf))
87946492Swpaul			bpf_mtap(vap->iv_rawbpf, m);
88046492Swpaul		vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
88146492Swpaul		m_freem(m);
88246492Swpaul		return IEEE80211_FC0_TYPE_MGT;
88346492Swpaul
88446492Swpaul	case IEEE80211_FC0_TYPE_CTL:
88546492Swpaul		vap->iv_stats.is_rx_ctl++;
88646492Swpaul		IEEE80211_NODE_STAT(ni, rx_ctrl);
88746492Swpaul		goto out;
88846492Swpaul	default:
88946492Swpaul		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
89046492Swpaul		    wh, NULL, "bad frame type 0x%x", type);
89146492Swpaul		/* should not come here */
89246492Swpaul		break;
89346492Swpaul	}
89446492Swpaulerr:
89546492Swpaul	ifp->if_ierrors++;
89646492Swpaulout:
89746492Swpaul	if (m != NULL) {
89846492Swpaul		if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
89946492Swpaul			bpf_mtap(vap->iv_rawbpf, m);
90046492Swpaul		m_freem(m);
90146492Swpaul	}
90246492Swpaul	return type;
90346492Swpaul#undef SEQ_LEQ
90446492Swpaul}
90546492Swpaul
90646492Swpaulstatic void
90746492Swpaulsta_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh,
90846492Swpaul    int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status)
90946492Swpaul{
91046492Swpaul	struct ieee80211vap *vap = ni->ni_vap;
91146492Swpaul
91246492Swpaul	if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
91346492Swpaul		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
91446492Swpaul		    ni->ni_macaddr, "open auth",
91546492Swpaul		    "bad sta auth mode %u", ni->ni_authmode);
91646492Swpaul		vap->iv_stats.is_rx_bad_auth++;	/* XXX */
91746492Swpaul		return;
91846492Swpaul	}
91946492Swpaul	if (vap->iv_state != IEEE80211_S_AUTH ||
92046492Swpaul	    seq != IEEE80211_AUTH_OPEN_RESPONSE) {
92146492Swpaul		vap->iv_stats.is_rx_bad_auth++;
92246492Swpaul		return;
92346563Swpaul	}
92446563Swpaul	if (status != 0) {
92546563Swpaul		IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
92646492Swpaul		    ni, "open auth failed (reason %d)", status);
92746492Swpaul		vap->iv_stats.is_rx_auth_fail++;
92846492Swpaul		vap->iv_stats.is_rx_authfail_code = status;
92946492Swpaul		ieee80211_new_state(vap, IEEE80211_S_SCAN,
93046492Swpaul		    IEEE80211_SCAN_FAIL_STATUS);
93146492Swpaul	} else
93246492Swpaul		ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
93346492Swpaul}
93446492Swpaul
93546492Swpaulstatic void
93646492Swpaulsta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
93746492Swpaul    uint8_t *frm, uint8_t *efrm, int rssi, int noise, uint32_t rstamp,
93846611Swpaul    uint16_t seq, uint16_t status)
93946611Swpaul{
94046611Swpaul	struct ieee80211vap *vap = ni->ni_vap;
94146611Swpaul	uint8_t *challenge;
94246611Swpaul	int estatus;
94346611Swpaul
94456965Swpaul	/*
94556965Swpaul	 * NB: this can happen as we allow pre-shared key
94656965Swpaul	 * authentication to be enabled w/o wep being turned
94756965Swpaul	 * on so that configuration of these can be done
94856965Swpaul	 * in any order.  It may be better to enforce the
94956965Swpaul	 * ordering in which case this check would just be
95056965Swpaul	 * for sanity/consistency.
95156965Swpaul	 */
95256965Swpaul	if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
95356965Swpaul		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
95446492Swpaul		    ni->ni_macaddr, "shared key auth",
95546492Swpaul		    "%s", " PRIVACY is disabled");
95646492Swpaul		estatus = IEEE80211_STATUS_ALG;
95746492Swpaul		goto bad;
95846563Swpaul	}
95946563Swpaul	/*
96046563Swpaul	 * Pre-shared key authentication is evil; accept
96146492Swpaul	 * it only if explicitly configured (it is supported
96246492Swpaul	 * mainly for compatibility with clients like OS X).
96346492Swpaul	 */
96446492Swpaul	if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
96546492Swpaul	    ni->ni_authmode != IEEE80211_AUTH_SHARED) {
96646492Swpaul		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
96746492Swpaul		    ni->ni_macaddr, "shared key auth",
96846492Swpaul		    "bad sta auth mode %u", ni->ni_authmode);
96967092Swpaul		vap->iv_stats.is_rx_bad_auth++;	/* XXX maybe a unique error? */
97046492Swpaul		estatus = IEEE80211_STATUS_ALG;
97146492Swpaul		goto bad;
97246492Swpaul	}
97361818Sroberto
97446492Swpaul	challenge = NULL;
97546492Swpaul	if (frm + 1 < efrm) {
97667092Swpaul		if ((frm[1] + 2) > (efrm - frm)) {
97746492Swpaul			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
97846492Swpaul			    ni->ni_macaddr, "shared key auth",
97961818Sroberto			    "ie %d/%d too long",
98061818Sroberto			    frm[0], (frm[1] + 2) - (efrm - frm));
98161818Sroberto			vap->iv_stats.is_rx_bad_auth++;
98261818Sroberto			estatus = IEEE80211_STATUS_CHALLENGE;
98346492Swpaul			goto bad;
98446492Swpaul		}
98546492Swpaul		if (*frm == IEEE80211_ELEMID_CHALLENGE)
98646492Swpaul			challenge = frm;
98746492Swpaul		frm += frm[1] + 2;
98846492Swpaul	}
98946492Swpaul	switch (seq) {
99046492Swpaul	case IEEE80211_AUTH_SHARED_CHALLENGE:
99146492Swpaul	case IEEE80211_AUTH_SHARED_RESPONSE:
99246492Swpaul		if (challenge == NULL) {
99346492Swpaul			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
99446492Swpaul			    ni->ni_macaddr, "shared key auth",
99546492Swpaul			    "%s", "no challenge");
99646492Swpaul			vap->iv_stats.is_rx_bad_auth++;
99746492Swpaul			estatus = IEEE80211_STATUS_CHALLENGE;
99846492Swpaul			goto bad;
99946492Swpaul		}
100046492Swpaul		if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
100146492Swpaul			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
100246492Swpaul			    ni->ni_macaddr, "shared key auth",
100346492Swpaul			    "bad challenge len %d", challenge[1]);
100446492Swpaul			vap->iv_stats.is_rx_bad_auth++;
100546492Swpaul			estatus = IEEE80211_STATUS_CHALLENGE;
100646492Swpaul			goto bad;
100746492Swpaul		}
100846492Swpaul	default:
100946492Swpaul		break;
101046492Swpaul	}
101146492Swpaul	if (vap->iv_state != IEEE80211_S_AUTH)
101246492Swpaul		return;
101346492Swpaul	switch (seq) {
101446492Swpaul	case IEEE80211_AUTH_SHARED_PASS:
101546492Swpaul		if (ni->ni_challenge != NULL) {
101646492Swpaul			FREE(ni->ni_challenge, M_80211_NODE);
101746492Swpaul			ni->ni_challenge = NULL;
101846492Swpaul		}
101965581Swpaul		if (status != 0) {
102065581Swpaul			IEEE80211_NOTE_FRAME(vap,
102165581Swpaul			    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, wh,
102246492Swpaul			    "shared key auth failed (reason %d)", status);
102346492Swpaul			vap->iv_stats.is_rx_auth_fail++;
102446492Swpaul			vap->iv_stats.is_rx_authfail_code = status;
102546492Swpaul			return;
102656965Swpaul		}
102756965Swpaul		ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
102856965Swpaul		break;
102953702Swpaul	case IEEE80211_AUTH_SHARED_CHALLENGE:
103053702Swpaul		if (!ieee80211_alloc_challenge(ni))
103153702Swpaul			return;
103253702Swpaul		/* XXX could optimize by passing recvd challenge */
103353702Swpaul		memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
103453702Swpaul		IEEE80211_SEND_MGMT(ni,
103553702Swpaul			IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
103653702Swpaul		break;
103753702Swpaul	default:
103853702Swpaul		IEEE80211_DISCARD(vap, IEEE80211_MSG_AUTH,
103953702Swpaul		    wh, "shared key auth", "bad seq %d", seq);
104053702Swpaul		vap->iv_stats.is_rx_bad_auth++;
104153702Swpaul		return;
104253702Swpaul	}
104353702Swpaul	return;
104453702Swpaulbad:
104553702Swpaul	/*
104646492Swpaul	 * Kick the state machine.  This short-circuits
104746492Swpaul	 * using the mgt frame timeout to trigger the
104846492Swpaul	 * state transition.
104946492Swpaul	 */
105046492Swpaul	if (vap->iv_state == IEEE80211_S_AUTH)
105146492Swpaul		ieee80211_new_state(vap, IEEE80211_S_SCAN,
105246492Swpaul		    IEEE80211_SCAN_FAIL_STATUS);
105346492Swpaul}
105461818Sroberto
105561818Srobertostatic int
105646492Swpaulieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm,
105746492Swpaul	const struct ieee80211_frame *wh)
105846492Swpaul{
105946492Swpaul#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
106046492Swpaul	struct ieee80211_wme_state *wme = &vap->iv_ic->ic_wme;
106146492Swpaul	u_int len = frm[1], qosinfo;
106246492Swpaul	int i;
106346492Swpaul
106446492Swpaul	if (len < sizeof(struct ieee80211_wme_param)-2) {
106546492Swpaul		IEEE80211_DISCARD_IE(vap,
106646492Swpaul		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
106746492Swpaul		    wh, "WME", "too short, len %u", len);
106846492Swpaul		return -1;
106946492Swpaul	}
107046492Swpaul	qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)];
107146492Swpaul	qosinfo &= WME_QOSINFO_COUNT;
107246492Swpaul	/* XXX do proper check for wraparound */
107346492Swpaul	if (qosinfo == wme->wme_wmeChanParams.cap_info)
107446492Swpaul		return 0;
107561818Sroberto	frm += __offsetof(struct ieee80211_wme_param, params_acParams);
107667092Swpaul	for (i = 0; i < WME_NUM_AC; i++) {
107746492Swpaul		struct wmeParams *wmep =
107846492Swpaul			&wme->wme_wmeChanParams.cap_wmeParams[i];
107946492Swpaul		/* NB: ACI not used */
108046492Swpaul		wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
108146492Swpaul		wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
108246492Swpaul		wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
108346492Swpaul		wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
108446492Swpaul		wmep->wmep_txopLimit = LE_READ_2(frm+2);
108546492Swpaul		frm += 4;
108646492Swpaul	}
108746492Swpaul	wme->wme_wmeChanParams.cap_info = qosinfo;
108846492Swpaul	return 1;
108967092Swpaul#undef MS
109067092Swpaul}
109167092Swpaul
109267092Swpaulstatic int
109346492Swpaulieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm,
109467092Swpaul	const struct ieee80211_frame *wh)
109546492Swpaul{
109646492Swpaul	struct ieee80211vap *vap = ni->ni_vap;
109746492Swpaul	const struct ieee80211_ath_ie *ath;
109846492Swpaul	u_int len = frm[1];
109946492Swpaul	int capschanged;
110046492Swpaul	uint16_t defkeyix;
110146492Swpaul
110246492Swpaul	if (len < sizeof(struct ieee80211_ath_ie)-2) {
110346492Swpaul		IEEE80211_DISCARD_IE(vap,
110447401Swpaul		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG,
110546492Swpaul		    wh, "Atheros", "too short, len %u", len);
110646492Swpaul		return -1;
110746492Swpaul	}
110846492Swpaul	ath = (const struct ieee80211_ath_ie *)frm;
110946492Swpaul	capschanged = (ni->ni_ath_flags != ath->ath_capability);
111046492Swpaul	defkeyix = LE_READ_2(ath->ath_defkeyix);
111146492Swpaul	if (capschanged || defkeyix != ni->ni_ath_defkeyix) {
111246492Swpaul		ni->ni_ath_flags = ath->ath_capability;
111346492Swpaul		ni->ni_ath_defkeyix = defkeyix;
111446492Swpaul		IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
111546492Swpaul		    "ath ie change: new caps 0x%x defkeyix 0x%x",
111646492Swpaul		    ni->ni_ath_flags, ni->ni_ath_defkeyix);
111746492Swpaul	}
111846492Swpaul	if (IEEE80211_ATH_CAP(vap, ni, ATHEROS_CAP_TURBO_PRIME)) {
111946611Swpaul		uint16_t curflags, newflags;
112046611Swpaul
112146611Swpaul		/*
112246611Swpaul		 * Check for turbo mode switch.  Calculate flags
112346611Swpaul		 * for the new mode and effect the switch.
112446611Swpaul		 */
112546492Swpaul		newflags = curflags = vap->iv_ic->ic_bsschan->ic_flags;
112646492Swpaul		/* NB: BOOST is not in ic_flags, so get it from the ie */
112746492Swpaul		if (ath->ath_capability & ATHEROS_CAP_BOOST)
112846492Swpaul			newflags |= IEEE80211_CHAN_TURBO;
112946492Swpaul		else
113046492Swpaul			newflags &= ~IEEE80211_CHAN_TURBO;
113146563Swpaul		if (newflags != curflags)
113246563Swpaul			ieee80211_dturbo_switch(vap, newflags);
113346563Swpaul	}
113446492Swpaul	return capschanged;
113546492Swpaul}
113646492Swpaul
113746492Swpaul/*
113846492Swpaul * Return non-zero if a background scan may be continued:
113946492Swpaul * o bg scan is active
114046492Swpaul * o no channel switch is pending
114146492Swpaul * o there has not been any traffic recently
114246492Swpaul *
114346492Swpaul * Note we do not check if there is an administrative enable;
114456965Swpaul * this is only done to start the scan.  We assume that any
114556965Swpaul * change in state will be accompanied by a request to cancel
114656965Swpaul * active scans which will otherwise cause this test to fail.
114756965Swpaul */
114856965Swpaulstatic __inline int
114956965Swpaulcontbgscan(struct ieee80211vap *vap)
115056965Swpaul{
115156965Swpaul	struct ieee80211com *ic = vap->iv_ic;
115256965Swpaul
115346492Swpaul	return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
115446492Swpaul	    (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 &&
115546492Swpaul	    vap->iv_state == IEEE80211_S_RUN &&		/* XXX? */
115646492Swpaul	    time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle));
115746492Swpaul}
115846492Swpaul
115946492Swpaul/*
116046492Swpaul * Return non-zero if a backgrond scan may be started:
116146492Swpaul * o bg scanning is administratively enabled
116246492Swpaul * o no channel switch is pending
116346492Swpaul * o we are not boosted on a dynamic turbo channel
116446492Swpaul * o there has not been a scan recently
116546492Swpaul * o there has not been any traffic recently
116646492Swpaul */
116753702Swpaulstatic __inline int
116846492Swpaulstartbgscan(struct ieee80211vap *vap)
116946492Swpaul{
117046492Swpaul	struct ieee80211com *ic = vap->iv_ic;
117153702Swpaul
117246492Swpaul	return ((vap->iv_flags & IEEE80211_F_BGSCAN) &&
117346492Swpaul	    (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 &&
117446492Swpaul	    !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
117546492Swpaul	    time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) &&
117646492Swpaul	    time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle));
117746492Swpaul}
117846492Swpaul
117946492Swpaulstatic void
118046492Swpaulsta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
118167092Swpaul	int subtype, int rssi, int noise, uint32_t rstamp)
118246492Swpaul{
118346492Swpaul#define	ISPROBE(_st)	((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
118446492Swpaul#define	ISREASSOC(_st)	((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
118546492Swpaul	struct ieee80211vap *vap = ni->ni_vap;
118646492Swpaul	struct ieee80211com *ic = ni->ni_ic;
118746492Swpaul	struct ieee80211_frame *wh;
118846492Swpaul	uint8_t *frm, *efrm;
118946492Swpaul	uint8_t *rates, *xrates, *wme, *htcap, *htinfo;
119046492Swpaul	uint8_t rate;
119146492Swpaul
119246492Swpaul	wh = mtod(m0, struct ieee80211_frame *);
119346492Swpaul	frm = (uint8_t *)&wh[1];
119446492Swpaul	efrm = mtod(m0, uint8_t *) + m0->m_len;
119546492Swpaul	switch (subtype) {
119667092Swpaul	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
119746492Swpaul	case IEEE80211_FC0_SUBTYPE_BEACON: {
119867092Swpaul		struct ieee80211_scanparams scan;
119967092Swpaul		/*
120046492Swpaul		 * We process beacon/probe response frames:
120167092Swpaul		 *    o when scanning, or
120246492Swpaul		 *    o station mode when associated (to collect state
120367092Swpaul		 *      updates such as 802.11g slot time), or
120467092Swpaul		 * Frames otherwise received are discarded.
120546492Swpaul		 */
120667092Swpaul		if (!((ic->ic_flags & IEEE80211_F_SCAN) || ni->ni_associd)) {
120746492Swpaul			vap->iv_stats.is_rx_mgtdiscard++;
120846492Swpaul			return;
120967092Swpaul		}
121067092Swpaul		/* XXX probe response in sta mode when !scanning? */
121146492Swpaul		if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
121267092Swpaul			return;
121346492Swpaul		/*
121446492Swpaul		 * Count frame now that we know it's to be processed.
121546492Swpaul		 */
121646492Swpaul		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
121746492Swpaul			vap->iv_stats.is_rx_beacon++;		/* XXX remove */
121846492Swpaul			IEEE80211_NODE_STAT(ni, rx_beacons);
121947401Swpaul		} else
122046492Swpaul			IEEE80211_NODE_STAT(ni, rx_proberesp);
122146492Swpaul		/*
122255831Swpaul		 * When operating in station mode, check for state updates.
122346492Swpaul		 * Be careful to ignore beacons received while doing a
122446492Swpaul		 * background scan.  We consider only 11g/WMM stuff right now.
122546492Swpaul		 */
122646492Swpaul		if (ni->ni_associd != 0 &&
122746492Swpaul		    ((ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
122846492Swpaul		     IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) {
122946492Swpaul			/* record tsf of last beacon */
123046492Swpaul			memcpy(ni->ni_tstamp.data, scan.tstamp,
123146492Swpaul				sizeof(ni->ni_tstamp));
123246492Swpaul			/* count beacon frame for s/w bmiss handling */
123346492Swpaul			vap->iv_swbmiss_count++;
123446492Swpaul			vap->iv_bmiss_count = 0;
123546492Swpaul			if (ni->ni_erp != scan.erp) {
123646492Swpaul				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
123746492Swpaul				    wh->i_addr2,
123846492Swpaul				    "erp change: was 0x%x, now 0x%x",
123946492Swpaul				    ni->ni_erp, scan.erp);
124046492Swpaul				if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
124146492Swpaul				    (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
124246492Swpaul					ic->ic_flags |= IEEE80211_F_USEPROT;
124346492Swpaul				else
124446492Swpaul					ic->ic_flags &= ~IEEE80211_F_USEPROT;
124546492Swpaul				ni->ni_erp = scan.erp;
124646492Swpaul				/* XXX statistic */
124746492Swpaul				/* XXX driver notification */
124846492Swpaul			}
124946492Swpaul			if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
125055831Swpaul				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
125146492Swpaul				    wh->i_addr2,
125246492Swpaul				    "capabilities change: was 0x%x, now 0x%x",
125346492Swpaul				    ni->ni_capinfo, scan.capinfo);
125446492Swpaul				/*
125546492Swpaul				 * NB: we assume short preamble doesn't
125646492Swpaul				 *     change dynamically
125746492Swpaul				 */
125846492Swpaul				ieee80211_set_shortslottime(ic,
125946492Swpaul					IEEE80211_IS_CHAN_A(ic->ic_bsschan) ||
126046492Swpaul					(scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
126146492Swpaul				ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME)
126246492Swpaul					       | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME);
126346492Swpaul				/* XXX statistic */
126446492Swpaul			}
126546492Swpaul			if (scan.wme != NULL &&
126646492Swpaul			    (ni->ni_flags & IEEE80211_NODE_QOS) &&
126746492Swpaul			    ieee80211_parse_wmeparams(vap, scan.wme, wh) > 0)
126846492Swpaul				ieee80211_wme_updateparams(vap);
126953702Swpaul			if (scan.ath != NULL)
127046492Swpaul				ieee80211_parse_athparams(ni, scan.ath, wh);
127146492Swpaul			if (scan.htcap != NULL && scan.htinfo != NULL) {
127246492Swpaul				ieee80211_parse_htcap(ni, scan.htcap);
127346492Swpaul				ieee80211_parse_htinfo(ni, scan.htinfo);
127446492Swpaul				/* XXX state changes? */
127546492Swpaul			}
127646492Swpaul			if (scan.tim != NULL) {
127746492Swpaul				struct ieee80211_tim_ie *tim =
127867092Swpaul				    (struct ieee80211_tim_ie *) scan.tim;
127946492Swpaul#if 0
128046492Swpaul				int aid = IEEE80211_AID(ni->ni_associd);
128146492Swpaul				int ix = aid / NBBY;
128246492Swpaul				int min = tim->tim_bitctl &~ 1;
128346492Swpaul				int max = tim->tim_len + min - 4;
128446492Swpaul				if ((tim->tim_bitctl&1) ||
128546492Swpaul				    (min <= ix && ix <= max &&
128646492Swpaul				     isset(tim->tim_bitmap - min, aid))) {
128746492Swpaul					/*
128846492Swpaul					 * XXX Do not let bg scan kick off
128946492Swpaul					 * we are expecting data.
129046492Swpaul					 */
129146492Swpaul					ic->ic_lastdata = ticks;
129246492Swpaul					ieee80211_sta_pwrsave(vap, 0);
129346492Swpaul				}
129446492Swpaul#endif
129546492Swpaul				ni->ni_dtim_count = tim->tim_count;
129646492Swpaul				ni->ni_dtim_period = tim->tim_period;
129746492Swpaul			}
129846492Swpaul			/*
129946492Swpaul			 * If scanning, pass the info to the scan module.
130046492Swpaul			 * Otherwise, check if it's the right time to do
130146492Swpaul			 * a background scan.  Background scanning must
130246492Swpaul			 * be enabled and we must not be operating in the
130346492Swpaul			 * turbo phase of dynamic turbo mode.  Then,
130446492Swpaul			 * it's been a while since the last background
130546492Swpaul			 * scan and if no data frames have come through
130646492Swpaul			 * recently, kick off a scan.  Note that this
130746492Swpaul			 * is the mechanism by which a background scan
130846492Swpaul			 * is started _and_ continued each time we
130946492Swpaul			 * return on-channel to receive a beacon from
131046492Swpaul			 * our ap.
131146492Swpaul			 */
131253702Swpaul			if (ic->ic_flags & IEEE80211_F_SCAN) {
131346492Swpaul				ieee80211_add_scan(vap, &scan, wh,
131446492Swpaul					subtype, rssi, noise, rstamp);
131546492Swpaul			} else if (contbgscan(vap)) {
131646492Swpaul				ieee80211_bg_scan(vap, 0);
131746492Swpaul			} else if (startbgscan(vap)) {
131846492Swpaul				vap->iv_stats.is_scan_bg++;
131946492Swpaul#if 0
132046492Swpaul				/* wakeup if we are sleeing */
132146492Swpaul				ieee80211_set_pwrsave(vap, 0);
132246492Swpaul#endif
132346492Swpaul				ieee80211_bg_scan(vap, 0);
132467092Swpaul			}
132567092Swpaul			return;
132667092Swpaul		}
132767092Swpaul		/*
132846492Swpaul		 * If scanning, just pass information to the scan module.
132967092Swpaul		 */
133046492Swpaul		if (ic->ic_flags & IEEE80211_F_SCAN) {
133146492Swpaul			if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
133246492Swpaul				/*
133346492Swpaul				 * Actively scanning a channel marked passive;
133446492Swpaul				 * send a probe request now that we know there
133546492Swpaul				 * is 802.11 traffic present.
133646492Swpaul				 *
133746492Swpaul				 * XXX check if the beacon we recv'd gives
133846492Swpaul				 * us what we need and suppress the probe req
133946492Swpaul				 */
134067092Swpaul				ieee80211_probe_curchan(vap, 1);
134146492Swpaul				ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
134246492Swpaul			}
134346492Swpaul			ieee80211_add_scan(vap, &scan, wh,
134446492Swpaul				subtype, rssi, noise, rstamp);
134546492Swpaul			return;
134646492Swpaul		}
134746492Swpaul		break;
134846492Swpaul	}
134946492Swpaul
135046492Swpaul	case IEEE80211_FC0_SUBTYPE_AUTH: {
135153702Swpaul		uint16_t algo, seq, status;
135246492Swpaul		/*
135346492Swpaul		 * auth frame format
135446492Swpaul		 *	[2] algorithm
135546492Swpaul		 *	[2] sequence
135646492Swpaul		 *	[2] status
135746492Swpaul		 *	[tlv*] challenge
135846492Swpaul		 */
135946492Swpaul		IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
136053702Swpaul		algo   = le16toh(*(uint16_t *)frm);
136153702Swpaul		seq    = le16toh(*(uint16_t *)(frm + 2));
136246492Swpaul		status = le16toh(*(uint16_t *)(frm + 4));
136353702Swpaul		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2,
136453702Swpaul		    "recv auth frame with algorithm %d seq %d", algo, seq);
136553702Swpaul
136653702Swpaul		if (vap->iv_flags & IEEE80211_F_COUNTERM) {
136753702Swpaul			IEEE80211_DISCARD(vap,
136853702Swpaul			    IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
136953702Swpaul			    wh, "auth", "%s", "TKIP countermeasures enabled");
137053702Swpaul			vap->iv_stats.is_rx_auth_countermeasures++;
137153702Swpaul			if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
137253702Swpaul				ieee80211_send_error(ni, wh->i_addr2,
137353702Swpaul					IEEE80211_FC0_SUBTYPE_AUTH,
137453702Swpaul					IEEE80211_REASON_MIC_FAILURE);
137553702Swpaul			}
137653702Swpaul			return;
137753702Swpaul		}
137853702Swpaul		if (algo == IEEE80211_AUTH_ALG_SHARED)
137953702Swpaul			sta_auth_shared(ni, wh, frm + 6, efrm, rssi,
138053702Swpaul			    noise, rstamp, seq, status);
138153702Swpaul		else if (algo == IEEE80211_AUTH_ALG_OPEN)
138253702Swpaul			sta_auth_open(ni, wh, rssi, noise, rstamp,
138353702Swpaul			    seq, status);
138453702Swpaul		else {
138553702Swpaul			IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
138653702Swpaul			    wh, "auth", "unsupported alg %d", algo);
138753702Swpaul			vap->iv_stats.is_rx_auth_unsupported++;
138853702Swpaul			return;
138953702Swpaul		}
139053702Swpaul		break;
139153702Swpaul	}
139253702Swpaul
139353702Swpaul	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
139453702Swpaul	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
139553702Swpaul		uint16_t capinfo, associd;
139653702Swpaul		uint16_t status;
139753702Swpaul
139853702Swpaul		if (vap->iv_state != IEEE80211_S_ASSOC) {
139953702Swpaul			vap->iv_stats.is_rx_mgtdiscard++;
140053702Swpaul			return;
140153702Swpaul		}
140253702Swpaul
140353702Swpaul		/*
140453702Swpaul		 * asresp frame format
140553702Swpaul		 *	[2] capability information
140653702Swpaul		 *	[2] status
140746492Swpaul		 *	[2] association ID
140846492Swpaul		 *	[tlv] supported rates
140953702Swpaul		 *	[tlv] extended supported rates
141046492Swpaul		 *	[tlv] WME
141146492Swpaul		 *	[tlv] HT capabilities
141246492Swpaul		 *	[tlv] HT info
141346492Swpaul		 */
141453702Swpaul		IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
141553702Swpaul		ni = vap->iv_bss;
141653702Swpaul		capinfo = le16toh(*(uint16_t *)frm);
141753702Swpaul		frm += 2;
141853702Swpaul		status = le16toh(*(uint16_t *)frm);
141953702Swpaul		frm += 2;
142053702Swpaul		if (status != 0) {
142153702Swpaul			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
142253702Swpaul			    wh->i_addr2, "%sassoc failed (reason %d)",
142353702Swpaul			    ISREASSOC(subtype) ?  "re" : "", status);
142453702Swpaul			vap->iv_stats.is_rx_auth_fail++;	/* XXX */
142553702Swpaul			return;
142653702Swpaul		}
142753702Swpaul		associd = le16toh(*(uint16_t *)frm);
142853702Swpaul		frm += 2;
142953702Swpaul
143053702Swpaul		rates = xrates = wme = htcap = htinfo = NULL;
143153702Swpaul		while (efrm - frm > 1) {
143253702Swpaul			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
143353702Swpaul			switch (*frm) {
143453702Swpaul			case IEEE80211_ELEMID_RATES:
143553702Swpaul				rates = frm;
143653702Swpaul				break;
143753702Swpaul			case IEEE80211_ELEMID_XRATES:
143853702Swpaul				xrates = frm;
143953702Swpaul				break;
144053702Swpaul			case IEEE80211_ELEMID_HTCAP:
144153702Swpaul				htcap = frm;
144253702Swpaul				break;
144353702Swpaul			case IEEE80211_ELEMID_HTINFO:
144453702Swpaul				htinfo = frm;
144553702Swpaul				break;
144653702Swpaul			case IEEE80211_ELEMID_VENDOR:
144753702Swpaul				if (iswmeoui(frm))
144853702Swpaul					wme = frm;
144953702Swpaul				else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
145053702Swpaul					/*
145153702Swpaul					 * Accept pre-draft HT ie's if the
145253702Swpaul					 * standard ones have not been seen.
145353702Swpaul					 */
145453702Swpaul					if (ishtcapoui(frm)) {
145553702Swpaul						if (htcap == NULL)
145653702Swpaul							htcap = frm;
145753702Swpaul					} else if (ishtinfooui(frm)) {
145853702Swpaul						if (htinfo == NULL)
145953702Swpaul							htcap = frm;
146053702Swpaul					}
146153702Swpaul				}
146253702Swpaul				/* XXX Atheros OUI support */
146353702Swpaul				break;
146453702Swpaul			}
146553702Swpaul			frm += frm[1] + 2;
146653702Swpaul		}
146753702Swpaul
146853702Swpaul		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
146953702Swpaul		if (xrates != NULL)
147053702Swpaul			IEEE80211_VERIFY_ELEMENT(xrates,
147153702Swpaul				IEEE80211_RATE_MAXSIZE - rates[1], return);
147253702Swpaul		rate = ieee80211_setup_rates(ni, rates, xrates,
147353702Swpaul				IEEE80211_F_JOIN |
147453702Swpaul				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
147553702Swpaul				IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
147653702Swpaul		if (rate & IEEE80211_RATE_BASIC) {
147753702Swpaul			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
147853702Swpaul			    wh->i_addr2,
147953702Swpaul			    "%sassoc failed (rate set mismatch)",
148053702Swpaul			    ISREASSOC(subtype) ?  "re" : "");
148153702Swpaul			vap->iv_stats.is_rx_assoc_norate++;
148253702Swpaul			ieee80211_new_state(vap, IEEE80211_S_SCAN,
148353702Swpaul			    IEEE80211_SCAN_FAIL_STATUS);
148453702Swpaul			return;
148553702Swpaul		}
148653702Swpaul
148753702Swpaul		ni->ni_capinfo = capinfo;
148853702Swpaul		ni->ni_associd = associd;
148953702Swpaul		if (ni->ni_jointime == 0)
149053702Swpaul			ni->ni_jointime = time_uptime;
149153702Swpaul		if (wme != NULL &&
149253702Swpaul		    ieee80211_parse_wmeparams(vap, wme, wh) >= 0) {
149353702Swpaul			ni->ni_flags |= IEEE80211_NODE_QOS;
149453702Swpaul			ieee80211_wme_updateparams(vap);
149553702Swpaul		} else
149653702Swpaul			ni->ni_flags &= ~IEEE80211_NODE_QOS;
149753702Swpaul		/*
149853702Swpaul		 * Setup HT state according to the negotiation.
149953702Swpaul		 *
150053702Swpaul		 * NB: shouldn't need to check if HT use is enabled but some
150153702Swpaul		 *     ap's send back HT ie's even when we don't indicate we
150253702Swpaul		 *     are HT capable in our AssocReq.
150353702Swpaul		 */
150453702Swpaul		if (htcap != NULL && htinfo != NULL &&
150553702Swpaul		    (vap->iv_flags_ext & IEEE80211_FEXT_HT)) {
150653702Swpaul			ieee80211_ht_node_init(ni, htcap);
150753702Swpaul			ieee80211_parse_htinfo(ni, htinfo);
150853702Swpaul			ieee80211_setup_htrates(ni, htcap,
150953702Swpaul			     IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
151053702Swpaul			ieee80211_setup_basic_htrates(ni, htinfo);
151153702Swpaul		}
151253702Swpaul		/*
151353702Swpaul		 * Configure state now that we are associated.
151453702Swpaul		 *
151553702Swpaul		 * XXX may need different/additional driver callbacks?
151653702Swpaul		 */
151753702Swpaul		if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
151853702Swpaul		    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
151953702Swpaul			ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
152053702Swpaul			ic->ic_flags &= ~IEEE80211_F_USEBARKER;
152153702Swpaul		} else {
152253702Swpaul			ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
152353702Swpaul			ic->ic_flags |= IEEE80211_F_USEBARKER;
152453702Swpaul		}
152553702Swpaul		ieee80211_set_shortslottime(ic,
152653702Swpaul			IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
152753702Swpaul			(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
152853702Swpaul		/*
152953702Swpaul		 * Honor ERP protection.
153053702Swpaul		 *
153153702Swpaul		 * NB: ni_erp should zero for non-11g operation.
153253702Swpaul		 */
153353702Swpaul		if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
153453702Swpaul		    (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
153553702Swpaul			ic->ic_flags |= IEEE80211_F_USEPROT;
153653702Swpaul		else
153753702Swpaul			ic->ic_flags &= ~IEEE80211_F_USEPROT;
153853702Swpaul		IEEE80211_NOTE_MAC(vap,
153953702Swpaul		    IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, wh->i_addr2,
154053702Swpaul		    "%sassoc success at aid %d: %s preamble, %s slot time%s%s%s%s%s%s",
154153702Swpaul		    ISREASSOC(subtype) ? "re" : "",
154253702Swpaul		    IEEE80211_NODE_AID(ni),
154353702Swpaul		    ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
154453702Swpaul		    ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
154553702Swpaul		    ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
154653702Swpaul		    ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
154753702Swpaul		    ni->ni_flags & IEEE80211_NODE_HT ?
154853702Swpaul			(ni->ni_chw == 40 ? ", HT40" : ", HT20") : "",
154953702Swpaul		    ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
155053702Swpaul		    IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ?
155153702Swpaul			", fast-frames" : "",
155253702Swpaul		    IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ?
155353702Swpaul			", turbo" : ""
155453702Swpaul		);
155553702Swpaul		ieee80211_new_state(vap, IEEE80211_S_RUN, subtype);
155653702Swpaul		break;
155753702Swpaul	}
155853702Swpaul
155953702Swpaul	case IEEE80211_FC0_SUBTYPE_DEAUTH: {
156053702Swpaul		uint16_t reason;
156153702Swpaul
156253702Swpaul		if (vap->iv_state == IEEE80211_S_SCAN) {
156353702Swpaul			vap->iv_stats.is_rx_mgtdiscard++;
156453702Swpaul			return;
156553702Swpaul		}
156653702Swpaul		if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
156753702Swpaul			/* NB: can happen when in promiscuous mode */
156853702Swpaul			vap->iv_stats.is_rx_mgtdiscard++;
156953702Swpaul			break;
157053702Swpaul		}
157153702Swpaul
157253702Swpaul		/*
157353702Swpaul		 * deauth frame format
157453702Swpaul		 *	[2] reason
157553702Swpaul		 */
157653702Swpaul		IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
157753702Swpaul		reason = le16toh(*(uint16_t *)frm);
157853702Swpaul
157953702Swpaul		vap->iv_stats.is_rx_deauth++;
158053702Swpaul		vap->iv_stats.is_rx_deauth_code = reason;
158153702Swpaul		IEEE80211_NODE_STAT(ni, rx_deauth);
158253702Swpaul
158353702Swpaul		IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
158453702Swpaul		    "recv deauthenticate (reason %d)", reason);
158553702Swpaul		ieee80211_new_state(vap, IEEE80211_S_AUTH,
158653702Swpaul		    (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH);
158753702Swpaul		break;
158853702Swpaul	}
158953702Swpaul
159053702Swpaul	case IEEE80211_FC0_SUBTYPE_DISASSOC: {
159153702Swpaul		uint16_t reason;
159253702Swpaul
159353702Swpaul		if (vap->iv_state != IEEE80211_S_RUN &&
159453702Swpaul		    vap->iv_state != IEEE80211_S_ASSOC &&
159553702Swpaul		    vap->iv_state != IEEE80211_S_AUTH) {
159653702Swpaul			vap->iv_stats.is_rx_mgtdiscard++;
159753702Swpaul			return;
159853702Swpaul		}
159953702Swpaul		if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
160053702Swpaul			/* NB: can happen when in promiscuous mode */
160153702Swpaul			vap->iv_stats.is_rx_mgtdiscard++;
160253702Swpaul			break;
160353702Swpaul		}
160453702Swpaul
160553702Swpaul		/*
160653702Swpaul		 * disassoc frame format
160753702Swpaul		 *	[2] reason
160853702Swpaul		 */
160953702Swpaul		IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
161053702Swpaul		reason = le16toh(*(uint16_t *)frm);
161153702Swpaul
161253702Swpaul		vap->iv_stats.is_rx_disassoc++;
161353702Swpaul		vap->iv_stats.is_rx_disassoc_code = reason;
161453702Swpaul		IEEE80211_NODE_STAT(ni, rx_disassoc);
161553702Swpaul
161653702Swpaul		IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
161753702Swpaul		    "recv disassociate (reason %d)", reason);
161853702Swpaul		ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
161953702Swpaul		break;
162053702Swpaul	}
1621
1622	case IEEE80211_FC0_SUBTYPE_ACTION:
1623		if (vap->iv_state == IEEE80211_S_RUN) {
1624			if (ieee80211_parse_action(ni, m0) == 0)
1625				ic->ic_recv_action(ni, frm, efrm);
1626		} else
1627			vap->iv_stats.is_rx_mgtdiscard++;
1628		break;
1629
1630	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
1631	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
1632	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
1633		vap->iv_stats.is_rx_mgtdiscard++;
1634		return;
1635	default:
1636		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
1637		     wh, "mgt", "subtype 0x%x not handled", subtype);
1638		vap->iv_stats.is_rx_badsubtype++;
1639		break;
1640	}
1641#undef ISREASSOC
1642#undef ISPROBE
1643}
1644