ieee80211_adhoc.c revision 178354
1266072Sdes/*-
2266072Sdes * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
3266072Sdes * All rights reserved.
4266072Sdes *
5266072Sdes * Redistribution and use in source and binary forms, with or without
6266072Sdes * modification, are permitted provided that the following conditions
7266072Sdes * are met:
8266072Sdes * 1. Redistributions of source code must retain the above copyright
9266072Sdes *    notice, this list of conditions and the following disclaimer.
10266072Sdes * 2. Redistributions in binary form must reproduce the above copyright
11266072Sdes *    notice, this list of conditions and the following disclaimer in the
12266072Sdes *    documentation and/or other materials provided with the distribution.
13266072Sdes *
14266072Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15266072Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16266072Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17266072Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18266072Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19266072Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20266072Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21266072Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22266072Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23266072Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24266072Sdes */
25266072Sdes
26266072Sdes#include <sys/cdefs.h>
27266072Sdes#ifdef __FreeBSD__
28266072Sdes__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_adhoc.c 178354 2008-04-20 20:35:46Z sam $");
29266072Sdes#endif
30266072Sdes
31266072Sdes/*
32266072Sdes * IEEE 802.11 IBSS mode support.
33266072Sdes */
34266072Sdes#include "opt_inet.h"
35266072Sdes#include "opt_wlan.h"
36266072Sdes
37266072Sdes#include <sys/param.h>
38266072Sdes#include <sys/systm.h>
39266072Sdes#include <sys/mbuf.h>
40266072Sdes#include <sys/malloc.h>
41266072Sdes#include <sys/kernel.h>
42266072Sdes
43266072Sdes#include <sys/socket.h>
44266072Sdes#include <sys/sockio.h>
45266072Sdes#include <sys/endian.h>
46266072Sdes#include <sys/errno.h>
47266072Sdes#include <sys/proc.h>
48266072Sdes#include <sys/sysctl.h>
49266072Sdes
50266072Sdes#include <net/if.h>
51266072Sdes#include <net/if_media.h>
52266072Sdes#include <net/if_llc.h>
53266072Sdes#include <net/ethernet.h>
54266072Sdes
55266072Sdes#include <net/bpf.h>
56266072Sdes
57266072Sdes#include <net80211/ieee80211_var.h>
58266072Sdes#include <net80211/ieee80211_adhoc.h>
59266072Sdes#include <net80211/ieee80211_input.h>
60266072Sdes
61266072Sdes#define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
62266072Sdes
63266072Sdesstatic	void adhoc_vattach(struct ieee80211vap *);
64266072Sdesstatic	int adhoc_newstate(struct ieee80211vap *, enum ieee80211_state, int);
65266072Sdesstatic int adhoc_input(struct ieee80211_node *, struct mbuf *,
66266072Sdes	int rssi, int noise, uint32_t rstamp);
67266072Sdesstatic void adhoc_recv_mgmt(struct ieee80211_node *, struct mbuf *,
68266072Sdes	int subtype, int rssi, int noise, uint32_t rstamp);
69266072Sdes
70266072Sdesvoid
71266072Sdesieee80211_adhoc_attach(struct ieee80211com *ic)
72266072Sdes{
73266072Sdes	ic->ic_vattach[IEEE80211_M_IBSS] = adhoc_vattach;
74266072Sdes	ic->ic_vattach[IEEE80211_M_AHDEMO] = adhoc_vattach;
75266072Sdes}
76266072Sdes
77266072Sdesvoid
78266072Sdesieee80211_adhoc_detach(struct ieee80211com *ic)
79266072Sdes{
80266072Sdes}
81266072Sdes
82266072Sdesstatic void
83266072Sdesadhoc_vdetach(struct ieee80211vap *vap)
84266072Sdes{
85266072Sdes}
86266072Sdes
87266072Sdesstatic void
88266072Sdesadhoc_vattach(struct ieee80211vap *vap)
89266072Sdes{
90266072Sdes	vap->iv_newstate = adhoc_newstate;
91266072Sdes	vap->iv_input = adhoc_input;
92266072Sdes	vap->iv_recv_mgmt = adhoc_recv_mgmt;
93266072Sdes	vap->iv_opdetach = adhoc_vdetach;
94266072Sdes}
95266072Sdes
96266072Sdes/*
97266072Sdes * IEEE80211_M_IBSS+IEEE80211_M_AHDEMO vap state machine handler.
98266072Sdes */
99266072Sdesstatic int
100266072Sdesadhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
101266072Sdes{
102266072Sdes#ifdef IEEE80211_DEBUG
103266072Sdes	struct ieee80211com *ic = vap->iv_ic;
104266072Sdes#endif
105266072Sdes	struct ieee80211_node *ni;
106266072Sdes	enum ieee80211_state ostate;
107266072Sdes
108266072Sdes	IEEE80211_LOCK_ASSERT(vap->iv_ic);
109266072Sdes
110266072Sdes	ostate = vap->iv_state;
111266072Sdes	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
112266072Sdes	    __func__, ieee80211_state_name[ostate],
113266072Sdes	    ieee80211_state_name[nstate], arg);
114266072Sdes	vap->iv_state = nstate;			/* state transition */
115266072Sdes	if (ostate != IEEE80211_S_SCAN)
116266072Sdes		ieee80211_cancel_scan(vap);	/* background scan */
117266072Sdes	ni = vap->iv_bss;			/* NB: no reference held */
118266072Sdes	if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
119266072Sdes		callout_stop(&vap->iv_swbmiss);
120266072Sdes	switch (nstate) {
121266072Sdes	case IEEE80211_S_INIT:
122266072Sdes		switch (ostate) {
123266072Sdes		case IEEE80211_S_SCAN:
124266072Sdes			ieee80211_cancel_scan(vap);
125266072Sdes			break;
126266072Sdes		default:
127266072Sdes			break;
128266072Sdes		}
129266072Sdes		if (ostate != IEEE80211_S_INIT) {
130266072Sdes			/* NB: optimize INIT -> INIT case */
131266072Sdes			ieee80211_reset_bss(vap);
132266072Sdes		}
133266072Sdes		break;
134266072Sdes	case IEEE80211_S_SCAN:
135266072Sdes		switch (ostate) {
136266072Sdes		case IEEE80211_S_INIT:
137266072Sdes		case IEEE80211_S_RUN:		/* beacon miss */
138266072Sdes			if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
139266072Sdes			    !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
140266072Sdes				/*
141266072Sdes				 * Already have a channel; bypass the
142266072Sdes				 * scan and startup immediately.
143266072Sdes				 */
144266072Sdes				ieee80211_create_ibss(vap, vap->iv_des_chan);
145266072Sdes				break;
146266072Sdes			}
147266072Sdes			/*
148266072Sdes			 * Initiate a scan.  We can come here as a result
149266072Sdes			 * of an IEEE80211_IOC_SCAN_REQ too in which case
150266072Sdes			 * the vap will be marked with IEEE80211_FEXT_SCANREQ
151266072Sdes			 * and the scan request parameters will be present
152266072Sdes			 * in iv_scanreq.  Otherwise we do the default.
153266072Sdes			 */
154266072Sdes			if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
155266072Sdes				ieee80211_check_scan(vap,
156266072Sdes				    vap->iv_scanreq_flags,
157266072Sdes				    vap->iv_scanreq_duration,
158266072Sdes				    vap->iv_scanreq_mindwell,
159266072Sdes				    vap->iv_scanreq_maxdwell,
160266072Sdes				    vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
161266072Sdes				vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
162266072Sdes			} else
163266072Sdes				ieee80211_check_scan_current(vap);
164266072Sdes			break;
165266072Sdes		case IEEE80211_S_SCAN:
166266072Sdes			/*
167266072Sdes			 * This can happen because of a change in state
168266072Sdes			 * that requires a reset.  Trigger a new scan
169266072Sdes			 * unless we're in manual roaming mode in which
170266072Sdes			 * case an application must issue an explicit request.
171266072Sdes			 */
172266072Sdes			if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
173266072Sdes				ieee80211_check_scan_current(vap);
174266072Sdes			break;
175266072Sdes		default:
176266072Sdes			goto invalid;
177266072Sdes		}
178266072Sdes		break;
179266072Sdes	case IEEE80211_S_RUN:
180266072Sdes		if (vap->iv_flags & IEEE80211_F_WPA) {
181266072Sdes			/* XXX validate prerequisites */
182266072Sdes		}
183266072Sdes		switch (ostate) {
184266072Sdes		case IEEE80211_S_SCAN:
185266072Sdes#ifdef IEEE80211_DEBUG
186266072Sdes			if (ieee80211_msg_debug(vap)) {
187266072Sdes				ieee80211_note(vap,
188266072Sdes				    "synchronized with %s ssid ",
189266072Sdes				    ether_sprintf(ni->ni_bssid));
190266072Sdes				ieee80211_print_essid(vap->iv_bss->ni_essid,
191266072Sdes				    ni->ni_esslen);
192266072Sdes				/* XXX MCS/HT */
193266072Sdes				printf(" channel %d start %uMb\n",
194266072Sdes				    ieee80211_chan2ieee(ic, ic->ic_curchan),
195266072Sdes				    IEEE80211_RATE2MBS(ni->ni_txrate));
196266072Sdes			}
197266072Sdes#endif
198266072Sdes			break;
199266072Sdes		default:
200266072Sdes			goto invalid;
201266072Sdes		}
202266072Sdes		/*
203266072Sdes		 * When 802.1x is not in use mark the port authorized
204266072Sdes		 * at this point so traffic can flow.
205266072Sdes		 */
206266072Sdes		if (ni->ni_authmode != IEEE80211_AUTH_8021X)
207266072Sdes			ieee80211_node_authorize(ni);
208266072Sdes		break;
209266072Sdes	case IEEE80211_S_SLEEP:
210266072Sdes		ieee80211_sta_pwrsave(vap, 0);
211266072Sdes		break;
212266072Sdes	default:
213266072Sdes	invalid:
214266072Sdes		IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
215266072Sdes		    "%s: invalid state transition %s -> %s\n", __func__,
216266072Sdes		    ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
217266072Sdes		break;
218266072Sdes	}
219266072Sdes	return 0;
220266072Sdes}
221266072Sdes
222266072Sdes/*
223266072Sdes * Decide if a received management frame should be
224266072Sdes * printed when debugging is enabled.  This filters some
225266072Sdes * of the less interesting frames that come frequently
226266072Sdes * (e.g. beacons).
227266072Sdes */
228266072Sdesstatic __inline int
229266072Sdesdoprint(struct ieee80211vap *vap, int subtype)
230266072Sdes{
231266072Sdes	switch (subtype) {
232266072Sdes	case IEEE80211_FC0_SUBTYPE_BEACON:
233266072Sdes		return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
234266072Sdes	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
235266072Sdes		return 1;
236266072Sdes	}
237266072Sdes	return 1;
238266072Sdes}
239266072Sdes
240266072Sdes/*
241266072Sdes * Process a received frame.  The node associated with the sender
242266072Sdes * should be supplied.  If nothing was found in the node table then
243266072Sdes * the caller is assumed to supply a reference to iv_bss instead.
244266072Sdes * The RSSI and a timestamp are also supplied.  The RSSI data is used
245266072Sdes * during AP scanning to select a AP to associate with; it can have
246266072Sdes * any units so long as values have consistent units and higher values
247266072Sdes * mean ``better signal''.  The receive timestamp is currently not used
248266072Sdes * by the 802.11 layer.
249266072Sdes */
250266072Sdesstatic int
251266072Sdesadhoc_input(struct ieee80211_node *ni, struct mbuf *m,
252266072Sdes	int rssi, int noise, uint32_t rstamp)
253266072Sdes{
254266072Sdes#define	SEQ_LEQ(a,b)	((int)((a)-(b)) <= 0)
255266072Sdes#define	HAS_SEQ(type)	((type & 0x4) == 0)
256266072Sdes	struct ieee80211vap *vap = ni->ni_vap;
257266072Sdes	struct ieee80211com *ic = ni->ni_ic;
258266072Sdes	struct ifnet *ifp = vap->iv_ifp;
259266072Sdes	struct ieee80211_frame *wh;
260266072Sdes	struct ieee80211_key *key;
261266072Sdes	struct ether_header *eh;
262266072Sdes	int hdrspace, need_tap;
263266072Sdes	uint8_t dir, type, subtype, qos;
264266072Sdes	uint8_t *bssid;
265266072Sdes	uint16_t rxseq;
266266072Sdes
267266072Sdes	if (m->m_flags & M_AMPDU) {
268266072Sdes		/*
269266072Sdes		 * Fastpath for A-MPDU reorder q resubmission.  Frames
270266072Sdes		 * w/ M_AMPDU marked have already passed through here
271266072Sdes		 * but were received out of order and been held on the
272266072Sdes		 * reorder queue.  When resubmitted they are marked
273266072Sdes		 * with the M_AMPDU flag and we can bypass most of the
274266072Sdes		 * normal processing.
275266072Sdes		 */
276266072Sdes		wh = mtod(m, struct ieee80211_frame *);
277266072Sdes		type = IEEE80211_FC0_TYPE_DATA;
278266072Sdes		dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
279266072Sdes		subtype = IEEE80211_FC0_SUBTYPE_QOS;
280266072Sdes		hdrspace = ieee80211_hdrspace(ic, wh);	/* XXX optimize? */
281266072Sdes		goto resubmit_ampdu;
282266072Sdes	}
283266072Sdes
284266072Sdes	KASSERT(ni != NULL, ("null node"));
285266072Sdes	ni->ni_inact = ni->ni_inact_reload;
286266072Sdes
287266072Sdes	need_tap = 1;			/* mbuf need to be tapped. */
288266072Sdes	type = -1;			/* undefined */
289266072Sdes
290266072Sdes	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
291266072Sdes		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
292266072Sdes		    ni->ni_macaddr, NULL,
293266072Sdes		    "too short (1): len %u", m->m_pkthdr.len);
294266072Sdes		vap->iv_stats.is_rx_tooshort++;
295266072Sdes		goto out;
296266072Sdes	}
297266072Sdes	/*
298266072Sdes	 * Bit of a cheat here, we use a pointer for a 3-address
299266072Sdes	 * frame format but don't reference fields past outside
300266072Sdes	 * ieee80211_frame_min w/o first validating the data is
301266072Sdes	 * present.
302266072Sdes	 */
303266072Sdes	wh = mtod(m, struct ieee80211_frame *);
304266072Sdes
305266072Sdes	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
306266072Sdes	    IEEE80211_FC0_VERSION_0) {
307266072Sdes		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
308266072Sdes		    ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
309266072Sdes		vap->iv_stats.is_rx_badversion++;
310266072Sdes		goto err;
311266072Sdes	}
312266072Sdes
313266072Sdes	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
314266072Sdes	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
315266072Sdes	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
316266072Sdes	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
317266072Sdes		if (dir != IEEE80211_FC1_DIR_NODS)
318266072Sdes			bssid = wh->i_addr1;
319266072Sdes		else if (type == IEEE80211_FC0_TYPE_CTL)
320266072Sdes			bssid = wh->i_addr1;
321266072Sdes		else {
322266072Sdes			if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
323266072Sdes				IEEE80211_DISCARD_MAC(vap,
324266072Sdes				    IEEE80211_MSG_ANY, ni->ni_macaddr,
325266072Sdes				    NULL, "too short (2): len %u",
326266072Sdes				    m->m_pkthdr.len);
327266072Sdes				vap->iv_stats.is_rx_tooshort++;
328266072Sdes				goto out;
329266072Sdes			}
330266072Sdes			bssid = wh->i_addr3;
331266072Sdes		}
332266072Sdes		/*
333266072Sdes		 * Validate the bssid.
334266072Sdes		 */
335266072Sdes		if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) &&
336266072Sdes		    !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
337266072Sdes			/* not interested in */
338266072Sdes			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
339266072Sdes			    bssid, NULL, "%s", "not to bss");
340266072Sdes			vap->iv_stats.is_rx_wrongbss++;
341266072Sdes			goto out;
342266072Sdes		}
343266072Sdes		/*
344266072Sdes		 * Data frame, cons up a node when it doesn't
345266072Sdes		 * exist. This should probably done after an ACL check.
346266072Sdes		 */
347266072Sdes		if (type == IEEE80211_FC0_TYPE_DATA &&
348266072Sdes		    ni == vap->iv_bss &&
349266072Sdes		    !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
350266072Sdes			/*
351266072Sdes			 * Fake up a node for this newly
352266072Sdes			 * discovered member of the IBSS.
353266072Sdes			 */
354266072Sdes			ni = ieee80211_fakeup_adhoc_node(vap, wh->i_addr2);
355266072Sdes			if (ni == NULL) {
356266072Sdes				/* NB: stat kept for alloc failure */
357266072Sdes				goto err;
358266072Sdes			}
359266072Sdes		}
360266072Sdes		IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
361266072Sdes		ni->ni_noise = noise;
362266072Sdes		ni->ni_rstamp = rstamp;
363266072Sdes		if (HAS_SEQ(type)) {
364266072Sdes			uint8_t tid = ieee80211_gettid(wh);
365266072Sdes			if (IEEE80211_QOS_HAS_SEQ(wh) &&
366266072Sdes			    TID_TO_WME_AC(tid) >= WME_AC_VI)
367266072Sdes				ic->ic_wme.wme_hipri_traffic++;
368266072Sdes			rxseq = le16toh(*(uint16_t *)wh->i_seq);
369266072Sdes			if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
370266072Sdes			    (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
371266072Sdes			    SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
372266072Sdes				/* duplicate, discard */
373266072Sdes				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
374266072Sdes				    bssid, "duplicate",
375266072Sdes				    "seqno <%u,%u> fragno <%u,%u> tid %u",
376266072Sdes				    rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
377266072Sdes				    ni->ni_rxseqs[tid] >>
378266072Sdes					IEEE80211_SEQ_SEQ_SHIFT,
379266072Sdes				    rxseq & IEEE80211_SEQ_FRAG_MASK,
380266072Sdes				    ni->ni_rxseqs[tid] &
381266072Sdes					IEEE80211_SEQ_FRAG_MASK,
382266072Sdes				    tid);
383266072Sdes				vap->iv_stats.is_rx_dup++;
384266072Sdes				IEEE80211_NODE_STAT(ni, rx_dup);
385266072Sdes				goto out;
386266072Sdes			}
387266072Sdes			ni->ni_rxseqs[tid] = rxseq;
388266072Sdes		}
389266072Sdes	}
390266072Sdes
391266072Sdes	switch (type) {
392266072Sdes	case IEEE80211_FC0_TYPE_DATA:
393266072Sdes		hdrspace = ieee80211_hdrspace(ic, wh);
394266072Sdes		if (m->m_len < hdrspace &&
395266072Sdes		    (m = m_pullup(m, hdrspace)) == NULL) {
396266072Sdes			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
397266072Sdes			    ni->ni_macaddr, NULL,
398266072Sdes			    "data too short: expecting %u", hdrspace);
399266072Sdes			vap->iv_stats.is_rx_tooshort++;
400266072Sdes			goto out;		/* XXX */
401266072Sdes		}
402266072Sdes		if (dir != IEEE80211_FC1_DIR_NODS) {
403266072Sdes			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
404266072Sdes			    wh, "data", "incorrect dir 0x%x", dir);
405266072Sdes			vap->iv_stats.is_rx_wrongdir++;
406266072Sdes			goto out;
407266072Sdes		}
408266072Sdes		/* XXX no power-save support */
409266072Sdes
410266072Sdes		/*
411266072Sdes		 * Handle A-MPDU re-ordering.  The station must be
412266072Sdes		 * associated and negotiated HT.  The frame must be
413266072Sdes		 * a QoS frame (not QoS null data) and not previously
414266072Sdes		 * processed for A-MPDU re-ordering.  If the frame is
415266072Sdes		 * to be processed directly then ieee80211_ampdu_reorder
416266072Sdes		 * will return 0; otherwise it has consumed the mbuf
417266072Sdes		 * and we should do nothing more with it.
418266072Sdes		 */
419266072Sdes		if ((ni->ni_flags & IEEE80211_NODE_HT) &&
420266072Sdes		    subtype == IEEE80211_FC0_SUBTYPE_QOS &&
421266072Sdes		    ieee80211_ampdu_reorder(ni, m) != 0) {
422266072Sdes			m = NULL;
423266072Sdes			goto out;
424266072Sdes		}
425266072Sdes	resubmit_ampdu:
426266072Sdes
427266072Sdes		/*
428266072Sdes		 * Handle privacy requirements.  Note that we
429266072Sdes		 * must not be preempted from here until after
430266072Sdes		 * we (potentially) call ieee80211_crypto_demic;
431266072Sdes		 * otherwise we may violate assumptions in the
432266072Sdes		 * crypto cipher modules used to do delayed update
433266072Sdes		 * of replay sequence numbers.
434266072Sdes		 */
435266072Sdes		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
436266072Sdes			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
437266072Sdes				/*
438266072Sdes				 * Discard encrypted frames when privacy is off.
439266072Sdes				 */
440266072Sdes				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
441266072Sdes				    wh, "WEP", "%s", "PRIVACY off");
442266072Sdes				vap->iv_stats.is_rx_noprivacy++;
443266072Sdes				IEEE80211_NODE_STAT(ni, rx_noprivacy);
444266072Sdes				goto out;
445266072Sdes			}
446266072Sdes			key = ieee80211_crypto_decap(ni, m, hdrspace);
447266072Sdes			if (key == NULL) {
448266072Sdes				/* NB: stats+msgs handled in crypto_decap */
449266072Sdes				IEEE80211_NODE_STAT(ni, rx_wepfail);
450266072Sdes				goto out;
451266072Sdes			}
452266072Sdes			wh = mtod(m, struct ieee80211_frame *);
453266072Sdes			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
454266072Sdes		} else {
455266072Sdes			/* XXX M_WEP and IEEE80211_F_PRIVACY */
456266072Sdes			key = NULL;
457266072Sdes		}
458266072Sdes
459266072Sdes		/*
460266072Sdes		 * Save QoS bits for use below--before we strip the header.
461266072Sdes		 */
462266072Sdes		if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
463266072Sdes			qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
464266072Sdes			    ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
465266072Sdes			    ((struct ieee80211_qosframe *)wh)->i_qos[0];
466266072Sdes		} else
467266072Sdes			qos = 0;
468266072Sdes
469266072Sdes		/*
470266072Sdes		 * Next up, any fragmentation.
471266072Sdes		 */
472266072Sdes		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
473266072Sdes			m = ieee80211_defrag(ni, m, hdrspace);
474266072Sdes			if (m == NULL) {
475266072Sdes				/* Fragment dropped or frame not complete yet */
476266072Sdes				goto out;
477266072Sdes			}
478266072Sdes		}
479266072Sdes		wh = NULL;		/* no longer valid, catch any uses */
480266072Sdes
481266072Sdes		/*
482266072Sdes		 * Next strip any MSDU crypto bits.
483266072Sdes		 */
484266072Sdes		if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
485266072Sdes			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
486266072Sdes			    ni->ni_macaddr, "data", "%s", "demic error");
487266072Sdes			vap->iv_stats.is_rx_demicfail++;
488266072Sdes			IEEE80211_NODE_STAT(ni, rx_demicfail);
489266072Sdes			goto out;
490266072Sdes		}
491266072Sdes
492266072Sdes		/* copy to listener after decrypt */
493266072Sdes		if (bpf_peers_present(vap->iv_rawbpf))
494266072Sdes			bpf_mtap(vap->iv_rawbpf, m);
495266072Sdes		need_tap = 0;
496266072Sdes
497266072Sdes		/*
498266072Sdes		 * Finally, strip the 802.11 header.
499266072Sdes		 */
500266072Sdes		m = ieee80211_decap(vap, m, hdrspace);
501266072Sdes		if (m == NULL) {
502266072Sdes			/* XXX mask bit to check for both */
503266072Sdes			/* don't count Null data frames as errors */
504266072Sdes			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
505266072Sdes			    subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
506266072Sdes				goto out;
507266072Sdes			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
508266072Sdes			    ni->ni_macaddr, "data", "%s", "decap error");
509266072Sdes			vap->iv_stats.is_rx_decap++;
510266072Sdes			IEEE80211_NODE_STAT(ni, rx_decap);
511266072Sdes			goto err;
512266072Sdes		}
513266072Sdes		eh = mtod(m, struct ether_header *);
514266072Sdes		if (!ieee80211_node_is_authorized(ni)) {
515266072Sdes			/*
516266072Sdes			 * Deny any non-PAE frames received prior to
517266072Sdes			 * authorization.  For open/shared-key
518266072Sdes			 * authentication the port is mark authorized
519266072Sdes			 * after authentication completes.  For 802.1x
520266072Sdes			 * the port is not marked authorized by the
521266072Sdes			 * authenticator until the handshake has completed.
522266072Sdes			 */
523266072Sdes			if (eh->ether_type != htons(ETHERTYPE_PAE)) {
524266072Sdes				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
525266072Sdes				    eh->ether_shost, "data",
526266072Sdes				    "unauthorized port: ether type 0x%x len %u",
527266072Sdes				    eh->ether_type, m->m_pkthdr.len);
528266072Sdes				vap->iv_stats.is_rx_unauth++;
529266072Sdes				IEEE80211_NODE_STAT(ni, rx_unauth);
530266072Sdes				goto err;
531266072Sdes			}
532266072Sdes		} else {
533266072Sdes			/*
534266072Sdes			 * When denying unencrypted frames, discard
535266072Sdes			 * any non-PAE frames received without encryption.
536266072Sdes			 */
537266072Sdes			if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
538266072Sdes			    (key == NULL && (m->m_flags & M_WEP) == 0) &&
539266072Sdes			    eh->ether_type != htons(ETHERTYPE_PAE)) {
540266072Sdes				/*
541266072Sdes				 * Drop unencrypted frames.
542266072Sdes				 */
543266072Sdes				vap->iv_stats.is_rx_unencrypted++;
544266072Sdes				IEEE80211_NODE_STAT(ni, rx_unencrypted);
545266072Sdes				goto out;
546266072Sdes			}
547266072Sdes		}
548266072Sdes		/* XXX require HT? */
549266072Sdes		if (qos & IEEE80211_QOS_AMSDU) {
550266072Sdes			m = ieee80211_decap_amsdu(ni, m);
551266072Sdes			if (m == NULL)
552266072Sdes				return IEEE80211_FC0_TYPE_DATA;
553266072Sdes		} else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
554266072Sdes#define	FF_LLC_SIZE	(sizeof(struct ether_header) + sizeof(struct llc))
555266072Sdes		    m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
556266072Sdes			struct llc *llc;
557266072Sdes
558266072Sdes			/*
559266072Sdes			 * Check for fast-frame tunnel encapsulation.
560266072Sdes			 */
561266072Sdes			if (m->m_len < FF_LLC_SIZE &&
562266072Sdes			    (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
563266072Sdes				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
564266072Sdes				    ni->ni_macaddr, "fast-frame",
565266072Sdes				    "%s", "m_pullup(llc) failed");
566266072Sdes				vap->iv_stats.is_rx_tooshort++;
567266072Sdes				return IEEE80211_FC0_TYPE_DATA;
568266072Sdes			}
569266072Sdes			llc = (struct llc *)(mtod(m, uint8_t *) +
570266072Sdes				sizeof(struct ether_header));
571266072Sdes			if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
572266072Sdes				m_adj(m, FF_LLC_SIZE);
573266072Sdes				m = ieee80211_decap_fastframe(ni, m);
574266072Sdes				if (m == NULL)
575266072Sdes					return IEEE80211_FC0_TYPE_DATA;
576266072Sdes			}
577266072Sdes		}
578266072Sdes#undef FF_LLC_SIZE
579266072Sdes		if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL)
580266072Sdes			ieee80211_deliver_data(ni->ni_wdsvap, ni, m);
581266072Sdes		else
582266072Sdes			ieee80211_deliver_data(vap, ni, m);
583266072Sdes		return IEEE80211_FC0_TYPE_DATA;
584266072Sdes
585266072Sdes	case IEEE80211_FC0_TYPE_MGT:
586266072Sdes		vap->iv_stats.is_rx_mgmt++;
587266072Sdes		IEEE80211_NODE_STAT(ni, rx_mgmt);
588266072Sdes		if (dir != IEEE80211_FC1_DIR_NODS) {
589266072Sdes			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
590266072Sdes			    wh, "data", "incorrect dir 0x%x", dir);
591266072Sdes			vap->iv_stats.is_rx_wrongdir++;
592266072Sdes			goto err;
593266072Sdes		}
594266072Sdes		if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
595266072Sdes			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
596266072Sdes			    ni->ni_macaddr, "mgt", "too short: len %u",
597266072Sdes			    m->m_pkthdr.len);
598266072Sdes			vap->iv_stats.is_rx_tooshort++;
599266072Sdes			goto out;
600266072Sdes		}
601266072Sdes#ifdef IEEE80211_DEBUG
602266072Sdes		if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
603266072Sdes		    ieee80211_msg_dumppkts(vap)) {
604266072Sdes			if_printf(ifp, "received %s from %s rssi %d\n",
605266072Sdes			    ieee80211_mgt_subtype_name[subtype >>
606266072Sdes				IEEE80211_FC0_SUBTYPE_SHIFT],
607266072Sdes			    ether_sprintf(wh->i_addr2), rssi);
608266072Sdes		}
609266072Sdes#endif
610266072Sdes		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
611266072Sdes			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
612266072Sdes			    wh, NULL, "%s", "WEP set but not permitted");
613266072Sdes			vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
614266072Sdes			goto out;
615266072Sdes		}
616266072Sdes		if (bpf_peers_present(vap->iv_rawbpf))
617266072Sdes			bpf_mtap(vap->iv_rawbpf, m);
618266072Sdes		/* NB: only IBSS mode gets mgt frames */
619266072Sdes		if (vap->iv_opmode == IEEE80211_M_IBSS)
620266072Sdes			vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
621266072Sdes		m_freem(m);
622266072Sdes		return IEEE80211_FC0_TYPE_MGT;
623266072Sdes
624266072Sdes	case IEEE80211_FC0_TYPE_CTL:
625266072Sdes		vap->iv_stats.is_rx_ctl++;
626266072Sdes		IEEE80211_NODE_STAT(ni, rx_ctrl);
627266072Sdes		goto out;
628266072Sdes	default:
629266072Sdes		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
630266072Sdes		    wh, "bad", "frame type 0x%x", type);
631266072Sdes		/* should not come here */
632266072Sdes		break;
633266072Sdes	}
634266072Sdeserr:
635266072Sdes	ifp->if_ierrors++;
636266072Sdesout:
637266072Sdes	if (m != NULL) {
638266072Sdes		if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
639266072Sdes			bpf_mtap(vap->iv_rawbpf, m);
640266072Sdes		m_freem(m);
641266072Sdes	}
642266072Sdes	return type;
643266072Sdes#undef SEQ_LEQ
644266072Sdes}
645266072Sdes
646266072Sdesstatic int
647266072Sdesis11bclient(const uint8_t *rates, const uint8_t *xrates)
648266072Sdes{
649266072Sdes	static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11);
650266072Sdes	int i;
651266072Sdes
652266072Sdes	/* NB: the 11b clients we care about will not have xrates */
653266072Sdes	if (xrates != NULL || rates == NULL)
654266072Sdes		return 0;
655266072Sdes	for (i = 0; i < rates[1]; i++) {
656266072Sdes		int r = rates[2+i] & IEEE80211_RATE_VAL;
657266072Sdes		if (r > 2*11 || ((1<<r) & brates) == 0)
658266072Sdes			return 0;
659266072Sdes	}
660266072Sdes	return 1;
661266072Sdes}
662266072Sdes
663266072Sdesstatic void
664266072Sdesadhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
665266072Sdes	int subtype, int rssi, int noise, uint32_t rstamp)
666266072Sdes{
667266072Sdes	struct ieee80211vap *vap = ni->ni_vap;
668266072Sdes	struct ieee80211com *ic = ni->ni_ic;
669266072Sdes	struct ieee80211_frame *wh;
670266072Sdes	uint8_t *frm, *efrm, *sfrm;
671266072Sdes	uint8_t *ssid, *rates, *xrates;
672266072Sdes
673266072Sdes	wh = mtod(m0, struct ieee80211_frame *);
674266072Sdes	frm = (uint8_t *)&wh[1];
675266072Sdes	efrm = mtod(m0, uint8_t *) + m0->m_len;
676266072Sdes	switch (subtype) {
677266072Sdes	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
678266072Sdes	case IEEE80211_FC0_SUBTYPE_BEACON: {
679266072Sdes		struct ieee80211_scanparams scan;
680266072Sdes		/*
681266072Sdes		 * We process beacon/probe response
682266072Sdes		 * frames to discover neighbors.
683266072Sdes		 */
684266072Sdes		if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
685266072Sdes			return;
686266072Sdes		/*
687266072Sdes		 * Count frame now that we know it's to be processed.
688266072Sdes		 */
689266072Sdes		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
690266072Sdes			vap->iv_stats.is_rx_beacon++;		/* XXX remove */
691266072Sdes			IEEE80211_NODE_STAT(ni, rx_beacons);
692266072Sdes		} else
693266072Sdes			IEEE80211_NODE_STAT(ni, rx_proberesp);
694266072Sdes		/*
695266072Sdes		 * If scanning, just pass information to the scan module.
696266072Sdes		 */
697266072Sdes		if (ic->ic_flags & IEEE80211_F_SCAN) {
698266072Sdes			if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
699266072Sdes				/*
700266072Sdes				 * Actively scanning a channel marked passive;
701266072Sdes				 * send a probe request now that we know there
702266072Sdes				 * is 802.11 traffic present.
703266072Sdes				 *
704266072Sdes				 * XXX check if the beacon we recv'd gives
705266072Sdes				 * us what we need and suppress the probe req
706266072Sdes				 */
707266072Sdes				ieee80211_probe_curchan(vap, 1);
708266072Sdes				ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
709266072Sdes			}
710266072Sdes			ieee80211_add_scan(vap, &scan, wh,
711266072Sdes				subtype, rssi, noise, rstamp);
712266072Sdes			return;
713266072Sdes		}
714266072Sdes		if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
715266072Sdes			if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
716266072Sdes				/*
717266072Sdes				 * Create a new entry in the neighbor table.
718266072Sdes				 */
719266072Sdes				ni = ieee80211_add_neighbor(vap, wh, &scan);
720266072Sdes			} else if (ni->ni_capinfo == 0) {
721266072Sdes				/*
722266072Sdes				 * Update faked node created on transmit.
723266072Sdes				 * Note this also updates the tsf.
724266072Sdes				 */
725266072Sdes				ieee80211_init_neighbor(ni, wh, &scan);
726266072Sdes			} else {
727266072Sdes				/*
728266072Sdes				 * Record tsf for potential resync.
729266072Sdes				 */
730266072Sdes				memcpy(ni->ni_tstamp.data, scan.tstamp,
731266072Sdes					sizeof(ni->ni_tstamp));
732266072Sdes			}
733266072Sdes			if (ni != NULL) {
734266072Sdes				IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
735266072Sdes				ni->ni_noise = noise;
736266072Sdes				ni->ni_rstamp = rstamp;
737266072Sdes			}
738266072Sdes		}
739266072Sdes		break;
740266072Sdes	}
741266072Sdes
742266072Sdes	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
743266072Sdes		if (vap->iv_state != IEEE80211_S_RUN) {
744266072Sdes			vap->iv_stats.is_rx_mgtdiscard++;
745266072Sdes			return;
746266072Sdes		}
747266072Sdes		if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
748266072Sdes			/* frame must be directed */
749266072Sdes			vap->iv_stats.is_rx_mgtdiscard++;	/* XXX stat */
750266072Sdes			return;
751266072Sdes		}
752266072Sdes
753266072Sdes		/*
754266072Sdes		 * prreq frame format
755266072Sdes		 *	[tlv] ssid
756266072Sdes		 *	[tlv] supported rates
757266072Sdes		 *	[tlv] extended supported rates
758266072Sdes		 */
759266072Sdes		ssid = rates = xrates = NULL;
760266072Sdes		sfrm = frm;
761266072Sdes		while (efrm - frm > 1) {
762266072Sdes			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
763266072Sdes			switch (*frm) {
764266072Sdes			case IEEE80211_ELEMID_SSID:
765266072Sdes				ssid = frm;
766266072Sdes				break;
767266072Sdes			case IEEE80211_ELEMID_RATES:
768266072Sdes				rates = frm;
769266072Sdes				break;
770266072Sdes			case IEEE80211_ELEMID_XRATES:
771266072Sdes				xrates = frm;
772266072Sdes				break;
773266072Sdes			}
774266072Sdes			frm += frm[1] + 2;
775266072Sdes		}
776266072Sdes		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
777266072Sdes		if (xrates != NULL)
778266072Sdes			IEEE80211_VERIFY_ELEMENT(xrates,
779266072Sdes				IEEE80211_RATE_MAXSIZE - rates[1], return);
780266072Sdes		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
781266072Sdes		IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return);
782266072Sdes		if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
783266072Sdes			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
784266072Sdes			    wh, NULL,
785266072Sdes			    "%s", "no ssid with ssid suppression enabled");
786266072Sdes			vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/
787266072Sdes			return;
788266072Sdes		}
789266072Sdes
790266072Sdes		/* XXX find a better class or define it's own */
791266072Sdes		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
792266072Sdes		    "%s", "recv probe req");
793266072Sdes		/*
794266072Sdes		 * Some legacy 11b clients cannot hack a complete
795266072Sdes		 * probe response frame.  When the request includes
796266072Sdes		 * only a bare-bones rate set, communicate this to
797266072Sdes		 * the transmit side.
798266072Sdes		 */
799266072Sdes		ieee80211_send_proberesp(vap, wh->i_addr2,
800266072Sdes		    is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0);
801266072Sdes		break;
802266072Sdes
803266072Sdes	case IEEE80211_FC0_SUBTYPE_ACTION: {
804266072Sdes		const struct ieee80211_action *ia;
805266072Sdes
806266072Sdes		if (vap->iv_state != IEEE80211_S_RUN) {
807266072Sdes			vap->iv_stats.is_rx_mgtdiscard++;
808266072Sdes			return;
809266072Sdes		}
810266072Sdes		/*
811266072Sdes		 * action frame format:
812266072Sdes		 *	[1] category
813266072Sdes		 *	[1] action
814266072Sdes		 *	[tlv] parameters
815266072Sdes		 */
816266072Sdes		IEEE80211_VERIFY_LENGTH(efrm - frm,
817266072Sdes			sizeof(struct ieee80211_action), return);
818266072Sdes		ia = (const struct ieee80211_action *) frm;
819266072Sdes
820266072Sdes		vap->iv_stats.is_rx_action++;
821266072Sdes		IEEE80211_NODE_STAT(ni, rx_action);
822266072Sdes
823266072Sdes		/* verify frame payloads but defer processing */
824266072Sdes		/* XXX maybe push this to method */
825266072Sdes		switch (ia->ia_category) {
826266072Sdes		case IEEE80211_ACTION_CAT_BA:
827266072Sdes			switch (ia->ia_action) {
828266072Sdes			case IEEE80211_ACTION_BA_ADDBA_REQUEST:
829266072Sdes				IEEE80211_VERIFY_LENGTH(efrm - frm,
830266072Sdes				    sizeof(struct ieee80211_action_ba_addbarequest),
831266072Sdes				    return);
832266072Sdes				break;
833266072Sdes			case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
834266072Sdes				IEEE80211_VERIFY_LENGTH(efrm - frm,
835266072Sdes				    sizeof(struct ieee80211_action_ba_addbaresponse),
836266072Sdes				    return);
837266072Sdes				break;
838266072Sdes			case IEEE80211_ACTION_BA_DELBA:
839266072Sdes				IEEE80211_VERIFY_LENGTH(efrm - frm,
840266072Sdes				    sizeof(struct ieee80211_action_ba_delba),
841266072Sdes				    return);
842266072Sdes				break;
843266072Sdes			}
844266072Sdes			break;
845266072Sdes		case IEEE80211_ACTION_CAT_HT:
846266072Sdes			switch (ia->ia_action) {
847266072Sdes			case IEEE80211_ACTION_HT_TXCHWIDTH:
848266072Sdes				IEEE80211_VERIFY_LENGTH(efrm - frm,
849266072Sdes				    sizeof(struct ieee80211_action_ht_txchwidth),
850266072Sdes				    return);
851266072Sdes				break;
852266072Sdes			}
853266072Sdes			break;
854266072Sdes		}
855266072Sdes		ic->ic_recv_action(ni, frm, efrm);
856266072Sdes		break;
857266072Sdes	}
858266072Sdes
859266072Sdes	case IEEE80211_FC0_SUBTYPE_AUTH:
860266072Sdes	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
861266072Sdes	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
862266072Sdes	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
863266072Sdes	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
864266072Sdes	case IEEE80211_FC0_SUBTYPE_DEAUTH:
865266072Sdes	case IEEE80211_FC0_SUBTYPE_DISASSOC:
866266072Sdes		vap->iv_stats.is_rx_mgtdiscard++;
867266072Sdes		return;
868266072Sdes
869266072Sdes	default:
870266072Sdes		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
871266072Sdes		     wh, "mgt", "subtype 0x%x not handled", subtype);
872266072Sdes		vap->iv_stats.is_rx_badsubtype++;
873266072Sdes		break;
874266072Sdes	}
875266072Sdes}
876266072Sdes#undef IEEE80211_VERIFY_LENGTH
877266072Sdes#undef IEEE80211_VERIFY_ELEMENT
878266072Sdes