ieee80211_adhoc.c revision 298376
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: head/sys/net80211/ieee80211_adhoc.c 298376 2016-04-20 21:15:55Z 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		 */
374297727Sadrian		if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) &&
375178354Ssam		    !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
376178354Ssam			/* not interested in */
377178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
378178354Ssam			    bssid, NULL, "%s", "not to bss");
379178354Ssam			vap->iv_stats.is_rx_wrongbss++;
380178354Ssam			goto out;
381178354Ssam		}
382178354Ssam		/*
383178354Ssam		 * Data frame, cons up a node when it doesn't
384178354Ssam		 * exist. This should probably done after an ACL check.
385178354Ssam		 */
386178354Ssam		if (type == IEEE80211_FC0_TYPE_DATA &&
387178354Ssam		    ni == vap->iv_bss &&
388178354Ssam		    !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
389178354Ssam			/*
390186904Ssam			 * Beware of frames that come in too early; we
391186904Ssam			 * can receive broadcast frames and creating sta
392186904Ssam			 * entries will blow up because there is no bss
393186904Ssam			 * channel yet.
394186904Ssam			 */
395186904Ssam			if (vap->iv_state != IEEE80211_S_RUN) {
396186904Ssam				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
397186904Ssam				    wh, "data", "not in RUN state (%s)",
398186904Ssam				    ieee80211_state_name[vap->iv_state]);
399186904Ssam				vap->iv_stats.is_rx_badstate++;
400186904Ssam				goto err;
401186904Ssam			}
402186904Ssam			/*
403178354Ssam			 * Fake up a node for this newly
404178354Ssam			 * discovered member of the IBSS.
405178354Ssam			 */
406178354Ssam			ni = ieee80211_fakeup_adhoc_node(vap, wh->i_addr2);
407178354Ssam			if (ni == NULL) {
408178354Ssam				/* NB: stat kept for alloc failure */
409178354Ssam				goto err;
410178354Ssam			}
411178354Ssam		}
412178354Ssam		IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
413192468Ssam		ni->ni_noise = nf;
414282820Sadrian		if (IEEE80211_HAS_SEQ(type, subtype) &&
415282820Sadrian		    IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
416178354Ssam			uint8_t tid = ieee80211_gettid(wh);
417178354Ssam			if (IEEE80211_QOS_HAS_SEQ(wh) &&
418178354Ssam			    TID_TO_WME_AC(tid) >= WME_AC_VI)
419178354Ssam				ic->ic_wme.wme_hipri_traffic++;
420296254Savos			if (! ieee80211_check_rxseq(ni, wh, bssid))
421178354Ssam				goto out;
422178354Ssam		}
423178354Ssam	}
424178354Ssam
425178354Ssam	switch (type) {
426178354Ssam	case IEEE80211_FC0_TYPE_DATA:
427178354Ssam		hdrspace = ieee80211_hdrspace(ic, wh);
428178354Ssam		if (m->m_len < hdrspace &&
429178354Ssam		    (m = m_pullup(m, hdrspace)) == NULL) {
430178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
431178354Ssam			    ni->ni_macaddr, NULL,
432178354Ssam			    "data too short: expecting %u", hdrspace);
433178354Ssam			vap->iv_stats.is_rx_tooshort++;
434178354Ssam			goto out;		/* XXX */
435178354Ssam		}
436178354Ssam		if (dir != IEEE80211_FC1_DIR_NODS) {
437178354Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
438178354Ssam			    wh, "data", "incorrect dir 0x%x", dir);
439178354Ssam			vap->iv_stats.is_rx_wrongdir++;
440178354Ssam			goto out;
441178354Ssam		}
442178354Ssam		/* XXX no power-save support */
443178354Ssam
444178354Ssam		/*
445183247Ssam		 * Handle A-MPDU re-ordering.  If the frame is to be
446183247Ssam		 * processed directly then ieee80211_ampdu_reorder
447178354Ssam		 * will return 0; otherwise it has consumed the mbuf
448178354Ssam		 * and we should do nothing more with it.
449178354Ssam		 */
450183247Ssam		if ((m->m_flags & M_AMPDU) &&
451178354Ssam		    ieee80211_ampdu_reorder(ni, m) != 0) {
452178354Ssam			m = NULL;
453178354Ssam			goto out;
454178354Ssam		}
455178354Ssam	resubmit_ampdu:
456178354Ssam
457178354Ssam		/*
458178354Ssam		 * Handle privacy requirements.  Note that we
459178354Ssam		 * must not be preempted from here until after
460178354Ssam		 * we (potentially) call ieee80211_crypto_demic;
461178354Ssam		 * otherwise we may violate assumptions in the
462178354Ssam		 * crypto cipher modules used to do delayed update
463178354Ssam		 * of replay sequence numbers.
464178354Ssam		 */
465260444Skevlo		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
466178354Ssam			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
467178354Ssam				/*
468178354Ssam				 * Discard encrypted frames when privacy is off.
469178354Ssam				 */
470178354Ssam				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
471178354Ssam				    wh, "WEP", "%s", "PRIVACY off");
472178354Ssam				vap->iv_stats.is_rx_noprivacy++;
473178354Ssam				IEEE80211_NODE_STAT(ni, rx_noprivacy);
474178354Ssam				goto out;
475178354Ssam			}
476178354Ssam			key = ieee80211_crypto_decap(ni, m, hdrspace);
477178354Ssam			if (key == NULL) {
478178354Ssam				/* NB: stats+msgs handled in crypto_decap */
479178354Ssam				IEEE80211_NODE_STAT(ni, rx_wepfail);
480178354Ssam				goto out;
481178354Ssam			}
482178354Ssam			wh = mtod(m, struct ieee80211_frame *);
483260444Skevlo			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
484178354Ssam		} else {
485178354Ssam			/* XXX M_WEP and IEEE80211_F_PRIVACY */
486178354Ssam			key = NULL;
487178354Ssam		}
488178354Ssam
489178354Ssam		/*
490178354Ssam		 * Save QoS bits for use below--before we strip the header.
491178354Ssam		 */
492178354Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
493178354Ssam			qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
494178354Ssam			    ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
495178354Ssam			    ((struct ieee80211_qosframe *)wh)->i_qos[0];
496178354Ssam		} else
497178354Ssam			qos = 0;
498178354Ssam
499178354Ssam		/*
500178354Ssam		 * Next up, any fragmentation.
501178354Ssam		 */
502178354Ssam		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
503178354Ssam			m = ieee80211_defrag(ni, m, hdrspace);
504178354Ssam			if (m == NULL) {
505178354Ssam				/* Fragment dropped or frame not complete yet */
506178354Ssam				goto out;
507178354Ssam			}
508178354Ssam		}
509178354Ssam		wh = NULL;		/* no longer valid, catch any uses */
510178354Ssam
511178354Ssam		/*
512178354Ssam		 * Next strip any MSDU crypto bits.
513178354Ssam		 */
514178354Ssam		if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
515178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
516178354Ssam			    ni->ni_macaddr, "data", "%s", "demic error");
517178354Ssam			vap->iv_stats.is_rx_demicfail++;
518178354Ssam			IEEE80211_NODE_STAT(ni, rx_demicfail);
519178354Ssam			goto out;
520178354Ssam		}
521178354Ssam
522178354Ssam		/* copy to listener after decrypt */
523192468Ssam		if (ieee80211_radiotap_active_vap(vap))
524192468Ssam			ieee80211_radiotap_rx(vap, m);
525178354Ssam		need_tap = 0;
526178354Ssam
527178354Ssam		/*
528178354Ssam		 * Finally, strip the 802.11 header.
529178354Ssam		 */
530178354Ssam		m = ieee80211_decap(vap, m, hdrspace);
531178354Ssam		if (m == NULL) {
532178354Ssam			/* XXX mask bit to check for both */
533178354Ssam			/* don't count Null data frames as errors */
534178354Ssam			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
535178354Ssam			    subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
536178354Ssam				goto out;
537178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
538178354Ssam			    ni->ni_macaddr, "data", "%s", "decap error");
539178354Ssam			vap->iv_stats.is_rx_decap++;
540178354Ssam			IEEE80211_NODE_STAT(ni, rx_decap);
541178354Ssam			goto err;
542178354Ssam		}
543178354Ssam		eh = mtod(m, struct ether_header *);
544178354Ssam		if (!ieee80211_node_is_authorized(ni)) {
545178354Ssam			/*
546178354Ssam			 * Deny any non-PAE frames received prior to
547178354Ssam			 * authorization.  For open/shared-key
548178354Ssam			 * authentication the port is mark authorized
549178354Ssam			 * after authentication completes.  For 802.1x
550178354Ssam			 * the port is not marked authorized by the
551178354Ssam			 * authenticator until the handshake has completed.
552178354Ssam			 */
553178354Ssam			if (eh->ether_type != htons(ETHERTYPE_PAE)) {
554178354Ssam				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
555178354Ssam				    eh->ether_shost, "data",
556178354Ssam				    "unauthorized port: ether type 0x%x len %u",
557178354Ssam				    eh->ether_type, m->m_pkthdr.len);
558178354Ssam				vap->iv_stats.is_rx_unauth++;
559178354Ssam				IEEE80211_NODE_STAT(ni, rx_unauth);
560178354Ssam				goto err;
561178354Ssam			}
562178354Ssam		} else {
563178354Ssam			/*
564178354Ssam			 * When denying unencrypted frames, discard
565178354Ssam			 * any non-PAE frames received without encryption.
566178354Ssam			 */
567178354Ssam			if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
568178354Ssam			    (key == NULL && (m->m_flags & M_WEP) == 0) &&
569178354Ssam			    eh->ether_type != htons(ETHERTYPE_PAE)) {
570178354Ssam				/*
571178354Ssam				 * Drop unencrypted frames.
572178354Ssam				 */
573178354Ssam				vap->iv_stats.is_rx_unencrypted++;
574178354Ssam				IEEE80211_NODE_STAT(ni, rx_unencrypted);
575178354Ssam				goto out;
576178354Ssam			}
577178354Ssam		}
578178354Ssam		/* XXX require HT? */
579178354Ssam		if (qos & IEEE80211_QOS_AMSDU) {
580178354Ssam			m = ieee80211_decap_amsdu(ni, m);
581178354Ssam			if (m == NULL)
582178354Ssam				return IEEE80211_FC0_TYPE_DATA;
583190391Ssam		} else {
584190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG
585190391Ssam			m = ieee80211_decap_fastframe(vap, ni, m);
586190391Ssam			if (m == NULL)
587178354Ssam				return IEEE80211_FC0_TYPE_DATA;
588190391Ssam#endif
589178354Ssam		}
590178354Ssam		if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL)
591178354Ssam			ieee80211_deliver_data(ni->ni_wdsvap, ni, m);
592178354Ssam		else
593178354Ssam			ieee80211_deliver_data(vap, ni, m);
594178354Ssam		return IEEE80211_FC0_TYPE_DATA;
595178354Ssam
596178354Ssam	case IEEE80211_FC0_TYPE_MGT:
597178354Ssam		vap->iv_stats.is_rx_mgmt++;
598178354Ssam		IEEE80211_NODE_STAT(ni, rx_mgmt);
599178354Ssam		if (dir != IEEE80211_FC1_DIR_NODS) {
600178354Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
601178354Ssam			    wh, "data", "incorrect dir 0x%x", dir);
602178354Ssam			vap->iv_stats.is_rx_wrongdir++;
603178354Ssam			goto err;
604178354Ssam		}
605178354Ssam		if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
606178354Ssam			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
607178354Ssam			    ni->ni_macaddr, "mgt", "too short: len %u",
608178354Ssam			    m->m_pkthdr.len);
609178354Ssam			vap->iv_stats.is_rx_tooshort++;
610178354Ssam			goto out;
611178354Ssam		}
612178354Ssam#ifdef IEEE80211_DEBUG
613178354Ssam		if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
614178354Ssam		    ieee80211_msg_dumppkts(vap)) {
615178354Ssam			if_printf(ifp, "received %s from %s rssi %d\n",
616298376Savos			    ieee80211_mgt_subtype_name(subtype),
617178354Ssam			    ether_sprintf(wh->i_addr2), rssi);
618178354Ssam		}
619178354Ssam#endif
620260444Skevlo		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
621178354Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
622178354Ssam			    wh, NULL, "%s", "WEP set but not permitted");
623178354Ssam			vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
624178354Ssam			goto out;
625178354Ssam		}
626283535Sadrian		vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
627191534Ssam		goto out;
628178354Ssam
629178354Ssam	case IEEE80211_FC0_TYPE_CTL:
630178354Ssam		vap->iv_stats.is_rx_ctl++;
631178354Ssam		IEEE80211_NODE_STAT(ni, rx_ctrl);
632191546Ssam		vap->iv_recv_ctl(ni, m, subtype);
633178354Ssam		goto out;
634191754Ssam
635178354Ssam	default:
636178354Ssam		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
637178354Ssam		    wh, "bad", "frame type 0x%x", type);
638178354Ssam		/* should not come here */
639178354Ssam		break;
640178354Ssam	}
641178354Ssamerr:
642271861Sglebius	if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
643178354Ssamout:
644178354Ssam	if (m != NULL) {
645192765Ssam		if (need_tap && ieee80211_radiotap_active_vap(vap))
646192468Ssam			ieee80211_radiotap_rx(vap, m);
647178354Ssam		m_freem(m);
648178354Ssam	}
649178354Ssam	return type;
650178354Ssam}
651178354Ssam
652178354Ssamstatic int
653178354Ssamis11bclient(const uint8_t *rates, const uint8_t *xrates)
654178354Ssam{
655178354Ssam	static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11);
656178354Ssam	int i;
657178354Ssam
658178354Ssam	/* NB: the 11b clients we care about will not have xrates */
659178354Ssam	if (xrates != NULL || rates == NULL)
660178354Ssam		return 0;
661178354Ssam	for (i = 0; i < rates[1]; i++) {
662178354Ssam		int r = rates[2+i] & IEEE80211_RATE_VAL;
663178354Ssam		if (r > 2*11 || ((1<<r) & brates) == 0)
664178354Ssam			return 0;
665178354Ssam	}
666178354Ssam	return 1;
667178354Ssam}
668178354Ssam
669178354Ssamstatic void
670178354Ssamadhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
671283535Sadrian	int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
672178354Ssam{
673178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
674178354Ssam	struct ieee80211com *ic = ni->ni_ic;
675283535Sadrian	struct ieee80211_channel *rxchan = ic->ic_curchan;
676178354Ssam	struct ieee80211_frame *wh;
677178354Ssam	uint8_t *frm, *efrm, *sfrm;
678178354Ssam	uint8_t *ssid, *rates, *xrates;
679246930Sadrian#if 0
680245928Sadrian	int ht_state_change = 0;
681246930Sadrian#endif
682178354Ssam
683178354Ssam	wh = mtod(m0, struct ieee80211_frame *);
684178354Ssam	frm = (uint8_t *)&wh[1];
685178354Ssam	efrm = mtod(m0, uint8_t *) + m0->m_len;
686178354Ssam	switch (subtype) {
687178354Ssam	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
688178354Ssam	case IEEE80211_FC0_SUBTYPE_BEACON: {
689178354Ssam		struct ieee80211_scanparams scan;
690283535Sadrian		struct ieee80211_channel *c;
691178354Ssam		/*
692178354Ssam		 * We process beacon/probe response
693178354Ssam		 * frames to discover neighbors.
694178354Ssam		 */
695283535Sadrian		if (rxs != NULL) {
696283535Sadrian			c = ieee80211_lookup_channel_rxstatus(vap, rxs);
697283535Sadrian			if (c != NULL)
698283535Sadrian				rxchan = c;
699283535Sadrian		}
700283535Sadrian		if (ieee80211_parse_beacon(ni, m0, rxchan, &scan) != 0)
701178354Ssam			return;
702178354Ssam		/*
703178354Ssam		 * Count frame now that we know it's to be processed.
704178354Ssam		 */
705178354Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
706178354Ssam			vap->iv_stats.is_rx_beacon++;		/* XXX remove */
707178354Ssam			IEEE80211_NODE_STAT(ni, rx_beacons);
708178354Ssam		} else
709178354Ssam			IEEE80211_NODE_STAT(ni, rx_proberesp);
710178354Ssam		/*
711178354Ssam		 * If scanning, just pass information to the scan module.
712178354Ssam		 */
713178354Ssam		if (ic->ic_flags & IEEE80211_F_SCAN) {
714178354Ssam			if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
715178354Ssam				/*
716178354Ssam				 * Actively scanning a channel marked passive;
717178354Ssam				 * send a probe request now that we know there
718178354Ssam				 * is 802.11 traffic present.
719178354Ssam				 *
720178354Ssam				 * XXX check if the beacon we recv'd gives
721178354Ssam				 * us what we need and suppress the probe req
722178354Ssam				 */
723178354Ssam				ieee80211_probe_curchan(vap, 1);
724178354Ssam				ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
725178354Ssam			}
726283535Sadrian			ieee80211_add_scan(vap, rxchan, &scan, wh,
727282742Sadrian			    subtype, rssi, nf);
728178354Ssam			return;
729178354Ssam		}
730178354Ssam		if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
731178354Ssam			if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
732178354Ssam				/*
733178354Ssam				 * Create a new entry in the neighbor table.
734178354Ssam				 */
735178354Ssam				ni = ieee80211_add_neighbor(vap, wh, &scan);
736178354Ssam			} else if (ni->ni_capinfo == 0) {
737178354Ssam				/*
738178354Ssam				 * Update faked node created on transmit.
739178354Ssam				 * Note this also updates the tsf.
740178354Ssam				 */
741178354Ssam				ieee80211_init_neighbor(ni, wh, &scan);
742178354Ssam			} else {
743178354Ssam				/*
744178354Ssam				 * Record tsf for potential resync.
745178354Ssam				 */
746178354Ssam				memcpy(ni->ni_tstamp.data, scan.tstamp,
747178354Ssam					sizeof(ni->ni_tstamp));
748178354Ssam			}
749245928Sadrian			/*
750245928Sadrian			 * This isn't enabled yet - otherwise it would
751245928Sadrian			 * update the HT parameters and channel width
752245928Sadrian			 * from any node, which could lead to lots of
753245928Sadrian			 * strange behaviour if the 11n nodes aren't
754245928Sadrian			 * exactly configured to match.
755245928Sadrian			 */
756245928Sadrian#if 0
757245928Sadrian			if (scan.htcap != NULL && scan.htinfo != NULL &&
758245928Sadrian			    (vap->iv_flags_ht & IEEE80211_FHT_HT)) {
759245928Sadrian				if (ieee80211_ht_updateparams(ni,
760245928Sadrian				    scan.htcap, scan.htinfo))
761245928Sadrian					ht_state_change = 1;
762245928Sadrian			}
763245928Sadrian#endif
764178354Ssam			if (ni != NULL) {
765178354Ssam				IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
766192468Ssam				ni->ni_noise = nf;
767178354Ssam			}
768246927Sadrian			/*
769246927Sadrian			 * Same here - the channel width change should
770246927Sadrian			 * be applied to the specific peer node, not
771246927Sadrian			 * to the ic.  Ie, the interface configuration
772246927Sadrian			 * should stay in its current channel width;
773246927Sadrian			 * but it should change the rate control and
774246927Sadrian			 * any queued frames for the given node only.
775246927Sadrian			 *
776246927Sadrian			 * Since there's no (current) way to inform
777246927Sadrian			 * the driver that a channel width change has
778246927Sadrian			 * occured for a single node, just stub this
779246927Sadrian			 * out.
780246927Sadrian			 */
781246927Sadrian#if 0
782245928Sadrian			if (ht_state_change)
783245928Sadrian				ieee80211_update_chw(ic);
784246927Sadrian#endif
785178354Ssam		}
786178354Ssam		break;
787178354Ssam	}
788178354Ssam
789178354Ssam	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
790178354Ssam		if (vap->iv_state != IEEE80211_S_RUN) {
791184268Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
792184268Ssam			    wh, NULL, "wrong state %s",
793184268Ssam			    ieee80211_state_name[vap->iv_state]);
794178354Ssam			vap->iv_stats.is_rx_mgtdiscard++;
795178354Ssam			return;
796178354Ssam		}
797178354Ssam		if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
798178354Ssam			/* frame must be directed */
799184268Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
800184268Ssam			    wh, NULL, "%s", "not unicast");
801178354Ssam			vap->iv_stats.is_rx_mgtdiscard++;	/* XXX stat */
802178354Ssam			return;
803178354Ssam		}
804178354Ssam
805178354Ssam		/*
806178354Ssam		 * prreq frame format
807178354Ssam		 *	[tlv] ssid
808178354Ssam		 *	[tlv] supported rates
809178354Ssam		 *	[tlv] extended supported rates
810178354Ssam		 */
811178354Ssam		ssid = rates = xrates = NULL;
812178354Ssam		sfrm = frm;
813178354Ssam		while (efrm - frm > 1) {
814178354Ssam			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
815178354Ssam			switch (*frm) {
816178354Ssam			case IEEE80211_ELEMID_SSID:
817178354Ssam				ssid = frm;
818178354Ssam				break;
819178354Ssam			case IEEE80211_ELEMID_RATES:
820178354Ssam				rates = frm;
821178354Ssam				break;
822178354Ssam			case IEEE80211_ELEMID_XRATES:
823178354Ssam				xrates = frm;
824178354Ssam				break;
825178354Ssam			}
826178354Ssam			frm += frm[1] + 2;
827178354Ssam		}
828178354Ssam		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
829178354Ssam		if (xrates != NULL)
830178354Ssam			IEEE80211_VERIFY_ELEMENT(xrates,
831178354Ssam				IEEE80211_RATE_MAXSIZE - rates[1], return);
832178354Ssam		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
833178354Ssam		IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return);
834178354Ssam		if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
835178354Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
836178354Ssam			    wh, NULL,
837178354Ssam			    "%s", "no ssid with ssid suppression enabled");
838178354Ssam			vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/
839178354Ssam			return;
840178354Ssam		}
841178354Ssam
842178354Ssam		/* XXX find a better class or define it's own */
843178354Ssam		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
844178354Ssam		    "%s", "recv probe req");
845178354Ssam		/*
846178354Ssam		 * Some legacy 11b clients cannot hack a complete
847178354Ssam		 * probe response frame.  When the request includes
848178354Ssam		 * only a bare-bones rate set, communicate this to
849178354Ssam		 * the transmit side.
850178354Ssam		 */
851178354Ssam		ieee80211_send_proberesp(vap, wh->i_addr2,
852178354Ssam		    is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0);
853178354Ssam		break;
854178354Ssam
855218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_ACTION:
856218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_ACTION_NOACK:
857218958Sbschmidt		if (ni == vap->iv_bss) {
858184268Ssam			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
859218958Sbschmidt			    wh, NULL, "%s", "unknown node");
860218958Sbschmidt			vap->iv_stats.is_rx_mgtdiscard++;
861218958Sbschmidt		} else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) &&
862218958Sbschmidt		    !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
863218958Sbschmidt			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
864218958Sbschmidt			    wh, NULL, "%s", "not for us");
865218958Sbschmidt			vap->iv_stats.is_rx_mgtdiscard++;
866218958Sbschmidt		} else if (vap->iv_state != IEEE80211_S_RUN) {
867218958Sbschmidt			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
868184268Ssam			    wh, NULL, "wrong state %s",
869184268Ssam			    ieee80211_state_name[vap->iv_state]);
870178354Ssam			vap->iv_stats.is_rx_mgtdiscard++;
871218958Sbschmidt		} else {
872218958Sbschmidt			if (ieee80211_parse_action(ni, m0) == 0)
873218958Sbschmidt				(void)ic->ic_recv_action(ni, wh, frm, efrm);
874178354Ssam		}
875178354Ssam		break;
876178354Ssam
877178354Ssam	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
878218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
879178354Ssam	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
880178354Ssam	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
881295795Savos	case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
882218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_ATIM:
883218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_DISASSOC:
884218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_AUTH:
885178354Ssam	case IEEE80211_FC0_SUBTYPE_DEAUTH:
886184268Ssam		IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
887218927Sbschmidt		    wh, NULL, "%s", "not handled");
888178354Ssam		vap->iv_stats.is_rx_mgtdiscard++;
889218927Sbschmidt		break;
890178354Ssam
891178354Ssam	default:
892178354Ssam		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
893218927Sbschmidt		    wh, "mgt", "subtype 0x%x not handled", subtype);
894178354Ssam		vap->iv_stats.is_rx_badsubtype++;
895178354Ssam		break;
896178354Ssam	}
897178354Ssam}
898178354Ssam#undef IEEE80211_VERIFY_LENGTH
899178354Ssam#undef IEEE80211_VERIFY_ELEMENT
900184276Ssam
901184276Ssamstatic void
902184276Ssamahdemo_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
903283535Sadrian	int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
904184276Ssam{
905184276Ssam	struct ieee80211vap *vap = ni->ni_vap;
906184276Ssam	struct ieee80211com *ic = ni->ni_ic;
907218927Sbschmidt	struct ieee80211_frame *wh;
908184276Ssam
909184276Ssam	/*
910184276Ssam	 * Process management frames when scanning; useful for doing
911184276Ssam	 * a site-survey.
912184276Ssam	 */
913184276Ssam	if (ic->ic_flags & IEEE80211_F_SCAN)
914283535Sadrian		adhoc_recv_mgmt(ni, m0, subtype, rxs, rssi, nf);
915218927Sbschmidt	else {
916218927Sbschmidt		wh = mtod(m0, struct ieee80211_frame *);
917218927Sbschmidt		switch (subtype) {
918218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
919218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
920218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
921218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
922218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
923218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
924295795Savos		case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
925218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_BEACON:
926218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_ATIM:
927218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_DISASSOC:
928218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_AUTH:
929218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_DEAUTH:
930218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_ACTION:
931218927Sbschmidt		case IEEE80211_FC0_SUBTYPE_ACTION_NOACK:
932218927Sbschmidt			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
933218927Sbschmidt			     wh, NULL, "%s", "not handled");
934218927Sbschmidt			vap->iv_stats.is_rx_mgtdiscard++;
935218927Sbschmidt			break;
936218927Sbschmidt		default:
937218927Sbschmidt			IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
938218927Sbschmidt			     wh, "mgt", "subtype 0x%x not handled", subtype);
939218927Sbschmidt			vap->iv_stats.is_rx_badsubtype++;
940218927Sbschmidt			break;
941218927Sbschmidt		}
942218927Sbschmidt	}
943184276Ssam}
944191546Ssam
945191546Ssamstatic void
946205277Srpauloadhoc_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype)
947191546Ssam{
948205277Srpaulo
949205277Srpaulo	switch (subtype) {
950205277Srpaulo	case IEEE80211_FC0_SUBTYPE_BAR:
951205277Srpaulo		ieee80211_recv_bar(ni, m);
952205277Srpaulo		break;
953205277Srpaulo	}
954191546Ssam}
955