ieee80211_sta.c revision 184268
1178354Ssam/*-
2178354Ssam * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
3178354Ssam * All rights reserved.
4178354Ssam *
5178354Ssam * Redistribution and use in source and binary forms, with or without
6178354Ssam * modification, are permitted provided that the following conditions
7178354Ssam * are met:
8178354Ssam * 1. Redistributions of source code must retain the above copyright
9178354Ssam *    notice, this list of conditions and the following disclaimer.
10178354Ssam * 2. Redistributions in binary form must reproduce the above copyright
11178354Ssam *    notice, this list of conditions and the following disclaimer in the
12178354Ssam *    documentation and/or other materials provided with the distribution.
13178354Ssam *
14178354Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15178354Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16178354Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17178354Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18178354Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19178354Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20178354Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21178354Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22178354Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23178354Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24178354Ssam */
25178354Ssam
26178354Ssam#include <sys/cdefs.h>
27178354Ssam#ifdef __FreeBSD__
28178354Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_sta.c 184268 2008-10-25 23:23:41Z sam $");
29178354Ssam#endif
30178354Ssam
31178354Ssam/*
32178354Ssam * IEEE 802.11 Station mode support.
33178354Ssam */
34178354Ssam#include "opt_inet.h"
35178354Ssam#include "opt_wlan.h"
36178354Ssam
37178354Ssam#include <sys/param.h>
38178354Ssam#include <sys/systm.h>
39178354Ssam#include <sys/mbuf.h>
40178354Ssam#include <sys/malloc.h>
41178354Ssam#include <sys/kernel.h>
42178354Ssam
43178354Ssam#include <sys/socket.h>
44178354Ssam#include <sys/sockio.h>
45178354Ssam#include <sys/endian.h>
46178354Ssam#include <sys/errno.h>
47178354Ssam#include <sys/proc.h>
48178354Ssam#include <sys/sysctl.h>
49178354Ssam
50178354Ssam#include <net/if.h>
51178354Ssam#include <net/if_media.h>
52178354Ssam#include <net/if_llc.h>
53178354Ssam#include <net/ethernet.h>
54178354Ssam
55178354Ssam#include <net/bpf.h>
56178354Ssam
57178354Ssam#include <net80211/ieee80211_var.h>
58178354Ssam#include <net80211/ieee80211_sta.h>
59178354Ssam#include <net80211/ieee80211_input.h>
60178354Ssam
61178354Ssam#define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
62178354Ssam
63178354Ssamstatic	void sta_vattach(struct ieee80211vap *);
64178354Ssamstatic	void sta_beacon_miss(struct ieee80211vap *);
65178354Ssamstatic	int sta_newstate(struct ieee80211vap *, enum ieee80211_state, int);
66178354Ssamstatic	int sta_input(struct ieee80211_node *, struct mbuf *,
67178354Ssam	    int rssi, int noise, uint32_t rstamp);
68178354Ssamstatic void sta_recv_mgmt(struct ieee80211_node *, struct mbuf *,
69178354Ssam	    int subtype, int rssi, int noise, uint32_t rstamp);
70178354Ssam
71178354Ssamvoid
72178354Ssamieee80211_sta_attach(struct ieee80211com *ic)
73178354Ssam{
74178354Ssam	ic->ic_vattach[IEEE80211_M_STA] = sta_vattach;
75178354Ssam}
76178354Ssam
77178354Ssamvoid
78178354Ssamieee80211_sta_detach(struct ieee80211com *ic)
79178354Ssam{
80178354Ssam}
81178354Ssam
82178354Ssamstatic void
83178354Ssamsta_vdetach(struct ieee80211vap *vap)
84178354Ssam{
85178354Ssam}
86178354Ssam
87178354Ssamstatic void
88178354Ssamsta_vattach(struct ieee80211vap *vap)
89178354Ssam{
90178354Ssam	vap->iv_newstate = sta_newstate;
91178354Ssam	vap->iv_input = sta_input;
92178354Ssam	vap->iv_recv_mgmt = sta_recv_mgmt;
93178354Ssam	vap->iv_opdetach = sta_vdetach;
94178354Ssam	vap->iv_bmiss = sta_beacon_miss;
95178354Ssam}
96178354Ssam
97178354Ssam/*
98178354Ssam * Handle a beacon miss event.  The common code filters out
99178354Ssam * spurious events that can happen when scanning and/or before
100178354Ssam * reaching RUN state.
101178354Ssam */
102178354Ssamstatic void
103178354Ssamsta_beacon_miss(struct ieee80211vap *vap)
104178354Ssam{
105178354Ssam	struct ieee80211com *ic = vap->iv_ic;
106178354Ssam
107178354Ssam	KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning"));
108178354Ssam	KASSERT(vap->iv_state == IEEE80211_S_RUN,
109178354Ssam	    ("wrong state %d", vap->iv_state));
110178354Ssam
111178354Ssam	IEEE80211_DPRINTF(vap,
112178354Ssam		IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
113178354Ssam		"beacon miss, mode %u state %s\n",
114178354Ssam		vap->iv_opmode, ieee80211_state_name[vap->iv_state]);
115178354Ssam
116178354Ssam	if (++vap->iv_bmiss_count < vap->iv_bmiss_max) {
117178354Ssam		/*
118178354Ssam		 * Send a directed probe req before falling back to a
119178354Ssam		 * scan; if we receive a response ic_bmiss_count will
120178354Ssam		 * be reset.  Some cards mistakenly report beacon miss
121178354Ssam		 * so this avoids the expensive scan if the ap is
122178354Ssam		 * still there.
123178354Ssam		 */
124178354Ssam		ieee80211_send_probereq(vap->iv_bss, vap->iv_myaddr,
125178354Ssam			vap->iv_bss->ni_bssid, vap->iv_bss->ni_bssid,
126178354Ssam			vap->iv_bss->ni_essid, vap->iv_bss->ni_esslen);
127178354Ssam		return;
128178354Ssam	}
129178354Ssam	vap->iv_bmiss_count = 0;
130178354Ssam	vap->iv_stats.is_beacon_miss++;
131178354Ssam	if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
132178354Ssam		/*
133178354Ssam		 * If we receive a beacon miss interrupt when using
134178354Ssam		 * dynamic turbo, attempt to switch modes before
135178354Ssam		 * reassociating.
136178354Ssam		 */
137178354Ssam		if (IEEE80211_ATH_CAP(vap, vap->iv_bss, IEEE80211_NODE_TURBOP))
138178354Ssam			ieee80211_dturbo_switch(vap,
139178354Ssam			    ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO);
140178354Ssam		/*
141178354Ssam		 * Try to reassociate before scanning for a new ap.
142178354Ssam		 */
143178354Ssam		ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1);
144178354Ssam	} else {
145178354Ssam		/*
146178354Ssam		 * Somebody else is controlling state changes (e.g.
147178354Ssam		 * a user-mode app) don't do anything that would
148178354Ssam		 * confuse them; just drop into scan mode so they'll
149178354Ssam		 * notified of the state change and given control.
150178354Ssam		 */
151178354Ssam		ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
152178354Ssam	}
153178354Ssam}
154178354Ssam
155178354Ssam/*
156178354Ssam * Handle deauth with reason.  We retry only for
157178354Ssam * the cases where we might succeed.  Otherwise
158178354Ssam * we downgrade the ap and scan.
159178354Ssam */
160178354Ssamstatic void
161178354Ssamsta_authretry(struct ieee80211vap *vap, struct ieee80211_node *ni, int reason)
162178354Ssam{
163178354Ssam	switch (reason) {
164178354Ssam	case IEEE80211_STATUS_SUCCESS:		/* NB: MLME assoc */
165178354Ssam	case IEEE80211_STATUS_TIMEOUT:
166178354Ssam	case IEEE80211_REASON_ASSOC_EXPIRE:
167178354Ssam	case IEEE80211_REASON_NOT_AUTHED:
168178354Ssam	case IEEE80211_REASON_NOT_ASSOCED:
169178354Ssam	case IEEE80211_REASON_ASSOC_LEAVE:
170178354Ssam	case IEEE80211_REASON_ASSOC_NOT_AUTHED:
171178354Ssam		IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, 1);
172178354Ssam		break;
173178354Ssam	default:
174178354Ssam		ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, reason);
175178354Ssam		if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
176178354Ssam			ieee80211_check_scan_current(vap);
177178354Ssam		break;
178178354Ssam	}
179178354Ssam}
180178354Ssam
181178354Ssam/*
182178354Ssam * IEEE80211_M_STA vap state machine handler.
183178354Ssam * This routine handles the main states in the 802.11 protocol.
184178354Ssam */
185178354Ssamstatic int
186178354Ssamsta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
187178354Ssam{
188178354Ssam	struct ieee80211com *ic = vap->iv_ic;
189178354Ssam	struct ieee80211_node *ni;
190178354Ssam	enum ieee80211_state ostate;
191178354Ssam
192178354Ssam	IEEE80211_LOCK_ASSERT(ic);
193178354Ssam
194178354Ssam	ostate = vap->iv_state;
195178354Ssam	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
196178354Ssam	    __func__, ieee80211_state_name[ostate],
197178354Ssam	    ieee80211_state_name[nstate], arg);
198178354Ssam	vap->iv_state = nstate;			/* state transition */
199178354Ssam	callout_stop(&vap->iv_mgtsend);		/* XXX callout_drain */
200178354Ssam	if (ostate != IEEE80211_S_SCAN)
201178354Ssam		ieee80211_cancel_scan(vap);	/* background scan */
202178354Ssam	ni = vap->iv_bss;			/* NB: no reference held */
203178354Ssam	if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
204178354Ssam		callout_stop(&vap->iv_swbmiss);
205178354Ssam	switch (nstate) {
206178354Ssam	case IEEE80211_S_INIT:
207178354Ssam		switch (ostate) {
208178354Ssam		case IEEE80211_S_SLEEP:
209178354Ssam			/* XXX wakeup */
210178354Ssam		case IEEE80211_S_RUN:
211178354Ssam			IEEE80211_SEND_MGMT(ni,
212178354Ssam			    IEEE80211_FC0_SUBTYPE_DISASSOC,
213178354Ssam			    IEEE80211_REASON_ASSOC_LEAVE);
214178354Ssam			ieee80211_sta_leave(ni);
215178354Ssam			break;
216178354Ssam		case IEEE80211_S_ASSOC:
217178354Ssam			IEEE80211_SEND_MGMT(ni,
218178354Ssam			    IEEE80211_FC0_SUBTYPE_DEAUTH,
219178354Ssam			    IEEE80211_REASON_AUTH_LEAVE);
220178354Ssam			break;
221178354Ssam		case IEEE80211_S_SCAN:
222178354Ssam			ieee80211_cancel_scan(vap);
223178354Ssam			break;
224178354Ssam		default:
225178354Ssam			goto invalid;
226178354Ssam		}
227178354Ssam		if (ostate != IEEE80211_S_INIT) {
228178354Ssam			/* NB: optimize INIT -> INIT case */
229178354Ssam			ieee80211_reset_bss(vap);
230178354Ssam		}
231178354Ssam		if (vap->iv_auth->ia_detach != NULL)
232178354Ssam			vap->iv_auth->ia_detach(vap);
233178354Ssam		break;
234178354Ssam	case IEEE80211_S_SCAN:
235178354Ssam		switch (ostate) {
236178354Ssam		case IEEE80211_S_INIT:
237178354Ssam			/*
238178354Ssam			 * Initiate a scan.  We can come here as a result
239178354Ssam			 * of an IEEE80211_IOC_SCAN_REQ too in which case
240178354Ssam			 * the vap will be marked with IEEE80211_FEXT_SCANREQ
241178354Ssam			 * and the scan request parameters will be present
242178354Ssam			 * in iv_scanreq.  Otherwise we do the default.
243178354Ssam			 */
244178354Ssam			if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
245178354Ssam				ieee80211_check_scan(vap,
246178354Ssam				    vap->iv_scanreq_flags,
247178354Ssam				    vap->iv_scanreq_duration,
248178354Ssam				    vap->iv_scanreq_mindwell,
249178354Ssam				    vap->iv_scanreq_maxdwell,
250178354Ssam				    vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
251178354Ssam				vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
252178354Ssam			} else
253178354Ssam				ieee80211_check_scan_current(vap);
254178354Ssam			break;
255178354Ssam		case IEEE80211_S_SCAN:
256178354Ssam		case IEEE80211_S_AUTH:
257178354Ssam		case IEEE80211_S_ASSOC:
258178354Ssam			/*
259178354Ssam			 * These can happen either because of a timeout
260178354Ssam			 * on an assoc/auth response or because of a
261178354Ssam			 * change in state that requires a reset.  For
262178354Ssam			 * the former we're called with a non-zero arg
263178354Ssam			 * that is the cause for the failure; pass this
264178354Ssam			 * to the scan code so it can update state.
265178354Ssam			 * Otherwise trigger a new scan unless we're in
266178354Ssam			 * manual roaming mode in which case an application
267178354Ssam			 * must issue an explicit scan request.
268178354Ssam			 */
269178354Ssam			if (arg != 0)
270178354Ssam				ieee80211_scan_assoc_fail(vap,
271178354Ssam					vap->iv_bss->ni_macaddr, arg);
272178354Ssam			if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
273178354Ssam				ieee80211_check_scan_current(vap);
274178354Ssam			break;
275178354Ssam		case IEEE80211_S_RUN:		/* beacon miss */
276178354Ssam			/*
277178354Ssam			 * Beacon miss.  Notify user space and if not
278178354Ssam			 * under control of a user application (roaming
279178354Ssam			 * manual) kick off a scan to re-connect.
280178354Ssam			 */
281178354Ssam			ieee80211_sta_leave(ni);
282178354Ssam			if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
283178354Ssam				ieee80211_check_scan_current(vap);
284178354Ssam			break;
285178354Ssam		default:
286178354Ssam			goto invalid;
287178354Ssam		}
288178354Ssam		break;
289178354Ssam	case IEEE80211_S_AUTH:
290178354Ssam		switch (ostate) {
291178354Ssam		case IEEE80211_S_INIT:
292178354Ssam		case IEEE80211_S_SCAN:
293178354Ssam			IEEE80211_SEND_MGMT(ni,
294178354Ssam			    IEEE80211_FC0_SUBTYPE_AUTH, 1);
295178354Ssam			break;
296178354Ssam		case IEEE80211_S_AUTH:
297178354Ssam		case IEEE80211_S_ASSOC:
298178354Ssam			switch (arg & 0xff) {
299178354Ssam			case IEEE80211_FC0_SUBTYPE_AUTH:
300178354Ssam				/* ??? */
301178354Ssam				IEEE80211_SEND_MGMT(ni,
302178354Ssam				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
303178354Ssam				break;
304178354Ssam			case IEEE80211_FC0_SUBTYPE_DEAUTH:
305178354Ssam				sta_authretry(vap, ni, arg>>8);
306178354Ssam				break;
307178354Ssam			}
308178354Ssam			break;
309178354Ssam		case IEEE80211_S_RUN:
310178354Ssam			switch (arg & 0xff) {
311178354Ssam			case IEEE80211_FC0_SUBTYPE_AUTH:
312178354Ssam				IEEE80211_SEND_MGMT(ni,
313178354Ssam				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
314178354Ssam				vap->iv_state = ostate;	/* stay RUN */
315178354Ssam				break;
316178354Ssam			case IEEE80211_FC0_SUBTYPE_DEAUTH:
317178354Ssam				ieee80211_sta_leave(ni);
318178354Ssam				if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
319178354Ssam					/* try to reauth */
320178354Ssam					IEEE80211_SEND_MGMT(ni,
321178354Ssam					    IEEE80211_FC0_SUBTYPE_AUTH, 1);
322178354Ssam				}
323178354Ssam				break;
324178354Ssam			}
325178354Ssam			break;
326178354Ssam		default:
327178354Ssam			goto invalid;
328178354Ssam		}
329178354Ssam		break;
330178354Ssam	case IEEE80211_S_ASSOC:
331178354Ssam		switch (ostate) {
332178354Ssam		case IEEE80211_S_AUTH:
333178354Ssam		case IEEE80211_S_ASSOC:
334178354Ssam			IEEE80211_SEND_MGMT(ni,
335178354Ssam			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
336178354Ssam			break;
337178354Ssam		case IEEE80211_S_SLEEP:		/* cannot happen */
338178354Ssam		case IEEE80211_S_RUN:
339178354Ssam			ieee80211_sta_leave(ni);
340178354Ssam			if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
341178354Ssam				IEEE80211_SEND_MGMT(ni, arg ?
342178354Ssam				    IEEE80211_FC0_SUBTYPE_REASSOC_REQ :
343178354Ssam				    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
344178354Ssam			}
345178354Ssam			break;
346178354Ssam		default:
347178354Ssam			goto invalid;
348178354Ssam		}
349178354Ssam		break;
350178354Ssam	case IEEE80211_S_RUN:
351178354Ssam		if (vap->iv_flags & IEEE80211_F_WPA) {
352178354Ssam			/* XXX validate prerequisites */
353178354Ssam		}
354178354Ssam		switch (ostate) {
355178354Ssam		case IEEE80211_S_RUN:
356178354Ssam			break;
357178354Ssam		case IEEE80211_S_AUTH:		/* when join is done in fw */
358178354Ssam		case IEEE80211_S_ASSOC:
359178354Ssam#ifdef IEEE80211_DEBUG
360178354Ssam			if (ieee80211_msg_debug(vap)) {
361178354Ssam				ieee80211_note(vap, "%s with %s ssid ",
362178354Ssam				    (vap->iv_opmode == IEEE80211_M_STA ?
363178354Ssam				    "associated" : "synchronized"),
364178354Ssam				    ether_sprintf(ni->ni_bssid));
365178354Ssam				ieee80211_print_essid(vap->iv_bss->ni_essid,
366178354Ssam				    ni->ni_esslen);
367178354Ssam				/* XXX MCS/HT */
368178354Ssam				printf(" channel %d start %uMb\n",
369178354Ssam				    ieee80211_chan2ieee(ic, ic->ic_curchan),
370178354Ssam				    IEEE80211_RATE2MBS(ni->ni_txrate));
371178354Ssam			}
372178354Ssam#endif
373178354Ssam			ieee80211_scan_assoc_success(vap, ni->ni_macaddr);
374178354Ssam			ieee80211_notify_node_join(ni,
375178354Ssam			    arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
376178354Ssam			break;
377178354Ssam		case IEEE80211_S_SLEEP:
378178354Ssam			ieee80211_sta_pwrsave(vap, 0);
379178354Ssam			break;
380178354Ssam		default:
381178354Ssam			goto invalid;
382178354Ssam		}
383178354Ssam		ieee80211_sync_curchan(ic);
384178354Ssam		if (ostate != IEEE80211_S_RUN &&
385178354Ssam		    (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)) {
386178354Ssam			/*
387178354Ssam			 * Start s/w beacon miss timer for devices w/o
388178354Ssam			 * hardware support.  We fudge a bit here since
389178354Ssam			 * we're doing this in software.
390178354Ssam			 */
391178354Ssam			vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS(
392178354Ssam				2 * vap->iv_bmissthreshold * ni->ni_intval);
393178354Ssam			vap->iv_swbmiss_count = 0;
394178354Ssam			callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
395178354Ssam				ieee80211_swbmiss, vap);
396178354Ssam		}
397178354Ssam		/*
398178354Ssam		 * When 802.1x is not in use mark the port authorized
399178354Ssam		 * at this point so traffic can flow.
400178354Ssam		 */
401178354Ssam		if (ni->ni_authmode != IEEE80211_AUTH_8021X)
402178354Ssam			ieee80211_node_authorize(ni);
403178354Ssam		break;
404178354Ssam	case IEEE80211_S_SLEEP:
405178354Ssam		ieee80211_sta_pwrsave(vap, 0);
406178354Ssam		break;
407178354Ssam	default:
408178354Ssam	invalid:
409184268Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
410184268Ssam		    "%s: unexpected state transition %s -> %s\n", __func__,
411178354Ssam		    ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
412178354Ssam		break;
413178354Ssam	}
414178354Ssam	return 0;
415178354Ssam}
416178354Ssam
417178354Ssam/*
418178354Ssam * Return non-zero if the frame is an echo of a multicast
419178354Ssam * frame sent by ourself.  The dir is known to be DSTODS.
420178354Ssam */
421178354Ssamstatic __inline int
422178354Ssamisdstods_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
423178354Ssam{
424178354Ssam#define	QWH4(wh)	((const struct ieee80211_qosframe_addr4 *)wh)
425178354Ssam#define	WH4(wh)		((const struct ieee80211_frame_addr4 *)wh)
426178354Ssam	const uint8_t *sa;
427178354Ssam
428178354Ssam	KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode"));
429178354Ssam
430178354Ssam	if (!IEEE80211_IS_MULTICAST(wh->i_addr3))
431178354Ssam		return 0;
432178354Ssam	sa = IEEE80211_QOS_HAS_SEQ(wh) ? QWH4(wh)->i_addr4 : WH4(wh)->i_addr4;
433178354Ssam	return IEEE80211_ADDR_EQ(sa, vap->iv_myaddr);
434178354Ssam#undef WH4
435178354Ssam#undef QWH4
436178354Ssam}
437178354Ssam
438178354Ssam/*
439178354Ssam * Return non-zero if the frame is an echo of a multicast
440178354Ssam * frame sent by ourself.  The dir is known to be FROMDS.
441178354Ssam */
442178354Ssamstatic __inline int
443178354Ssamisfromds_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
444178354Ssam{
445178354Ssam	KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode"));
446178354Ssam
447178354Ssam	if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
448178354Ssam		return 0;
449178354Ssam	return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr);
450178354Ssam}
451178354Ssam
452178354Ssam/*
453178354Ssam * Decide if a received management frame should be
454178354Ssam * printed when debugging is enabled.  This filters some
455178354Ssam * of the less interesting frames that come frequently
456178354Ssam * (e.g. beacons).
457178354Ssam */
458178354Ssamstatic __inline int
459178354Ssamdoprint(struct ieee80211vap *vap, int subtype)
460178354Ssam{
461178354Ssam	switch (subtype) {
462178354Ssam	case IEEE80211_FC0_SUBTYPE_BEACON:
463178354Ssam		return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
464178354Ssam	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
465178354Ssam		return 0;
466178354Ssam	}
467178354Ssam	return 1;
468178354Ssam}
469178354Ssam
470178354Ssam/*
471178354Ssam * Process a received frame.  The node associated with the sender
472178354Ssam * should be supplied.  If nothing was found in the node table then
473178354Ssam * the caller is assumed to supply a reference to iv_bss instead.
474178354Ssam * The RSSI and a timestamp are also supplied.  The RSSI data is used
475178354Ssam * during AP scanning to select a AP to associate with; it can have
476178354Ssam * any units so long as values have consistent units and higher values
477178354Ssam * mean ``better signal''.  The receive timestamp is currently not used
478178354Ssam * by the 802.11 layer.
479178354Ssam */
480178354Ssamstatic int
481178354Ssamsta_input(struct ieee80211_node *ni, struct mbuf *m,
482178354Ssam	int rssi, int noise, uint32_t rstamp)
483178354Ssam{
484178354Ssam#define	SEQ_LEQ(a,b)	((int)((a)-(b)) <= 0)
485178354Ssam#define	HAS_SEQ(type)	((type & 0x4) == 0)
486178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
487178354Ssam	struct ieee80211com *ic = ni->ni_ic;
488178354Ssam	struct ifnet *ifp = vap->iv_ifp;
489178354Ssam	struct ieee80211_frame *wh;
490178354Ssam	struct ieee80211_key *key;
491178354Ssam	struct ether_header *eh;
492178354Ssam	int hdrspace, need_tap;
493178354Ssam	uint8_t dir, type, subtype, qos;
494178354Ssam	uint8_t *bssid;
495178354Ssam	uint16_t rxseq;
496178354Ssam
497183247Ssam	if (m->m_flags & M_AMPDU_MPDU) {
498178354Ssam		/*
499178354Ssam		 * Fastpath for A-MPDU reorder q resubmission.  Frames
500183247Ssam		 * w/ M_AMPDU_MPDU marked have already passed through
501183247Ssam		 * here but were received out of order and been held on
502183247Ssam		 * the reorder queue.  When resubmitted they are marked
503183247Ssam		 * with the M_AMPDU_MPDU flag and we can bypass most of
504183247Ssam		 * the normal processing.
505178354Ssam		 */
506178354Ssam		wh = mtod(m, struct ieee80211_frame *);
507178354Ssam		type = IEEE80211_FC0_TYPE_DATA;
508178354Ssam		dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
509178354Ssam		subtype = IEEE80211_FC0_SUBTYPE_QOS;
510178354Ssam		hdrspace = ieee80211_hdrspace(ic, wh);	/* XXX optimize? */
511178354Ssam		goto resubmit_ampdu;
512178354Ssam	}
513178354Ssam
514178354Ssam	KASSERT(ni != NULL, ("null node"));
515178354Ssam	ni->ni_inact = ni->ni_inact_reload;
516178354Ssam
517178354Ssam	need_tap = 1;			/* mbuf need to be tapped. */
518178354Ssam	type = -1;			/* undefined */
519178354Ssam
520178354Ssam	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
521178354Ssam		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
522178354Ssam		    ni->ni_macaddr, NULL,
523178354Ssam		    "too short (1): len %u", m->m_pkthdr.len);
524178354Ssam		vap->iv_stats.is_rx_tooshort++;
525178354Ssam		goto out;
526178354Ssam	}
527178354Ssam	/*
528178354Ssam	 * Bit of a cheat here, we use a pointer for a 3-address
529178354Ssam	 * frame format but don't reference fields past outside
530178354Ssam	 * ieee80211_frame_min w/o first validating the data is
531178354Ssam	 * present.
532178354Ssam	 */
533178354Ssam	wh = mtod(m, struct ieee80211_frame *);
534178354Ssam
535178354Ssam	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
536178354Ssam	    IEEE80211_FC0_VERSION_0) {
537178354Ssam		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
538178354Ssam		    ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
539178354Ssam		vap->iv_stats.is_rx_badversion++;
540178354Ssam		goto err;
541178354Ssam	}
542178354Ssam
543178354Ssam	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
544178354Ssam	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
545178354Ssam	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
546178354Ssam	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
547178354Ssam		bssid = wh->i_addr2;
548178354Ssam		if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
549178354Ssam			/* not interested in */
550178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
551178354Ssam			    bssid, NULL, "%s", "not to bss");
552178354Ssam			vap->iv_stats.is_rx_wrongbss++;
553178354Ssam			goto out;
554178354Ssam		}
555178354Ssam		IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
556178354Ssam		ni->ni_noise = noise;
557178354Ssam		ni->ni_rstamp = rstamp;
558178354Ssam		if (HAS_SEQ(type)) {
559178354Ssam			uint8_t tid = ieee80211_gettid(wh);
560178354Ssam			if (IEEE80211_QOS_HAS_SEQ(wh) &&
561178354Ssam			    TID_TO_WME_AC(tid) >= WME_AC_VI)
562178354Ssam				ic->ic_wme.wme_hipri_traffic++;
563178354Ssam			rxseq = le16toh(*(uint16_t *)wh->i_seq);
564178354Ssam			if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
565178354Ssam			    (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
566178354Ssam			    SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
567178354Ssam				/* duplicate, discard */
568178354Ssam				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
569178354Ssam				    bssid, "duplicate",
570178354Ssam				    "seqno <%u,%u> fragno <%u,%u> tid %u",
571178354Ssam				    rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
572178354Ssam				    ni->ni_rxseqs[tid] >>
573178354Ssam					IEEE80211_SEQ_SEQ_SHIFT,
574178354Ssam				    rxseq & IEEE80211_SEQ_FRAG_MASK,
575178354Ssam				    ni->ni_rxseqs[tid] &
576178354Ssam					IEEE80211_SEQ_FRAG_MASK,
577178354Ssam				    tid);
578178354Ssam				vap->iv_stats.is_rx_dup++;
579178354Ssam				IEEE80211_NODE_STAT(ni, rx_dup);
580178354Ssam				goto out;
581178354Ssam			}
582178354Ssam			ni->ni_rxseqs[tid] = rxseq;
583178354Ssam		}
584178354Ssam	}
585178354Ssam
586178354Ssam	switch (type) {
587178354Ssam	case IEEE80211_FC0_TYPE_DATA:
588178354Ssam		hdrspace = ieee80211_hdrspace(ic, wh);
589178354Ssam		if (m->m_len < hdrspace &&
590178354Ssam		    (m = m_pullup(m, hdrspace)) == NULL) {
591178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
592178354Ssam			    ni->ni_macaddr, NULL,
593178354Ssam			    "data too short: expecting %u", hdrspace);
594178354Ssam			vap->iv_stats.is_rx_tooshort++;
595178354Ssam			goto out;		/* XXX */
596178354Ssam		}
597178354Ssam		/*
598183247Ssam		 * Handle A-MPDU re-ordering.  If the frame is to be
599183247Ssam		 * processed directly then ieee80211_ampdu_reorder
600178354Ssam		 * will return 0; otherwise it has consumed the mbuf
601178354Ssam		 * and we should do nothing more with it.
602178354Ssam		 */
603183247Ssam		if ((m->m_flags & M_AMPDU) &&
604178354Ssam		    (dir == IEEE80211_FC1_DIR_FROMDS ||
605178354Ssam		     dir == IEEE80211_FC1_DIR_DSTODS) &&
606178354Ssam		    ieee80211_ampdu_reorder(ni, m) != 0) {
607178354Ssam			m = NULL;
608178354Ssam			goto out;
609178354Ssam		}
610178354Ssam	resubmit_ampdu:
611178354Ssam		if (dir == IEEE80211_FC1_DIR_FROMDS) {
612178354Ssam			if ((ifp->if_flags & IFF_SIMPLEX) &&
613178354Ssam			    isfromds_mcastecho(vap, wh)) {
614178354Ssam				/*
615178354Ssam				 * In IEEE802.11 network, multicast
616178354Ssam				 * packets sent from "me" are broadcast
617178354Ssam				 * from the AP; silently discard for
618178354Ssam				 * SIMPLEX interface.
619178354Ssam				 */
620178354Ssam				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
621178354Ssam				    wh, "data", "%s", "multicast echo");
622178354Ssam				vap->iv_stats.is_rx_mcastecho++;
623178354Ssam				goto out;
624178354Ssam			}
625178354Ssam			if ((vap->iv_flags & IEEE80211_F_DWDS) &&
626178354Ssam			    IEEE80211_IS_MULTICAST(wh->i_addr1)) {
627178354Ssam				/*
628178354Ssam				 * DWDS sta's must drop 3-address mcast frames
629178354Ssam				 * as they will be sent separately as a 4-addr
630178354Ssam				 * frame.  Accepting the 3-addr frame will
631178354Ssam				 * confuse the bridge into thinking the sending
632178354Ssam				 * sta is located at the end of WDS link.
633178354Ssam				 */
634178354Ssam				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
635178354Ssam				    "3-address data", "%s", "DWDS enabled");
636178354Ssam				vap->iv_stats.is_rx_mcastecho++;
637178354Ssam				goto out;
638178354Ssam			}
639178354Ssam		} else if (dir == IEEE80211_FC1_DIR_DSTODS) {
640178354Ssam			if ((vap->iv_flags & IEEE80211_F_DWDS) == 0) {
641178354Ssam				IEEE80211_DISCARD(vap,
642178354Ssam				    IEEE80211_MSG_INPUT, wh, "4-address data",
643178354Ssam				    "%s", "DWDS not enabled");
644178354Ssam				vap->iv_stats.is_rx_wrongdir++;
645178354Ssam				goto out;
646178354Ssam			}
647178354Ssam			if ((ifp->if_flags & IFF_SIMPLEX) &&
648178354Ssam			    isdstods_mcastecho(vap, wh)) {
649178354Ssam				/*
650178354Ssam				 * In IEEE802.11 network, multicast
651178354Ssam				 * packets sent from "me" are broadcast
652178354Ssam				 * from the AP; silently discard for
653178354Ssam				 * SIMPLEX interface.
654178354Ssam				 */
655178354Ssam				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
656178354Ssam				    "4-address data", "%s", "multicast echo");
657178354Ssam				vap->iv_stats.is_rx_mcastecho++;
658178354Ssam				goto out;
659178354Ssam			}
660178354Ssam		} else {
661178354Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
662178354Ssam			    "data", "incorrect dir 0x%x", dir);
663178354Ssam			vap->iv_stats.is_rx_wrongdir++;
664178354Ssam			goto out;
665178354Ssam		}
666178354Ssam
667178354Ssam		/*
668178354Ssam		 * Handle privacy requirements.  Note that we
669178354Ssam		 * must not be preempted from here until after
670178354Ssam		 * we (potentially) call ieee80211_crypto_demic;
671178354Ssam		 * otherwise we may violate assumptions in the
672178354Ssam		 * crypto cipher modules used to do delayed update
673178354Ssam		 * of replay sequence numbers.
674178354Ssam		 */
675178354Ssam		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
676178354Ssam			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
677178354Ssam				/*
678178354Ssam				 * Discard encrypted frames when privacy is off.
679178354Ssam				 */
680178354Ssam				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
681178354Ssam				    wh, "WEP", "%s", "PRIVACY off");
682178354Ssam				vap->iv_stats.is_rx_noprivacy++;
683178354Ssam				IEEE80211_NODE_STAT(ni, rx_noprivacy);
684178354Ssam				goto out;
685178354Ssam			}
686178354Ssam			key = ieee80211_crypto_decap(ni, m, hdrspace);
687178354Ssam			if (key == NULL) {
688178354Ssam				/* NB: stats+msgs handled in crypto_decap */
689178354Ssam				IEEE80211_NODE_STAT(ni, rx_wepfail);
690178354Ssam				goto out;
691178354Ssam			}
692178354Ssam			wh = mtod(m, struct ieee80211_frame *);
693178354Ssam			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
694178354Ssam		} else {
695178354Ssam			/* XXX M_WEP and IEEE80211_F_PRIVACY */
696178354Ssam			key = NULL;
697178354Ssam		}
698178354Ssam
699178354Ssam		/*
700178354Ssam		 * Save QoS bits for use below--before we strip the header.
701178354Ssam		 */
702178354Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
703178354Ssam			qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
704178354Ssam			    ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
705178354Ssam			    ((struct ieee80211_qosframe *)wh)->i_qos[0];
706178354Ssam		} else
707178354Ssam			qos = 0;
708178354Ssam
709178354Ssam		/*
710178354Ssam		 * Next up, any fragmentation.
711178354Ssam		 */
712178354Ssam		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
713178354Ssam			m = ieee80211_defrag(ni, m, hdrspace);
714178354Ssam			if (m == NULL) {
715178354Ssam				/* Fragment dropped or frame not complete yet */
716178354Ssam				goto out;
717178354Ssam			}
718178354Ssam		}
719178354Ssam		wh = NULL;		/* no longer valid, catch any uses */
720178354Ssam
721178354Ssam		/*
722178354Ssam		 * Next strip any MSDU crypto bits.
723178354Ssam		 */
724178354Ssam		if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
725178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
726178354Ssam			    ni->ni_macaddr, "data", "%s", "demic error");
727178354Ssam			vap->iv_stats.is_rx_demicfail++;
728178354Ssam			IEEE80211_NODE_STAT(ni, rx_demicfail);
729178354Ssam			goto out;
730178354Ssam		}
731178354Ssam
732178354Ssam		/* copy to listener after decrypt */
733178354Ssam		if (bpf_peers_present(vap->iv_rawbpf))
734178354Ssam			bpf_mtap(vap->iv_rawbpf, m);
735178354Ssam		need_tap = 0;
736178354Ssam
737178354Ssam		/*
738178354Ssam		 * Finally, strip the 802.11 header.
739178354Ssam		 */
740178354Ssam		m = ieee80211_decap(vap, m, hdrspace);
741178354Ssam		if (m == NULL) {
742178354Ssam			/* XXX mask bit to check for both */
743178354Ssam			/* don't count Null data frames as errors */
744178354Ssam			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
745178354Ssam			    subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
746178354Ssam				goto out;
747178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
748178354Ssam			    ni->ni_macaddr, "data", "%s", "decap error");
749178354Ssam			vap->iv_stats.is_rx_decap++;
750178354Ssam			IEEE80211_NODE_STAT(ni, rx_decap);
751178354Ssam			goto err;
752178354Ssam		}
753178354Ssam		eh = mtod(m, struct ether_header *);
754178354Ssam		if (!ieee80211_node_is_authorized(ni)) {
755178354Ssam			/*
756178354Ssam			 * Deny any non-PAE frames received prior to
757178354Ssam			 * authorization.  For open/shared-key
758178354Ssam			 * authentication the port is mark authorized
759178354Ssam			 * after authentication completes.  For 802.1x
760178354Ssam			 * the port is not marked authorized by the
761178354Ssam			 * authenticator until the handshake has completed.
762178354Ssam			 */
763178354Ssam			if (eh->ether_type != htons(ETHERTYPE_PAE)) {
764178354Ssam				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
765178354Ssam				    eh->ether_shost, "data",
766178354Ssam				    "unauthorized port: ether type 0x%x len %u",
767178354Ssam				    eh->ether_type, m->m_pkthdr.len);
768178354Ssam				vap->iv_stats.is_rx_unauth++;
769178354Ssam				IEEE80211_NODE_STAT(ni, rx_unauth);
770178354Ssam				goto err;
771178354Ssam			}
772178354Ssam		} else {
773178354Ssam			/*
774178354Ssam			 * When denying unencrypted frames, discard
775178354Ssam			 * any non-PAE frames received without encryption.
776178354Ssam			 */
777178354Ssam			if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
778178354Ssam			    (key == NULL && (m->m_flags & M_WEP) == 0) &&
779178354Ssam			    eh->ether_type != htons(ETHERTYPE_PAE)) {
780178354Ssam				/*
781178354Ssam				 * Drop unencrypted frames.
782178354Ssam				 */
783178354Ssam				vap->iv_stats.is_rx_unencrypted++;
784178354Ssam				IEEE80211_NODE_STAT(ni, rx_unencrypted);
785178354Ssam				goto out;
786178354Ssam			}
787178354Ssam		}
788178354Ssam		/* XXX require HT? */
789178354Ssam		if (qos & IEEE80211_QOS_AMSDU) {
790178354Ssam			m = ieee80211_decap_amsdu(ni, m);
791178354Ssam			if (m == NULL)
792178354Ssam				return IEEE80211_FC0_TYPE_DATA;
793178354Ssam		} else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
794178354Ssam#define	FF_LLC_SIZE	(sizeof(struct ether_header) + sizeof(struct llc))
795178354Ssam		    m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
796178354Ssam			struct llc *llc;
797178354Ssam
798178354Ssam			/*
799178354Ssam			 * Check for fast-frame tunnel encapsulation.
800178354Ssam			 */
801178354Ssam			if (m->m_len < FF_LLC_SIZE &&
802178354Ssam			    (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
803178354Ssam				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
804178354Ssam				    ni->ni_macaddr, "fast-frame",
805178354Ssam				    "%s", "m_pullup(llc) failed");
806178354Ssam				vap->iv_stats.is_rx_tooshort++;
807178354Ssam				return IEEE80211_FC0_TYPE_DATA;
808178354Ssam			}
809178354Ssam			llc = (struct llc *)(mtod(m, uint8_t *) +
810178354Ssam				sizeof(struct ether_header));
811178354Ssam			if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
812178354Ssam				m_adj(m, FF_LLC_SIZE);
813178354Ssam				m = ieee80211_decap_fastframe(ni, m);
814178354Ssam				if (m == NULL)
815178354Ssam					return IEEE80211_FC0_TYPE_DATA;
816178354Ssam			}
817178354Ssam		}
818178354Ssam#undef FF_LLC_SIZE
819178354Ssam		ieee80211_deliver_data(vap, ni, m);
820178354Ssam		return IEEE80211_FC0_TYPE_DATA;
821178354Ssam
822178354Ssam	case IEEE80211_FC0_TYPE_MGT:
823178354Ssam		vap->iv_stats.is_rx_mgmt++;
824178354Ssam		IEEE80211_NODE_STAT(ni, rx_mgmt);
825178354Ssam		if (dir != IEEE80211_FC1_DIR_NODS) {
826178354Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
827178354Ssam			    wh, "data", "incorrect dir 0x%x", dir);
828178354Ssam			vap->iv_stats.is_rx_wrongdir++;
829178354Ssam			goto err;
830178354Ssam		}
831178354Ssam		if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
832178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
833178354Ssam			    ni->ni_macaddr, "mgt", "too short: len %u",
834178354Ssam			    m->m_pkthdr.len);
835178354Ssam			vap->iv_stats.is_rx_tooshort++;
836178354Ssam			goto out;
837178354Ssam		}
838178354Ssam#ifdef IEEE80211_DEBUG
839178354Ssam		if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
840178354Ssam		    ieee80211_msg_dumppkts(vap)) {
841178354Ssam			if_printf(ifp, "received %s from %s rssi %d\n",
842178354Ssam			    ieee80211_mgt_subtype_name[subtype >>
843178354Ssam				IEEE80211_FC0_SUBTYPE_SHIFT],
844178354Ssam			    ether_sprintf(wh->i_addr2), rssi);
845178354Ssam		}
846178354Ssam#endif
847178354Ssam		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
848178354Ssam			if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
849178354Ssam				/*
850178354Ssam				 * Only shared key auth frames with a challenge
851178354Ssam				 * should be encrypted, discard all others.
852178354Ssam				 */
853178354Ssam				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
854178354Ssam				    wh, ieee80211_mgt_subtype_name[subtype >>
855178354Ssam					IEEE80211_FC0_SUBTYPE_SHIFT],
856178354Ssam				    "%s", "WEP set but not permitted");
857178354Ssam				vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
858178354Ssam				goto out;
859178354Ssam			}
860178354Ssam			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
861178354Ssam				/*
862178354Ssam				 * Discard encrypted frames when privacy is off.
863178354Ssam				 */
864178354Ssam				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
865178354Ssam				    wh, "mgt", "%s", "WEP set but PRIVACY off");
866178354Ssam				vap->iv_stats.is_rx_noprivacy++;
867178354Ssam				goto out;
868178354Ssam			}
869178354Ssam			hdrspace = ieee80211_hdrspace(ic, wh);
870178354Ssam			key = ieee80211_crypto_decap(ni, m, hdrspace);
871178354Ssam			if (key == NULL) {
872178354Ssam				/* NB: stats+msgs handled in crypto_decap */
873178354Ssam				goto out;
874178354Ssam			}
875178354Ssam			wh = mtod(m, struct ieee80211_frame *);
876178354Ssam			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
877178354Ssam		}
878178354Ssam		if (bpf_peers_present(vap->iv_rawbpf))
879178354Ssam			bpf_mtap(vap->iv_rawbpf, m);
880178354Ssam		vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
881178354Ssam		m_freem(m);
882178354Ssam		return IEEE80211_FC0_TYPE_MGT;
883178354Ssam
884178354Ssam	case IEEE80211_FC0_TYPE_CTL:
885178354Ssam		vap->iv_stats.is_rx_ctl++;
886178354Ssam		IEEE80211_NODE_STAT(ni, rx_ctrl);
887178354Ssam		goto out;
888178354Ssam	default:
889178354Ssam		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
890178354Ssam		    wh, NULL, "bad frame type 0x%x", type);
891178354Ssam		/* should not come here */
892178354Ssam		break;
893178354Ssam	}
894178354Ssamerr:
895178354Ssam	ifp->if_ierrors++;
896178354Ssamout:
897178354Ssam	if (m != NULL) {
898178354Ssam		if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
899178354Ssam			bpf_mtap(vap->iv_rawbpf, m);
900178354Ssam		m_freem(m);
901178354Ssam	}
902178354Ssam	return type;
903178354Ssam#undef SEQ_LEQ
904178354Ssam}
905178354Ssam
906178354Ssamstatic void
907178354Ssamsta_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh,
908178354Ssam    int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status)
909178354Ssam{
910178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
911178354Ssam
912178354Ssam	if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
913178354Ssam		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
914178354Ssam		    ni->ni_macaddr, "open auth",
915178354Ssam		    "bad sta auth mode %u", ni->ni_authmode);
916178354Ssam		vap->iv_stats.is_rx_bad_auth++;	/* XXX */
917178354Ssam		return;
918178354Ssam	}
919178354Ssam	if (vap->iv_state != IEEE80211_S_AUTH ||
920178354Ssam	    seq != IEEE80211_AUTH_OPEN_RESPONSE) {
921178354Ssam		vap->iv_stats.is_rx_bad_auth++;
922178354Ssam		return;
923178354Ssam	}
924178354Ssam	if (status != 0) {
925178354Ssam		IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
926178354Ssam		    ni, "open auth failed (reason %d)", status);
927178354Ssam		vap->iv_stats.is_rx_auth_fail++;
928178354Ssam		vap->iv_stats.is_rx_authfail_code = status;
929178354Ssam		ieee80211_new_state(vap, IEEE80211_S_SCAN,
930178354Ssam		    IEEE80211_SCAN_FAIL_STATUS);
931178354Ssam	} else
932178354Ssam		ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
933178354Ssam}
934178354Ssam
935178354Ssamstatic void
936178354Ssamsta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
937178354Ssam    uint8_t *frm, uint8_t *efrm, int rssi, int noise, uint32_t rstamp,
938178354Ssam    uint16_t seq, uint16_t status)
939178354Ssam{
940178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
941178354Ssam	uint8_t *challenge;
942178354Ssam	int estatus;
943178354Ssam
944178354Ssam	/*
945178354Ssam	 * NB: this can happen as we allow pre-shared key
946178354Ssam	 * authentication to be enabled w/o wep being turned
947178354Ssam	 * on so that configuration of these can be done
948178354Ssam	 * in any order.  It may be better to enforce the
949178354Ssam	 * ordering in which case this check would just be
950178354Ssam	 * for sanity/consistency.
951178354Ssam	 */
952178354Ssam	if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
953178354Ssam		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
954178354Ssam		    ni->ni_macaddr, "shared key auth",
955178354Ssam		    "%s", " PRIVACY is disabled");
956178354Ssam		estatus = IEEE80211_STATUS_ALG;
957178354Ssam		goto bad;
958178354Ssam	}
959178354Ssam	/*
960178354Ssam	 * Pre-shared key authentication is evil; accept
961178354Ssam	 * it only if explicitly configured (it is supported
962178354Ssam	 * mainly for compatibility with clients like OS X).
963178354Ssam	 */
964178354Ssam	if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
965178354Ssam	    ni->ni_authmode != IEEE80211_AUTH_SHARED) {
966178354Ssam		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
967178354Ssam		    ni->ni_macaddr, "shared key auth",
968178354Ssam		    "bad sta auth mode %u", ni->ni_authmode);
969178354Ssam		vap->iv_stats.is_rx_bad_auth++;	/* XXX maybe a unique error? */
970178354Ssam		estatus = IEEE80211_STATUS_ALG;
971178354Ssam		goto bad;
972178354Ssam	}
973178354Ssam
974178354Ssam	challenge = NULL;
975178354Ssam	if (frm + 1 < efrm) {
976178354Ssam		if ((frm[1] + 2) > (efrm - frm)) {
977178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
978178354Ssam			    ni->ni_macaddr, "shared key auth",
979178354Ssam			    "ie %d/%d too long",
980178354Ssam			    frm[0], (frm[1] + 2) - (efrm - frm));
981178354Ssam			vap->iv_stats.is_rx_bad_auth++;
982178354Ssam			estatus = IEEE80211_STATUS_CHALLENGE;
983178354Ssam			goto bad;
984178354Ssam		}
985178354Ssam		if (*frm == IEEE80211_ELEMID_CHALLENGE)
986178354Ssam			challenge = frm;
987178354Ssam		frm += frm[1] + 2;
988178354Ssam	}
989178354Ssam	switch (seq) {
990178354Ssam	case IEEE80211_AUTH_SHARED_CHALLENGE:
991178354Ssam	case IEEE80211_AUTH_SHARED_RESPONSE:
992178354Ssam		if (challenge == NULL) {
993178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
994178354Ssam			    ni->ni_macaddr, "shared key auth",
995178354Ssam			    "%s", "no challenge");
996178354Ssam			vap->iv_stats.is_rx_bad_auth++;
997178354Ssam			estatus = IEEE80211_STATUS_CHALLENGE;
998178354Ssam			goto bad;
999178354Ssam		}
1000178354Ssam		if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
1001178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
1002178354Ssam			    ni->ni_macaddr, "shared key auth",
1003178354Ssam			    "bad challenge len %d", challenge[1]);
1004178354Ssam			vap->iv_stats.is_rx_bad_auth++;
1005178354Ssam			estatus = IEEE80211_STATUS_CHALLENGE;
1006178354Ssam			goto bad;
1007178354Ssam		}
1008178354Ssam	default:
1009178354Ssam		break;
1010178354Ssam	}
1011178354Ssam	if (vap->iv_state != IEEE80211_S_AUTH)
1012178354Ssam		return;
1013178354Ssam	switch (seq) {
1014178354Ssam	case IEEE80211_AUTH_SHARED_PASS:
1015178354Ssam		if (ni->ni_challenge != NULL) {
1016184210Sdes			FREE(ni->ni_challenge, M_80211_NODE);
1017178354Ssam			ni->ni_challenge = NULL;
1018178354Ssam		}
1019178354Ssam		if (status != 0) {
1020178354Ssam			IEEE80211_NOTE_FRAME(vap,
1021178354Ssam			    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, wh,
1022178354Ssam			    "shared key auth failed (reason %d)", status);
1023178354Ssam			vap->iv_stats.is_rx_auth_fail++;
1024178354Ssam			vap->iv_stats.is_rx_authfail_code = status;
1025178354Ssam			return;
1026178354Ssam		}
1027178354Ssam		ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
1028178354Ssam		break;
1029178354Ssam	case IEEE80211_AUTH_SHARED_CHALLENGE:
1030178354Ssam		if (!ieee80211_alloc_challenge(ni))
1031178354Ssam			return;
1032178354Ssam		/* XXX could optimize by passing recvd challenge */
1033178354Ssam		memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
1034178354Ssam		IEEE80211_SEND_MGMT(ni,
1035178354Ssam			IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
1036178354Ssam		break;
1037178354Ssam	default:
1038178354Ssam		IEEE80211_DISCARD(vap, IEEE80211_MSG_AUTH,
1039178354Ssam		    wh, "shared key auth", "bad seq %d", seq);
1040178354Ssam		vap->iv_stats.is_rx_bad_auth++;
1041178354Ssam		return;
1042178354Ssam	}
1043178354Ssam	return;
1044178354Ssambad:
1045178354Ssam	/*
1046178354Ssam	 * Kick the state machine.  This short-circuits
1047178354Ssam	 * using the mgt frame timeout to trigger the
1048178354Ssam	 * state transition.
1049178354Ssam	 */
1050178354Ssam	if (vap->iv_state == IEEE80211_S_AUTH)
1051178354Ssam		ieee80211_new_state(vap, IEEE80211_S_SCAN,
1052178354Ssam		    IEEE80211_SCAN_FAIL_STATUS);
1053178354Ssam}
1054178354Ssam
1055178354Ssamstatic int
1056178354Ssamieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm,
1057178354Ssam	const struct ieee80211_frame *wh)
1058178354Ssam{
1059178354Ssam#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
1060178354Ssam	struct ieee80211_wme_state *wme = &vap->iv_ic->ic_wme;
1061178354Ssam	u_int len = frm[1], qosinfo;
1062178354Ssam	int i;
1063178354Ssam
1064178354Ssam	if (len < sizeof(struct ieee80211_wme_param)-2) {
1065178354Ssam		IEEE80211_DISCARD_IE(vap,
1066178354Ssam		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
1067178354Ssam		    wh, "WME", "too short, len %u", len);
1068178354Ssam		return -1;
1069178354Ssam	}
1070178354Ssam	qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)];
1071178354Ssam	qosinfo &= WME_QOSINFO_COUNT;
1072178354Ssam	/* XXX do proper check for wraparound */
1073178354Ssam	if (qosinfo == wme->wme_wmeChanParams.cap_info)
1074178354Ssam		return 0;
1075178354Ssam	frm += __offsetof(struct ieee80211_wme_param, params_acParams);
1076178354Ssam	for (i = 0; i < WME_NUM_AC; i++) {
1077178354Ssam		struct wmeParams *wmep =
1078178354Ssam			&wme->wme_wmeChanParams.cap_wmeParams[i];
1079178354Ssam		/* NB: ACI not used */
1080178354Ssam		wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
1081178354Ssam		wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
1082178354Ssam		wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
1083178354Ssam		wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
1084178354Ssam		wmep->wmep_txopLimit = LE_READ_2(frm+2);
1085178354Ssam		frm += 4;
1086178354Ssam	}
1087178354Ssam	wme->wme_wmeChanParams.cap_info = qosinfo;
1088178354Ssam	return 1;
1089178354Ssam#undef MS
1090178354Ssam}
1091178354Ssam
1092178354Ssamstatic int
1093178354Ssamieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm,
1094178354Ssam	const struct ieee80211_frame *wh)
1095178354Ssam{
1096178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
1097178354Ssam	const struct ieee80211_ath_ie *ath;
1098178354Ssam	u_int len = frm[1];
1099178354Ssam	int capschanged;
1100178354Ssam	uint16_t defkeyix;
1101178354Ssam
1102178354Ssam	if (len < sizeof(struct ieee80211_ath_ie)-2) {
1103178354Ssam		IEEE80211_DISCARD_IE(vap,
1104178354Ssam		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG,
1105178354Ssam		    wh, "Atheros", "too short, len %u", len);
1106178354Ssam		return -1;
1107178354Ssam	}
1108178354Ssam	ath = (const struct ieee80211_ath_ie *)frm;
1109178354Ssam	capschanged = (ni->ni_ath_flags != ath->ath_capability);
1110178354Ssam	defkeyix = LE_READ_2(ath->ath_defkeyix);
1111178354Ssam	if (capschanged || defkeyix != ni->ni_ath_defkeyix) {
1112178354Ssam		ni->ni_ath_flags = ath->ath_capability;
1113178354Ssam		ni->ni_ath_defkeyix = defkeyix;
1114178354Ssam		IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
1115178354Ssam		    "ath ie change: new caps 0x%x defkeyix 0x%x",
1116178354Ssam		    ni->ni_ath_flags, ni->ni_ath_defkeyix);
1117178354Ssam	}
1118178354Ssam	if (IEEE80211_ATH_CAP(vap, ni, ATHEROS_CAP_TURBO_PRIME)) {
1119178354Ssam		uint16_t curflags, newflags;
1120178354Ssam
1121178354Ssam		/*
1122178354Ssam		 * Check for turbo mode switch.  Calculate flags
1123178354Ssam		 * for the new mode and effect the switch.
1124178354Ssam		 */
1125178354Ssam		newflags = curflags = vap->iv_ic->ic_bsschan->ic_flags;
1126178354Ssam		/* NB: BOOST is not in ic_flags, so get it from the ie */
1127178354Ssam		if (ath->ath_capability & ATHEROS_CAP_BOOST)
1128178354Ssam			newflags |= IEEE80211_CHAN_TURBO;
1129178354Ssam		else
1130178354Ssam			newflags &= ~IEEE80211_CHAN_TURBO;
1131178354Ssam		if (newflags != curflags)
1132178354Ssam			ieee80211_dturbo_switch(vap, newflags);
1133178354Ssam	}
1134178354Ssam	return capschanged;
1135178354Ssam}
1136178354Ssam
1137178354Ssam/*
1138178354Ssam * Return non-zero if a background scan may be continued:
1139178354Ssam * o bg scan is active
1140178354Ssam * o no channel switch is pending
1141178354Ssam * o there has not been any traffic recently
1142178354Ssam *
1143178354Ssam * Note we do not check if there is an administrative enable;
1144178354Ssam * this is only done to start the scan.  We assume that any
1145178354Ssam * change in state will be accompanied by a request to cancel
1146178354Ssam * active scans which will otherwise cause this test to fail.
1147178354Ssam */
1148178354Ssamstatic __inline int
1149178354Ssamcontbgscan(struct ieee80211vap *vap)
1150178354Ssam{
1151178354Ssam	struct ieee80211com *ic = vap->iv_ic;
1152178354Ssam
1153178354Ssam	return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
1154178354Ssam	    (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 &&
1155178354Ssam	    vap->iv_state == IEEE80211_S_RUN &&		/* XXX? */
1156178354Ssam	    time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle));
1157178354Ssam}
1158178354Ssam
1159178354Ssam/*
1160178354Ssam * Return non-zero if a backgrond scan may be started:
1161178354Ssam * o bg scanning is administratively enabled
1162178354Ssam * o no channel switch is pending
1163178354Ssam * o we are not boosted on a dynamic turbo channel
1164178354Ssam * o there has not been a scan recently
1165178354Ssam * o there has not been any traffic recently
1166178354Ssam */
1167178354Ssamstatic __inline int
1168178354Ssamstartbgscan(struct ieee80211vap *vap)
1169178354Ssam{
1170178354Ssam	struct ieee80211com *ic = vap->iv_ic;
1171178354Ssam
1172178354Ssam	return ((vap->iv_flags & IEEE80211_F_BGSCAN) &&
1173178354Ssam	    (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 &&
1174178354Ssam	    !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
1175178354Ssam	    time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) &&
1176178354Ssam	    time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle));
1177178354Ssam}
1178178354Ssam
1179178354Ssamstatic void
1180178354Ssamsta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
1181178354Ssam	int subtype, int rssi, int noise, uint32_t rstamp)
1182178354Ssam{
1183178354Ssam#define	ISPROBE(_st)	((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
1184178354Ssam#define	ISREASSOC(_st)	((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
1185178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
1186178354Ssam	struct ieee80211com *ic = ni->ni_ic;
1187178354Ssam	struct ieee80211_frame *wh;
1188178354Ssam	uint8_t *frm, *efrm;
1189178354Ssam	uint8_t *rates, *xrates, *wme, *htcap, *htinfo;
1190178354Ssam	uint8_t rate;
1191178354Ssam
1192178354Ssam	wh = mtod(m0, struct ieee80211_frame *);
1193178354Ssam	frm = (uint8_t *)&wh[1];
1194178354Ssam	efrm = mtod(m0, uint8_t *) + m0->m_len;
1195178354Ssam	switch (subtype) {
1196178354Ssam	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
1197178354Ssam	case IEEE80211_FC0_SUBTYPE_BEACON: {
1198178354Ssam		struct ieee80211_scanparams scan;
1199178354Ssam		/*
1200178354Ssam		 * We process beacon/probe response frames:
1201178354Ssam		 *    o when scanning, or
1202178354Ssam		 *    o station mode when associated (to collect state
1203178354Ssam		 *      updates such as 802.11g slot time), or
1204178354Ssam		 * Frames otherwise received are discarded.
1205178354Ssam		 */
1206178354Ssam		if (!((ic->ic_flags & IEEE80211_F_SCAN) || ni->ni_associd)) {
1207178354Ssam			vap->iv_stats.is_rx_mgtdiscard++;
1208178354Ssam			return;
1209178354Ssam		}
1210178354Ssam		/* XXX probe response in sta mode when !scanning? */
1211178354Ssam		if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
1212178354Ssam			return;
1213178354Ssam		/*
1214178354Ssam		 * Count frame now that we know it's to be processed.
1215178354Ssam		 */
1216178354Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
1217178354Ssam			vap->iv_stats.is_rx_beacon++;		/* XXX remove */
1218178354Ssam			IEEE80211_NODE_STAT(ni, rx_beacons);
1219178354Ssam		} else
1220178354Ssam			IEEE80211_NODE_STAT(ni, rx_proberesp);
1221178354Ssam		/*
1222178354Ssam		 * When operating in station mode, check for state updates.
1223178354Ssam		 * Be careful to ignore beacons received while doing a
1224178354Ssam		 * background scan.  We consider only 11g/WMM stuff right now.
1225178354Ssam		 */
1226178354Ssam		if (ni->ni_associd != 0 &&
1227178354Ssam		    ((ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
1228178354Ssam		     IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) {
1229178354Ssam			/* record tsf of last beacon */
1230178354Ssam			memcpy(ni->ni_tstamp.data, scan.tstamp,
1231178354Ssam				sizeof(ni->ni_tstamp));
1232178354Ssam			/* count beacon frame for s/w bmiss handling */
1233178354Ssam			vap->iv_swbmiss_count++;
1234178354Ssam			vap->iv_bmiss_count = 0;
1235178354Ssam			if (ni->ni_erp != scan.erp) {
1236178354Ssam				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
1237178354Ssam				    wh->i_addr2,
1238178354Ssam				    "erp change: was 0x%x, now 0x%x",
1239178354Ssam				    ni->ni_erp, scan.erp);
1240178354Ssam				if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
1241178354Ssam				    (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
1242178354Ssam					ic->ic_flags |= IEEE80211_F_USEPROT;
1243178354Ssam				else
1244178354Ssam					ic->ic_flags &= ~IEEE80211_F_USEPROT;
1245178354Ssam				ni->ni_erp = scan.erp;
1246178354Ssam				/* XXX statistic */
1247178354Ssam				/* XXX driver notification */
1248178354Ssam			}
1249178354Ssam			if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
1250178354Ssam				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
1251178354Ssam				    wh->i_addr2,
1252178354Ssam				    "capabilities change: was 0x%x, now 0x%x",
1253178354Ssam				    ni->ni_capinfo, scan.capinfo);
1254178354Ssam				/*
1255178354Ssam				 * NB: we assume short preamble doesn't
1256178354Ssam				 *     change dynamically
1257178354Ssam				 */
1258178354Ssam				ieee80211_set_shortslottime(ic,
1259178354Ssam					IEEE80211_IS_CHAN_A(ic->ic_bsschan) ||
1260178354Ssam					(scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
1261178354Ssam				ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME)
1262178354Ssam					       | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME);
1263178354Ssam				/* XXX statistic */
1264178354Ssam			}
1265178354Ssam			if (scan.wme != NULL &&
1266178354Ssam			    (ni->ni_flags & IEEE80211_NODE_QOS) &&
1267178354Ssam			    ieee80211_parse_wmeparams(vap, scan.wme, wh) > 0)
1268178354Ssam				ieee80211_wme_updateparams(vap);
1269178354Ssam			if (scan.ath != NULL)
1270178354Ssam				ieee80211_parse_athparams(ni, scan.ath, wh);
1271183254Ssam			if (scan.htcap != NULL && scan.htinfo != NULL &&
1272183254Ssam			    (vap->iv_flags_ext & IEEE80211_FEXT_HT)) {
1273183254Ssam				ieee80211_ht_updateparams(ni,
1274183254Ssam				    scan.htcap, scan.htinfo);
1275178354Ssam				/* XXX state changes? */
1276178354Ssam			}
1277178354Ssam			if (scan.tim != NULL) {
1278178354Ssam				struct ieee80211_tim_ie *tim =
1279178354Ssam				    (struct ieee80211_tim_ie *) scan.tim;
1280178354Ssam#if 0
1281178354Ssam				int aid = IEEE80211_AID(ni->ni_associd);
1282178354Ssam				int ix = aid / NBBY;
1283178354Ssam				int min = tim->tim_bitctl &~ 1;
1284178354Ssam				int max = tim->tim_len + min - 4;
1285178354Ssam				if ((tim->tim_bitctl&1) ||
1286178354Ssam				    (min <= ix && ix <= max &&
1287178354Ssam				     isset(tim->tim_bitmap - min, aid))) {
1288178354Ssam					/*
1289178354Ssam					 * XXX Do not let bg scan kick off
1290178354Ssam					 * we are expecting data.
1291178354Ssam					 */
1292178354Ssam					ic->ic_lastdata = ticks;
1293178354Ssam					ieee80211_sta_pwrsave(vap, 0);
1294178354Ssam				}
1295178354Ssam#endif
1296178354Ssam				ni->ni_dtim_count = tim->tim_count;
1297178354Ssam				ni->ni_dtim_period = tim->tim_period;
1298178354Ssam			}
1299178354Ssam			/*
1300178354Ssam			 * If scanning, pass the info to the scan module.
1301178354Ssam			 * Otherwise, check if it's the right time to do
1302178354Ssam			 * a background scan.  Background scanning must
1303178354Ssam			 * be enabled and we must not be operating in the
1304178354Ssam			 * turbo phase of dynamic turbo mode.  Then,
1305178354Ssam			 * it's been a while since the last background
1306178354Ssam			 * scan and if no data frames have come through
1307178354Ssam			 * recently, kick off a scan.  Note that this
1308178354Ssam			 * is the mechanism by which a background scan
1309178354Ssam			 * is started _and_ continued each time we
1310178354Ssam			 * return on-channel to receive a beacon from
1311178354Ssam			 * our ap.
1312178354Ssam			 */
1313178354Ssam			if (ic->ic_flags & IEEE80211_F_SCAN) {
1314178354Ssam				ieee80211_add_scan(vap, &scan, wh,
1315178354Ssam					subtype, rssi, noise, rstamp);
1316178354Ssam			} else if (contbgscan(vap)) {
1317178354Ssam				ieee80211_bg_scan(vap, 0);
1318178354Ssam			} else if (startbgscan(vap)) {
1319178354Ssam				vap->iv_stats.is_scan_bg++;
1320178354Ssam#if 0
1321178354Ssam				/* wakeup if we are sleeing */
1322178354Ssam				ieee80211_set_pwrsave(vap, 0);
1323178354Ssam#endif
1324178354Ssam				ieee80211_bg_scan(vap, 0);
1325178354Ssam			}
1326178354Ssam			return;
1327178354Ssam		}
1328178354Ssam		/*
1329178354Ssam		 * If scanning, just pass information to the scan module.
1330178354Ssam		 */
1331178354Ssam		if (ic->ic_flags & IEEE80211_F_SCAN) {
1332178354Ssam			if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
1333178354Ssam				/*
1334178354Ssam				 * Actively scanning a channel marked passive;
1335178354Ssam				 * send a probe request now that we know there
1336178354Ssam				 * is 802.11 traffic present.
1337178354Ssam				 *
1338178354Ssam				 * XXX check if the beacon we recv'd gives
1339178354Ssam				 * us what we need and suppress the probe req
1340178354Ssam				 */
1341178354Ssam				ieee80211_probe_curchan(vap, 1);
1342178354Ssam				ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
1343178354Ssam			}
1344178354Ssam			ieee80211_add_scan(vap, &scan, wh,
1345178354Ssam				subtype, rssi, noise, rstamp);
1346178354Ssam			return;
1347178354Ssam		}
1348178354Ssam		break;
1349178354Ssam	}
1350178354Ssam
1351178354Ssam	case IEEE80211_FC0_SUBTYPE_AUTH: {
1352178354Ssam		uint16_t algo, seq, status;
1353178354Ssam		/*
1354178354Ssam		 * auth frame format
1355178354Ssam		 *	[2] algorithm
1356178354Ssam		 *	[2] sequence
1357178354Ssam		 *	[2] status
1358178354Ssam		 *	[tlv*] challenge
1359178354Ssam		 */
1360178354Ssam		IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
1361178354Ssam		algo   = le16toh(*(uint16_t *)frm);
1362178354Ssam		seq    = le16toh(*(uint16_t *)(frm + 2));
1363178354Ssam		status = le16toh(*(uint16_t *)(frm + 4));
1364178354Ssam		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2,
1365178354Ssam		    "recv auth frame with algorithm %d seq %d", algo, seq);
1366178354Ssam
1367178354Ssam		if (vap->iv_flags & IEEE80211_F_COUNTERM) {
1368178354Ssam			IEEE80211_DISCARD(vap,
1369178354Ssam			    IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
1370178354Ssam			    wh, "auth", "%s", "TKIP countermeasures enabled");
1371178354Ssam			vap->iv_stats.is_rx_auth_countermeasures++;
1372178354Ssam			if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
1373178354Ssam				ieee80211_send_error(ni, wh->i_addr2,
1374178354Ssam					IEEE80211_FC0_SUBTYPE_AUTH,
1375178354Ssam					IEEE80211_REASON_MIC_FAILURE);
1376178354Ssam			}
1377178354Ssam			return;
1378178354Ssam		}
1379178354Ssam		if (algo == IEEE80211_AUTH_ALG_SHARED)
1380178354Ssam			sta_auth_shared(ni, wh, frm + 6, efrm, rssi,
1381178354Ssam			    noise, rstamp, seq, status);
1382178354Ssam		else if (algo == IEEE80211_AUTH_ALG_OPEN)
1383178354Ssam			sta_auth_open(ni, wh, rssi, noise, rstamp,
1384178354Ssam			    seq, status);
1385178354Ssam		else {
1386178354Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
1387178354Ssam			    wh, "auth", "unsupported alg %d", algo);
1388178354Ssam			vap->iv_stats.is_rx_auth_unsupported++;
1389178354Ssam			return;
1390178354Ssam		}
1391178354Ssam		break;
1392178354Ssam	}
1393178354Ssam
1394178354Ssam	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
1395178354Ssam	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
1396178354Ssam		uint16_t capinfo, associd;
1397178354Ssam		uint16_t status;
1398178354Ssam
1399178354Ssam		if (vap->iv_state != IEEE80211_S_ASSOC) {
1400178354Ssam			vap->iv_stats.is_rx_mgtdiscard++;
1401178354Ssam			return;
1402178354Ssam		}
1403178354Ssam
1404178354Ssam		/*
1405178354Ssam		 * asresp frame format
1406178354Ssam		 *	[2] capability information
1407178354Ssam		 *	[2] status
1408178354Ssam		 *	[2] association ID
1409178354Ssam		 *	[tlv] supported rates
1410178354Ssam		 *	[tlv] extended supported rates
1411178354Ssam		 *	[tlv] WME
1412178354Ssam		 *	[tlv] HT capabilities
1413178354Ssam		 *	[tlv] HT info
1414178354Ssam		 */
1415178354Ssam		IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
1416178354Ssam		ni = vap->iv_bss;
1417178354Ssam		capinfo = le16toh(*(uint16_t *)frm);
1418178354Ssam		frm += 2;
1419178354Ssam		status = le16toh(*(uint16_t *)frm);
1420178354Ssam		frm += 2;
1421178354Ssam		if (status != 0) {
1422178354Ssam			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
1423178354Ssam			    wh->i_addr2, "%sassoc failed (reason %d)",
1424178354Ssam			    ISREASSOC(subtype) ?  "re" : "", status);
1425178354Ssam			vap->iv_stats.is_rx_auth_fail++;	/* XXX */
1426178354Ssam			return;
1427178354Ssam		}
1428178354Ssam		associd = le16toh(*(uint16_t *)frm);
1429178354Ssam		frm += 2;
1430178354Ssam
1431178354Ssam		rates = xrates = wme = htcap = htinfo = NULL;
1432178354Ssam		while (efrm - frm > 1) {
1433178354Ssam			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
1434178354Ssam			switch (*frm) {
1435178354Ssam			case IEEE80211_ELEMID_RATES:
1436178354Ssam				rates = frm;
1437178354Ssam				break;
1438178354Ssam			case IEEE80211_ELEMID_XRATES:
1439178354Ssam				xrates = frm;
1440178354Ssam				break;
1441178354Ssam			case IEEE80211_ELEMID_HTCAP:
1442178354Ssam				htcap = frm;
1443178354Ssam				break;
1444178354Ssam			case IEEE80211_ELEMID_HTINFO:
1445178354Ssam				htinfo = frm;
1446178354Ssam				break;
1447178354Ssam			case IEEE80211_ELEMID_VENDOR:
1448178354Ssam				if (iswmeoui(frm))
1449178354Ssam					wme = frm;
1450178354Ssam				else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
1451178354Ssam					/*
1452178354Ssam					 * Accept pre-draft HT ie's if the
1453178354Ssam					 * standard ones have not been seen.
1454178354Ssam					 */
1455178354Ssam					if (ishtcapoui(frm)) {
1456178354Ssam						if (htcap == NULL)
1457178354Ssam							htcap = frm;
1458178354Ssam					} else if (ishtinfooui(frm)) {
1459178354Ssam						if (htinfo == NULL)
1460178354Ssam							htcap = frm;
1461178354Ssam					}
1462178354Ssam				}
1463178354Ssam				/* XXX Atheros OUI support */
1464178354Ssam				break;
1465178354Ssam			}
1466178354Ssam			frm += frm[1] + 2;
1467178354Ssam		}
1468178354Ssam
1469178354Ssam		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
1470178354Ssam		if (xrates != NULL)
1471178354Ssam			IEEE80211_VERIFY_ELEMENT(xrates,
1472178354Ssam				IEEE80211_RATE_MAXSIZE - rates[1], return);
1473178354Ssam		rate = ieee80211_setup_rates(ni, rates, xrates,
1474178354Ssam				IEEE80211_F_JOIN |
1475178354Ssam				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
1476178354Ssam				IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
1477178354Ssam		if (rate & IEEE80211_RATE_BASIC) {
1478178354Ssam			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
1479178354Ssam			    wh->i_addr2,
1480178354Ssam			    "%sassoc failed (rate set mismatch)",
1481178354Ssam			    ISREASSOC(subtype) ?  "re" : "");
1482178354Ssam			vap->iv_stats.is_rx_assoc_norate++;
1483178354Ssam			ieee80211_new_state(vap, IEEE80211_S_SCAN,
1484178354Ssam			    IEEE80211_SCAN_FAIL_STATUS);
1485178354Ssam			return;
1486178354Ssam		}
1487178354Ssam
1488178354Ssam		ni->ni_capinfo = capinfo;
1489178354Ssam		ni->ni_associd = associd;
1490178354Ssam		if (ni->ni_jointime == 0)
1491178354Ssam			ni->ni_jointime = time_uptime;
1492178354Ssam		if (wme != NULL &&
1493178354Ssam		    ieee80211_parse_wmeparams(vap, wme, wh) >= 0) {
1494178354Ssam			ni->ni_flags |= IEEE80211_NODE_QOS;
1495178354Ssam			ieee80211_wme_updateparams(vap);
1496178354Ssam		} else
1497178354Ssam			ni->ni_flags &= ~IEEE80211_NODE_QOS;
1498178354Ssam		/*
1499178354Ssam		 * Setup HT state according to the negotiation.
1500178354Ssam		 *
1501178354Ssam		 * NB: shouldn't need to check if HT use is enabled but some
1502178354Ssam		 *     ap's send back HT ie's even when we don't indicate we
1503178354Ssam		 *     are HT capable in our AssocReq.
1504178354Ssam		 */
1505178354Ssam		if (htcap != NULL && htinfo != NULL &&
1506178354Ssam		    (vap->iv_flags_ext & IEEE80211_FEXT_HT)) {
1507183254Ssam			ieee80211_ht_node_init(ni);
1508183254Ssam			ieee80211_ht_updateparams(ni, htcap, htinfo);
1509178354Ssam			ieee80211_setup_htrates(ni, htcap,
1510178354Ssam			     IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
1511178354Ssam			ieee80211_setup_basic_htrates(ni, htinfo);
1512178354Ssam		}
1513178354Ssam		/*
1514178354Ssam		 * Configure state now that we are associated.
1515178354Ssam		 *
1516178354Ssam		 * XXX may need different/additional driver callbacks?
1517178354Ssam		 */
1518178354Ssam		if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
1519178354Ssam		    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
1520178354Ssam			ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
1521178354Ssam			ic->ic_flags &= ~IEEE80211_F_USEBARKER;
1522178354Ssam		} else {
1523178354Ssam			ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
1524178354Ssam			ic->ic_flags |= IEEE80211_F_USEBARKER;
1525178354Ssam		}
1526178354Ssam		ieee80211_set_shortslottime(ic,
1527178354Ssam			IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
1528178354Ssam			(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
1529178354Ssam		/*
1530178354Ssam		 * Honor ERP protection.
1531178354Ssam		 *
1532178354Ssam		 * NB: ni_erp should zero for non-11g operation.
1533178354Ssam		 */
1534178354Ssam		if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
1535178354Ssam		    (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
1536178354Ssam			ic->ic_flags |= IEEE80211_F_USEPROT;
1537178354Ssam		else
1538178354Ssam			ic->ic_flags &= ~IEEE80211_F_USEPROT;
1539178354Ssam		IEEE80211_NOTE_MAC(vap,
1540178354Ssam		    IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, wh->i_addr2,
1541183256Ssam		    "%sassoc success at aid %d: %s preamble, %s slot time%s%s%s%s%s%s%s%s",
1542178354Ssam		    ISREASSOC(subtype) ? "re" : "",
1543178354Ssam		    IEEE80211_NODE_AID(ni),
1544178354Ssam		    ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
1545178354Ssam		    ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
1546178354Ssam		    ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
1547178354Ssam		    ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
1548178354Ssam		    ni->ni_flags & IEEE80211_NODE_HT ?
1549182834Ssam			(ni->ni_chw == 40 ? ", HT40" : ", HT20") : "",
1550178354Ssam		    ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
1551183255Ssam		    ni->ni_flags & IEEE80211_NODE_MIMO_RTS ? " (+SMPS-DYN)" :
1552183255Ssam			ni->ni_flags & IEEE80211_NODE_MIMO_PS ? " (+SMPS)" : "",
1553183256Ssam		    ni->ni_flags & IEEE80211_NODE_RIFS ? " (+RIFS)" : "",
1554178354Ssam		    IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ?
1555178354Ssam			", fast-frames" : "",
1556178354Ssam		    IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ?
1557178354Ssam			", turbo" : ""
1558178354Ssam		);
1559178354Ssam		ieee80211_new_state(vap, IEEE80211_S_RUN, subtype);
1560178354Ssam		break;
1561178354Ssam	}
1562178354Ssam
1563178354Ssam	case IEEE80211_FC0_SUBTYPE_DEAUTH: {
1564178354Ssam		uint16_t reason;
1565178354Ssam
1566178354Ssam		if (vap->iv_state == IEEE80211_S_SCAN) {
1567178354Ssam			vap->iv_stats.is_rx_mgtdiscard++;
1568178354Ssam			return;
1569178354Ssam		}
1570178354Ssam		if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
1571178354Ssam			/* NB: can happen when in promiscuous mode */
1572178354Ssam			vap->iv_stats.is_rx_mgtdiscard++;
1573178354Ssam			break;
1574178354Ssam		}
1575178354Ssam
1576178354Ssam		/*
1577178354Ssam		 * deauth frame format
1578178354Ssam		 *	[2] reason
1579178354Ssam		 */
1580178354Ssam		IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
1581178354Ssam		reason = le16toh(*(uint16_t *)frm);
1582178354Ssam
1583178354Ssam		vap->iv_stats.is_rx_deauth++;
1584178354Ssam		vap->iv_stats.is_rx_deauth_code = reason;
1585178354Ssam		IEEE80211_NODE_STAT(ni, rx_deauth);
1586178354Ssam
1587178354Ssam		IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
1588178354Ssam		    "recv deauthenticate (reason %d)", reason);
1589178354Ssam		ieee80211_new_state(vap, IEEE80211_S_AUTH,
1590178354Ssam		    (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH);
1591178354Ssam		break;
1592178354Ssam	}
1593178354Ssam
1594178354Ssam	case IEEE80211_FC0_SUBTYPE_DISASSOC: {
1595178354Ssam		uint16_t reason;
1596178354Ssam
1597178354Ssam		if (vap->iv_state != IEEE80211_S_RUN &&
1598178354Ssam		    vap->iv_state != IEEE80211_S_ASSOC &&
1599178354Ssam		    vap->iv_state != IEEE80211_S_AUTH) {
1600178354Ssam			vap->iv_stats.is_rx_mgtdiscard++;
1601178354Ssam			return;
1602178354Ssam		}
1603178354Ssam		if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
1604178354Ssam			/* NB: can happen when in promiscuous mode */
1605178354Ssam			vap->iv_stats.is_rx_mgtdiscard++;
1606178354Ssam			break;
1607178354Ssam		}
1608178354Ssam
1609178354Ssam		/*
1610178354Ssam		 * disassoc frame format
1611178354Ssam		 *	[2] reason
1612178354Ssam		 */
1613178354Ssam		IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
1614178354Ssam		reason = le16toh(*(uint16_t *)frm);
1615178354Ssam
1616178354Ssam		vap->iv_stats.is_rx_disassoc++;
1617178354Ssam		vap->iv_stats.is_rx_disassoc_code = reason;
1618178354Ssam		IEEE80211_NODE_STAT(ni, rx_disassoc);
1619178354Ssam
1620178354Ssam		IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
1621178354Ssam		    "recv disassociate (reason %d)", reason);
1622178354Ssam		ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
1623178354Ssam		break;
1624178354Ssam	}
1625178354Ssam
1626178354Ssam	case IEEE80211_FC0_SUBTYPE_ACTION:
1627178354Ssam		if (vap->iv_state == IEEE80211_S_RUN) {
1628178354Ssam			if (ieee80211_parse_action(ni, m0) == 0)
1629178354Ssam				ic->ic_recv_action(ni, frm, efrm);
1630178354Ssam		} else
1631178354Ssam			vap->iv_stats.is_rx_mgtdiscard++;
1632178354Ssam		break;
1633178354Ssam
1634178354Ssam	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
1635178354Ssam	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
1636178354Ssam	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
1637178354Ssam		vap->iv_stats.is_rx_mgtdiscard++;
1638178354Ssam		return;
1639178354Ssam	default:
1640178354Ssam		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
1641178354Ssam		     wh, "mgt", "subtype 0x%x not handled", subtype);
1642178354Ssam		vap->iv_stats.is_rx_badsubtype++;
1643178354Ssam		break;
1644178354Ssam	}
1645178354Ssam#undef ISREASSOC
1646178354Ssam#undef ISPROBE
1647178354Ssam}
1648