1178354Ssam/*-
2186904Ssam * Copyright (c) 2007-2009 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: stable/11/sys/net80211/ieee80211_adhoc.c 344969 2019-03-09 12:54:10Z avos $");
29178354Ssam#endif
30178354Ssam
31178354Ssam/*
32178354Ssam * IEEE 802.11 IBSS 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>
51257176Sglebius#include <net/if_var.h>
52178354Ssam#include <net/if_media.h>
53178354Ssam#include <net/if_llc.h>
54178354Ssam#include <net/ethernet.h>
55178354Ssam
56178354Ssam#include <net/bpf.h>
57178354Ssam
58178354Ssam#include <net80211/ieee80211_var.h>
59178354Ssam#include <net80211/ieee80211_adhoc.h>
60178354Ssam#include <net80211/ieee80211_input.h>
61190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG
62190391Ssam#include <net80211/ieee80211_superg.h>
63190391Ssam#endif
64186904Ssam#ifdef IEEE80211_SUPPORT_TDMA
65186904Ssam#include <net80211/ieee80211_tdma.h>
66186904Ssam#endif
67244060Sadrian#include <net80211/ieee80211_sta.h>
68178354Ssam
69178354Ssam#define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
70178354Ssam
71178354Ssamstatic	void adhoc_vattach(struct ieee80211vap *);
72178354Ssamstatic	int adhoc_newstate(struct ieee80211vap *, enum ieee80211_state, int);
73283535Sadrianstatic int adhoc_input(struct ieee80211_node *, struct mbuf *,
74283535Sadrian	    const struct ieee80211_rx_stats *, int, int);
75178354Ssamstatic void adhoc_recv_mgmt(struct ieee80211_node *, struct mbuf *,
76283535Sadrian	int subtype, const struct ieee80211_rx_stats *, int, int);
77184276Ssamstatic void ahdemo_recv_mgmt(struct ieee80211_node *, struct mbuf *,
78283535Sadrian	    int subtype, const struct ieee80211_rx_stats *rxs, int, int);
79191546Ssamstatic void adhoc_recv_ctl(struct ieee80211_node *, struct mbuf *, int subtype);
80178354Ssam
81178354Ssamvoid
82178354Ssamieee80211_adhoc_attach(struct ieee80211com *ic)
83178354Ssam{
84178354Ssam	ic->ic_vattach[IEEE80211_M_IBSS] = adhoc_vattach;
85178354Ssam	ic->ic_vattach[IEEE80211_M_AHDEMO] = adhoc_vattach;
86178354Ssam}
87178354Ssam
88178354Ssamvoid
89178354Ssamieee80211_adhoc_detach(struct ieee80211com *ic)
90178354Ssam{
91178354Ssam}
92178354Ssam
93178354Ssamstatic void
94178354Ssamadhoc_vdetach(struct ieee80211vap *vap)
95178354Ssam{
96178354Ssam}
97178354Ssam
98178354Ssamstatic void
99178354Ssamadhoc_vattach(struct ieee80211vap *vap)
100178354Ssam{
101178354Ssam	vap->iv_newstate = adhoc_newstate;
102178354Ssam	vap->iv_input = adhoc_input;
103184276Ssam	if (vap->iv_opmode == IEEE80211_M_IBSS)
104184276Ssam		vap->iv_recv_mgmt = adhoc_recv_mgmt;
105184276Ssam	else
106184276Ssam		vap->iv_recv_mgmt = ahdemo_recv_mgmt;
107191546Ssam	vap->iv_recv_ctl = adhoc_recv_ctl;
108178354Ssam	vap->iv_opdetach = adhoc_vdetach;
109186904Ssam#ifdef IEEE80211_SUPPORT_TDMA
110186904Ssam	/*
111186904Ssam	 * Throw control to tdma support.  Note we do this
112186904Ssam	 * after setting up our callbacks so it can piggyback
113186904Ssam	 * on top of us.
114186904Ssam	 */
115186904Ssam	if (vap->iv_caps & IEEE80211_C_TDMA)
116186904Ssam		ieee80211_tdma_vattach(vap);
117186904Ssam#endif
118178354Ssam}
119178354Ssam
120188466Ssamstatic void
121188466Ssamsta_leave(void *arg, struct ieee80211_node *ni)
122188466Ssam{
123188466Ssam	struct ieee80211vap *vap = arg;
124188466Ssam
125188466Ssam	if (ni->ni_vap == vap && ni != vap->iv_bss)
126188466Ssam		ieee80211_node_leave(ni);
127188466Ssam}
128188466Ssam
129178354Ssam/*
130178354Ssam * IEEE80211_M_IBSS+IEEE80211_M_AHDEMO vap state machine handler.
131178354Ssam */
132178354Ssamstatic int
133178354Ssamadhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
134178354Ssam{
135178354Ssam	struct ieee80211com *ic = vap->iv_ic;
136178354Ssam	struct ieee80211_node *ni;
137178354Ssam	enum ieee80211_state ostate;
138178354Ssam
139178354Ssam	IEEE80211_LOCK_ASSERT(vap->iv_ic);
140178354Ssam
141178354Ssam	ostate = vap->iv_state;
142178354Ssam	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
143178354Ssam	    __func__, ieee80211_state_name[ostate],
144178354Ssam	    ieee80211_state_name[nstate], arg);
145178354Ssam	vap->iv_state = nstate;			/* state transition */
146178354Ssam	if (ostate != IEEE80211_S_SCAN)
147178354Ssam		ieee80211_cancel_scan(vap);	/* background scan */
148178354Ssam	ni = vap->iv_bss;			/* NB: no reference held */
149178354Ssam	switch (nstate) {
150178354Ssam	case IEEE80211_S_INIT:
151178354Ssam		switch (ostate) {
152178354Ssam		case IEEE80211_S_SCAN:
153178354Ssam			ieee80211_cancel_scan(vap);
154178354Ssam			break;
155178354Ssam		default:
156178354Ssam			break;
157178354Ssam		}
158178354Ssam		if (ostate != IEEE80211_S_INIT) {
159178354Ssam			/* NB: optimize INIT -> INIT case */
160178354Ssam			ieee80211_reset_bss(vap);
161178354Ssam		}
162178354Ssam		break;
163178354Ssam	case IEEE80211_S_SCAN:
164178354Ssam		switch (ostate) {
165188466Ssam		case IEEE80211_S_RUN:		/* beacon miss */
166188466Ssam			/* purge station table; entries are stale */
167188466Ssam			ieee80211_iterate_nodes(&ic->ic_sta, sta_leave, vap);
168188466Ssam			/* fall thru... */
169178354Ssam		case IEEE80211_S_INIT:
170178354Ssam			if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
171178354Ssam			    !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
172178354Ssam				/*
173178354Ssam				 * Already have a channel; bypass the
174178354Ssam				 * scan and startup immediately.
175178354Ssam				 */
176244078Sadrian				ieee80211_create_ibss(vap,
177244078Sadrian				    ieee80211_ht_adjust_channel(ic,
178244078Sadrian				    vap->iv_des_chan, vap->iv_flags_ht));
179178354Ssam				break;
180178354Ssam			}
181178354Ssam			/*
182178354Ssam			 * Initiate a scan.  We can come here as a result
183178354Ssam			 * of an IEEE80211_IOC_SCAN_REQ too in which case
184178354Ssam			 * the vap will be marked with IEEE80211_FEXT_SCANREQ
185178354Ssam			 * and the scan request parameters will be present
186178354Ssam			 * in iv_scanreq.  Otherwise we do the default.
187178354Ssam			 */
188178354Ssam			if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
189178354Ssam				ieee80211_check_scan(vap,
190178354Ssam				    vap->iv_scanreq_flags,
191178354Ssam				    vap->iv_scanreq_duration,
192178354Ssam				    vap->iv_scanreq_mindwell,
193178354Ssam				    vap->iv_scanreq_maxdwell,
194178354Ssam				    vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
195178354Ssam				vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
196178354Ssam			} else
197178354Ssam				ieee80211_check_scan_current(vap);
198178354Ssam			break;
199178354Ssam		case IEEE80211_S_SCAN:
200178354Ssam			/*
201178354Ssam			 * This can happen because of a change in state
202178354Ssam			 * that requires a reset.  Trigger a new scan
203178354Ssam			 * unless we're in manual roaming mode in which
204178354Ssam			 * case an application must issue an explicit request.
205178354Ssam			 */
206178354Ssam			if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
207178354Ssam				ieee80211_check_scan_current(vap);
208178354Ssam			break;
209178354Ssam		default:
210178354Ssam			goto invalid;
211178354Ssam		}
212178354Ssam		break;
213178354Ssam	case IEEE80211_S_RUN:
214178354Ssam		if (vap->iv_flags & IEEE80211_F_WPA) {
215178354Ssam			/* XXX validate prerequisites */
216178354Ssam		}
217178354Ssam		switch (ostate) {
218178354Ssam		case IEEE80211_S_SCAN:
219178354Ssam#ifdef IEEE80211_DEBUG
220178354Ssam			if (ieee80211_msg_debug(vap)) {
221178354Ssam				ieee80211_note(vap,
222178354Ssam				    "synchronized with %s ssid ",
223178354Ssam				    ether_sprintf(ni->ni_bssid));
224178354Ssam				ieee80211_print_essid(vap->iv_bss->ni_essid,
225178354Ssam				    ni->ni_esslen);
226178354Ssam				/* XXX MCS/HT */
227178354Ssam				printf(" channel %d start %uMb\n",
228178354Ssam				    ieee80211_chan2ieee(ic, ic->ic_curchan),
229178354Ssam				    IEEE80211_RATE2MBS(ni->ni_txrate));
230178354Ssam			}
231178354Ssam#endif
232178354Ssam			break;
233282405Sadrian		case IEEE80211_S_RUN:	/* IBSS merge */
234282405Sadrian			break;
235178354Ssam		default:
236178354Ssam			goto invalid;
237178354Ssam		}
238178354Ssam		/*
239178354Ssam		 * When 802.1x is not in use mark the port authorized
240178354Ssam		 * at this point so traffic can flow.
241178354Ssam		 */
242178354Ssam		if (ni->ni_authmode != IEEE80211_AUTH_8021X)
243178354Ssam			ieee80211_node_authorize(ni);
244184345Ssam		/*
245184345Ssam		 * Fake association when joining an existing bss.
246184345Ssam		 */
247184345Ssam		if (!IEEE80211_ADDR_EQ(ni->ni_macaddr, vap->iv_myaddr) &&
248184345Ssam		    ic->ic_newassoc != NULL)
249184345Ssam			ic->ic_newassoc(ni, ostate != IEEE80211_S_RUN);
250178354Ssam		break;
251178354Ssam	case IEEE80211_S_SLEEP:
252241138Sadrian		vap->iv_sta_ps(vap, 0);
253178354Ssam		break;
254178354Ssam	default:
255178354Ssam	invalid:
256184268Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
257184268Ssam		    "%s: unexpected state transition %s -> %s\n", __func__,
258178354Ssam		    ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
259178354Ssam		break;
260178354Ssam	}
261178354Ssam	return 0;
262178354Ssam}
263178354Ssam
264178354Ssam/*
265178354Ssam * Decide if a received management frame should be
266178354Ssam * printed when debugging is enabled.  This filters some
267178354Ssam * of the less interesting frames that come frequently
268178354Ssam * (e.g. beacons).
269178354Ssam */
270178354Ssamstatic __inline int
271178354Ssamdoprint(struct ieee80211vap *vap, int subtype)
272178354Ssam{
273178354Ssam	switch (subtype) {
274178354Ssam	case IEEE80211_FC0_SUBTYPE_BEACON:
275178354Ssam		return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
276178354Ssam	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
277178354Ssam		return 1;
278178354Ssam	}
279178354Ssam	return 1;
280178354Ssam}
281178354Ssam
282178354Ssam/*
283178354Ssam * Process a received frame.  The node associated with the sender
284178354Ssam * should be supplied.  If nothing was found in the node table then
285178354Ssam * the caller is assumed to supply a reference to iv_bss instead.
286178354Ssam * The RSSI and a timestamp are also supplied.  The RSSI data is used
287178354Ssam * during AP scanning to select a AP to associate with; it can have
288178354Ssam * any units so long as values have consistent units and higher values
289178354Ssam * mean ``better signal''.  The receive timestamp is currently not used
290178354Ssam * by the 802.11 layer.
291178354Ssam */
292178354Ssamstatic int
293283535Sadrianadhoc_input(struct ieee80211_node *ni, struct mbuf *m,
294283535Sadrian    const struct ieee80211_rx_stats *rxs, int rssi, int nf)
295178354Ssam{
296178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
297178354Ssam	struct ieee80211com *ic = ni->ni_ic;
298178354Ssam	struct ifnet *ifp = vap->iv_ifp;
299178354Ssam	struct ieee80211_frame *wh;
300178354Ssam	struct ieee80211_key *key;
301178354Ssam	struct ether_header *eh;
302203422Srpaulo	int hdrspace, need_tap = 1;	/* mbuf need to be tapped. */
303178354Ssam	uint8_t dir, type, subtype, qos;
304178354Ssam	uint8_t *bssid;
305178354Ssam
306183247Ssam	if (m->m_flags & M_AMPDU_MPDU) {
307178354Ssam		/*
308178354Ssam		 * Fastpath for A-MPDU reorder q resubmission.  Frames
309183247Ssam		 * w/ M_AMPDU_MPDU marked have already passed through
310183247Ssam		 * here but were received out of order and been held on
311183247Ssam		 * the reorder queue.  When resubmitted they are marked
312183247Ssam		 * with the M_AMPDU_MPDU flag and we can bypass most of
313183247Ssam		 * the normal processing.
314178354Ssam		 */
315178354Ssam		wh = mtod(m, struct ieee80211_frame *);
316178354Ssam		type = IEEE80211_FC0_TYPE_DATA;
317178354Ssam		dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
318178354Ssam		subtype = IEEE80211_FC0_SUBTYPE_QOS;
319178354Ssam		hdrspace = ieee80211_hdrspace(ic, wh);	/* XXX optimize? */
320178354Ssam		goto resubmit_ampdu;
321178354Ssam	}
322178354Ssam
323178354Ssam	KASSERT(ni != NULL, ("null node"));
324178354Ssam	ni->ni_inact = ni->ni_inact_reload;
325178354Ssam
326178354Ssam	type = -1;			/* undefined */
327178354Ssam
328178354Ssam	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
329178354Ssam		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
330178354Ssam		    ni->ni_macaddr, NULL,
331178354Ssam		    "too short (1): len %u", m->m_pkthdr.len);
332178354Ssam		vap->iv_stats.is_rx_tooshort++;
333178354Ssam		goto out;
334178354Ssam	}
335178354Ssam	/*
336178354Ssam	 * Bit of a cheat here, we use a pointer for a 3-address
337178354Ssam	 * frame format but don't reference fields past outside
338178354Ssam	 * ieee80211_frame_min w/o first validating the data is
339178354Ssam	 * present.
340178354Ssam	 */
341178354Ssam	wh = mtod(m, struct ieee80211_frame *);
342178354Ssam
343178354Ssam	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
344178354Ssam	    IEEE80211_FC0_VERSION_0) {
345178354Ssam		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
346191547Ssam		    ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x",
347191547Ssam		    wh->i_fc[0], wh->i_fc[1]);
348178354Ssam		vap->iv_stats.is_rx_badversion++;
349178354Ssam		goto err;
350178354Ssam	}
351178354Ssam
352178354Ssam	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
353178354Ssam	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
354178354Ssam	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
355178354Ssam	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
356178354Ssam		if (dir != IEEE80211_FC1_DIR_NODS)
357178354Ssam			bssid = wh->i_addr1;
358178354Ssam		else if (type == IEEE80211_FC0_TYPE_CTL)
359178354Ssam			bssid = wh->i_addr1;
360178354Ssam		else {
361178354Ssam			if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
362178354Ssam				IEEE80211_DISCARD_MAC(vap,
363178354Ssam				    IEEE80211_MSG_ANY, ni->ni_macaddr,
364178354Ssam				    NULL, "too short (2): len %u",
365178354Ssam				    m->m_pkthdr.len);
366178354Ssam				vap->iv_stats.is_rx_tooshort++;
367178354Ssam				goto out;
368178354Ssam			}
369178354Ssam			bssid = wh->i_addr3;
370178354Ssam		}
371178354Ssam		/*
372178354Ssam		 * Validate the bssid.
373178354Ssam		 */
374302202Sadrian		if (!(type == IEEE80211_FC0_TYPE_MGT &&
375302202Sadrian		     (subtype == IEEE80211_FC0_SUBTYPE_BEACON ||
376302202Sadrian		      subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) &&
377302202Sadrian		    !IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) &&
378178354Ssam		    !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
379178354Ssam			/* not interested in */
380178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
381178354Ssam			    bssid, NULL, "%s", "not to bss");
382178354Ssam			vap->iv_stats.is_rx_wrongbss++;
383178354Ssam			goto out;
384178354Ssam		}
385178354Ssam		/*
386178354Ssam		 * Data frame, cons up a node when it doesn't
387178354Ssam		 * exist. This should probably done after an ACL check.
388178354Ssam		 */
389178354Ssam		if (type == IEEE80211_FC0_TYPE_DATA &&
390178354Ssam		    ni == vap->iv_bss &&
391178354Ssam		    !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
392178354Ssam			/*
393186904Ssam			 * Beware of frames that come in too early; we
394186904Ssam			 * can receive broadcast frames and creating sta
395186904Ssam			 * entries will blow up because there is no bss
396186904Ssam			 * channel yet.
397186904Ssam			 */
398186904Ssam			if (vap->iv_state != IEEE80211_S_RUN) {
399186904Ssam				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
400186904Ssam				    wh, "data", "not in RUN state (%s)",
401186904Ssam				    ieee80211_state_name[vap->iv_state]);
402186904Ssam				vap->iv_stats.is_rx_badstate++;
403186904Ssam				goto err;
404186904Ssam			}
405186904Ssam			/*
406178354Ssam			 * Fake up a node for this newly
407178354Ssam			 * discovered member of the IBSS.
408178354Ssam			 */
409178354Ssam			ni = ieee80211_fakeup_adhoc_node(vap, wh->i_addr2);
410178354Ssam			if (ni == NULL) {
411178354Ssam				/* NB: stat kept for alloc failure */
412178354Ssam				goto err;
413178354Ssam			}
414178354Ssam		}
415178354Ssam		IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
416192468Ssam		ni->ni_noise = nf;
417282820Sadrian		if (IEEE80211_HAS_SEQ(type, subtype) &&
418282820Sadrian		    IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
419178354Ssam			uint8_t tid = ieee80211_gettid(wh);
420178354Ssam			if (IEEE80211_QOS_HAS_SEQ(wh) &&
421178354Ssam			    TID_TO_WME_AC(tid) >= WME_AC_VI)
422178354Ssam				ic->ic_wme.wme_hipri_traffic++;
423296254Savos			if (! ieee80211_check_rxseq(ni, wh, bssid))
424178354Ssam				goto out;
425178354Ssam		}
426178354Ssam	}
427178354Ssam
428178354Ssam	switch (type) {
429178354Ssam	case IEEE80211_FC0_TYPE_DATA:
430178354Ssam		hdrspace = ieee80211_hdrspace(ic, wh);
431178354Ssam		if (m->m_len < hdrspace &&
432178354Ssam		    (m = m_pullup(m, hdrspace)) == NULL) {
433178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
434178354Ssam			    ni->ni_macaddr, NULL,
435178354Ssam			    "data too short: expecting %u", hdrspace);
436178354Ssam			vap->iv_stats.is_rx_tooshort++;
437178354Ssam			goto out;		/* XXX */
438178354Ssam		}
439178354Ssam		if (dir != IEEE80211_FC1_DIR_NODS) {
440178354Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
441178354Ssam			    wh, "data", "incorrect dir 0x%x", dir);
442178354Ssam			vap->iv_stats.is_rx_wrongdir++;
443178354Ssam			goto out;
444178354Ssam		}
445178354Ssam		/* XXX no power-save support */
446178354Ssam
447178354Ssam		/*
448183247Ssam		 * Handle A-MPDU re-ordering.  If the frame is to be
449183247Ssam		 * processed directly then ieee80211_ampdu_reorder
450178354Ssam		 * will return 0; otherwise it has consumed the mbuf
451178354Ssam		 * and we should do nothing more with it.
452178354Ssam		 */
453183247Ssam		if ((m->m_flags & M_AMPDU) &&
454178354Ssam		    ieee80211_ampdu_reorder(ni, m) != 0) {
455178354Ssam			m = NULL;
456178354Ssam			goto out;
457178354Ssam		}
458178354Ssam	resubmit_ampdu:
459178354Ssam
460178354Ssam		/*
461178354Ssam		 * Handle privacy requirements.  Note that we
462178354Ssam		 * must not be preempted from here until after
463178354Ssam		 * we (potentially) call ieee80211_crypto_demic;
464178354Ssam		 * otherwise we may violate assumptions in the
465178354Ssam		 * crypto cipher modules used to do delayed update
466178354Ssam		 * of replay sequence numbers.
467178354Ssam		 */
468260444Skevlo		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
469178354Ssam			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
470178354Ssam				/*
471178354Ssam				 * Discard encrypted frames when privacy is off.
472178354Ssam				 */
473178354Ssam				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
474178354Ssam				    wh, "WEP", "%s", "PRIVACY off");
475178354Ssam				vap->iv_stats.is_rx_noprivacy++;
476178354Ssam				IEEE80211_NODE_STAT(ni, rx_noprivacy);
477178354Ssam				goto out;
478178354Ssam			}
479178354Ssam			key = ieee80211_crypto_decap(ni, m, hdrspace);
480178354Ssam			if (key == NULL) {
481178354Ssam				/* NB: stats+msgs handled in crypto_decap */
482178354Ssam				IEEE80211_NODE_STAT(ni, rx_wepfail);
483178354Ssam				goto out;
484178354Ssam			}
485178354Ssam			wh = mtod(m, struct ieee80211_frame *);
486260444Skevlo			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
487178354Ssam		} else {
488178354Ssam			/* XXX M_WEP and IEEE80211_F_PRIVACY */
489178354Ssam			key = NULL;
490178354Ssam		}
491178354Ssam
492178354Ssam		/*
493178354Ssam		 * Save QoS bits for use below--before we strip the header.
494178354Ssam		 */
495344969Savos		if (subtype == IEEE80211_FC0_SUBTYPE_QOS)
496344969Savos			qos = ieee80211_getqos(wh)[0];
497344969Savos		else
498178354Ssam			qos = 0;
499178354Ssam
500178354Ssam		/*
501178354Ssam		 * Next up, any fragmentation.
502178354Ssam		 */
503178354Ssam		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
504178354Ssam			m = ieee80211_defrag(ni, m, hdrspace);
505178354Ssam			if (m == NULL) {
506178354Ssam				/* Fragment dropped or frame not complete yet */
507178354Ssam				goto out;
508178354Ssam			}
509178354Ssam		}
510178354Ssam		wh = NULL;		/* no longer valid, catch any uses */
511178354Ssam
512178354Ssam		/*
513178354Ssam		 * Next strip any MSDU crypto bits.
514178354Ssam		 */
515178354Ssam		if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
516178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
517178354Ssam			    ni->ni_macaddr, "data", "%s", "demic error");
518178354Ssam			vap->iv_stats.is_rx_demicfail++;
519178354Ssam			IEEE80211_NODE_STAT(ni, rx_demicfail);
520178354Ssam			goto out;
521178354Ssam		}
522178354Ssam
523178354Ssam		/* copy to listener after decrypt */
524192468Ssam		if (ieee80211_radiotap_active_vap(vap))
525192468Ssam			ieee80211_radiotap_rx(vap, m);
526178354Ssam		need_tap = 0;
527178354Ssam
528178354Ssam		/*
529178354Ssam		 * Finally, strip the 802.11 header.
530178354Ssam		 */
531178354Ssam		m = ieee80211_decap(vap, m, hdrspace);
532178354Ssam		if (m == NULL) {
533178354Ssam			/* XXX mask bit to check for both */
534178354Ssam			/* don't count Null data frames as errors */
535178354Ssam			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
536178354Ssam			    subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
537178354Ssam				goto out;
538178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
539178354Ssam			    ni->ni_macaddr, "data", "%s", "decap error");
540178354Ssam			vap->iv_stats.is_rx_decap++;
541178354Ssam			IEEE80211_NODE_STAT(ni, rx_decap);
542178354Ssam			goto err;
543178354Ssam		}
544178354Ssam		eh = mtod(m, struct ether_header *);
545178354Ssam		if (!ieee80211_node_is_authorized(ni)) {
546178354Ssam			/*
547178354Ssam			 * Deny any non-PAE frames received prior to
548178354Ssam			 * authorization.  For open/shared-key
549178354Ssam			 * authentication the port is mark authorized
550178354Ssam			 * after authentication completes.  For 802.1x
551178354Ssam			 * the port is not marked authorized by the
552178354Ssam			 * authenticator until the handshake has completed.
553178354Ssam			 */
554178354Ssam			if (eh->ether_type != htons(ETHERTYPE_PAE)) {
555178354Ssam				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
556178354Ssam				    eh->ether_shost, "data",
557178354Ssam				    "unauthorized port: ether type 0x%x len %u",
558178354Ssam				    eh->ether_type, m->m_pkthdr.len);
559178354Ssam				vap->iv_stats.is_rx_unauth++;
560178354Ssam				IEEE80211_NODE_STAT(ni, rx_unauth);
561178354Ssam				goto err;
562178354Ssam			}
563178354Ssam		} else {
564178354Ssam			/*
565178354Ssam			 * When denying unencrypted frames, discard
566178354Ssam			 * any non-PAE frames received without encryption.
567178354Ssam			 */
568178354Ssam			if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
569178354Ssam			    (key == NULL && (m->m_flags & M_WEP) == 0) &&
570178354Ssam			    eh->ether_type != htons(ETHERTYPE_PAE)) {
571178354Ssam				/*
572178354Ssam				 * Drop unencrypted frames.
573178354Ssam				 */
574178354Ssam				vap->iv_stats.is_rx_unencrypted++;
575178354Ssam				IEEE80211_NODE_STAT(ni, rx_unencrypted);
576178354Ssam				goto out;
577178354Ssam			}
578178354Ssam		}
579178354Ssam		/* XXX require HT? */
580178354Ssam		if (qos & IEEE80211_QOS_AMSDU) {
581178354Ssam			m = ieee80211_decap_amsdu(ni, m);
582178354Ssam			if (m == NULL)
583178354Ssam				return IEEE80211_FC0_TYPE_DATA;
584190391Ssam		} else {
585190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG
586190391Ssam			m = ieee80211_decap_fastframe(vap, ni, m);
587190391Ssam			if (m == NULL)
588178354Ssam				return IEEE80211_FC0_TYPE_DATA;
589190391Ssam#endif
590178354Ssam		}
591178354Ssam		if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL)
592178354Ssam			ieee80211_deliver_data(ni->ni_wdsvap, ni, m);
593178354Ssam		else
594178354Ssam			ieee80211_deliver_data(vap, ni, m);
595178354Ssam		return IEEE80211_FC0_TYPE_DATA;
596178354Ssam
597178354Ssam	case IEEE80211_FC0_TYPE_MGT:
598178354Ssam		vap->iv_stats.is_rx_mgmt++;
599178354Ssam		IEEE80211_NODE_STAT(ni, rx_mgmt);
600178354Ssam		if (dir != IEEE80211_FC1_DIR_NODS) {
601178354Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
602178354Ssam			    wh, "data", "incorrect dir 0x%x", dir);
603178354Ssam			vap->iv_stats.is_rx_wrongdir++;
604178354Ssam			goto err;
605178354Ssam		}
606178354Ssam		if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
607178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
608178354Ssam			    ni->ni_macaddr, "mgt", "too short: len %u",
609178354Ssam			    m->m_pkthdr.len);
610178354Ssam			vap->iv_stats.is_rx_tooshort++;
611178354Ssam			goto out;
612178354Ssam		}
613178354Ssam#ifdef IEEE80211_DEBUG
614178354Ssam		if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
615178354Ssam		    ieee80211_msg_dumppkts(vap)) {
616178354Ssam			if_printf(ifp, "received %s from %s rssi %d\n",
617298376Savos			    ieee80211_mgt_subtype_name(subtype),
618178354Ssam			    ether_sprintf(wh->i_addr2), rssi);
619178354Ssam		}
620178354Ssam#endif
621260444Skevlo		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
622178354Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
623178354Ssam			    wh, NULL, "%s", "WEP set but not permitted");
624178354Ssam			vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
625178354Ssam			goto out;
626178354Ssam		}
627283535Sadrian		vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
628191534Ssam		goto out;
629178354Ssam
630178354Ssam	case IEEE80211_FC0_TYPE_CTL:
631178354Ssam		vap->iv_stats.is_rx_ctl++;
632178354Ssam		IEEE80211_NODE_STAT(ni, rx_ctrl);
633191546Ssam		vap->iv_recv_ctl(ni, m, subtype);
634178354Ssam		goto out;
635191754Ssam
636178354Ssam	default:
637178354Ssam		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
638178354Ssam		    wh, "bad", "frame type 0x%x", type);
639178354Ssam		/* should not come here */
640178354Ssam		break;
641178354Ssam	}
642178354Ssamerr:
643271861Sglebius	if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
644178354Ssamout:
645178354Ssam	if (m != NULL) {
646192765Ssam		if (need_tap && ieee80211_radiotap_active_vap(vap))
647192468Ssam			ieee80211_radiotap_rx(vap, m);
648178354Ssam		m_freem(m);
649178354Ssam	}
650178354Ssam	return type;
651178354Ssam}
652178354Ssam
653178354Ssamstatic int
654178354Ssamis11bclient(const uint8_t *rates, const uint8_t *xrates)
655178354Ssam{
656178354Ssam	static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11);
657178354Ssam	int i;
658178354Ssam
659178354Ssam	/* NB: the 11b clients we care about will not have xrates */
660178354Ssam	if (xrates != NULL || rates == NULL)
661178354Ssam		return 0;
662178354Ssam	for (i = 0; i < rates[1]; i++) {
663178354Ssam		int r = rates[2+i] & IEEE80211_RATE_VAL;
664178354Ssam		if (r > 2*11 || ((1<<r) & brates) == 0)
665178354Ssam			return 0;
666178354Ssam	}
667178354Ssam	return 1;
668178354Ssam}
669178354Ssam
670178354Ssamstatic void
671178354Ssamadhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
672283535Sadrian	int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
673178354Ssam{
674178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
675178354Ssam	struct ieee80211com *ic = ni->ni_ic;
676283535Sadrian	struct ieee80211_channel *rxchan = ic->ic_curchan;
677178354Ssam	struct ieee80211_frame *wh;
678299575Savos	uint8_t *frm, *efrm;
679178354Ssam	uint8_t *ssid, *rates, *xrates;
680246930Sadrian#if 0
681245928Sadrian	int ht_state_change = 0;
682246930Sadrian#endif
683178354Ssam
684178354Ssam	wh = mtod(m0, struct ieee80211_frame *);
685178354Ssam	frm = (uint8_t *)&wh[1];
686178354Ssam	efrm = mtod(m0, uint8_t *) + m0->m_len;
687178354Ssam	switch (subtype) {
688178354Ssam	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
689178354Ssam	case IEEE80211_FC0_SUBTYPE_BEACON: {
690178354Ssam		struct ieee80211_scanparams scan;
691283535Sadrian		struct ieee80211_channel *c;
692178354Ssam		/*
693178354Ssam		 * We process beacon/probe response
694178354Ssam		 * frames to discover neighbors.
695178354Ssam		 */
696283535Sadrian		if (rxs != NULL) {
697283535Sadrian			c = ieee80211_lookup_channel_rxstatus(vap, rxs);
698283535Sadrian			if (c != NULL)
699283535Sadrian				rxchan = c;
700283535Sadrian		}
701283535Sadrian		if (ieee80211_parse_beacon(ni, m0, rxchan, &scan) != 0)
702178354Ssam			return;
703178354Ssam		/*
704178354Ssam		 * Count frame now that we know it's to be processed.
705178354Ssam		 */
706178354Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
707178354Ssam			vap->iv_stats.is_rx_beacon++;		/* XXX remove */
708178354Ssam			IEEE80211_NODE_STAT(ni, rx_beacons);
709178354Ssam		} else
710178354Ssam			IEEE80211_NODE_STAT(ni, rx_proberesp);
711178354Ssam		/*
712178354Ssam		 * If scanning, just pass information to the scan module.
713178354Ssam		 */
714178354Ssam		if (ic->ic_flags & IEEE80211_F_SCAN) {
715178354Ssam			if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
716178354Ssam				/*
717178354Ssam				 * Actively scanning a channel marked passive;
718178354Ssam				 * send a probe request now that we know there
719178354Ssam				 * is 802.11 traffic present.
720178354Ssam				 *
721178354Ssam				 * XXX check if the beacon we recv'd gives
722178354Ssam				 * us what we need and suppress the probe req
723178354Ssam				 */
724178354Ssam				ieee80211_probe_curchan(vap, 1);
725178354Ssam				ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
726178354Ssam			}
727283535Sadrian			ieee80211_add_scan(vap, rxchan, &scan, wh,
728282742Sadrian			    subtype, rssi, nf);
729178354Ssam			return;
730178354Ssam		}
731178354Ssam		if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
732178354Ssam			if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
733178354Ssam				/*
734178354Ssam				 * Create a new entry in the neighbor table.
735330458Seadler				 *
736330458Seadler				 * XXX TODO:
737330458Seadler				 *
738330458Seadler				 * Here we're not scanning; so if we have an
739330458Seadler				 * SSID then make sure it matches our SSID.
740330458Seadler				 * Otherwise this code will match on all IBSS
741330458Seadler				 * beacons/probe requests for all SSIDs,
742330458Seadler				 * filling the node table with nodes that
743330458Seadler				 * aren't ours.
744178354Ssam				 */
745330458Seadler				if (ieee80211_ibss_node_check_new(ni, &scan))
746330458Seadler					ni = ieee80211_add_neighbor(vap, wh, &scan);
747330458Seadler				else
748330458Seadler					ni = NULL;
749178354Ssam			} else if (ni->ni_capinfo == 0) {
750178354Ssam				/*
751178354Ssam				 * Update faked node created on transmit.
752178354Ssam				 * Note this also updates the tsf.
753178354Ssam				 */
754178354Ssam				ieee80211_init_neighbor(ni, wh, &scan);
755178354Ssam			} else {
756178354Ssam				/*
757178354Ssam				 * Record tsf for potential resync.
758178354Ssam				 */
759178354Ssam				memcpy(ni->ni_tstamp.data, scan.tstamp,
760178354Ssam					sizeof(ni->ni_tstamp));
761178354Ssam			}
762245928Sadrian			/*
763245928Sadrian			 * This isn't enabled yet - otherwise it would
764245928Sadrian			 * update the HT parameters and channel width
765245928Sadrian			 * from any node, which could lead to lots of
766245928Sadrian			 * strange behaviour if the 11n nodes aren't
767245928Sadrian			 * exactly configured to match.
768245928Sadrian			 */
769245928Sadrian#if 0
770245928Sadrian			if (scan.htcap != NULL && scan.htinfo != NULL &&
771245928Sadrian			    (vap->iv_flags_ht & IEEE80211_FHT_HT)) {
772245928Sadrian				if (ieee80211_ht_updateparams(ni,
773245928Sadrian				    scan.htcap, scan.htinfo))
774245928Sadrian					ht_state_change = 1;
775245928Sadrian			}
776245928Sadrian#endif
777178354Ssam			if (ni != NULL) {
778178354Ssam				IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
779192468Ssam				ni->ni_noise = nf;
780178354Ssam			}
781246927Sadrian			/*
782246927Sadrian			 * Same here - the channel width change should
783246927Sadrian			 * be applied to the specific peer node, not
784246927Sadrian			 * to the ic.  Ie, the interface configuration
785246927Sadrian			 * should stay in its current channel width;
786246927Sadrian			 * but it should change the rate control and
787246927Sadrian			 * any queued frames for the given node only.
788246927Sadrian			 *
789246927Sadrian			 * Since there's no (current) way to inform
790246927Sadrian			 * the driver that a channel width change has
791298995Spfg			 * occurred for a single node, just stub this
792246927Sadrian			 * out.
793246927Sadrian			 */
794246927Sadrian#if 0
795245928Sadrian			if (ht_state_change)
796245928Sadrian				ieee80211_update_chw(ic);
797246927Sadrian#endif
798178354Ssam		}
799178354Ssam		break;
800178354Ssam	}
801178354Ssam
802178354Ssam	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
803178354Ssam		if (vap->iv_state != IEEE80211_S_RUN) {
804184268Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
805184268Ssam			    wh, NULL, "wrong state %s",
806184268Ssam			    ieee80211_state_name[vap->iv_state]);
807178354Ssam			vap->iv_stats.is_rx_mgtdiscard++;
808178354Ssam			return;
809178354Ssam		}
810178354Ssam		if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
811178354Ssam			/* frame must be directed */
812184268Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
813184268Ssam			    wh, NULL, "%s", "not unicast");
814178354Ssam			vap->iv_stats.is_rx_mgtdiscard++;	/* XXX stat */
815178354Ssam			return;
816178354Ssam		}
817178354Ssam
818178354Ssam		/*
819178354Ssam		 * prreq frame format
820178354Ssam		 *	[tlv] ssid
821178354Ssam		 *	[tlv] supported rates
822178354Ssam		 *	[tlv] extended supported rates
823178354Ssam		 */
824178354Ssam		ssid = rates = xrates = NULL;
825178354Ssam		while (efrm - frm > 1) {
826178354Ssam			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
827178354Ssam			switch (*frm) {
828178354Ssam			case IEEE80211_ELEMID_SSID:
829178354Ssam				ssid = frm;
830178354Ssam				break;
831178354Ssam			case IEEE80211_ELEMID_RATES:
832178354Ssam				rates = frm;
833178354Ssam				break;
834178354Ssam			case IEEE80211_ELEMID_XRATES:
835178354Ssam				xrates = frm;
836178354Ssam				break;
837178354Ssam			}
838178354Ssam			frm += frm[1] + 2;
839178354Ssam		}
840178354Ssam		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
841178354Ssam		if (xrates != NULL)
842178354Ssam			IEEE80211_VERIFY_ELEMENT(xrates,
843178354Ssam				IEEE80211_RATE_MAXSIZE - rates[1], return);
844178354Ssam		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
845178354Ssam		IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return);
846178354Ssam		if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
847178354Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
848178354Ssam			    wh, NULL,
849178354Ssam			    "%s", "no ssid with ssid suppression enabled");
850178354Ssam			vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/
851178354Ssam			return;
852178354Ssam		}
853178354Ssam
854178354Ssam		/* XXX find a better class or define it's own */
855178354Ssam		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
856178354Ssam		    "%s", "recv probe req");
857178354Ssam		/*
858178354Ssam		 * Some legacy 11b clients cannot hack a complete
859178354Ssam		 * probe response frame.  When the request includes
860178354Ssam		 * only a bare-bones rate set, communicate this to
861178354Ssam		 * the transmit side.
862178354Ssam		 */
863178354Ssam		ieee80211_send_proberesp(vap, wh->i_addr2,
864178354Ssam		    is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0);
865178354Ssam		break;
866178354Ssam
867218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_ACTION:
868218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_ACTION_NOACK:
869298757Sadrian		if ((ni == vap->iv_bss) &&
870298757Sadrian		    !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
871184268Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
872218958Sbschmidt			    wh, NULL, "%s", "unknown node");
873218958Sbschmidt			vap->iv_stats.is_rx_mgtdiscard++;
874218958Sbschmidt		} else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) &&
875218958Sbschmidt		    !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
876218958Sbschmidt			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
877218958Sbschmidt			    wh, NULL, "%s", "not for us");
878218958Sbschmidt			vap->iv_stats.is_rx_mgtdiscard++;
879218958Sbschmidt		} else if (vap->iv_state != IEEE80211_S_RUN) {
880218958Sbschmidt			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
881184268Ssam			    wh, NULL, "wrong state %s",
882184268Ssam			    ieee80211_state_name[vap->iv_state]);
883178354Ssam			vap->iv_stats.is_rx_mgtdiscard++;
884218958Sbschmidt		} else {
885218958Sbschmidt			if (ieee80211_parse_action(ni, m0) == 0)
886218958Sbschmidt				(void)ic->ic_recv_action(ni, wh, frm, efrm);
887178354Ssam		}
888178354Ssam		break;
889178354Ssam
890178354Ssam	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
891218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
892178354Ssam	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
893178354Ssam	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
894295795Savos	case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
895218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_ATIM:
896218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_DISASSOC:
897218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_AUTH:
898178354Ssam	case IEEE80211_FC0_SUBTYPE_DEAUTH:
899184268Ssam		IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
900218927Sbschmidt		    wh, NULL, "%s", "not handled");
901178354Ssam		vap->iv_stats.is_rx_mgtdiscard++;
902218927Sbschmidt		break;
903178354Ssam
904178354Ssam	default:
905178354Ssam		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
906218927Sbschmidt		    wh, "mgt", "subtype 0x%x not handled", subtype);
907178354Ssam		vap->iv_stats.is_rx_badsubtype++;
908178354Ssam		break;
909178354Ssam	}
910178354Ssam}
911178354Ssam#undef IEEE80211_VERIFY_LENGTH
912178354Ssam#undef IEEE80211_VERIFY_ELEMENT
913184276Ssam
914184276Ssamstatic void
915184276Ssamahdemo_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
916283535Sadrian	int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
917184276Ssam{
918184276Ssam	struct ieee80211vap *vap = ni->ni_vap;
919184276Ssam	struct ieee80211com *ic = ni->ni_ic;
920218927Sbschmidt	struct ieee80211_frame *wh;
921184276Ssam
922184276Ssam	/*
923184276Ssam	 * Process management frames when scanning; useful for doing
924184276Ssam	 * a site-survey.
925184276Ssam	 */
926184276Ssam	if (ic->ic_flags & IEEE80211_F_SCAN)
927283535Sadrian		adhoc_recv_mgmt(ni, m0, subtype, rxs, rssi, nf);
928218927Sbschmidt	else {
929218927Sbschmidt		wh = mtod(m0, struct ieee80211_frame *);
930218927Sbschmidt		switch (subtype) {
931218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
932218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
933218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
934218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
935218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
936218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
937295795Savos		case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
938218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_BEACON:
939218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_ATIM:
940218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_DISASSOC:
941218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_AUTH:
942218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_DEAUTH:
943218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_ACTION:
944218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_ACTION_NOACK:
945218927Sbschmidt			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
946218927Sbschmidt			     wh, NULL, "%s", "not handled");
947218927Sbschmidt			vap->iv_stats.is_rx_mgtdiscard++;
948218927Sbschmidt			break;
949218927Sbschmidt		default:
950218927Sbschmidt			IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
951218927Sbschmidt			     wh, "mgt", "subtype 0x%x not handled", subtype);
952218927Sbschmidt			vap->iv_stats.is_rx_badsubtype++;
953218927Sbschmidt			break;
954218927Sbschmidt		}
955218927Sbschmidt	}
956184276Ssam}
957191546Ssam
958191546Ssamstatic void
959205277Srpauloadhoc_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype)
960191546Ssam{
961205277Srpaulo
962205277Srpaulo	switch (subtype) {
963205277Srpaulo	case IEEE80211_FC0_SUBTYPE_BAR:
964205277Srpaulo		ieee80211_recv_bar(ni, m);
965205277Srpaulo		break;
966205277Srpaulo	}
967191546Ssam}
968