xref: /dflybsd-src/sys/netproto/802_11/wlan/ieee80211_sta.c (revision 2c7ccc4aa5b4f829c69afd3a1f846132f1915d03)
132176cfdSRui Paulo /*-
232176cfdSRui Paulo  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
332176cfdSRui Paulo  * All rights reserved.
432176cfdSRui Paulo  *
532176cfdSRui Paulo  * Redistribution and use in source and binary forms, with or without
632176cfdSRui Paulo  * modification, are permitted provided that the following conditions
732176cfdSRui Paulo  * are met:
832176cfdSRui Paulo  * 1. Redistributions of source code must retain the above copyright
932176cfdSRui Paulo  *    notice, this list of conditions and the following disclaimer.
1032176cfdSRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
1132176cfdSRui Paulo  *    notice, this list of conditions and the following disclaimer in the
1232176cfdSRui Paulo  *    documentation and/or other materials provided with the distribution.
1332176cfdSRui Paulo  *
1432176cfdSRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1532176cfdSRui Paulo  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1632176cfdSRui Paulo  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1732176cfdSRui Paulo  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1832176cfdSRui Paulo  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1932176cfdSRui Paulo  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2032176cfdSRui Paulo  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2132176cfdSRui Paulo  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2232176cfdSRui Paulo  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2332176cfdSRui Paulo  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2432176cfdSRui Paulo  */
2532176cfdSRui Paulo 
26085ff963SMatthew Dillon #include <sys/cdefs.h>
27085ff963SMatthew Dillon #ifdef __FreeBSD__
28085ff963SMatthew Dillon __FBSDID("$FreeBSD$");
29085ff963SMatthew Dillon #endif
30085ff963SMatthew Dillon 
3132176cfdSRui Paulo /*
3232176cfdSRui Paulo  * IEEE 802.11 Station mode support.
3332176cfdSRui Paulo  */
3432176cfdSRui Paulo #include "opt_inet.h"
3532176cfdSRui Paulo #include "opt_wlan.h"
3632176cfdSRui Paulo 
3732176cfdSRui Paulo #include <sys/param.h>
3832176cfdSRui Paulo #include <sys/systm.h>
3932176cfdSRui Paulo #include <sys/mbuf.h>
4032176cfdSRui Paulo #include <sys/malloc.h>
4132176cfdSRui Paulo #include <sys/kernel.h>
4232176cfdSRui Paulo 
4332176cfdSRui Paulo #include <sys/socket.h>
4432176cfdSRui Paulo #include <sys/sockio.h>
4532176cfdSRui Paulo #include <sys/endian.h>
4632176cfdSRui Paulo #include <sys/errno.h>
4732176cfdSRui Paulo #include <sys/proc.h>
4832176cfdSRui Paulo #include <sys/sysctl.h>
4932176cfdSRui Paulo 
5032176cfdSRui Paulo #include <net/if.h>
5132176cfdSRui Paulo #include <net/if_media.h>
5232176cfdSRui Paulo #include <net/if_llc.h>
53085ff963SMatthew Dillon #include <net/if_dl.h>
54085ff963SMatthew Dillon #include <net/if_var.h>
5532176cfdSRui Paulo #include <net/ethernet.h>
5632176cfdSRui Paulo 
5732176cfdSRui Paulo #include <net/bpf.h>
5832176cfdSRui Paulo 
5932176cfdSRui Paulo #include <netproto/802_11/ieee80211_var.h>
6032176cfdSRui Paulo #include <netproto/802_11/ieee80211_sta.h>
6132176cfdSRui Paulo #include <netproto/802_11/ieee80211_input.h>
6232176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG
6332176cfdSRui Paulo #include <netproto/802_11/ieee80211_superg.h>
6432176cfdSRui Paulo #endif
65085ff963SMatthew Dillon #include <netproto/802_11/ieee80211_ratectl.h>
66085ff963SMatthew Dillon #include <netproto/802_11/ieee80211_sta.h>
6732176cfdSRui Paulo 
6832176cfdSRui Paulo #define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
6932176cfdSRui Paulo 
7032176cfdSRui Paulo static	void sta_vattach(struct ieee80211vap *);
7132176cfdSRui Paulo static	void sta_beacon_miss(struct ieee80211vap *);
7232176cfdSRui Paulo static	int sta_newstate(struct ieee80211vap *, enum ieee80211_state, int);
7332176cfdSRui Paulo static	int sta_input(struct ieee80211_node *, struct mbuf *, int, int);
7432176cfdSRui Paulo static void sta_recv_mgmt(struct ieee80211_node *, struct mbuf *,
7532176cfdSRui Paulo 	    int subtype, int rssi, int nf);
7632176cfdSRui Paulo static void sta_recv_ctl(struct ieee80211_node *, struct mbuf *, int subtype);
7732176cfdSRui Paulo 
7832176cfdSRui Paulo void
7932176cfdSRui Paulo ieee80211_sta_attach(struct ieee80211com *ic)
8032176cfdSRui Paulo {
8132176cfdSRui Paulo 	ic->ic_vattach[IEEE80211_M_STA] = sta_vattach;
8232176cfdSRui Paulo }
8332176cfdSRui Paulo 
8432176cfdSRui Paulo void
8532176cfdSRui Paulo ieee80211_sta_detach(struct ieee80211com *ic)
8632176cfdSRui Paulo {
8732176cfdSRui Paulo }
8832176cfdSRui Paulo 
8932176cfdSRui Paulo static void
9032176cfdSRui Paulo sta_vdetach(struct ieee80211vap *vap)
9132176cfdSRui Paulo {
9232176cfdSRui Paulo }
9332176cfdSRui Paulo 
9432176cfdSRui Paulo static void
9532176cfdSRui Paulo sta_vattach(struct ieee80211vap *vap)
9632176cfdSRui Paulo {
9732176cfdSRui Paulo 	vap->iv_newstate = sta_newstate;
9832176cfdSRui Paulo 	vap->iv_input = sta_input;
9932176cfdSRui Paulo 	vap->iv_recv_mgmt = sta_recv_mgmt;
10032176cfdSRui Paulo 	vap->iv_recv_ctl = sta_recv_ctl;
10132176cfdSRui Paulo 	vap->iv_opdetach = sta_vdetach;
10232176cfdSRui Paulo 	vap->iv_bmiss = sta_beacon_miss;
10332176cfdSRui Paulo }
10432176cfdSRui Paulo 
10532176cfdSRui Paulo /*
10632176cfdSRui Paulo  * Handle a beacon miss event.  The common code filters out
10732176cfdSRui Paulo  * spurious events that can happen when scanning and/or before
10832176cfdSRui Paulo  * reaching RUN state.
10932176cfdSRui Paulo  */
11032176cfdSRui Paulo static void
11132176cfdSRui Paulo sta_beacon_miss(struct ieee80211vap *vap)
11232176cfdSRui Paulo {
11332176cfdSRui Paulo 	struct ieee80211com *ic = vap->iv_ic;
11432176cfdSRui Paulo 
115085ff963SMatthew Dillon 	IEEE80211_LOCK_ASSERT(ic);
116085ff963SMatthew Dillon 
11732176cfdSRui Paulo 	KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning"));
11832176cfdSRui Paulo 	KASSERT(vap->iv_state >= IEEE80211_S_RUN,
11932176cfdSRui Paulo 	    ("wrong state %s", ieee80211_state_name[vap->iv_state]));
12032176cfdSRui Paulo 
12132176cfdSRui Paulo 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
12232176cfdSRui Paulo 	    "beacon miss, mode %s state %s\n",
12332176cfdSRui Paulo 	    ieee80211_opmode_name[vap->iv_opmode],
12432176cfdSRui Paulo 	    ieee80211_state_name[vap->iv_state]);
12532176cfdSRui Paulo 
12632176cfdSRui Paulo 	if (vap->iv_state == IEEE80211_S_CSA) {
12732176cfdSRui Paulo 		/*
12832176cfdSRui Paulo 		 * A Channel Switch is pending; assume we missed the
12932176cfdSRui Paulo 		 * beacon that would've completed the process and just
13032176cfdSRui Paulo 		 * force the switch.  If we made a mistake we'll not
13132176cfdSRui Paulo 		 * find the AP on the new channel and fall back to a
13232176cfdSRui Paulo 		 * normal scan.
13332176cfdSRui Paulo 		 */
13432176cfdSRui Paulo 		ieee80211_csa_completeswitch(ic);
13532176cfdSRui Paulo 		return;
13632176cfdSRui Paulo 	}
13732176cfdSRui Paulo 	if (++vap->iv_bmiss_count < vap->iv_bmiss_max) {
13832176cfdSRui Paulo 		/*
13932176cfdSRui Paulo 		 * Send a directed probe req before falling back to a
14032176cfdSRui Paulo 		 * scan; if we receive a response ic_bmiss_count will
14132176cfdSRui Paulo 		 * be reset.  Some cards mistakenly report beacon miss
14232176cfdSRui Paulo 		 * so this avoids the expensive scan if the ap is
14332176cfdSRui Paulo 		 * still there.
14432176cfdSRui Paulo 		 */
14532176cfdSRui Paulo 		ieee80211_send_probereq(vap->iv_bss, vap->iv_myaddr,
14632176cfdSRui Paulo 			vap->iv_bss->ni_bssid, vap->iv_bss->ni_bssid,
14732176cfdSRui Paulo 			vap->iv_bss->ni_essid, vap->iv_bss->ni_esslen);
14832176cfdSRui Paulo 		return;
14932176cfdSRui Paulo 	}
150085ff963SMatthew Dillon 
151085ff963SMatthew Dillon 	callout_stop(&vap->iv_swbmiss);
15232176cfdSRui Paulo 	vap->iv_bmiss_count = 0;
15332176cfdSRui Paulo 	vap->iv_stats.is_beacon_miss++;
15432176cfdSRui Paulo 	if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
15532176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG
15632176cfdSRui Paulo 		struct ieee80211com *ic = vap->iv_ic;
15732176cfdSRui Paulo 
15832176cfdSRui Paulo 		/*
15932176cfdSRui Paulo 		 * If we receive a beacon miss interrupt when using
16032176cfdSRui Paulo 		 * dynamic turbo, attempt to switch modes before
16132176cfdSRui Paulo 		 * reassociating.
16232176cfdSRui Paulo 		 */
16332176cfdSRui Paulo 		if (IEEE80211_ATH_CAP(vap, vap->iv_bss, IEEE80211_NODE_TURBOP))
16432176cfdSRui Paulo 			ieee80211_dturbo_switch(vap,
16532176cfdSRui Paulo 			    ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO);
16632176cfdSRui Paulo #endif
16732176cfdSRui Paulo 		/*
16832176cfdSRui Paulo 		 * Try to reassociate before scanning for a new ap.
16932176cfdSRui Paulo 		 */
17032176cfdSRui Paulo 		ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1);
17132176cfdSRui Paulo 	} else {
17232176cfdSRui Paulo 		/*
17332176cfdSRui Paulo 		 * Somebody else is controlling state changes (e.g.
17432176cfdSRui Paulo 		 * a user-mode app) don't do anything that would
17532176cfdSRui Paulo 		 * confuse them; just drop into scan mode so they'll
17632176cfdSRui Paulo 		 * notified of the state change and given control.
17732176cfdSRui Paulo 		 */
17832176cfdSRui Paulo 		ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
17932176cfdSRui Paulo 	}
18032176cfdSRui Paulo }
18132176cfdSRui Paulo 
18232176cfdSRui Paulo /*
18332176cfdSRui Paulo  * Handle deauth with reason.  We retry only for
18432176cfdSRui Paulo  * the cases where we might succeed.  Otherwise
18532176cfdSRui Paulo  * we downgrade the ap and scan.
18632176cfdSRui Paulo  */
18732176cfdSRui Paulo static void
18832176cfdSRui Paulo sta_authretry(struct ieee80211vap *vap, struct ieee80211_node *ni, int reason)
18932176cfdSRui Paulo {
19032176cfdSRui Paulo 	switch (reason) {
19132176cfdSRui Paulo 	case IEEE80211_STATUS_SUCCESS:		/* NB: MLME assoc */
19232176cfdSRui Paulo 	case IEEE80211_STATUS_TIMEOUT:
19332176cfdSRui Paulo 	case IEEE80211_REASON_ASSOC_EXPIRE:
19432176cfdSRui Paulo 	case IEEE80211_REASON_NOT_AUTHED:
19532176cfdSRui Paulo 	case IEEE80211_REASON_NOT_ASSOCED:
19632176cfdSRui Paulo 	case IEEE80211_REASON_ASSOC_LEAVE:
19732176cfdSRui Paulo 	case IEEE80211_REASON_ASSOC_NOT_AUTHED:
19832176cfdSRui Paulo 		IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, 1);
19932176cfdSRui Paulo 		break;
20032176cfdSRui Paulo 	default:
20132176cfdSRui Paulo 		ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, reason);
20232176cfdSRui Paulo 		if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
20332176cfdSRui Paulo 			ieee80211_check_scan_current(vap);
20432176cfdSRui Paulo 		break;
20532176cfdSRui Paulo 	}
20632176cfdSRui Paulo }
20732176cfdSRui Paulo 
20832176cfdSRui Paulo /*
20932176cfdSRui Paulo  * IEEE80211_M_STA vap state machine handler.
21032176cfdSRui Paulo  * This routine handles the main states in the 802.11 protocol.
21132176cfdSRui Paulo  */
21232176cfdSRui Paulo static int
21332176cfdSRui Paulo sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
21432176cfdSRui Paulo {
21532176cfdSRui Paulo 	struct ieee80211com *ic = vap->iv_ic;
21632176cfdSRui Paulo 	struct ieee80211_node *ni;
21732176cfdSRui Paulo 	enum ieee80211_state ostate;
218085ff963SMatthew Dillon 
219085ff963SMatthew Dillon 	IEEE80211_LOCK_ASSERT(ic);
220085ff963SMatthew Dillon 
22132176cfdSRui Paulo 	ostate = vap->iv_state;
22232176cfdSRui Paulo 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
22332176cfdSRui Paulo 	    __func__, ieee80211_state_name[ostate],
22432176cfdSRui Paulo 	    ieee80211_state_name[nstate], arg);
22532176cfdSRui Paulo 	vap->iv_state = nstate;			/* state transition */
226085ff963SMatthew Dillon 	callout_stop(&vap->iv_mgtsend);		/* XXX callout_drain */
22732176cfdSRui Paulo 	if (ostate != IEEE80211_S_SCAN)
22832176cfdSRui Paulo 		ieee80211_cancel_scan(vap);	/* background scan */
22932176cfdSRui Paulo 	ni = vap->iv_bss;			/* NB: no reference held */
23032176cfdSRui Paulo 	if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
23132176cfdSRui Paulo 		callout_stop(&vap->iv_swbmiss);
23232176cfdSRui Paulo 	switch (nstate) {
23332176cfdSRui Paulo 	case IEEE80211_S_INIT:
23432176cfdSRui Paulo 		switch (ostate) {
23532176cfdSRui Paulo 		case IEEE80211_S_SLEEP:
23632176cfdSRui Paulo 			/* XXX wakeup */
237d98a0bcfSMatthew Dillon 			/* XXX driver hook to wakeup the hardware? */
23832176cfdSRui Paulo 		case IEEE80211_S_RUN:
23932176cfdSRui Paulo 			IEEE80211_SEND_MGMT(ni,
24032176cfdSRui Paulo 			    IEEE80211_FC0_SUBTYPE_DISASSOC,
24132176cfdSRui Paulo 			    IEEE80211_REASON_ASSOC_LEAVE);
24232176cfdSRui Paulo 			ieee80211_sta_leave(ni);
24332176cfdSRui Paulo 			break;
24432176cfdSRui Paulo 		case IEEE80211_S_ASSOC:
24532176cfdSRui Paulo 			IEEE80211_SEND_MGMT(ni,
24632176cfdSRui Paulo 			    IEEE80211_FC0_SUBTYPE_DEAUTH,
24732176cfdSRui Paulo 			    IEEE80211_REASON_AUTH_LEAVE);
24832176cfdSRui Paulo 			break;
24932176cfdSRui Paulo 		case IEEE80211_S_SCAN:
25032176cfdSRui Paulo 			ieee80211_cancel_scan(vap);
25132176cfdSRui Paulo 			break;
25232176cfdSRui Paulo 		default:
25332176cfdSRui Paulo 			goto invalid;
25432176cfdSRui Paulo 		}
25532176cfdSRui Paulo 		if (ostate != IEEE80211_S_INIT) {
25632176cfdSRui Paulo 			/* NB: optimize INIT -> INIT case */
25732176cfdSRui Paulo 			ieee80211_reset_bss(vap);
25832176cfdSRui Paulo 		}
25932176cfdSRui Paulo 		if (vap->iv_auth->ia_detach != NULL)
26032176cfdSRui Paulo 			vap->iv_auth->ia_detach(vap);
26132176cfdSRui Paulo 		break;
26232176cfdSRui Paulo 	case IEEE80211_S_SCAN:
26332176cfdSRui Paulo 		switch (ostate) {
26432176cfdSRui Paulo 		case IEEE80211_S_INIT:
26532176cfdSRui Paulo 			/*
26632176cfdSRui Paulo 			 * Initiate a scan.  We can come here as a result
26732176cfdSRui Paulo 			 * of an IEEE80211_IOC_SCAN_REQ too in which case
26832176cfdSRui Paulo 			 * the vap will be marked with IEEE80211_FEXT_SCANREQ
26932176cfdSRui Paulo 			 * and the scan request parameters will be present
27032176cfdSRui Paulo 			 * in iv_scanreq.  Otherwise we do the default.
27132176cfdSRui Paulo 			 */
27232176cfdSRui Paulo 			if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
27332176cfdSRui Paulo 				ieee80211_check_scan(vap,
27432176cfdSRui Paulo 				    vap->iv_scanreq_flags,
27532176cfdSRui Paulo 				    vap->iv_scanreq_duration,
27632176cfdSRui Paulo 				    vap->iv_scanreq_mindwell,
27732176cfdSRui Paulo 				    vap->iv_scanreq_maxdwell,
27832176cfdSRui Paulo 				    vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
27932176cfdSRui Paulo 				vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
28032176cfdSRui Paulo 			} else
28132176cfdSRui Paulo 				ieee80211_check_scan_current(vap);
28232176cfdSRui Paulo 			break;
28332176cfdSRui Paulo 		case IEEE80211_S_SCAN:
28432176cfdSRui Paulo 		case IEEE80211_S_AUTH:
28532176cfdSRui Paulo 		case IEEE80211_S_ASSOC:
28632176cfdSRui Paulo 			/*
28732176cfdSRui Paulo 			 * These can happen either because of a timeout
28832176cfdSRui Paulo 			 * on an assoc/auth response or because of a
28932176cfdSRui Paulo 			 * change in state that requires a reset.  For
29032176cfdSRui Paulo 			 * the former we're called with a non-zero arg
29132176cfdSRui Paulo 			 * that is the cause for the failure; pass this
29232176cfdSRui Paulo 			 * to the scan code so it can update state.
29332176cfdSRui Paulo 			 * Otherwise trigger a new scan unless we're in
29432176cfdSRui Paulo 			 * manual roaming mode in which case an application
29532176cfdSRui Paulo 			 * must issue an explicit scan request.
29632176cfdSRui Paulo 			 */
29732176cfdSRui Paulo 			if (arg != 0)
29832176cfdSRui Paulo 				ieee80211_scan_assoc_fail(vap,
29932176cfdSRui Paulo 					vap->iv_bss->ni_macaddr, arg);
30032176cfdSRui Paulo 			if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
30132176cfdSRui Paulo 				ieee80211_check_scan_current(vap);
30232176cfdSRui Paulo 			break;
303d98a0bcfSMatthew Dillon 		case IEEE80211_S_SLEEP:		/* beacon miss */
304d98a0bcfSMatthew Dillon 			/*
305d98a0bcfSMatthew Dillon 			 * XXX if in sleep we need to wakeup the hardware.
306d98a0bcfSMatthew Dillon 			 */
307d98a0bcfSMatthew Dillon 			/* FALLTHROUGH */
30832176cfdSRui Paulo 		case IEEE80211_S_RUN:		/* beacon miss */
30932176cfdSRui Paulo 			/*
31032176cfdSRui Paulo 			 * Beacon miss.  Notify user space and if not
31132176cfdSRui Paulo 			 * under control of a user application (roaming
31232176cfdSRui Paulo 			 * manual) kick off a scan to re-connect.
31332176cfdSRui Paulo 			 */
314d98a0bcfSMatthew Dillon 
31532176cfdSRui Paulo 			ieee80211_sta_leave(ni);
31632176cfdSRui Paulo 			if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
31732176cfdSRui Paulo 				ieee80211_check_scan_current(vap);
31832176cfdSRui Paulo 			break;
31932176cfdSRui Paulo 		default:
32032176cfdSRui Paulo 			goto invalid;
32132176cfdSRui Paulo 		}
32232176cfdSRui Paulo 		break;
32332176cfdSRui Paulo 	case IEEE80211_S_AUTH:
32432176cfdSRui Paulo 		switch (ostate) {
32532176cfdSRui Paulo 		case IEEE80211_S_INIT:
32632176cfdSRui Paulo 		case IEEE80211_S_SCAN:
32732176cfdSRui Paulo 			IEEE80211_SEND_MGMT(ni,
32832176cfdSRui Paulo 			    IEEE80211_FC0_SUBTYPE_AUTH, 1);
32932176cfdSRui Paulo 			break;
33032176cfdSRui Paulo 		case IEEE80211_S_AUTH:
33132176cfdSRui Paulo 		case IEEE80211_S_ASSOC:
33232176cfdSRui Paulo 			switch (arg & 0xff) {
33332176cfdSRui Paulo 			case IEEE80211_FC0_SUBTYPE_AUTH:
33432176cfdSRui Paulo 				/* ??? */
33532176cfdSRui Paulo 				IEEE80211_SEND_MGMT(ni,
33632176cfdSRui Paulo 				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
33732176cfdSRui Paulo 				break;
33832176cfdSRui Paulo 			case IEEE80211_FC0_SUBTYPE_DEAUTH:
33932176cfdSRui Paulo 				sta_authretry(vap, ni, arg>>8);
34032176cfdSRui Paulo 				break;
34132176cfdSRui Paulo 			}
34232176cfdSRui Paulo 			break;
34332176cfdSRui Paulo 		case IEEE80211_S_RUN:
34432176cfdSRui Paulo 			switch (arg & 0xff) {
34532176cfdSRui Paulo 			case IEEE80211_FC0_SUBTYPE_AUTH:
34632176cfdSRui Paulo 				IEEE80211_SEND_MGMT(ni,
34732176cfdSRui Paulo 				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
34832176cfdSRui Paulo 				vap->iv_state = ostate;	/* stay RUN */
34932176cfdSRui Paulo 				break;
35032176cfdSRui Paulo 			case IEEE80211_FC0_SUBTYPE_DEAUTH:
35132176cfdSRui Paulo 				ieee80211_sta_leave(ni);
35232176cfdSRui Paulo 				if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
35332176cfdSRui Paulo 					/* try to reauth */
35432176cfdSRui Paulo 					IEEE80211_SEND_MGMT(ni,
35532176cfdSRui Paulo 					    IEEE80211_FC0_SUBTYPE_AUTH, 1);
35632176cfdSRui Paulo 				}
35732176cfdSRui Paulo 				break;
35832176cfdSRui Paulo 			}
35932176cfdSRui Paulo 			break;
36032176cfdSRui Paulo 		default:
36132176cfdSRui Paulo 			goto invalid;
36232176cfdSRui Paulo 		}
36332176cfdSRui Paulo 		break;
36432176cfdSRui Paulo 	case IEEE80211_S_ASSOC:
36532176cfdSRui Paulo 		switch (ostate) {
36632176cfdSRui Paulo 		case IEEE80211_S_AUTH:
36732176cfdSRui Paulo 		case IEEE80211_S_ASSOC:
36832176cfdSRui Paulo 			IEEE80211_SEND_MGMT(ni,
36932176cfdSRui Paulo 			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
37032176cfdSRui Paulo 			break;
37132176cfdSRui Paulo 		case IEEE80211_S_SLEEP:		/* cannot happen */
37232176cfdSRui Paulo 		case IEEE80211_S_RUN:
37332176cfdSRui Paulo 			ieee80211_sta_leave(ni);
37432176cfdSRui Paulo 			if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
37532176cfdSRui Paulo 				IEEE80211_SEND_MGMT(ni, arg ?
37632176cfdSRui Paulo 				    IEEE80211_FC0_SUBTYPE_REASSOC_REQ :
37732176cfdSRui Paulo 				    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
37832176cfdSRui Paulo 			}
37932176cfdSRui Paulo 			break;
38032176cfdSRui Paulo 		default:
38132176cfdSRui Paulo 			goto invalid;
38232176cfdSRui Paulo 		}
38332176cfdSRui Paulo 		break;
38432176cfdSRui Paulo 	case IEEE80211_S_RUN:
38532176cfdSRui Paulo 		if (vap->iv_flags & IEEE80211_F_WPA) {
38632176cfdSRui Paulo 			/* XXX validate prerequisites */
38732176cfdSRui Paulo 		}
38832176cfdSRui Paulo 		switch (ostate) {
38932176cfdSRui Paulo 		case IEEE80211_S_RUN:
39032176cfdSRui Paulo 		case IEEE80211_S_CSA:
39132176cfdSRui Paulo 			break;
39232176cfdSRui Paulo 		case IEEE80211_S_AUTH:		/* when join is done in fw */
39332176cfdSRui Paulo 		case IEEE80211_S_ASSOC:
39432176cfdSRui Paulo #ifdef IEEE80211_DEBUG
39532176cfdSRui Paulo 			if (ieee80211_msg_debug(vap)) {
3961e290df3SAntonio Huete Jimenez 				ieee80211_note(vap, "%s with %s ssid ",
39732176cfdSRui Paulo 				    (vap->iv_opmode == IEEE80211_M_STA ?
39832176cfdSRui Paulo 				    "associated" : "synchronized"),
399085ff963SMatthew Dillon 				    ether_sprintf(ni->ni_bssid));
40032176cfdSRui Paulo 				ieee80211_print_essid(vap->iv_bss->ni_essid,
40132176cfdSRui Paulo 				    ni->ni_esslen);
40232176cfdSRui Paulo 				/* XXX MCS/HT */
4036168f72eSRui Paulo 				kprintf(" channel %d start %uMb\n",
40432176cfdSRui Paulo 				    ieee80211_chan2ieee(ic, ic->ic_curchan),
40532176cfdSRui Paulo 				    IEEE80211_RATE2MBS(ni->ni_txrate));
40632176cfdSRui Paulo 			}
40732176cfdSRui Paulo #endif
40832176cfdSRui Paulo 			ieee80211_scan_assoc_success(vap, ni->ni_macaddr);
40932176cfdSRui Paulo 			ieee80211_notify_node_join(ni,
41032176cfdSRui Paulo 			    arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
41132176cfdSRui Paulo 			break;
41232176cfdSRui Paulo 		case IEEE80211_S_SLEEP:
413d98a0bcfSMatthew Dillon 			/* Wake up from sleep */
414d98a0bcfSMatthew Dillon 			vap->iv_sta_ps(vap, 0);
41532176cfdSRui Paulo 			break;
41632176cfdSRui Paulo 		default:
41732176cfdSRui Paulo 			goto invalid;
41832176cfdSRui Paulo 		}
41932176cfdSRui Paulo 		ieee80211_sync_curchan(ic);
42032176cfdSRui Paulo 		if (ostate != IEEE80211_S_RUN &&
42132176cfdSRui Paulo 		    (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)) {
42232176cfdSRui Paulo 			/*
42332176cfdSRui Paulo 			 * Start s/w beacon miss timer for devices w/o
42432176cfdSRui Paulo 			 * hardware support.  We fudge a bit here since
42532176cfdSRui Paulo 			 * we're doing this in software.
42632176cfdSRui Paulo 			 */
42732176cfdSRui Paulo 			vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS(
42832176cfdSRui Paulo 				2 * vap->iv_bmissthreshold * ni->ni_intval);
42932176cfdSRui Paulo 			vap->iv_swbmiss_count = 0;
43032176cfdSRui Paulo 			callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
431085ff963SMatthew Dillon 				ieee80211_swbmiss, vap);
43232176cfdSRui Paulo 		}
43332176cfdSRui Paulo 		/*
43432176cfdSRui Paulo 		 * When 802.1x is not in use mark the port authorized
43532176cfdSRui Paulo 		 * at this point so traffic can flow.
43632176cfdSRui Paulo 		 */
43732176cfdSRui Paulo 		if (ni->ni_authmode != IEEE80211_AUTH_8021X)
43832176cfdSRui Paulo 			ieee80211_node_authorize(ni);
43932176cfdSRui Paulo 		/*
44032176cfdSRui Paulo 		 * Fake association when joining an existing bss.
441d98a0bcfSMatthew Dillon 		 *
442d98a0bcfSMatthew Dillon 		 * Don't do this if we're doing SLEEP->RUN.
44332176cfdSRui Paulo 		 */
444d98a0bcfSMatthew Dillon 		if (ic->ic_newassoc != NULL && ostate != IEEE80211_S_SLEEP)
445d98a0bcfSMatthew Dillon 			ic->ic_newassoc(vap->iv_bss, (ostate != IEEE80211_S_RUN));
44632176cfdSRui Paulo 		break;
44732176cfdSRui Paulo 	case IEEE80211_S_CSA:
44832176cfdSRui Paulo 		if (ostate != IEEE80211_S_RUN)
44932176cfdSRui Paulo 			goto invalid;
45032176cfdSRui Paulo 		break;
45132176cfdSRui Paulo 	case IEEE80211_S_SLEEP:
452085ff963SMatthew Dillon 		vap->iv_sta_ps(vap, 1);
45332176cfdSRui Paulo 		break;
45432176cfdSRui Paulo 	default:
45532176cfdSRui Paulo 	invalid:
45632176cfdSRui Paulo 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
45732176cfdSRui Paulo 		    "%s: unexpected state transition %s -> %s\n", __func__,
45832176cfdSRui Paulo 		    ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
45932176cfdSRui Paulo 		break;
46032176cfdSRui Paulo 	}
46132176cfdSRui Paulo 	return 0;
46232176cfdSRui Paulo }
46332176cfdSRui Paulo 
46432176cfdSRui Paulo /*
46532176cfdSRui Paulo  * Return non-zero if the frame is an echo of a multicast
46632176cfdSRui Paulo  * frame sent by ourself.  The dir is known to be DSTODS.
46732176cfdSRui Paulo  */
46832176cfdSRui Paulo static __inline int
46932176cfdSRui Paulo isdstods_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
47032176cfdSRui Paulo {
47132176cfdSRui Paulo #define	QWH4(wh)	((const struct ieee80211_qosframe_addr4 *)wh)
47232176cfdSRui Paulo #define	WH4(wh)		((const struct ieee80211_frame_addr4 *)wh)
47332176cfdSRui Paulo 	const uint8_t *sa;
47432176cfdSRui Paulo 
47532176cfdSRui Paulo 	KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode"));
47632176cfdSRui Paulo 
47732176cfdSRui Paulo 	if (!IEEE80211_IS_MULTICAST(wh->i_addr3))
47832176cfdSRui Paulo 		return 0;
47932176cfdSRui Paulo 	sa = IEEE80211_QOS_HAS_SEQ(wh) ? QWH4(wh)->i_addr4 : WH4(wh)->i_addr4;
48032176cfdSRui Paulo 	return IEEE80211_ADDR_EQ(sa, vap->iv_myaddr);
48132176cfdSRui Paulo #undef WH4
48232176cfdSRui Paulo #undef QWH4
48332176cfdSRui Paulo }
48432176cfdSRui Paulo 
48532176cfdSRui Paulo /*
48632176cfdSRui Paulo  * Return non-zero if the frame is an echo of a multicast
48732176cfdSRui Paulo  * frame sent by ourself.  The dir is known to be FROMDS.
48832176cfdSRui Paulo  */
48932176cfdSRui Paulo static __inline int
49032176cfdSRui Paulo isfromds_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
49132176cfdSRui Paulo {
49232176cfdSRui Paulo 	KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode"));
49332176cfdSRui Paulo 
49432176cfdSRui Paulo 	if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
49532176cfdSRui Paulo 		return 0;
49632176cfdSRui Paulo 	return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr);
49732176cfdSRui Paulo }
49832176cfdSRui Paulo 
49932176cfdSRui Paulo /*
50032176cfdSRui Paulo  * Decide if a received management frame should be
50132176cfdSRui Paulo  * printed when debugging is enabled.  This filters some
50232176cfdSRui Paulo  * of the less interesting frames that come frequently
50332176cfdSRui Paulo  * (e.g. beacons).
50432176cfdSRui Paulo  */
50532176cfdSRui Paulo static __inline int
50632176cfdSRui Paulo doprint(struct ieee80211vap *vap, int subtype)
50732176cfdSRui Paulo {
50832176cfdSRui Paulo 	switch (subtype) {
50932176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_BEACON:
51032176cfdSRui Paulo 		return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
51132176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
51232176cfdSRui Paulo 		return 0;
51332176cfdSRui Paulo 	}
51432176cfdSRui Paulo 	return 1;
51532176cfdSRui Paulo }
51632176cfdSRui Paulo 
51732176cfdSRui Paulo /*
51832176cfdSRui Paulo  * Process a received frame.  The node associated with the sender
51932176cfdSRui Paulo  * should be supplied.  If nothing was found in the node table then
52032176cfdSRui Paulo  * the caller is assumed to supply a reference to iv_bss instead.
52132176cfdSRui Paulo  * The RSSI and a timestamp are also supplied.  The RSSI data is used
52232176cfdSRui Paulo  * during AP scanning to select a AP to associate with; it can have
52332176cfdSRui Paulo  * any units so long as values have consistent units and higher values
52432176cfdSRui Paulo  * mean ``better signal''.  The receive timestamp is currently not used
52532176cfdSRui Paulo  * by the 802.11 layer.
52632176cfdSRui Paulo  */
52732176cfdSRui Paulo static int
52832176cfdSRui Paulo sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
52932176cfdSRui Paulo {
53032176cfdSRui Paulo #define	HAS_SEQ(type)	((type & 0x4) == 0)
53132176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
53232176cfdSRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
53332176cfdSRui Paulo 	struct ifnet *ifp = vap->iv_ifp;
53432176cfdSRui Paulo 	struct ieee80211_frame *wh;
53532176cfdSRui Paulo 	struct ieee80211_key *key;
53632176cfdSRui Paulo 	struct ether_header *eh;
53732176cfdSRui Paulo 	int hdrspace, need_tap = 1;	/* mbuf need to be tapped. */
53832176cfdSRui Paulo 	uint8_t dir, type, subtype, qos;
53932176cfdSRui Paulo 	uint8_t *bssid;
54032176cfdSRui Paulo 	uint16_t rxseq;
54132176cfdSRui Paulo 
54232176cfdSRui Paulo 	if (m->m_flags & M_AMPDU_MPDU) {
54332176cfdSRui Paulo 		/*
54432176cfdSRui Paulo 		 * Fastpath for A-MPDU reorder q resubmission.  Frames
54532176cfdSRui Paulo 		 * w/ M_AMPDU_MPDU marked have already passed through
54632176cfdSRui Paulo 		 * here but were received out of order and been held on
54732176cfdSRui Paulo 		 * the reorder queue.  When resubmitted they are marked
54832176cfdSRui Paulo 		 * with the M_AMPDU_MPDU flag and we can bypass most of
54932176cfdSRui Paulo 		 * the normal processing.
55032176cfdSRui Paulo 		 */
55132176cfdSRui Paulo 		wh = mtod(m, struct ieee80211_frame *);
55232176cfdSRui Paulo 		type = IEEE80211_FC0_TYPE_DATA;
55332176cfdSRui Paulo 		dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
55432176cfdSRui Paulo 		subtype = IEEE80211_FC0_SUBTYPE_QOS;
55532176cfdSRui Paulo 		hdrspace = ieee80211_hdrspace(ic, wh);	/* XXX optimize? */
55632176cfdSRui Paulo 		goto resubmit_ampdu;
55732176cfdSRui Paulo 	}
55832176cfdSRui Paulo 
55932176cfdSRui Paulo 	KASSERT(ni != NULL, ("null node"));
56032176cfdSRui Paulo 	ni->ni_inact = ni->ni_inact_reload;
56132176cfdSRui Paulo 
56232176cfdSRui Paulo 	type = -1;			/* undefined */
56332176cfdSRui Paulo 
56432176cfdSRui Paulo 	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
56532176cfdSRui Paulo 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
56632176cfdSRui Paulo 		    ni->ni_macaddr, NULL,
56732176cfdSRui Paulo 		    "too short (1): len %u", m->m_pkthdr.len);
56832176cfdSRui Paulo 		vap->iv_stats.is_rx_tooshort++;
56932176cfdSRui Paulo 		goto out;
57032176cfdSRui Paulo 	}
57132176cfdSRui Paulo 	/*
57232176cfdSRui Paulo 	 * Bit of a cheat here, we use a pointer for a 3-address
57332176cfdSRui Paulo 	 * frame format but don't reference fields past outside
57432176cfdSRui Paulo 	 * ieee80211_frame_min w/o first validating the data is
57532176cfdSRui Paulo 	 * present.
57632176cfdSRui Paulo 	 */
57732176cfdSRui Paulo 	wh = mtod(m, struct ieee80211_frame *);
57832176cfdSRui Paulo 
57932176cfdSRui Paulo 	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
58032176cfdSRui Paulo 	    IEEE80211_FC0_VERSION_0) {
58132176cfdSRui Paulo 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
58232176cfdSRui Paulo 		    ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x",
58332176cfdSRui Paulo 		    wh->i_fc[0], wh->i_fc[1]);
58432176cfdSRui Paulo 		vap->iv_stats.is_rx_badversion++;
58532176cfdSRui Paulo 		goto err;
58632176cfdSRui Paulo 	}
58732176cfdSRui Paulo 
58832176cfdSRui Paulo 	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
58932176cfdSRui Paulo 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
59032176cfdSRui Paulo 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
59132176cfdSRui Paulo 	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
59232176cfdSRui Paulo 		bssid = wh->i_addr2;
59332176cfdSRui Paulo 		if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
59432176cfdSRui Paulo 			/* not interested in */
59532176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
59632176cfdSRui Paulo 			    bssid, NULL, "%s", "not to bss");
59732176cfdSRui Paulo 			vap->iv_stats.is_rx_wrongbss++;
59832176cfdSRui Paulo 			goto out;
59932176cfdSRui Paulo 		}
600085ff963SMatthew Dillon 
601085ff963SMatthew Dillon 		/*
602085ff963SMatthew Dillon 		 * Some devices may be in a promiscuous mode
603085ff963SMatthew Dillon 		 * where they receive frames for multiple station
604085ff963SMatthew Dillon 		 * addresses.
605085ff963SMatthew Dillon 		 *
606085ff963SMatthew Dillon 		 * If we receive a data frame that isn't
607085ff963SMatthew Dillon 		 * destined to our VAP MAC, drop it.
608085ff963SMatthew Dillon 		 *
609085ff963SMatthew Dillon 		 * XXX TODO: This is only enforced when not scanning;
610085ff963SMatthew Dillon 		 * XXX it assumes a software-driven scan will put the NIC
611085ff963SMatthew Dillon 		 * XXX into a "no data frames" mode before setting this
612085ff963SMatthew Dillon 		 * XXX flag. Otherwise it may be possible that we'll still
613085ff963SMatthew Dillon 		 * XXX process data frames whilst scanning.
614085ff963SMatthew Dillon 		 */
615085ff963SMatthew Dillon 		if ((! IEEE80211_IS_MULTICAST(wh->i_addr1))
616085ff963SMatthew Dillon 		    && (! IEEE80211_ADDR_EQ(wh->i_addr1, IF_LLADDR(ifp)))) {
617085ff963SMatthew Dillon 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
618085ff963SMatthew Dillon 			    bssid, NULL, "not to cur sta: lladdr=%6D, addr1=%6D",
619085ff963SMatthew Dillon 			    IF_LLADDR(ifp), ":", wh->i_addr1, ":");
620085ff963SMatthew Dillon 			vap->iv_stats.is_rx_wrongbss++;
621085ff963SMatthew Dillon 			goto out;
622085ff963SMatthew Dillon 		}
623085ff963SMatthew Dillon 
62432176cfdSRui Paulo 		IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
62532176cfdSRui Paulo 		ni->ni_noise = nf;
62636e4ebd1SJoe Talbott 		if (HAS_SEQ(type) && !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
62732176cfdSRui Paulo 			uint8_t tid = ieee80211_gettid(wh);
62832176cfdSRui Paulo 			if (IEEE80211_QOS_HAS_SEQ(wh) &&
62932176cfdSRui Paulo 			    TID_TO_WME_AC(tid) >= WME_AC_VI)
63032176cfdSRui Paulo 				ic->ic_wme.wme_hipri_traffic++;
63132176cfdSRui Paulo 			rxseq = le16toh(*(uint16_t *)wh->i_seq);
632085ff963SMatthew Dillon 			if (! ieee80211_check_rxseq(ni, wh)) {
63332176cfdSRui Paulo 				/* duplicate, discard */
63432176cfdSRui Paulo 				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
63532176cfdSRui Paulo 				    bssid, "duplicate",
63632176cfdSRui Paulo 				    "seqno <%u,%u> fragno <%u,%u> tid %u",
63732176cfdSRui Paulo 				    rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
63832176cfdSRui Paulo 				    ni->ni_rxseqs[tid] >>
63932176cfdSRui Paulo 					IEEE80211_SEQ_SEQ_SHIFT,
64032176cfdSRui Paulo 				    rxseq & IEEE80211_SEQ_FRAG_MASK,
64132176cfdSRui Paulo 				    ni->ni_rxseqs[tid] &
64232176cfdSRui Paulo 					IEEE80211_SEQ_FRAG_MASK,
64332176cfdSRui Paulo 				    tid);
64432176cfdSRui Paulo 				vap->iv_stats.is_rx_dup++;
64532176cfdSRui Paulo 				IEEE80211_NODE_STAT(ni, rx_dup);
64632176cfdSRui Paulo 				goto out;
64732176cfdSRui Paulo 			}
64832176cfdSRui Paulo 			ni->ni_rxseqs[tid] = rxseq;
64932176cfdSRui Paulo 		}
65032176cfdSRui Paulo 	}
65132176cfdSRui Paulo 
65232176cfdSRui Paulo 	switch (type) {
65332176cfdSRui Paulo 	case IEEE80211_FC0_TYPE_DATA:
65432176cfdSRui Paulo 		hdrspace = ieee80211_hdrspace(ic, wh);
65532176cfdSRui Paulo 		if (m->m_len < hdrspace &&
65632176cfdSRui Paulo 		    (m = m_pullup(m, hdrspace)) == NULL) {
65732176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
65832176cfdSRui Paulo 			    ni->ni_macaddr, NULL,
65932176cfdSRui Paulo 			    "data too short: expecting %u", hdrspace);
66032176cfdSRui Paulo 			vap->iv_stats.is_rx_tooshort++;
66132176cfdSRui Paulo 			goto out;		/* XXX */
66232176cfdSRui Paulo 		}
66332176cfdSRui Paulo 		/*
66432176cfdSRui Paulo 		 * Handle A-MPDU re-ordering.  If the frame is to be
66532176cfdSRui Paulo 		 * processed directly then ieee80211_ampdu_reorder
66632176cfdSRui Paulo 		 * will return 0; otherwise it has consumed the mbuf
66732176cfdSRui Paulo 		 * and we should do nothing more with it.
66832176cfdSRui Paulo 		 */
66932176cfdSRui Paulo 		if ((m->m_flags & M_AMPDU) &&
67032176cfdSRui Paulo 		    (dir == IEEE80211_FC1_DIR_FROMDS ||
67132176cfdSRui Paulo 		     dir == IEEE80211_FC1_DIR_DSTODS) &&
67232176cfdSRui Paulo 		    ieee80211_ampdu_reorder(ni, m) != 0) {
67332176cfdSRui Paulo 			m = NULL;
67432176cfdSRui Paulo 			goto out;
67532176cfdSRui Paulo 		}
67632176cfdSRui Paulo 	resubmit_ampdu:
67732176cfdSRui Paulo 		if (dir == IEEE80211_FC1_DIR_FROMDS) {
67832176cfdSRui Paulo 			if ((ifp->if_flags & IFF_SIMPLEX) &&
67932176cfdSRui Paulo 			    isfromds_mcastecho(vap, wh)) {
68032176cfdSRui Paulo 				/*
68132176cfdSRui Paulo 				 * In IEEE802.11 network, multicast
68232176cfdSRui Paulo 				 * packets sent from "me" are broadcast
68332176cfdSRui Paulo 				 * from the AP; silently discard for
68432176cfdSRui Paulo 				 * SIMPLEX interface.
68532176cfdSRui Paulo 				 */
68632176cfdSRui Paulo 				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
68732176cfdSRui Paulo 				    wh, "data", "%s", "multicast echo");
68832176cfdSRui Paulo 				vap->iv_stats.is_rx_mcastecho++;
68932176cfdSRui Paulo 				goto out;
69032176cfdSRui Paulo 			}
69132176cfdSRui Paulo 			if ((vap->iv_flags & IEEE80211_F_DWDS) &&
69232176cfdSRui Paulo 			    IEEE80211_IS_MULTICAST(wh->i_addr1)) {
69332176cfdSRui Paulo 				/*
69432176cfdSRui Paulo 				 * DWDS sta's must drop 3-address mcast frames
69532176cfdSRui Paulo 				 * as they will be sent separately as a 4-addr
69632176cfdSRui Paulo 				 * frame.  Accepting the 3-addr frame will
69732176cfdSRui Paulo 				 * confuse the bridge into thinking the sending
69832176cfdSRui Paulo 				 * sta is located at the end of WDS link.
69932176cfdSRui Paulo 				 */
70032176cfdSRui Paulo 				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
70132176cfdSRui Paulo 				    "3-address data", "%s", "DWDS enabled");
70232176cfdSRui Paulo 				vap->iv_stats.is_rx_mcastecho++;
70332176cfdSRui Paulo 				goto out;
70432176cfdSRui Paulo 			}
70532176cfdSRui Paulo 		} else if (dir == IEEE80211_FC1_DIR_DSTODS) {
70632176cfdSRui Paulo 			if ((vap->iv_flags & IEEE80211_F_DWDS) == 0) {
70732176cfdSRui Paulo 				IEEE80211_DISCARD(vap,
70832176cfdSRui Paulo 				    IEEE80211_MSG_INPUT, wh, "4-address data",
70932176cfdSRui Paulo 				    "%s", "DWDS not enabled");
71032176cfdSRui Paulo 				vap->iv_stats.is_rx_wrongdir++;
71132176cfdSRui Paulo 				goto out;
71232176cfdSRui Paulo 			}
71332176cfdSRui Paulo 			if ((ifp->if_flags & IFF_SIMPLEX) &&
71432176cfdSRui Paulo 			    isdstods_mcastecho(vap, wh)) {
71532176cfdSRui Paulo 				/*
71632176cfdSRui Paulo 				 * In IEEE802.11 network, multicast
71732176cfdSRui Paulo 				 * packets sent from "me" are broadcast
71832176cfdSRui Paulo 				 * from the AP; silently discard for
71932176cfdSRui Paulo 				 * SIMPLEX interface.
72032176cfdSRui Paulo 				 */
72132176cfdSRui Paulo 				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
72232176cfdSRui Paulo 				    "4-address data", "%s", "multicast echo");
72332176cfdSRui Paulo 				vap->iv_stats.is_rx_mcastecho++;
72432176cfdSRui Paulo 				goto out;
72532176cfdSRui Paulo 			}
72632176cfdSRui Paulo 		} else {
72732176cfdSRui Paulo 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
72832176cfdSRui Paulo 			    "data", "incorrect dir 0x%x", dir);
72932176cfdSRui Paulo 			vap->iv_stats.is_rx_wrongdir++;
73032176cfdSRui Paulo 			goto out;
73132176cfdSRui Paulo 		}
73232176cfdSRui Paulo 
73332176cfdSRui Paulo 		/*
73432176cfdSRui Paulo 		 * Handle privacy requirements.  Note that we
73532176cfdSRui Paulo 		 * must not be preempted from here until after
73632176cfdSRui Paulo 		 * we (potentially) call ieee80211_crypto_demic;
73732176cfdSRui Paulo 		 * otherwise we may violate assumptions in the
73832176cfdSRui Paulo 		 * crypto cipher modules used to do delayed update
73932176cfdSRui Paulo 		 * of replay sequence numbers.
74032176cfdSRui Paulo 		 */
741085ff963SMatthew Dillon 		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
74232176cfdSRui Paulo 			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
74332176cfdSRui Paulo 				/*
74432176cfdSRui Paulo 				 * Discard encrypted frames when privacy is off.
74532176cfdSRui Paulo 				 */
74632176cfdSRui Paulo 				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
74732176cfdSRui Paulo 				    wh, "WEP", "%s", "PRIVACY off");
74832176cfdSRui Paulo 				vap->iv_stats.is_rx_noprivacy++;
74932176cfdSRui Paulo 				IEEE80211_NODE_STAT(ni, rx_noprivacy);
75032176cfdSRui Paulo 				goto out;
75132176cfdSRui Paulo 			}
75232176cfdSRui Paulo 			key = ieee80211_crypto_decap(ni, m, hdrspace);
75332176cfdSRui Paulo 			if (key == NULL) {
75432176cfdSRui Paulo 				/* NB: stats+msgs handled in crypto_decap */
75532176cfdSRui Paulo 				IEEE80211_NODE_STAT(ni, rx_wepfail);
75632176cfdSRui Paulo 				goto out;
75732176cfdSRui Paulo 			}
75832176cfdSRui Paulo 			wh = mtod(m, struct ieee80211_frame *);
759085ff963SMatthew Dillon 			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
76032176cfdSRui Paulo 		} else {
76132176cfdSRui Paulo 			/* XXX M_WEP and IEEE80211_F_PRIVACY */
76232176cfdSRui Paulo 			key = NULL;
76332176cfdSRui Paulo 		}
76432176cfdSRui Paulo 
76532176cfdSRui Paulo 		/*
76632176cfdSRui Paulo 		 * Save QoS bits for use below--before we strip the header.
76732176cfdSRui Paulo 		 */
76832176cfdSRui Paulo 		if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
76932176cfdSRui Paulo 			qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
77032176cfdSRui Paulo 			    ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
77132176cfdSRui Paulo 			    ((struct ieee80211_qosframe *)wh)->i_qos[0];
77232176cfdSRui Paulo 		} else
77332176cfdSRui Paulo 			qos = 0;
77432176cfdSRui Paulo 
77532176cfdSRui Paulo 		/*
77632176cfdSRui Paulo 		 * Next up, any fragmentation.
77732176cfdSRui Paulo 		 */
77832176cfdSRui Paulo 		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
77932176cfdSRui Paulo 			m = ieee80211_defrag(ni, m, hdrspace);
78032176cfdSRui Paulo 			if (m == NULL) {
78132176cfdSRui Paulo 				/* Fragment dropped or frame not complete yet */
78232176cfdSRui Paulo 				goto out;
78332176cfdSRui Paulo 			}
78432176cfdSRui Paulo 		}
78532176cfdSRui Paulo 		wh = NULL;		/* no longer valid, catch any uses */
78632176cfdSRui Paulo 
78732176cfdSRui Paulo 		/*
78832176cfdSRui Paulo 		 * Next strip any MSDU crypto bits.
78932176cfdSRui Paulo 		 */
79032176cfdSRui Paulo 		if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
79132176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
79232176cfdSRui Paulo 			    ni->ni_macaddr, "data", "%s", "demic error");
79332176cfdSRui Paulo 			vap->iv_stats.is_rx_demicfail++;
79432176cfdSRui Paulo 			IEEE80211_NODE_STAT(ni, rx_demicfail);
79532176cfdSRui Paulo 			goto out;
79632176cfdSRui Paulo 		}
79732176cfdSRui Paulo 
79832176cfdSRui Paulo 		/* copy to listener after decrypt */
79932176cfdSRui Paulo 		if (ieee80211_radiotap_active_vap(vap))
80032176cfdSRui Paulo 			ieee80211_radiotap_rx(vap, m);
80132176cfdSRui Paulo 		need_tap = 0;
80232176cfdSRui Paulo 
80332176cfdSRui Paulo 		/*
80432176cfdSRui Paulo 		 * Finally, strip the 802.11 header.
80532176cfdSRui Paulo 		 */
80632176cfdSRui Paulo 		m = ieee80211_decap(vap, m, hdrspace);
80732176cfdSRui Paulo 		if (m == NULL) {
80832176cfdSRui Paulo 			/* XXX mask bit to check for both */
80932176cfdSRui Paulo 			/* don't count Null data frames as errors */
81032176cfdSRui Paulo 			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
81132176cfdSRui Paulo 			    subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
81232176cfdSRui Paulo 				goto out;
81332176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
81432176cfdSRui Paulo 			    ni->ni_macaddr, "data", "%s", "decap error");
81532176cfdSRui Paulo 			vap->iv_stats.is_rx_decap++;
81632176cfdSRui Paulo 			IEEE80211_NODE_STAT(ni, rx_decap);
81732176cfdSRui Paulo 			goto err;
81832176cfdSRui Paulo 		}
81932176cfdSRui Paulo 		eh = mtod(m, struct ether_header *);
82032176cfdSRui Paulo 		if (!ieee80211_node_is_authorized(ni)) {
82132176cfdSRui Paulo 			/*
82232176cfdSRui Paulo 			 * Deny any non-PAE frames received prior to
82332176cfdSRui Paulo 			 * authorization.  For open/shared-key
82432176cfdSRui Paulo 			 * authentication the port is mark authorized
82532176cfdSRui Paulo 			 * after authentication completes.  For 802.1x
82632176cfdSRui Paulo 			 * the port is not marked authorized by the
82732176cfdSRui Paulo 			 * authenticator until the handshake has completed.
82832176cfdSRui Paulo 			 */
82932176cfdSRui Paulo 			if (eh->ether_type != htons(ETHERTYPE_PAE)) {
83032176cfdSRui Paulo 				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
83132176cfdSRui Paulo 				    eh->ether_shost, "data",
83232176cfdSRui Paulo 				    "unauthorized port: ether type 0x%x len %u",
83332176cfdSRui Paulo 				    eh->ether_type, m->m_pkthdr.len);
83432176cfdSRui Paulo 				vap->iv_stats.is_rx_unauth++;
83532176cfdSRui Paulo 				IEEE80211_NODE_STAT(ni, rx_unauth);
83632176cfdSRui Paulo 				goto err;
83732176cfdSRui Paulo 			}
83832176cfdSRui Paulo 		} else {
83932176cfdSRui Paulo 			/*
84032176cfdSRui Paulo 			 * When denying unencrypted frames, discard
84132176cfdSRui Paulo 			 * any non-PAE frames received without encryption.
84232176cfdSRui Paulo 			 */
84332176cfdSRui Paulo 			if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
84432176cfdSRui Paulo 			    (key == NULL && (m->m_flags & M_WEP) == 0) &&
84532176cfdSRui Paulo 			    eh->ether_type != htons(ETHERTYPE_PAE)) {
84632176cfdSRui Paulo 				/*
84732176cfdSRui Paulo 				 * Drop unencrypted frames.
84832176cfdSRui Paulo 				 */
84932176cfdSRui Paulo 				vap->iv_stats.is_rx_unencrypted++;
85032176cfdSRui Paulo 				IEEE80211_NODE_STAT(ni, rx_unencrypted);
85132176cfdSRui Paulo 				goto out;
85232176cfdSRui Paulo 			}
85332176cfdSRui Paulo 		}
85432176cfdSRui Paulo 		/* XXX require HT? */
85532176cfdSRui Paulo 		if (qos & IEEE80211_QOS_AMSDU) {
85632176cfdSRui Paulo 			m = ieee80211_decap_amsdu(ni, m);
85732176cfdSRui Paulo 			if (m == NULL)
85832176cfdSRui Paulo 				return IEEE80211_FC0_TYPE_DATA;
85932176cfdSRui Paulo 		} else {
86032176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG
86132176cfdSRui Paulo 			m = ieee80211_decap_fastframe(vap, ni, m);
86232176cfdSRui Paulo 			if (m == NULL)
86332176cfdSRui Paulo 				return IEEE80211_FC0_TYPE_DATA;
86432176cfdSRui Paulo #endif
86532176cfdSRui Paulo 		}
86632176cfdSRui Paulo 		ieee80211_deliver_data(vap, ni, m);
86732176cfdSRui Paulo 		return IEEE80211_FC0_TYPE_DATA;
86832176cfdSRui Paulo 
86932176cfdSRui Paulo 	case IEEE80211_FC0_TYPE_MGT:
87032176cfdSRui Paulo 		vap->iv_stats.is_rx_mgmt++;
87132176cfdSRui Paulo 		IEEE80211_NODE_STAT(ni, rx_mgmt);
87232176cfdSRui Paulo 		if (dir != IEEE80211_FC1_DIR_NODS) {
87332176cfdSRui Paulo 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
87432176cfdSRui Paulo 			    wh, "data", "incorrect dir 0x%x", dir);
87532176cfdSRui Paulo 			vap->iv_stats.is_rx_wrongdir++;
87632176cfdSRui Paulo 			goto err;
87732176cfdSRui Paulo 		}
87832176cfdSRui Paulo 		if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
87932176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
88032176cfdSRui Paulo 			    ni->ni_macaddr, "mgt", "too short: len %u",
88132176cfdSRui Paulo 			    m->m_pkthdr.len);
88232176cfdSRui Paulo 			vap->iv_stats.is_rx_tooshort++;
88332176cfdSRui Paulo 			goto out;
88432176cfdSRui Paulo 		}
88532176cfdSRui Paulo #ifdef IEEE80211_DEBUG
88632176cfdSRui Paulo 		if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
88732176cfdSRui Paulo 		    ieee80211_msg_dumppkts(vap)) {
8881e290df3SAntonio Huete Jimenez 			if_printf(ifp, "received %s from %s rssi %d\n",
88932176cfdSRui Paulo 			    ieee80211_mgt_subtype_name[subtype >>
89032176cfdSRui Paulo 				IEEE80211_FC0_SUBTYPE_SHIFT],
891085ff963SMatthew Dillon 			    ether_sprintf(wh->i_addr2), rssi);
89232176cfdSRui Paulo 		}
89332176cfdSRui Paulo #endif
894085ff963SMatthew Dillon 		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
89532176cfdSRui Paulo 			if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
89632176cfdSRui Paulo 				/*
89732176cfdSRui Paulo 				 * Only shared key auth frames with a challenge
89832176cfdSRui Paulo 				 * should be encrypted, discard all others.
89932176cfdSRui Paulo 				 */
90032176cfdSRui Paulo 				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
90132176cfdSRui Paulo 				    wh, ieee80211_mgt_subtype_name[subtype >>
90232176cfdSRui Paulo 					IEEE80211_FC0_SUBTYPE_SHIFT],
90332176cfdSRui Paulo 				    "%s", "WEP set but not permitted");
90432176cfdSRui Paulo 				vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
90532176cfdSRui Paulo 				goto out;
90632176cfdSRui Paulo 			}
90732176cfdSRui Paulo 			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
90832176cfdSRui Paulo 				/*
90932176cfdSRui Paulo 				 * Discard encrypted frames when privacy is off.
91032176cfdSRui Paulo 				 */
91132176cfdSRui Paulo 				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
91232176cfdSRui Paulo 				    wh, "mgt", "%s", "WEP set but PRIVACY off");
91332176cfdSRui Paulo 				vap->iv_stats.is_rx_noprivacy++;
91432176cfdSRui Paulo 				goto out;
91532176cfdSRui Paulo 			}
91632176cfdSRui Paulo 			hdrspace = ieee80211_hdrspace(ic, wh);
91732176cfdSRui Paulo 			key = ieee80211_crypto_decap(ni, m, hdrspace);
91832176cfdSRui Paulo 			if (key == NULL) {
91932176cfdSRui Paulo 				/* NB: stats+msgs handled in crypto_decap */
92032176cfdSRui Paulo 				goto out;
92132176cfdSRui Paulo 			}
92232176cfdSRui Paulo 			wh = mtod(m, struct ieee80211_frame *);
923085ff963SMatthew Dillon 			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
92432176cfdSRui Paulo 		}
92532176cfdSRui Paulo 		vap->iv_recv_mgmt(ni, m, subtype, rssi, nf);
92632176cfdSRui Paulo 		goto out;
92732176cfdSRui Paulo 
92832176cfdSRui Paulo 	case IEEE80211_FC0_TYPE_CTL:
92932176cfdSRui Paulo 		vap->iv_stats.is_rx_ctl++;
93032176cfdSRui Paulo 		IEEE80211_NODE_STAT(ni, rx_ctrl);
93132176cfdSRui Paulo 		vap->iv_recv_ctl(ni, m, subtype);
93232176cfdSRui Paulo 		goto out;
93332176cfdSRui Paulo 
93432176cfdSRui Paulo 	default:
93532176cfdSRui Paulo 		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
93632176cfdSRui Paulo 		    wh, NULL, "bad frame type 0x%x", type);
93732176cfdSRui Paulo 		/* should not come here */
93832176cfdSRui Paulo 		break;
93932176cfdSRui Paulo 	}
94032176cfdSRui Paulo err:
941d40991efSSepherosa Ziehau 	IFNET_STAT_INC(ifp, ierrors, 1);
94232176cfdSRui Paulo out:
94332176cfdSRui Paulo 	if (m != NULL) {
94432176cfdSRui Paulo 		if (need_tap && ieee80211_radiotap_active_vap(vap))
94532176cfdSRui Paulo 			ieee80211_radiotap_rx(vap, m);
94632176cfdSRui Paulo 		m_freem(m);
94732176cfdSRui Paulo 	}
94832176cfdSRui Paulo 	return type;
94932176cfdSRui Paulo }
95032176cfdSRui Paulo 
95132176cfdSRui Paulo static void
95232176cfdSRui Paulo sta_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh,
95332176cfdSRui Paulo     int rssi, int nf, uint16_t seq, uint16_t status)
95432176cfdSRui Paulo {
95532176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
95632176cfdSRui Paulo 
95732176cfdSRui Paulo 	if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
95832176cfdSRui Paulo 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
95932176cfdSRui Paulo 		    ni->ni_macaddr, "open auth",
96032176cfdSRui Paulo 		    "bad sta auth mode %u", ni->ni_authmode);
96132176cfdSRui Paulo 		vap->iv_stats.is_rx_bad_auth++;	/* XXX */
96232176cfdSRui Paulo 		return;
96332176cfdSRui Paulo 	}
96432176cfdSRui Paulo 	if (vap->iv_state != IEEE80211_S_AUTH ||
96532176cfdSRui Paulo 	    seq != IEEE80211_AUTH_OPEN_RESPONSE) {
96632176cfdSRui Paulo 		vap->iv_stats.is_rx_bad_auth++;
96732176cfdSRui Paulo 		return;
96832176cfdSRui Paulo 	}
96932176cfdSRui Paulo 	if (status != 0) {
97032176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
97132176cfdSRui Paulo 		    ni, "open auth failed (reason %d)", status);
97232176cfdSRui Paulo 		vap->iv_stats.is_rx_auth_fail++;
97332176cfdSRui Paulo 		vap->iv_stats.is_rx_authfail_code = status;
97432176cfdSRui Paulo 		ieee80211_new_state(vap, IEEE80211_S_SCAN,
97532176cfdSRui Paulo 		    IEEE80211_SCAN_FAIL_STATUS);
97632176cfdSRui Paulo 	} else
97732176cfdSRui Paulo 		ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
97832176cfdSRui Paulo }
97932176cfdSRui Paulo 
98032176cfdSRui Paulo static void
98132176cfdSRui Paulo sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
98232176cfdSRui Paulo     uint8_t *frm, uint8_t *efrm, int rssi, int nf,
98332176cfdSRui Paulo     uint16_t seq, uint16_t status)
98432176cfdSRui Paulo {
98532176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
98632176cfdSRui Paulo 	uint8_t *challenge;
987085ff963SMatthew Dillon 	int estatus;
98832176cfdSRui Paulo 
98932176cfdSRui Paulo 	/*
99032176cfdSRui Paulo 	 * NB: this can happen as we allow pre-shared key
99132176cfdSRui Paulo 	 * authentication to be enabled w/o wep being turned
99232176cfdSRui Paulo 	 * on so that configuration of these can be done
99332176cfdSRui Paulo 	 * in any order.  It may be better to enforce the
99432176cfdSRui Paulo 	 * ordering in which case this check would just be
99532176cfdSRui Paulo 	 * for sanity/consistency.
99632176cfdSRui Paulo 	 */
99732176cfdSRui Paulo 	if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
99832176cfdSRui Paulo 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
99932176cfdSRui Paulo 		    ni->ni_macaddr, "shared key auth",
100032176cfdSRui Paulo 		    "%s", " PRIVACY is disabled");
1001085ff963SMatthew Dillon 		estatus = IEEE80211_STATUS_ALG;
100232176cfdSRui Paulo 		goto bad;
100332176cfdSRui Paulo 	}
100432176cfdSRui Paulo 	/*
100532176cfdSRui Paulo 	 * Pre-shared key authentication is evil; accept
100632176cfdSRui Paulo 	 * it only if explicitly configured (it is supported
100732176cfdSRui Paulo 	 * mainly for compatibility with clients like OS X).
100832176cfdSRui Paulo 	 */
100932176cfdSRui Paulo 	if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
101032176cfdSRui Paulo 	    ni->ni_authmode != IEEE80211_AUTH_SHARED) {
101132176cfdSRui Paulo 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
101232176cfdSRui Paulo 		    ni->ni_macaddr, "shared key auth",
101332176cfdSRui Paulo 		    "bad sta auth mode %u", ni->ni_authmode);
101432176cfdSRui Paulo 		vap->iv_stats.is_rx_bad_auth++;	/* XXX maybe a unique error? */
1015085ff963SMatthew Dillon 		estatus = IEEE80211_STATUS_ALG;
101632176cfdSRui Paulo 		goto bad;
101732176cfdSRui Paulo 	}
101832176cfdSRui Paulo 
101932176cfdSRui Paulo 	challenge = NULL;
102032176cfdSRui Paulo 	if (frm + 1 < efrm) {
102132176cfdSRui Paulo 		if ((frm[1] + 2) > (efrm - frm)) {
102232176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
102332176cfdSRui Paulo 			    ni->ni_macaddr, "shared key auth",
1024*2c7ccc4aSSascha Wildner 			    "ie %d/%ld too long",
1025085ff963SMatthew Dillon 			    frm[0], (frm[1] + 2) - (efrm - frm));
102632176cfdSRui Paulo 			vap->iv_stats.is_rx_bad_auth++;
1027085ff963SMatthew Dillon 			estatus = IEEE80211_STATUS_CHALLENGE;
102832176cfdSRui Paulo 			goto bad;
102932176cfdSRui Paulo 		}
103032176cfdSRui Paulo 		if (*frm == IEEE80211_ELEMID_CHALLENGE)
103132176cfdSRui Paulo 			challenge = frm;
103232176cfdSRui Paulo 		frm += frm[1] + 2;
103332176cfdSRui Paulo 	}
103432176cfdSRui Paulo 	switch (seq) {
103532176cfdSRui Paulo 	case IEEE80211_AUTH_SHARED_CHALLENGE:
103632176cfdSRui Paulo 	case IEEE80211_AUTH_SHARED_RESPONSE:
103732176cfdSRui Paulo 		if (challenge == NULL) {
103832176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
103932176cfdSRui Paulo 			    ni->ni_macaddr, "shared key auth",
104032176cfdSRui Paulo 			    "%s", "no challenge");
104132176cfdSRui Paulo 			vap->iv_stats.is_rx_bad_auth++;
1042085ff963SMatthew Dillon 			estatus = IEEE80211_STATUS_CHALLENGE;
104332176cfdSRui Paulo 			goto bad;
104432176cfdSRui Paulo 		}
104532176cfdSRui Paulo 		if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
104632176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
104732176cfdSRui Paulo 			    ni->ni_macaddr, "shared key auth",
104832176cfdSRui Paulo 			    "bad challenge len %d", challenge[1]);
104932176cfdSRui Paulo 			vap->iv_stats.is_rx_bad_auth++;
1050085ff963SMatthew Dillon 			estatus = IEEE80211_STATUS_CHALLENGE;
105132176cfdSRui Paulo 			goto bad;
105232176cfdSRui Paulo 		}
105332176cfdSRui Paulo 	default:
105432176cfdSRui Paulo 		break;
105532176cfdSRui Paulo 	}
105632176cfdSRui Paulo 	if (vap->iv_state != IEEE80211_S_AUTH)
105732176cfdSRui Paulo 		return;
105832176cfdSRui Paulo 	switch (seq) {
105932176cfdSRui Paulo 	case IEEE80211_AUTH_SHARED_PASS:
106032176cfdSRui Paulo 		if (ni->ni_challenge != NULL) {
106132176cfdSRui Paulo 			kfree(ni->ni_challenge, M_80211_NODE);
106232176cfdSRui Paulo 			ni->ni_challenge = NULL;
106332176cfdSRui Paulo 		}
106432176cfdSRui Paulo 		if (status != 0) {
106532176cfdSRui Paulo 			IEEE80211_NOTE_FRAME(vap,
106632176cfdSRui Paulo 			    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, wh,
106732176cfdSRui Paulo 			    "shared key auth failed (reason %d)", status);
106832176cfdSRui Paulo 			vap->iv_stats.is_rx_auth_fail++;
106932176cfdSRui Paulo 			vap->iv_stats.is_rx_authfail_code = status;
107032176cfdSRui Paulo 			return;
107132176cfdSRui Paulo 		}
107232176cfdSRui Paulo 		ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
107332176cfdSRui Paulo 		break;
107432176cfdSRui Paulo 	case IEEE80211_AUTH_SHARED_CHALLENGE:
107532176cfdSRui Paulo 		if (!ieee80211_alloc_challenge(ni))
107632176cfdSRui Paulo 			return;
107732176cfdSRui Paulo 		/* XXX could optimize by passing recvd challenge */
107832176cfdSRui Paulo 		memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
107932176cfdSRui Paulo 		IEEE80211_SEND_MGMT(ni,
108032176cfdSRui Paulo 			IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
108132176cfdSRui Paulo 		break;
108232176cfdSRui Paulo 	default:
108332176cfdSRui Paulo 		IEEE80211_DISCARD(vap, IEEE80211_MSG_AUTH,
108432176cfdSRui Paulo 		    wh, "shared key auth", "bad seq %d", seq);
108532176cfdSRui Paulo 		vap->iv_stats.is_rx_bad_auth++;
108632176cfdSRui Paulo 		return;
108732176cfdSRui Paulo 	}
108832176cfdSRui Paulo 	return;
108932176cfdSRui Paulo bad:
109032176cfdSRui Paulo 	/*
109132176cfdSRui Paulo 	 * Kick the state machine.  This short-circuits
109232176cfdSRui Paulo 	 * using the mgt frame timeout to trigger the
109332176cfdSRui Paulo 	 * state transition.
109432176cfdSRui Paulo 	 */
109532176cfdSRui Paulo 	if (vap->iv_state == IEEE80211_S_AUTH)
109632176cfdSRui Paulo 		ieee80211_new_state(vap, IEEE80211_S_SCAN,
109732176cfdSRui Paulo 		    IEEE80211_SCAN_FAIL_STATUS);
109832176cfdSRui Paulo }
109932176cfdSRui Paulo 
1100085ff963SMatthew Dillon int
110132176cfdSRui Paulo ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm,
110232176cfdSRui Paulo 	const struct ieee80211_frame *wh)
110332176cfdSRui Paulo {
110432176cfdSRui Paulo #define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
110532176cfdSRui Paulo 	struct ieee80211_wme_state *wme = &vap->iv_ic->ic_wme;
110632176cfdSRui Paulo 	u_int len = frm[1], qosinfo;
110732176cfdSRui Paulo 	int i;
110832176cfdSRui Paulo 
110932176cfdSRui Paulo 	if (len < sizeof(struct ieee80211_wme_param)-2) {
111032176cfdSRui Paulo 		IEEE80211_DISCARD_IE(vap,
111132176cfdSRui Paulo 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
111232176cfdSRui Paulo 		    wh, "WME", "too short, len %u", len);
111332176cfdSRui Paulo 		return -1;
111432176cfdSRui Paulo 	}
111532176cfdSRui Paulo 	qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)];
111632176cfdSRui Paulo 	qosinfo &= WME_QOSINFO_COUNT;
111732176cfdSRui Paulo 	/* XXX do proper check for wraparound */
111832176cfdSRui Paulo 	if (qosinfo == wme->wme_wmeChanParams.cap_info)
111932176cfdSRui Paulo 		return 0;
112032176cfdSRui Paulo 	frm += __offsetof(struct ieee80211_wme_param, params_acParams);
112132176cfdSRui Paulo 	for (i = 0; i < WME_NUM_AC; i++) {
112232176cfdSRui Paulo 		struct wmeParams *wmep =
112332176cfdSRui Paulo 			&wme->wme_wmeChanParams.cap_wmeParams[i];
112432176cfdSRui Paulo 		/* NB: ACI not used */
112532176cfdSRui Paulo 		wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
112632176cfdSRui Paulo 		wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
112732176cfdSRui Paulo 		wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
112832176cfdSRui Paulo 		wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
112932176cfdSRui Paulo 		wmep->wmep_txopLimit = LE_READ_2(frm+2);
113032176cfdSRui Paulo 		frm += 4;
113132176cfdSRui Paulo 	}
113232176cfdSRui Paulo 	wme->wme_wmeChanParams.cap_info = qosinfo;
113332176cfdSRui Paulo 	return 1;
113432176cfdSRui Paulo #undef MS
113532176cfdSRui Paulo }
113632176cfdSRui Paulo 
113732176cfdSRui Paulo /*
113832176cfdSRui Paulo  * Process 11h Channel Switch Announcement (CSA) ie.  If this
113932176cfdSRui Paulo  * is the first CSA then initiate the switch.  Otherwise we
114032176cfdSRui Paulo  * track state and trigger completion and/or cancel of the switch.
114132176cfdSRui Paulo  * XXX should be public for IBSS use
114232176cfdSRui Paulo  */
114332176cfdSRui Paulo static void
114432176cfdSRui Paulo ieee80211_parse_csaparams(struct ieee80211vap *vap, uint8_t *frm,
114532176cfdSRui Paulo 	const struct ieee80211_frame *wh)
114632176cfdSRui Paulo {
114732176cfdSRui Paulo 	struct ieee80211com *ic = vap->iv_ic;
114832176cfdSRui Paulo 	const struct ieee80211_csa_ie *csa =
114932176cfdSRui Paulo 	    (const struct ieee80211_csa_ie *) frm;
115032176cfdSRui Paulo 
115132176cfdSRui Paulo 	KASSERT(vap->iv_state >= IEEE80211_S_RUN,
115232176cfdSRui Paulo 	    ("state %s", ieee80211_state_name[vap->iv_state]));
115332176cfdSRui Paulo 
115432176cfdSRui Paulo 	if (csa->csa_mode > 1) {
115532176cfdSRui Paulo 		IEEE80211_DISCARD_IE(vap,
115632176cfdSRui Paulo 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH,
115732176cfdSRui Paulo 		    wh, "CSA", "invalid mode %u", csa->csa_mode);
115832176cfdSRui Paulo 		return;
115932176cfdSRui Paulo 	}
1160085ff963SMatthew Dillon 	IEEE80211_LOCK(ic);
116132176cfdSRui Paulo 	if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0) {
116232176cfdSRui Paulo 		/*
116332176cfdSRui Paulo 		 * Convert the channel number to a channel reference.  We
116432176cfdSRui Paulo 		 * try first to preserve turbo attribute of the current
116532176cfdSRui Paulo 		 * channel then fallback.  Note this will not work if the
116632176cfdSRui Paulo 		 * CSA specifies a channel that requires a band switch (e.g.
116732176cfdSRui Paulo 		 * 11a => 11g).  This is intentional as 11h is defined only
116832176cfdSRui Paulo 		 * for 5GHz/11a and because the switch does not involve a
116932176cfdSRui Paulo 		 * reassociation, protocol state (capabilities, negotated
117032176cfdSRui Paulo 		 * rates, etc) may/will be wrong.
117132176cfdSRui Paulo 		 */
117232176cfdSRui Paulo 		struct ieee80211_channel *c =
117332176cfdSRui Paulo 		    ieee80211_find_channel_byieee(ic, csa->csa_newchan,
117432176cfdSRui Paulo 			(ic->ic_bsschan->ic_flags & IEEE80211_CHAN_ALLTURBO));
117532176cfdSRui Paulo 		if (c == NULL) {
117632176cfdSRui Paulo 			c = ieee80211_find_channel_byieee(ic,
117732176cfdSRui Paulo 			    csa->csa_newchan,
117832176cfdSRui Paulo 			    (ic->ic_bsschan->ic_flags & IEEE80211_CHAN_ALL));
117932176cfdSRui Paulo 			if (c == NULL) {
118032176cfdSRui Paulo 				IEEE80211_DISCARD_IE(vap,
118132176cfdSRui Paulo 				    IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH,
118232176cfdSRui Paulo 				    wh, "CSA", "invalid channel %u",
118332176cfdSRui Paulo 				    csa->csa_newchan);
118432176cfdSRui Paulo 				goto done;
118532176cfdSRui Paulo 			}
118632176cfdSRui Paulo 		}
118732176cfdSRui Paulo #if IEEE80211_CSA_COUNT_MIN > 0
118832176cfdSRui Paulo 		if (csa->csa_count < IEEE80211_CSA_COUNT_MIN) {
118932176cfdSRui Paulo 			/*
119032176cfdSRui Paulo 			 * Require at least IEEE80211_CSA_COUNT_MIN count to
119132176cfdSRui Paulo 			 * reduce the risk of being redirected by a fabricated
119232176cfdSRui Paulo 			 * CSA.  If a valid CSA is dropped we'll still get a
119332176cfdSRui Paulo 			 * beacon miss when the AP leaves the channel so we'll
119432176cfdSRui Paulo 			 * eventually follow to the new channel.
119532176cfdSRui Paulo 			 *
119632176cfdSRui Paulo 			 * NOTE: this violates the 11h spec that states that
119732176cfdSRui Paulo 			 * count may be any value and if 0 then a switch
119832176cfdSRui Paulo 			 * should happen asap.
119932176cfdSRui Paulo 			 */
120032176cfdSRui Paulo 			IEEE80211_DISCARD_IE(vap,
120132176cfdSRui Paulo 			    IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH,
120232176cfdSRui Paulo 			    wh, "CSA", "count %u too small, must be >= %u",
120332176cfdSRui Paulo 			    csa->csa_count, IEEE80211_CSA_COUNT_MIN);
120432176cfdSRui Paulo 			goto done;
120532176cfdSRui Paulo 		}
120632176cfdSRui Paulo #endif
120732176cfdSRui Paulo 		ieee80211_csa_startswitch(ic, c, csa->csa_mode, csa->csa_count);
120832176cfdSRui Paulo 	} else {
120932176cfdSRui Paulo 		/*
121032176cfdSRui Paulo 		 * Validate this ie against the initial CSA.  We require
121132176cfdSRui Paulo 		 * mode and channel not change and the count must be
121232176cfdSRui Paulo 		 * monotonically decreasing.  This may be pointless and
121332176cfdSRui Paulo 		 * canceling the switch as a result may be too paranoid but
121432176cfdSRui Paulo 		 * in the worst case if we drop out of CSA because of this
121532176cfdSRui Paulo 		 * and the AP does move then we'll just end up taking a
121632176cfdSRui Paulo 		 * beacon miss and scan to find the AP.
121732176cfdSRui Paulo 		 *
121832176cfdSRui Paulo 		 * XXX may want <= on count as we also process ProbeResp
121932176cfdSRui Paulo 		 * frames and those may come in w/ the same count as the
122032176cfdSRui Paulo 		 * previous beacon; but doing so leaves us open to a stuck
122132176cfdSRui Paulo 		 * count until we add a dead-man timer
122232176cfdSRui Paulo 		 */
122332176cfdSRui Paulo 		if (!(csa->csa_count < ic->ic_csa_count &&
122432176cfdSRui Paulo 		      csa->csa_mode == ic->ic_csa_mode &&
122532176cfdSRui Paulo 		      csa->csa_newchan == ieee80211_chan2ieee(ic, ic->ic_csa_newchan))) {
122632176cfdSRui Paulo 			IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_DOTH, wh,
122732176cfdSRui Paulo 			    "CSA ie mismatch, initial ie <%d,%d,%d>, "
122832176cfdSRui Paulo 			    "this ie <%d,%d,%d>", ic->ic_csa_mode,
1229*2c7ccc4aSSascha Wildner 			    ic->ic_csa_newchan->ic_ieee, ic->ic_csa_count,
123032176cfdSRui Paulo 			    csa->csa_mode, csa->csa_newchan, csa->csa_count);
123132176cfdSRui Paulo 			ieee80211_csa_cancelswitch(ic);
123232176cfdSRui Paulo 		} else {
123332176cfdSRui Paulo 			if (csa->csa_count <= 1)
123432176cfdSRui Paulo 				ieee80211_csa_completeswitch(ic);
123532176cfdSRui Paulo 			else
123632176cfdSRui Paulo 				ic->ic_csa_count = csa->csa_count;
123732176cfdSRui Paulo 		}
123832176cfdSRui Paulo 	}
123932176cfdSRui Paulo done:
1240085ff963SMatthew Dillon 	IEEE80211_UNLOCK(ic);
124132176cfdSRui Paulo }
124232176cfdSRui Paulo 
124332176cfdSRui Paulo /*
124432176cfdSRui Paulo  * Return non-zero if a background scan may be continued:
124532176cfdSRui Paulo  * o bg scan is active
124632176cfdSRui Paulo  * o no channel switch is pending
124732176cfdSRui Paulo  * o there has not been any traffic recently
124832176cfdSRui Paulo  *
124932176cfdSRui Paulo  * Note we do not check if there is an administrative enable;
125032176cfdSRui Paulo  * this is only done to start the scan.  We assume that any
125132176cfdSRui Paulo  * change in state will be accompanied by a request to cancel
125232176cfdSRui Paulo  * active scans which will otherwise cause this test to fail.
125332176cfdSRui Paulo  */
125432176cfdSRui Paulo static __inline int
125532176cfdSRui Paulo contbgscan(struct ieee80211vap *vap)
125632176cfdSRui Paulo {
125732176cfdSRui Paulo 	struct ieee80211com *ic = vap->iv_ic;
125832176cfdSRui Paulo 
125932176cfdSRui Paulo 	return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
126032176cfdSRui Paulo 	    (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 &&
126132176cfdSRui Paulo 	    vap->iv_state == IEEE80211_S_RUN &&		/* XXX? */
126232176cfdSRui Paulo 	    time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle));
126332176cfdSRui Paulo }
126432176cfdSRui Paulo 
126532176cfdSRui Paulo /*
126632176cfdSRui Paulo  * Return non-zero if a backgrond scan may be started:
126732176cfdSRui Paulo  * o bg scanning is administratively enabled
126832176cfdSRui Paulo  * o no channel switch is pending
126932176cfdSRui Paulo  * o we are not boosted on a dynamic turbo channel
127032176cfdSRui Paulo  * o there has not been a scan recently
127132176cfdSRui Paulo  * o there has not been any traffic recently
127232176cfdSRui Paulo  */
127332176cfdSRui Paulo static __inline int
127432176cfdSRui Paulo startbgscan(struct ieee80211vap *vap)
127532176cfdSRui Paulo {
127632176cfdSRui Paulo 	struct ieee80211com *ic = vap->iv_ic;
127732176cfdSRui Paulo 
127832176cfdSRui Paulo 	return ((vap->iv_flags & IEEE80211_F_BGSCAN) &&
127932176cfdSRui Paulo 	    (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 &&
128032176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG
128132176cfdSRui Paulo 	    !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
128232176cfdSRui Paulo #endif
128332176cfdSRui Paulo 	    time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) &&
128432176cfdSRui Paulo 	    time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle));
128532176cfdSRui Paulo }
128632176cfdSRui Paulo 
128732176cfdSRui Paulo static void
128832176cfdSRui Paulo sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
128932176cfdSRui Paulo 	int subtype, int rssi, int nf)
129032176cfdSRui Paulo {
129132176cfdSRui Paulo #define	ISPROBE(_st)	((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
129232176cfdSRui Paulo #define	ISREASSOC(_st)	((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
129332176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
129432176cfdSRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
129532176cfdSRui Paulo 	struct ieee80211_frame *wh;
129632176cfdSRui Paulo 	uint8_t *frm, *efrm;
129732176cfdSRui Paulo 	uint8_t *rates, *xrates, *wme, *htcap, *htinfo;
129832176cfdSRui Paulo 	uint8_t rate;
1299085ff963SMatthew Dillon 	int ht_state_change = 0;
130032176cfdSRui Paulo 
130132176cfdSRui Paulo 	wh = mtod(m0, struct ieee80211_frame *);
130232176cfdSRui Paulo 	frm = (uint8_t *)&wh[1];
130332176cfdSRui Paulo 	efrm = mtod(m0, uint8_t *) + m0->m_len;
130432176cfdSRui Paulo 	switch (subtype) {
130532176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
130632176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_BEACON: {
130732176cfdSRui Paulo 		struct ieee80211_scanparams scan;
130832176cfdSRui Paulo 		/*
130932176cfdSRui Paulo 		 * We process beacon/probe response frames:
131032176cfdSRui Paulo 		 *    o when scanning, or
131132176cfdSRui Paulo 		 *    o station mode when associated (to collect state
131232176cfdSRui Paulo 		 *      updates such as 802.11g slot time)
131332176cfdSRui Paulo 		 * Frames otherwise received are discarded.
131432176cfdSRui Paulo 		 */
131532176cfdSRui Paulo 		if (!((ic->ic_flags & IEEE80211_F_SCAN) || ni->ni_associd)) {
131632176cfdSRui Paulo 			vap->iv_stats.is_rx_mgtdiscard++;
131732176cfdSRui Paulo 			return;
131832176cfdSRui Paulo 		}
131932176cfdSRui Paulo 		/* XXX probe response in sta mode when !scanning? */
1320085ff963SMatthew Dillon 		if (ieee80211_parse_beacon(ni, m0, &scan) != 0) {
1321085ff963SMatthew Dillon 			if (! (ic->ic_flags & IEEE80211_F_SCAN))
1322085ff963SMatthew Dillon 				vap->iv_stats.is_beacon_bad++;
132332176cfdSRui Paulo 			return;
1324085ff963SMatthew Dillon 		}
1325085ff963SMatthew Dillon 
132632176cfdSRui Paulo 		/*
132732176cfdSRui Paulo 		 * Count frame now that we know it's to be processed.
132832176cfdSRui Paulo 		 */
132932176cfdSRui Paulo 		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
133032176cfdSRui Paulo 			vap->iv_stats.is_rx_beacon++;		/* XXX remove */
133132176cfdSRui Paulo 			IEEE80211_NODE_STAT(ni, rx_beacons);
133232176cfdSRui Paulo 		} else
133332176cfdSRui Paulo 			IEEE80211_NODE_STAT(ni, rx_proberesp);
133432176cfdSRui Paulo 		/*
133532176cfdSRui Paulo 		 * When operating in station mode, check for state updates.
133632176cfdSRui Paulo 		 * Be careful to ignore beacons received while doing a
133732176cfdSRui Paulo 		 * background scan.  We consider only 11g/WMM stuff right now.
133832176cfdSRui Paulo 		 */
133932176cfdSRui Paulo 		if (ni->ni_associd != 0 &&
134032176cfdSRui Paulo 		    ((ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
134132176cfdSRui Paulo 		     IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) {
134232176cfdSRui Paulo 			/* record tsf of last beacon */
134332176cfdSRui Paulo 			memcpy(ni->ni_tstamp.data, scan.tstamp,
134432176cfdSRui Paulo 				sizeof(ni->ni_tstamp));
134532176cfdSRui Paulo 			/* count beacon frame for s/w bmiss handling */
134632176cfdSRui Paulo 			vap->iv_swbmiss_count++;
134732176cfdSRui Paulo 			vap->iv_bmiss_count = 0;
134832176cfdSRui Paulo 			if (ni->ni_erp != scan.erp) {
134932176cfdSRui Paulo 				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
135032176cfdSRui Paulo 				    wh->i_addr2,
135132176cfdSRui Paulo 				    "erp change: was 0x%x, now 0x%x",
135232176cfdSRui Paulo 				    ni->ni_erp, scan.erp);
135332176cfdSRui Paulo 				if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
135432176cfdSRui Paulo 				    (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
135532176cfdSRui Paulo 					ic->ic_flags |= IEEE80211_F_USEPROT;
135632176cfdSRui Paulo 				else
135732176cfdSRui Paulo 					ic->ic_flags &= ~IEEE80211_F_USEPROT;
135832176cfdSRui Paulo 				ni->ni_erp = scan.erp;
135932176cfdSRui Paulo 				/* XXX statistic */
136032176cfdSRui Paulo 				/* XXX driver notification */
136132176cfdSRui Paulo 			}
136232176cfdSRui Paulo 			if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
136332176cfdSRui Paulo 				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
136432176cfdSRui Paulo 				    wh->i_addr2,
136532176cfdSRui Paulo 				    "capabilities change: was 0x%x, now 0x%x",
136632176cfdSRui Paulo 				    ni->ni_capinfo, scan.capinfo);
136732176cfdSRui Paulo 				/*
136832176cfdSRui Paulo 				 * NB: we assume short preamble doesn't
136932176cfdSRui Paulo 				 *     change dynamically
137032176cfdSRui Paulo 				 */
137132176cfdSRui Paulo 				ieee80211_set_shortslottime(ic,
137232176cfdSRui Paulo 					IEEE80211_IS_CHAN_A(ic->ic_bsschan) ||
137332176cfdSRui Paulo 					(scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
137432176cfdSRui Paulo 				ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME)
137532176cfdSRui Paulo 					       | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME);
137632176cfdSRui Paulo 				/* XXX statistic */
137732176cfdSRui Paulo 			}
137832176cfdSRui Paulo 			if (scan.wme != NULL &&
137932176cfdSRui Paulo 			    (ni->ni_flags & IEEE80211_NODE_QOS) &&
138032176cfdSRui Paulo 			    ieee80211_parse_wmeparams(vap, scan.wme, wh) > 0)
138132176cfdSRui Paulo 				ieee80211_wme_updateparams(vap);
138232176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG
138332176cfdSRui Paulo 			if (scan.ath != NULL)
138432176cfdSRui Paulo 				ieee80211_parse_athparams(ni, scan.ath, wh);
138532176cfdSRui Paulo #endif
138632176cfdSRui Paulo 			if (scan.htcap != NULL && scan.htinfo != NULL &&
138732176cfdSRui Paulo 			    (vap->iv_flags_ht & IEEE80211_FHT_HT)) {
138832176cfdSRui Paulo 				/* XXX state changes? */
1389085ff963SMatthew Dillon 				if (ieee80211_ht_updateparams(ni,
1390085ff963SMatthew Dillon 				    scan.htcap, scan.htinfo))
1391085ff963SMatthew Dillon 					ht_state_change = 1;
139232176cfdSRui Paulo 			}
1393085ff963SMatthew Dillon 			if (scan.quiet)
1394085ff963SMatthew Dillon 				ic->ic_set_quiet(ni, scan.quiet);
1395d98a0bcfSMatthew Dillon 
139632176cfdSRui Paulo 			if (scan.tim != NULL) {
139732176cfdSRui Paulo 				struct ieee80211_tim_ie *tim =
139832176cfdSRui Paulo 				    (struct ieee80211_tim_ie *) scan.tim;
1399d98a0bcfSMatthew Dillon 				/*
1400d98a0bcfSMatthew Dillon 				 * XXX Check/debug this code; see if it's about
1401d98a0bcfSMatthew Dillon 				 * the right time to force the VAP awake if we
1402d98a0bcfSMatthew Dillon 				 * receive a frame destined for us?
1403d98a0bcfSMatthew Dillon 				 */
140432176cfdSRui Paulo 				int aid = IEEE80211_AID(ni->ni_associd);
140532176cfdSRui Paulo 				int ix = aid / NBBY;
140632176cfdSRui Paulo 				int min = tim->tim_bitctl &~ 1;
140732176cfdSRui Paulo 				int max = tim->tim_len + min - 4;
1408d98a0bcfSMatthew Dillon 
140932176cfdSRui Paulo 				/*
1410d98a0bcfSMatthew Dillon 				 * Only do this for unicast traffic in the TIM
1411d98a0bcfSMatthew Dillon 				 * The multicast traffic notification for
1412d98a0bcfSMatthew Dillon 				 * the scan notification stuff should occur
1413d98a0bcfSMatthew Dillon 				 * differently.
141432176cfdSRui Paulo 				 */
1415d98a0bcfSMatthew Dillon 				if (min <= ix && ix <= max &&
1416d98a0bcfSMatthew Dillon 				     isset(tim->tim_bitmap - min, aid)) {
1417d98a0bcfSMatthew Dillon 					ieee80211_sta_tim_notify(vap, 1);
141832176cfdSRui Paulo 					ic->ic_lastdata = ticks;
1419d98a0bcfSMatthew Dillon 				}
1420d98a0bcfSMatthew Dillon 
1421d98a0bcfSMatthew Dillon 				/*
1422d98a0bcfSMatthew Dillon 				 * XXX TODO: do a separate notification
1423d98a0bcfSMatthew Dillon 				 * for the multicast bit being set.
1424d98a0bcfSMatthew Dillon 				 */
1425d98a0bcfSMatthew Dillon #if 0
1426d98a0bcfSMatthew Dillon 				if (tim->tim_bitctl & 1) {
1427d98a0bcfSMatthew Dillon 					ieee80211_sta_tim_notify(vap, 1);
1428d98a0bcfSMatthew Dillon 					ic->ic_lastdata = ticks;
142932176cfdSRui Paulo 				}
143032176cfdSRui Paulo #endif
1431085ff963SMatthew Dillon 
143232176cfdSRui Paulo 				ni->ni_dtim_count = tim->tim_count;
143332176cfdSRui Paulo 				ni->ni_dtim_period = tim->tim_period;
143432176cfdSRui Paulo 			}
143532176cfdSRui Paulo 			if (scan.csa != NULL &&
143632176cfdSRui Paulo 			    (vap->iv_flags & IEEE80211_F_DOTH))
143732176cfdSRui Paulo 				ieee80211_parse_csaparams(vap, scan.csa, wh);
143832176cfdSRui Paulo 			else if (ic->ic_flags & IEEE80211_F_CSAPENDING) {
143932176cfdSRui Paulo 				/*
144032176cfdSRui Paulo 				 * No CSA ie or 11h disabled, but a channel
144132176cfdSRui Paulo 				 * switch is pending; drop out so we aren't
144232176cfdSRui Paulo 				 * stuck in CSA state.  If the AP really is
144332176cfdSRui Paulo 				 * moving we'll get a beacon miss and scan.
144432176cfdSRui Paulo 				 */
1445085ff963SMatthew Dillon 				IEEE80211_LOCK(ic);
144632176cfdSRui Paulo 				ieee80211_csa_cancelswitch(ic);
1447085ff963SMatthew Dillon 				IEEE80211_UNLOCK(ic);
144832176cfdSRui Paulo 			}
144932176cfdSRui Paulo 			/*
145032176cfdSRui Paulo 			 * If scanning, pass the info to the scan module.
145132176cfdSRui Paulo 			 * Otherwise, check if it's the right time to do
145232176cfdSRui Paulo 			 * a background scan.  Background scanning must
145332176cfdSRui Paulo 			 * be enabled and we must not be operating in the
145432176cfdSRui Paulo 			 * turbo phase of dynamic turbo mode.  Then,
145532176cfdSRui Paulo 			 * it's been a while since the last background
145632176cfdSRui Paulo 			 * scan and if no data frames have come through
145732176cfdSRui Paulo 			 * recently, kick off a scan.  Note that this
145832176cfdSRui Paulo 			 * is the mechanism by which a background scan
145932176cfdSRui Paulo 			 * is started _and_ continued each time we
146032176cfdSRui Paulo 			 * return on-channel to receive a beacon from
146132176cfdSRui Paulo 			 * our ap.
146232176cfdSRui Paulo 			 */
146332176cfdSRui Paulo 			if (ic->ic_flags & IEEE80211_F_SCAN) {
146432176cfdSRui Paulo 				ieee80211_add_scan(vap, &scan, wh,
146532176cfdSRui Paulo 					subtype, rssi, nf);
146632176cfdSRui Paulo 			} else if (contbgscan(vap)) {
146732176cfdSRui Paulo 				ieee80211_bg_scan(vap, 0);
146832176cfdSRui Paulo 			} else if (startbgscan(vap)) {
146932176cfdSRui Paulo 				vap->iv_stats.is_scan_bg++;
147032176cfdSRui Paulo #if 0
147132176cfdSRui Paulo 				/* wakeup if we are sleeing */
147232176cfdSRui Paulo 				ieee80211_set_pwrsave(vap, 0);
147332176cfdSRui Paulo #endif
147432176cfdSRui Paulo 				ieee80211_bg_scan(vap, 0);
147532176cfdSRui Paulo 			}
1476d98a0bcfSMatthew Dillon 
1477d98a0bcfSMatthew Dillon 			/*
1478d98a0bcfSMatthew Dillon 			 * Put the station to sleep if we haven't seen
1479d98a0bcfSMatthew Dillon 			 * traffic in a while.
1480d98a0bcfSMatthew Dillon 			 */
1481085ff963SMatthew Dillon 			IEEE80211_LOCK(ic);
1482d98a0bcfSMatthew Dillon 			ieee80211_sta_ps_timer_check(vap);
1483085ff963SMatthew Dillon 			IEEE80211_UNLOCK(ic);
1484d98a0bcfSMatthew Dillon 
1485085ff963SMatthew Dillon 			/*
1486085ff963SMatthew Dillon 			 * If we've had a channel width change (eg HT20<->HT40)
1487085ff963SMatthew Dillon 			 * then schedule a delayed driver notification.
1488085ff963SMatthew Dillon 			 */
1489085ff963SMatthew Dillon 			if (ht_state_change)
1490085ff963SMatthew Dillon 				ieee80211_update_chw(ic);
149132176cfdSRui Paulo 			return;
149232176cfdSRui Paulo 		}
149332176cfdSRui Paulo 		/*
149432176cfdSRui Paulo 		 * If scanning, just pass information to the scan module.
149532176cfdSRui Paulo 		 */
149632176cfdSRui Paulo 		if (ic->ic_flags & IEEE80211_F_SCAN) {
149732176cfdSRui Paulo 			if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
149832176cfdSRui Paulo 				/*
149932176cfdSRui Paulo 				 * Actively scanning a channel marked passive;
150032176cfdSRui Paulo 				 * send a probe request now that we know there
150132176cfdSRui Paulo 				 * is 802.11 traffic present.
150232176cfdSRui Paulo 				 *
150332176cfdSRui Paulo 				 * XXX check if the beacon we recv'd gives
150432176cfdSRui Paulo 				 * us what we need and suppress the probe req
150532176cfdSRui Paulo 				 */
150632176cfdSRui Paulo 				ieee80211_probe_curchan(vap, 1);
150732176cfdSRui Paulo 				ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
150832176cfdSRui Paulo 			}
150932176cfdSRui Paulo 			ieee80211_add_scan(vap, &scan, wh, subtype, rssi, nf);
151032176cfdSRui Paulo 			return;
151132176cfdSRui Paulo 		}
151232176cfdSRui Paulo 		break;
151332176cfdSRui Paulo 	}
151432176cfdSRui Paulo 
151532176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_AUTH: {
151632176cfdSRui Paulo 		uint16_t algo, seq, status;
151732176cfdSRui Paulo 		/*
151832176cfdSRui Paulo 		 * auth frame format
151932176cfdSRui Paulo 		 *	[2] algorithm
152032176cfdSRui Paulo 		 *	[2] sequence
152132176cfdSRui Paulo 		 *	[2] status
152232176cfdSRui Paulo 		 *	[tlv*] challenge
152332176cfdSRui Paulo 		 */
152432176cfdSRui Paulo 		IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
152532176cfdSRui Paulo 		algo   = le16toh(*(uint16_t *)frm);
152632176cfdSRui Paulo 		seq    = le16toh(*(uint16_t *)(frm + 2));
152732176cfdSRui Paulo 		status = le16toh(*(uint16_t *)(frm + 4));
152832176cfdSRui Paulo 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2,
152932176cfdSRui Paulo 		    "recv auth frame with algorithm %d seq %d", algo, seq);
153032176cfdSRui Paulo 
153132176cfdSRui Paulo 		if (vap->iv_flags & IEEE80211_F_COUNTERM) {
153232176cfdSRui Paulo 			IEEE80211_DISCARD(vap,
153332176cfdSRui Paulo 			    IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
153432176cfdSRui Paulo 			    wh, "auth", "%s", "TKIP countermeasures enabled");
153532176cfdSRui Paulo 			vap->iv_stats.is_rx_auth_countermeasures++;
153632176cfdSRui Paulo 			if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
153732176cfdSRui Paulo 				ieee80211_send_error(ni, wh->i_addr2,
153832176cfdSRui Paulo 					IEEE80211_FC0_SUBTYPE_AUTH,
153932176cfdSRui Paulo 					IEEE80211_REASON_MIC_FAILURE);
154032176cfdSRui Paulo 			}
154132176cfdSRui Paulo 			return;
154232176cfdSRui Paulo 		}
154332176cfdSRui Paulo 		if (algo == IEEE80211_AUTH_ALG_SHARED)
154432176cfdSRui Paulo 			sta_auth_shared(ni, wh, frm + 6, efrm, rssi, nf,
154532176cfdSRui Paulo 			    seq, status);
154632176cfdSRui Paulo 		else if (algo == IEEE80211_AUTH_ALG_OPEN)
154732176cfdSRui Paulo 			sta_auth_open(ni, wh, rssi, nf, seq, status);
154832176cfdSRui Paulo 		else {
154932176cfdSRui Paulo 			IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
155032176cfdSRui Paulo 			    wh, "auth", "unsupported alg %d", algo);
155132176cfdSRui Paulo 			vap->iv_stats.is_rx_auth_unsupported++;
155232176cfdSRui Paulo 			return;
155332176cfdSRui Paulo 		}
155432176cfdSRui Paulo 		break;
155532176cfdSRui Paulo 	}
155632176cfdSRui Paulo 
155732176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
155832176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
155932176cfdSRui Paulo 		uint16_t capinfo, associd;
156032176cfdSRui Paulo 		uint16_t status;
156132176cfdSRui Paulo 
156232176cfdSRui Paulo 		if (vap->iv_state != IEEE80211_S_ASSOC) {
156332176cfdSRui Paulo 			vap->iv_stats.is_rx_mgtdiscard++;
156432176cfdSRui Paulo 			return;
156532176cfdSRui Paulo 		}
156632176cfdSRui Paulo 
156732176cfdSRui Paulo 		/*
156832176cfdSRui Paulo 		 * asresp frame format
156932176cfdSRui Paulo 		 *	[2] capability information
157032176cfdSRui Paulo 		 *	[2] status
157132176cfdSRui Paulo 		 *	[2] association ID
157232176cfdSRui Paulo 		 *	[tlv] supported rates
157332176cfdSRui Paulo 		 *	[tlv] extended supported rates
157432176cfdSRui Paulo 		 *	[tlv] WME
157532176cfdSRui Paulo 		 *	[tlv] HT capabilities
157632176cfdSRui Paulo 		 *	[tlv] HT info
157732176cfdSRui Paulo 		 */
157832176cfdSRui Paulo 		IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
157932176cfdSRui Paulo 		ni = vap->iv_bss;
158032176cfdSRui Paulo 		capinfo = le16toh(*(uint16_t *)frm);
158132176cfdSRui Paulo 		frm += 2;
158232176cfdSRui Paulo 		status = le16toh(*(uint16_t *)frm);
158332176cfdSRui Paulo 		frm += 2;
158432176cfdSRui Paulo 		if (status != 0) {
158532176cfdSRui Paulo 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
158632176cfdSRui Paulo 			    wh->i_addr2, "%sassoc failed (reason %d)",
158732176cfdSRui Paulo 			    ISREASSOC(subtype) ?  "re" : "", status);
158832176cfdSRui Paulo 			vap->iv_stats.is_rx_auth_fail++;	/* XXX */
158932176cfdSRui Paulo 			return;
159032176cfdSRui Paulo 		}
159132176cfdSRui Paulo 		associd = le16toh(*(uint16_t *)frm);
159232176cfdSRui Paulo 		frm += 2;
159332176cfdSRui Paulo 
159432176cfdSRui Paulo 		rates = xrates = wme = htcap = htinfo = NULL;
159532176cfdSRui Paulo 		while (efrm - frm > 1) {
159632176cfdSRui Paulo 			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
159732176cfdSRui Paulo 			switch (*frm) {
159832176cfdSRui Paulo 			case IEEE80211_ELEMID_RATES:
159932176cfdSRui Paulo 				rates = frm;
160032176cfdSRui Paulo 				break;
160132176cfdSRui Paulo 			case IEEE80211_ELEMID_XRATES:
160232176cfdSRui Paulo 				xrates = frm;
160332176cfdSRui Paulo 				break;
160432176cfdSRui Paulo 			case IEEE80211_ELEMID_HTCAP:
160532176cfdSRui Paulo 				htcap = frm;
160632176cfdSRui Paulo 				break;
160732176cfdSRui Paulo 			case IEEE80211_ELEMID_HTINFO:
160832176cfdSRui Paulo 				htinfo = frm;
160932176cfdSRui Paulo 				break;
161032176cfdSRui Paulo 			case IEEE80211_ELEMID_VENDOR:
161132176cfdSRui Paulo 				if (iswmeoui(frm))
161232176cfdSRui Paulo 					wme = frm;
161332176cfdSRui Paulo 				else if (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) {
161432176cfdSRui Paulo 					/*
161532176cfdSRui Paulo 					 * Accept pre-draft HT ie's if the
161632176cfdSRui Paulo 					 * standard ones have not been seen.
161732176cfdSRui Paulo 					 */
161832176cfdSRui Paulo 					if (ishtcapoui(frm)) {
161932176cfdSRui Paulo 						if (htcap == NULL)
162032176cfdSRui Paulo 							htcap = frm;
162132176cfdSRui Paulo 					} else if (ishtinfooui(frm)) {
162232176cfdSRui Paulo 						if (htinfo == NULL)
1623085ff963SMatthew Dillon 							htinfo = frm;
162432176cfdSRui Paulo 					}
162532176cfdSRui Paulo 				}
162632176cfdSRui Paulo 				/* XXX Atheros OUI support */
162732176cfdSRui Paulo 				break;
162832176cfdSRui Paulo 			}
162932176cfdSRui Paulo 			frm += frm[1] + 2;
163032176cfdSRui Paulo 		}
163132176cfdSRui Paulo 
163232176cfdSRui Paulo 		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
163332176cfdSRui Paulo 		if (xrates != NULL)
163432176cfdSRui Paulo 			IEEE80211_VERIFY_ELEMENT(xrates,
163532176cfdSRui Paulo 				IEEE80211_RATE_MAXSIZE - rates[1], return);
163632176cfdSRui Paulo 		rate = ieee80211_setup_rates(ni, rates, xrates,
163732176cfdSRui Paulo 				IEEE80211_F_JOIN |
163832176cfdSRui Paulo 				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
163932176cfdSRui Paulo 				IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
164032176cfdSRui Paulo 		if (rate & IEEE80211_RATE_BASIC) {
164132176cfdSRui Paulo 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
164232176cfdSRui Paulo 			    wh->i_addr2,
164332176cfdSRui Paulo 			    "%sassoc failed (rate set mismatch)",
164432176cfdSRui Paulo 			    ISREASSOC(subtype) ?  "re" : "");
164532176cfdSRui Paulo 			vap->iv_stats.is_rx_assoc_norate++;
164632176cfdSRui Paulo 			ieee80211_new_state(vap, IEEE80211_S_SCAN,
164732176cfdSRui Paulo 			    IEEE80211_SCAN_FAIL_STATUS);
164832176cfdSRui Paulo 			return;
164932176cfdSRui Paulo 		}
165032176cfdSRui Paulo 
165132176cfdSRui Paulo 		ni->ni_capinfo = capinfo;
165232176cfdSRui Paulo 		ni->ni_associd = associd;
165332176cfdSRui Paulo 		if (ni->ni_jointime == 0)
1654cec73927SMatthew Dillon 			ni->ni_jointime = time_uptime;
165532176cfdSRui Paulo 		if (wme != NULL &&
165632176cfdSRui Paulo 		    ieee80211_parse_wmeparams(vap, wme, wh) >= 0) {
165732176cfdSRui Paulo 			ni->ni_flags |= IEEE80211_NODE_QOS;
165832176cfdSRui Paulo 			ieee80211_wme_updateparams(vap);
165932176cfdSRui Paulo 		} else
166032176cfdSRui Paulo 			ni->ni_flags &= ~IEEE80211_NODE_QOS;
166132176cfdSRui Paulo 		/*
166232176cfdSRui Paulo 		 * Setup HT state according to the negotiation.
166332176cfdSRui Paulo 		 *
166432176cfdSRui Paulo 		 * NB: shouldn't need to check if HT use is enabled but some
166532176cfdSRui Paulo 		 *     ap's send back HT ie's even when we don't indicate we
166632176cfdSRui Paulo 		 *     are HT capable in our AssocReq.
166732176cfdSRui Paulo 		 */
166832176cfdSRui Paulo 		if (htcap != NULL && htinfo != NULL &&
166932176cfdSRui Paulo 		    (vap->iv_flags_ht & IEEE80211_FHT_HT)) {
167032176cfdSRui Paulo 			ieee80211_ht_node_init(ni);
167132176cfdSRui Paulo 			ieee80211_ht_updateparams(ni, htcap, htinfo);
167232176cfdSRui Paulo 			ieee80211_setup_htrates(ni, htcap,
167332176cfdSRui Paulo 			     IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
167432176cfdSRui Paulo 			ieee80211_setup_basic_htrates(ni, htinfo);
167532176cfdSRui Paulo 			ieee80211_node_setuptxparms(ni);
16764fbce6bdSSascha Wildner 			ieee80211_ratectl_node_init(ni);
167732176cfdSRui Paulo 		} else {
167832176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG
167932176cfdSRui Paulo 			if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_ATH))
168032176cfdSRui Paulo 				ieee80211_ff_node_init(ni);
168132176cfdSRui Paulo #endif
168232176cfdSRui Paulo 		}
168332176cfdSRui Paulo 		/*
168432176cfdSRui Paulo 		 * Configure state now that we are associated.
168532176cfdSRui Paulo 		 *
168632176cfdSRui Paulo 		 * XXX may need different/additional driver callbacks?
168732176cfdSRui Paulo 		 */
168832176cfdSRui Paulo 		if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
168932176cfdSRui Paulo 		    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
169032176cfdSRui Paulo 			ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
169132176cfdSRui Paulo 			ic->ic_flags &= ~IEEE80211_F_USEBARKER;
169232176cfdSRui Paulo 		} else {
169332176cfdSRui Paulo 			ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
169432176cfdSRui Paulo 			ic->ic_flags |= IEEE80211_F_USEBARKER;
169532176cfdSRui Paulo 		}
169632176cfdSRui Paulo 		ieee80211_set_shortslottime(ic,
169732176cfdSRui Paulo 			IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
169832176cfdSRui Paulo 			(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
169932176cfdSRui Paulo 		/*
170032176cfdSRui Paulo 		 * Honor ERP protection.
170132176cfdSRui Paulo 		 *
170232176cfdSRui Paulo 		 * NB: ni_erp should zero for non-11g operation.
170332176cfdSRui Paulo 		 */
170432176cfdSRui Paulo 		if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
170532176cfdSRui Paulo 		    (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
170632176cfdSRui Paulo 			ic->ic_flags |= IEEE80211_F_USEPROT;
170732176cfdSRui Paulo 		else
170832176cfdSRui Paulo 			ic->ic_flags &= ~IEEE80211_F_USEPROT;
170932176cfdSRui Paulo 		IEEE80211_NOTE_MAC(vap,
171032176cfdSRui Paulo 		    IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, wh->i_addr2,
171132176cfdSRui Paulo 		    "%sassoc success at aid %d: %s preamble, %s slot time%s%s%s%s%s%s%s%s",
171232176cfdSRui Paulo 		    ISREASSOC(subtype) ? "re" : "",
171332176cfdSRui Paulo 		    IEEE80211_NODE_AID(ni),
171432176cfdSRui Paulo 		    ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
171532176cfdSRui Paulo 		    ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
171632176cfdSRui Paulo 		    ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
171732176cfdSRui Paulo 		    ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
171832176cfdSRui Paulo 		    ni->ni_flags & IEEE80211_NODE_HT ?
171932176cfdSRui Paulo 			(ni->ni_chw == 40 ? ", HT40" : ", HT20") : "",
172032176cfdSRui Paulo 		    ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
172132176cfdSRui Paulo 		    ni->ni_flags & IEEE80211_NODE_MIMO_RTS ? " (+SMPS-DYN)" :
172232176cfdSRui Paulo 			ni->ni_flags & IEEE80211_NODE_MIMO_PS ? " (+SMPS)" : "",
172332176cfdSRui Paulo 		    ni->ni_flags & IEEE80211_NODE_RIFS ? " (+RIFS)" : "",
172432176cfdSRui Paulo 		    IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ?
172532176cfdSRui Paulo 			", fast-frames" : "",
172632176cfdSRui Paulo 		    IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ?
172732176cfdSRui Paulo 			", turbo" : ""
172832176cfdSRui Paulo 		);
172932176cfdSRui Paulo 		ieee80211_new_state(vap, IEEE80211_S_RUN, subtype);
173032176cfdSRui Paulo 		break;
173132176cfdSRui Paulo 	}
173232176cfdSRui Paulo 
173332176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_DEAUTH: {
173432176cfdSRui Paulo 		uint16_t reason;
173532176cfdSRui Paulo 
173632176cfdSRui Paulo 		if (vap->iv_state == IEEE80211_S_SCAN) {
173732176cfdSRui Paulo 			vap->iv_stats.is_rx_mgtdiscard++;
173832176cfdSRui Paulo 			return;
173932176cfdSRui Paulo 		}
174032176cfdSRui Paulo 		if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
174132176cfdSRui Paulo 			/* NB: can happen when in promiscuous mode */
174232176cfdSRui Paulo 			vap->iv_stats.is_rx_mgtdiscard++;
174332176cfdSRui Paulo 			break;
174432176cfdSRui Paulo 		}
174532176cfdSRui Paulo 
174632176cfdSRui Paulo 		/*
174732176cfdSRui Paulo 		 * deauth frame format
174832176cfdSRui Paulo 		 *	[2] reason
174932176cfdSRui Paulo 		 */
175032176cfdSRui Paulo 		IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
175132176cfdSRui Paulo 		reason = le16toh(*(uint16_t *)frm);
175232176cfdSRui Paulo 
175332176cfdSRui Paulo 		vap->iv_stats.is_rx_deauth++;
175432176cfdSRui Paulo 		vap->iv_stats.is_rx_deauth_code = reason;
175532176cfdSRui Paulo 		IEEE80211_NODE_STAT(ni, rx_deauth);
175632176cfdSRui Paulo 
175732176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
175832176cfdSRui Paulo 		    "recv deauthenticate (reason %d)", reason);
175932176cfdSRui Paulo 		ieee80211_new_state(vap, IEEE80211_S_AUTH,
176032176cfdSRui Paulo 		    (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH);
176132176cfdSRui Paulo 		break;
176232176cfdSRui Paulo 	}
176332176cfdSRui Paulo 
176432176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_DISASSOC: {
176532176cfdSRui Paulo 		uint16_t reason;
176632176cfdSRui Paulo 
176732176cfdSRui Paulo 		if (vap->iv_state != IEEE80211_S_RUN &&
176832176cfdSRui Paulo 		    vap->iv_state != IEEE80211_S_ASSOC &&
176932176cfdSRui Paulo 		    vap->iv_state != IEEE80211_S_AUTH) {
177032176cfdSRui Paulo 			vap->iv_stats.is_rx_mgtdiscard++;
177132176cfdSRui Paulo 			return;
177232176cfdSRui Paulo 		}
177332176cfdSRui Paulo 		if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
177432176cfdSRui Paulo 			/* NB: can happen when in promiscuous mode */
177532176cfdSRui Paulo 			vap->iv_stats.is_rx_mgtdiscard++;
177632176cfdSRui Paulo 			break;
177732176cfdSRui Paulo 		}
177832176cfdSRui Paulo 
177932176cfdSRui Paulo 		/*
178032176cfdSRui Paulo 		 * disassoc frame format
178132176cfdSRui Paulo 		 *	[2] reason
178232176cfdSRui Paulo 		 */
178332176cfdSRui Paulo 		IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
178432176cfdSRui Paulo 		reason = le16toh(*(uint16_t *)frm);
178532176cfdSRui Paulo 
178632176cfdSRui Paulo 		vap->iv_stats.is_rx_disassoc++;
178732176cfdSRui Paulo 		vap->iv_stats.is_rx_disassoc_code = reason;
178832176cfdSRui Paulo 		IEEE80211_NODE_STAT(ni, rx_disassoc);
178932176cfdSRui Paulo 
179032176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
179132176cfdSRui Paulo 		    "recv disassociate (reason %d)", reason);
179232176cfdSRui Paulo 		ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
179332176cfdSRui Paulo 		break;
179432176cfdSRui Paulo 	}
179532176cfdSRui Paulo 
179632176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_ACTION:
1797085ff963SMatthew Dillon 	case IEEE80211_FC0_SUBTYPE_ACTION_NOACK:
1798085ff963SMatthew Dillon 		if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) &&
1799085ff963SMatthew Dillon 		    !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
1800085ff963SMatthew Dillon 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1801085ff963SMatthew Dillon 			    wh, NULL, "%s", "not for us");
1802085ff963SMatthew Dillon 			vap->iv_stats.is_rx_mgtdiscard++;
1803085ff963SMatthew Dillon 		} else if (vap->iv_state != IEEE80211_S_RUN) {
1804085ff963SMatthew Dillon 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1805085ff963SMatthew Dillon 			    wh, NULL, "wrong state %s",
1806085ff963SMatthew Dillon 			    ieee80211_state_name[vap->iv_state]);
1807085ff963SMatthew Dillon 			vap->iv_stats.is_rx_mgtdiscard++;
1808085ff963SMatthew Dillon 		} else {
180932176cfdSRui Paulo 			if (ieee80211_parse_action(ni, m0) == 0)
1810085ff963SMatthew Dillon 				(void)ic->ic_recv_action(ni, wh, frm, efrm);
1811085ff963SMatthew Dillon 		}
1812085ff963SMatthew Dillon 		break;
1813085ff963SMatthew Dillon 
1814085ff963SMatthew Dillon 	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
1815085ff963SMatthew Dillon 	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
1816085ff963SMatthew Dillon 	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
1817085ff963SMatthew Dillon 	case IEEE80211_FC0_SUBTYPE_ATIM:
1818085ff963SMatthew Dillon 		IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1819085ff963SMatthew Dillon 		    wh, NULL, "%s", "not handled");
182032176cfdSRui Paulo 		vap->iv_stats.is_rx_mgtdiscard++;
182132176cfdSRui Paulo 		break;
182232176cfdSRui Paulo 
182332176cfdSRui Paulo 	default:
182432176cfdSRui Paulo 		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
182532176cfdSRui Paulo 		    wh, "mgt", "subtype 0x%x not handled", subtype);
182632176cfdSRui Paulo 		vap->iv_stats.is_rx_badsubtype++;
182732176cfdSRui Paulo 		break;
182832176cfdSRui Paulo 	}
182932176cfdSRui Paulo #undef ISREASSOC
183032176cfdSRui Paulo #undef ISPROBE
183132176cfdSRui Paulo }
183232176cfdSRui Paulo 
183332176cfdSRui Paulo static void
1834085ff963SMatthew Dillon sta_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype)
183532176cfdSRui Paulo {
1836085ff963SMatthew Dillon 	switch (subtype) {
1837085ff963SMatthew Dillon 	case IEEE80211_FC0_SUBTYPE_BAR:
1838085ff963SMatthew Dillon 		ieee80211_recv_bar(ni, m);
1839085ff963SMatthew Dillon 		break;
1840085ff963SMatthew Dillon 	}
184132176cfdSRui Paulo }
1842