xref: /haiku/src/libs/compat/freebsd_wlan/net80211/ieee80211_adhoc.c (revision da3ca31854ae1f34c46d4d29e44837da03d8cafa)
1753c7e08SAugustin Cavalier /*-
286021fd4SAugustin Cavalier  * SPDX-License-Identifier: BSD-2-Clause
38244a9baSAugustin Cavalier  *
4753c7e08SAugustin Cavalier  * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
5753c7e08SAugustin Cavalier  * All rights reserved.
6753c7e08SAugustin Cavalier  *
7753c7e08SAugustin Cavalier  * Redistribution and use in source and binary forms, with or without
8753c7e08SAugustin Cavalier  * modification, are permitted provided that the following conditions
9753c7e08SAugustin Cavalier  * are met:
10753c7e08SAugustin Cavalier  * 1. Redistributions of source code must retain the above copyright
11753c7e08SAugustin Cavalier  *    notice, this list of conditions and the following disclaimer.
12753c7e08SAugustin Cavalier  * 2. Redistributions in binary form must reproduce the above copyright
13753c7e08SAugustin Cavalier  *    notice, this list of conditions and the following disclaimer in the
14753c7e08SAugustin Cavalier  *    documentation and/or other materials provided with the distribution.
15753c7e08SAugustin Cavalier  *
16753c7e08SAugustin Cavalier  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17753c7e08SAugustin Cavalier  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18753c7e08SAugustin Cavalier  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19753c7e08SAugustin Cavalier  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20753c7e08SAugustin Cavalier  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21753c7e08SAugustin Cavalier  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22753c7e08SAugustin Cavalier  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23753c7e08SAugustin Cavalier  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24753c7e08SAugustin Cavalier  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25753c7e08SAugustin Cavalier  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26753c7e08SAugustin Cavalier  */
27753c7e08SAugustin Cavalier 
28753c7e08SAugustin Cavalier /*
29753c7e08SAugustin Cavalier  * IEEE 802.11 IBSS mode support.
30753c7e08SAugustin Cavalier  */
31753c7e08SAugustin Cavalier #include "opt_inet.h"
32753c7e08SAugustin Cavalier #include "opt_wlan.h"
33753c7e08SAugustin Cavalier 
34753c7e08SAugustin Cavalier #include <sys/param.h>
35753c7e08SAugustin Cavalier #include <sys/systm.h>
36753c7e08SAugustin Cavalier #include <sys/mbuf.h>
37753c7e08SAugustin Cavalier #include <sys/malloc.h>
38753c7e08SAugustin Cavalier #include <sys/kernel.h>
39753c7e08SAugustin Cavalier 
40753c7e08SAugustin Cavalier #include <sys/socket.h>
41753c7e08SAugustin Cavalier #include <sys/sockio.h>
42753c7e08SAugustin Cavalier #include <sys/endian.h>
43753c7e08SAugustin Cavalier #include <sys/errno.h>
44753c7e08SAugustin Cavalier #include <sys/proc.h>
45753c7e08SAugustin Cavalier #include <sys/sysctl.h>
46753c7e08SAugustin Cavalier 
47753c7e08SAugustin Cavalier #include <net/if.h>
48753c7e08SAugustin Cavalier #include <net/if_var.h>
49753c7e08SAugustin Cavalier #include <net/if_media.h>
50753c7e08SAugustin Cavalier #include <net/if_llc.h>
5186021fd4SAugustin Cavalier #include <net/if_private.h>
52753c7e08SAugustin Cavalier #include <net/ethernet.h>
53753c7e08SAugustin Cavalier 
54753c7e08SAugustin Cavalier #include <net/bpf.h>
55753c7e08SAugustin Cavalier 
56753c7e08SAugustin Cavalier #include <net80211/ieee80211_var.h>
57753c7e08SAugustin Cavalier #include <net80211/ieee80211_adhoc.h>
58753c7e08SAugustin Cavalier #include <net80211/ieee80211_input.h>
59753c7e08SAugustin Cavalier #ifdef IEEE80211_SUPPORT_SUPERG
60753c7e08SAugustin Cavalier #include <net80211/ieee80211_superg.h>
61753c7e08SAugustin Cavalier #endif
62753c7e08SAugustin Cavalier #ifdef IEEE80211_SUPPORT_TDMA
63753c7e08SAugustin Cavalier #include <net80211/ieee80211_tdma.h>
64753c7e08SAugustin Cavalier #endif
65753c7e08SAugustin Cavalier #include <net80211/ieee80211_sta.h>
66753c7e08SAugustin Cavalier 
67753c7e08SAugustin Cavalier #define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
68753c7e08SAugustin Cavalier 
69753c7e08SAugustin Cavalier static	void adhoc_vattach(struct ieee80211vap *);
70753c7e08SAugustin Cavalier static	int adhoc_newstate(struct ieee80211vap *, enum ieee80211_state, int);
71753c7e08SAugustin Cavalier static int adhoc_input(struct ieee80211_node *, struct mbuf *,
72753c7e08SAugustin Cavalier 	    const struct ieee80211_rx_stats *, int, int);
73753c7e08SAugustin Cavalier static void adhoc_recv_mgmt(struct ieee80211_node *, struct mbuf *,
74753c7e08SAugustin Cavalier 	int subtype, const struct ieee80211_rx_stats *, int, int);
75753c7e08SAugustin Cavalier static void ahdemo_recv_mgmt(struct ieee80211_node *, struct mbuf *,
76753c7e08SAugustin Cavalier 	    int subtype, const struct ieee80211_rx_stats *rxs, int, int);
77753c7e08SAugustin Cavalier static void adhoc_recv_ctl(struct ieee80211_node *, struct mbuf *, int subtype);
78753c7e08SAugustin Cavalier 
79753c7e08SAugustin Cavalier void
ieee80211_adhoc_attach(struct ieee80211com * ic)80753c7e08SAugustin Cavalier ieee80211_adhoc_attach(struct ieee80211com *ic)
81753c7e08SAugustin Cavalier {
82753c7e08SAugustin Cavalier 	ic->ic_vattach[IEEE80211_M_IBSS] = adhoc_vattach;
83753c7e08SAugustin Cavalier 	ic->ic_vattach[IEEE80211_M_AHDEMO] = adhoc_vattach;
84753c7e08SAugustin Cavalier }
85753c7e08SAugustin Cavalier 
86753c7e08SAugustin Cavalier void
ieee80211_adhoc_detach(struct ieee80211com * ic)87753c7e08SAugustin Cavalier ieee80211_adhoc_detach(struct ieee80211com *ic)
88753c7e08SAugustin Cavalier {
89753c7e08SAugustin Cavalier }
90753c7e08SAugustin Cavalier 
91753c7e08SAugustin Cavalier static void
adhoc_vdetach(struct ieee80211vap * vap)92753c7e08SAugustin Cavalier adhoc_vdetach(struct ieee80211vap *vap)
93753c7e08SAugustin Cavalier {
94753c7e08SAugustin Cavalier }
95753c7e08SAugustin Cavalier 
96753c7e08SAugustin Cavalier static void
adhoc_vattach(struct ieee80211vap * vap)97753c7e08SAugustin Cavalier adhoc_vattach(struct ieee80211vap *vap)
98753c7e08SAugustin Cavalier {
99753c7e08SAugustin Cavalier 	vap->iv_newstate = adhoc_newstate;
100753c7e08SAugustin Cavalier 	vap->iv_input = adhoc_input;
101753c7e08SAugustin Cavalier 	if (vap->iv_opmode == IEEE80211_M_IBSS)
102753c7e08SAugustin Cavalier 		vap->iv_recv_mgmt = adhoc_recv_mgmt;
103753c7e08SAugustin Cavalier 	else
104753c7e08SAugustin Cavalier 		vap->iv_recv_mgmt = ahdemo_recv_mgmt;
105753c7e08SAugustin Cavalier 	vap->iv_recv_ctl = adhoc_recv_ctl;
106753c7e08SAugustin Cavalier 	vap->iv_opdetach = adhoc_vdetach;
107753c7e08SAugustin Cavalier #ifdef IEEE80211_SUPPORT_TDMA
108753c7e08SAugustin Cavalier 	/*
109753c7e08SAugustin Cavalier 	 * Throw control to tdma support.  Note we do this
110753c7e08SAugustin Cavalier 	 * after setting up our callbacks so it can piggyback
111753c7e08SAugustin Cavalier 	 * on top of us.
112753c7e08SAugustin Cavalier 	 */
113753c7e08SAugustin Cavalier 	if (vap->iv_caps & IEEE80211_C_TDMA)
114753c7e08SAugustin Cavalier 		ieee80211_tdma_vattach(vap);
115753c7e08SAugustin Cavalier #endif
116753c7e08SAugustin Cavalier }
117753c7e08SAugustin Cavalier 
118753c7e08SAugustin Cavalier static void
sta_leave(void * arg,struct ieee80211_node * ni)119753c7e08SAugustin Cavalier sta_leave(void *arg, struct ieee80211_node *ni)
120753c7e08SAugustin Cavalier {
1218244a9baSAugustin Cavalier 	struct ieee80211vap *vap = ni->ni_vap;
122753c7e08SAugustin Cavalier 
1238244a9baSAugustin Cavalier 	if (ni != vap->iv_bss)
124753c7e08SAugustin Cavalier 		ieee80211_node_leave(ni);
125753c7e08SAugustin Cavalier }
126753c7e08SAugustin Cavalier 
127753c7e08SAugustin Cavalier /*
128753c7e08SAugustin Cavalier  * IEEE80211_M_IBSS+IEEE80211_M_AHDEMO vap state machine handler.
129753c7e08SAugustin Cavalier  */
130753c7e08SAugustin Cavalier static int
adhoc_newstate(struct ieee80211vap * vap,enum ieee80211_state nstate,int arg)131753c7e08SAugustin Cavalier adhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
132753c7e08SAugustin Cavalier {
133753c7e08SAugustin Cavalier 	struct ieee80211com *ic = vap->iv_ic;
134753c7e08SAugustin Cavalier 	struct ieee80211_node *ni;
135753c7e08SAugustin Cavalier 	enum ieee80211_state ostate;
136753c7e08SAugustin Cavalier 
137753c7e08SAugustin Cavalier 	IEEE80211_LOCK_ASSERT(vap->iv_ic);
138753c7e08SAugustin Cavalier 
139753c7e08SAugustin Cavalier 	ostate = vap->iv_state;
140753c7e08SAugustin Cavalier 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
141753c7e08SAugustin Cavalier 	    __func__, ieee80211_state_name[ostate],
142753c7e08SAugustin Cavalier 	    ieee80211_state_name[nstate], arg);
143753c7e08SAugustin Cavalier 	vap->iv_state = nstate;			/* state transition */
144753c7e08SAugustin Cavalier 	if (ostate != IEEE80211_S_SCAN)
145753c7e08SAugustin Cavalier 		ieee80211_cancel_scan(vap);	/* background scan */
146753c7e08SAugustin Cavalier 	ni = vap->iv_bss;			/* NB: no reference held */
147753c7e08SAugustin Cavalier 	switch (nstate) {
148753c7e08SAugustin Cavalier 	case IEEE80211_S_INIT:
149753c7e08SAugustin Cavalier 		switch (ostate) {
150753c7e08SAugustin Cavalier 		case IEEE80211_S_SCAN:
151753c7e08SAugustin Cavalier 			ieee80211_cancel_scan(vap);
152753c7e08SAugustin Cavalier 			break;
153753c7e08SAugustin Cavalier 		default:
154753c7e08SAugustin Cavalier 			break;
155753c7e08SAugustin Cavalier 		}
156753c7e08SAugustin Cavalier 		if (ostate != IEEE80211_S_INIT) {
157753c7e08SAugustin Cavalier 			/* NB: optimize INIT -> INIT case */
158753c7e08SAugustin Cavalier 			ieee80211_reset_bss(vap);
159753c7e08SAugustin Cavalier 		}
160753c7e08SAugustin Cavalier 		break;
161753c7e08SAugustin Cavalier 	case IEEE80211_S_SCAN:
162753c7e08SAugustin Cavalier 		switch (ostate) {
163753c7e08SAugustin Cavalier 		case IEEE80211_S_RUN:		/* beacon miss */
164753c7e08SAugustin Cavalier 			/* purge station table; entries are stale */
1658244a9baSAugustin Cavalier 			ieee80211_iterate_nodes_vap(&ic->ic_sta, vap,
1668244a9baSAugustin Cavalier 			    sta_leave, NULL);
167753c7e08SAugustin Cavalier 			/* fall thru... */
168753c7e08SAugustin Cavalier 		case IEEE80211_S_INIT:
169753c7e08SAugustin Cavalier 			if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
170753c7e08SAugustin Cavalier 			    !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
171753c7e08SAugustin Cavalier 				/*
172753c7e08SAugustin Cavalier 				 * Already have a channel; bypass the
173753c7e08SAugustin Cavalier 				 * scan and startup immediately.
174753c7e08SAugustin Cavalier 				 */
175753c7e08SAugustin Cavalier 				ieee80211_create_ibss(vap,
176753c7e08SAugustin Cavalier 				    ieee80211_ht_adjust_channel(ic,
177753c7e08SAugustin Cavalier 				    vap->iv_des_chan, vap->iv_flags_ht));
178753c7e08SAugustin Cavalier 				break;
179753c7e08SAugustin Cavalier 			}
180753c7e08SAugustin Cavalier 			/*
181753c7e08SAugustin Cavalier 			 * Initiate a scan.  We can come here as a result
182753c7e08SAugustin Cavalier 			 * of an IEEE80211_IOC_SCAN_REQ too in which case
183753c7e08SAugustin Cavalier 			 * the vap will be marked with IEEE80211_FEXT_SCANREQ
184753c7e08SAugustin Cavalier 			 * and the scan request parameters will be present
185753c7e08SAugustin Cavalier 			 * in iv_scanreq.  Otherwise we do the default.
186753c7e08SAugustin Cavalier 			 */
187753c7e08SAugustin Cavalier 			if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
188753c7e08SAugustin Cavalier 				ieee80211_check_scan(vap,
189753c7e08SAugustin Cavalier 				    vap->iv_scanreq_flags,
190753c7e08SAugustin Cavalier 				    vap->iv_scanreq_duration,
191753c7e08SAugustin Cavalier 				    vap->iv_scanreq_mindwell,
192753c7e08SAugustin Cavalier 				    vap->iv_scanreq_maxdwell,
193753c7e08SAugustin Cavalier 				    vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
194753c7e08SAugustin Cavalier 				vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
195753c7e08SAugustin Cavalier 			} else
196753c7e08SAugustin Cavalier 				ieee80211_check_scan_current(vap);
197753c7e08SAugustin Cavalier 			break;
198753c7e08SAugustin Cavalier 		case IEEE80211_S_SCAN:
199753c7e08SAugustin Cavalier 			/*
200753c7e08SAugustin Cavalier 			 * This can happen because of a change in state
201753c7e08SAugustin Cavalier 			 * that requires a reset.  Trigger a new scan
202753c7e08SAugustin Cavalier 			 * unless we're in manual roaming mode in which
203753c7e08SAugustin Cavalier 			 * case an application must issue an explicit request.
204753c7e08SAugustin Cavalier 			 */
205753c7e08SAugustin Cavalier 			if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
206753c7e08SAugustin Cavalier 				ieee80211_check_scan_current(vap);
207753c7e08SAugustin Cavalier 			break;
208753c7e08SAugustin Cavalier 		default:
209753c7e08SAugustin Cavalier 			goto invalid;
210753c7e08SAugustin Cavalier 		}
211753c7e08SAugustin Cavalier 		break;
212753c7e08SAugustin Cavalier 	case IEEE80211_S_RUN:
213753c7e08SAugustin Cavalier 		if (vap->iv_flags & IEEE80211_F_WPA) {
214753c7e08SAugustin Cavalier 			/* XXX validate prerequisites */
215753c7e08SAugustin Cavalier 		}
216753c7e08SAugustin Cavalier 		switch (ostate) {
2178244a9baSAugustin Cavalier 		case IEEE80211_S_INIT:
2188244a9baSAugustin Cavalier 			/*
2198244a9baSAugustin Cavalier 			 * Already have a channel; bypass the
2208244a9baSAugustin Cavalier 			 * scan and startup immediately.
2218244a9baSAugustin Cavalier 			 * Note that ieee80211_create_ibss will call
2228244a9baSAugustin Cavalier 			 * back to do a RUN->RUN state change.
2238244a9baSAugustin Cavalier 			 */
2248244a9baSAugustin Cavalier 			ieee80211_create_ibss(vap,
2258244a9baSAugustin Cavalier 			    ieee80211_ht_adjust_channel(ic,
2268244a9baSAugustin Cavalier 				ic->ic_curchan, vap->iv_flags_ht));
2278244a9baSAugustin Cavalier 			/* NB: iv_bss is changed on return */
2288244a9baSAugustin Cavalier 			ni = vap->iv_bss;
2298244a9baSAugustin Cavalier 			break;
230753c7e08SAugustin Cavalier 		case IEEE80211_S_SCAN:
231753c7e08SAugustin Cavalier #ifdef IEEE80211_DEBUG
232753c7e08SAugustin Cavalier 			if (ieee80211_msg_debug(vap)) {
233753c7e08SAugustin Cavalier 				ieee80211_note(vap,
234753c7e08SAugustin Cavalier 				    "synchronized with %s ssid ",
235753c7e08SAugustin Cavalier 				    ether_sprintf(ni->ni_bssid));
236753c7e08SAugustin Cavalier 				ieee80211_print_essid(vap->iv_bss->ni_essid,
237753c7e08SAugustin Cavalier 				    ni->ni_esslen);
238753c7e08SAugustin Cavalier 				/* XXX MCS/HT */
239753c7e08SAugustin Cavalier 				printf(" channel %d start %uMb\n",
240753c7e08SAugustin Cavalier 				    ieee80211_chan2ieee(ic, ic->ic_curchan),
241753c7e08SAugustin Cavalier 				    IEEE80211_RATE2MBS(ni->ni_txrate));
242753c7e08SAugustin Cavalier 			}
243753c7e08SAugustin Cavalier #endif
244753c7e08SAugustin Cavalier 			break;
245753c7e08SAugustin Cavalier 		case IEEE80211_S_RUN:	/* IBSS merge */
246753c7e08SAugustin Cavalier 			break;
247753c7e08SAugustin Cavalier 		default:
248753c7e08SAugustin Cavalier 			goto invalid;
249753c7e08SAugustin Cavalier 		}
250753c7e08SAugustin Cavalier 		/*
251753c7e08SAugustin Cavalier 		 * When 802.1x is not in use mark the port authorized
252753c7e08SAugustin Cavalier 		 * at this point so traffic can flow.
253753c7e08SAugustin Cavalier 		 */
254753c7e08SAugustin Cavalier 		if (ni->ni_authmode != IEEE80211_AUTH_8021X)
255753c7e08SAugustin Cavalier 			ieee80211_node_authorize(ni);
256753c7e08SAugustin Cavalier 		/*
257753c7e08SAugustin Cavalier 		 * Fake association when joining an existing bss.
258753c7e08SAugustin Cavalier 		 */
259753c7e08SAugustin Cavalier 		if (!IEEE80211_ADDR_EQ(ni->ni_macaddr, vap->iv_myaddr) &&
260753c7e08SAugustin Cavalier 		    ic->ic_newassoc != NULL)
261753c7e08SAugustin Cavalier 			ic->ic_newassoc(ni, ostate != IEEE80211_S_RUN);
262753c7e08SAugustin Cavalier 		break;
263753c7e08SAugustin Cavalier 	case IEEE80211_S_SLEEP:
264753c7e08SAugustin Cavalier 		vap->iv_sta_ps(vap, 0);
265753c7e08SAugustin Cavalier 		break;
266753c7e08SAugustin Cavalier 	default:
267753c7e08SAugustin Cavalier 	invalid:
268753c7e08SAugustin Cavalier 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
269753c7e08SAugustin Cavalier 		    "%s: unexpected state transition %s -> %s\n", __func__,
270753c7e08SAugustin Cavalier 		    ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
271753c7e08SAugustin Cavalier 		break;
272753c7e08SAugustin Cavalier 	}
273753c7e08SAugustin Cavalier 	return 0;
274753c7e08SAugustin Cavalier }
275753c7e08SAugustin Cavalier 
276753c7e08SAugustin Cavalier /*
277753c7e08SAugustin Cavalier  * Decide if a received management frame should be
278753c7e08SAugustin Cavalier  * printed when debugging is enabled.  This filters some
279753c7e08SAugustin Cavalier  * of the less interesting frames that come frequently
280753c7e08SAugustin Cavalier  * (e.g. beacons).
281753c7e08SAugustin Cavalier  */
282753c7e08SAugustin Cavalier static __inline int
doprint(struct ieee80211vap * vap,int subtype)283753c7e08SAugustin Cavalier doprint(struct ieee80211vap *vap, int subtype)
284753c7e08SAugustin Cavalier {
285753c7e08SAugustin Cavalier 	switch (subtype) {
286753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_BEACON:
287753c7e08SAugustin Cavalier 		return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
288753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
289753c7e08SAugustin Cavalier 		return 1;
290753c7e08SAugustin Cavalier 	}
291753c7e08SAugustin Cavalier 	return 1;
292753c7e08SAugustin Cavalier }
293753c7e08SAugustin Cavalier 
294753c7e08SAugustin Cavalier /*
295753c7e08SAugustin Cavalier  * Process a received frame.  The node associated with the sender
296753c7e08SAugustin Cavalier  * should be supplied.  If nothing was found in the node table then
297753c7e08SAugustin Cavalier  * the caller is assumed to supply a reference to iv_bss instead.
298753c7e08SAugustin Cavalier  * The RSSI and a timestamp are also supplied.  The RSSI data is used
299753c7e08SAugustin Cavalier  * during AP scanning to select a AP to associate with; it can have
300753c7e08SAugustin Cavalier  * any units so long as values have consistent units and higher values
301753c7e08SAugustin Cavalier  * mean ``better signal''.  The receive timestamp is currently not used
302753c7e08SAugustin Cavalier  * by the 802.11 layer.
303753c7e08SAugustin Cavalier  */
304753c7e08SAugustin Cavalier static int
adhoc_input(struct ieee80211_node * ni,struct mbuf * m,const struct ieee80211_rx_stats * rxs,int rssi,int nf)305753c7e08SAugustin Cavalier adhoc_input(struct ieee80211_node *ni, struct mbuf *m,
306753c7e08SAugustin Cavalier     const struct ieee80211_rx_stats *rxs, int rssi, int nf)
307753c7e08SAugustin Cavalier {
308753c7e08SAugustin Cavalier 	struct ieee80211vap *vap = ni->ni_vap;
309753c7e08SAugustin Cavalier 	struct ieee80211com *ic = ni->ni_ic;
310753c7e08SAugustin Cavalier 	struct ifnet *ifp = vap->iv_ifp;
311753c7e08SAugustin Cavalier 	struct ieee80211_frame *wh;
312753c7e08SAugustin Cavalier 	struct ieee80211_key *key;
313753c7e08SAugustin Cavalier 	struct ether_header *eh;
314753c7e08SAugustin Cavalier 	int hdrspace, need_tap = 1;	/* mbuf need to be tapped. */
315753c7e08SAugustin Cavalier 	uint8_t dir, type, subtype, qos;
316753c7e08SAugustin Cavalier 	uint8_t *bssid;
3178244a9baSAugustin Cavalier 	int is_hw_decrypted = 0;
3188244a9baSAugustin Cavalier 	int has_decrypted = 0;
3198244a9baSAugustin Cavalier 
3208244a9baSAugustin Cavalier 	/*
3218244a9baSAugustin Cavalier 	 * Some devices do hardware decryption all the way through
3228244a9baSAugustin Cavalier 	 * to pretending the frame wasn't encrypted in the first place.
3238244a9baSAugustin Cavalier 	 * So, tag it appropriately so it isn't discarded inappropriately.
3248244a9baSAugustin Cavalier 	 */
3258244a9baSAugustin Cavalier 	if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED))
3268244a9baSAugustin Cavalier 		is_hw_decrypted = 1;
327753c7e08SAugustin Cavalier 
328753c7e08SAugustin Cavalier 	if (m->m_flags & M_AMPDU_MPDU) {
329753c7e08SAugustin Cavalier 		/*
330753c7e08SAugustin Cavalier 		 * Fastpath for A-MPDU reorder q resubmission.  Frames
331753c7e08SAugustin Cavalier 		 * w/ M_AMPDU_MPDU marked have already passed through
332753c7e08SAugustin Cavalier 		 * here but were received out of order and been held on
333753c7e08SAugustin Cavalier 		 * the reorder queue.  When resubmitted they are marked
334753c7e08SAugustin Cavalier 		 * with the M_AMPDU_MPDU flag and we can bypass most of
335753c7e08SAugustin Cavalier 		 * the normal processing.
336753c7e08SAugustin Cavalier 		 */
337753c7e08SAugustin Cavalier 		wh = mtod(m, struct ieee80211_frame *);
338753c7e08SAugustin Cavalier 		type = IEEE80211_FC0_TYPE_DATA;
339753c7e08SAugustin Cavalier 		dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
34086021fd4SAugustin Cavalier 		subtype = IEEE80211_FC0_SUBTYPE_QOS_DATA;
341753c7e08SAugustin Cavalier 		hdrspace = ieee80211_hdrspace(ic, wh);	/* XXX optimize? */
342753c7e08SAugustin Cavalier 		goto resubmit_ampdu;
343753c7e08SAugustin Cavalier 	}
344753c7e08SAugustin Cavalier 
345753c7e08SAugustin Cavalier 	KASSERT(ni != NULL, ("null node"));
346753c7e08SAugustin Cavalier 	ni->ni_inact = ni->ni_inact_reload;
347753c7e08SAugustin Cavalier 
348753c7e08SAugustin Cavalier 	type = -1;			/* undefined */
349753c7e08SAugustin Cavalier 
350753c7e08SAugustin Cavalier 	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
351753c7e08SAugustin Cavalier 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
352753c7e08SAugustin Cavalier 		    ni->ni_macaddr, NULL,
353753c7e08SAugustin Cavalier 		    "too short (1): len %u", m->m_pkthdr.len);
354753c7e08SAugustin Cavalier 		vap->iv_stats.is_rx_tooshort++;
355753c7e08SAugustin Cavalier 		goto out;
356753c7e08SAugustin Cavalier 	}
357753c7e08SAugustin Cavalier 	/*
358753c7e08SAugustin Cavalier 	 * Bit of a cheat here, we use a pointer for a 3-address
359753c7e08SAugustin Cavalier 	 * frame format but don't reference fields past outside
360753c7e08SAugustin Cavalier 	 * ieee80211_frame_min w/o first validating the data is
361753c7e08SAugustin Cavalier 	 * present.
362753c7e08SAugustin Cavalier 	 */
363753c7e08SAugustin Cavalier 	wh = mtod(m, struct ieee80211_frame *);
364753c7e08SAugustin Cavalier 
365*da3ca318SAugustin Cavalier 	if (!IEEE80211_IS_FC0_CHECK_VER(wh, IEEE80211_FC0_VERSION_0)) {
366753c7e08SAugustin Cavalier 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
367753c7e08SAugustin Cavalier 		    ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x",
368753c7e08SAugustin Cavalier 		    wh->i_fc[0], wh->i_fc[1]);
369753c7e08SAugustin Cavalier 		vap->iv_stats.is_rx_badversion++;
370753c7e08SAugustin Cavalier 		goto err;
371753c7e08SAugustin Cavalier 	}
372753c7e08SAugustin Cavalier 
373753c7e08SAugustin Cavalier 	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
374753c7e08SAugustin Cavalier 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
375753c7e08SAugustin Cavalier 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
376753c7e08SAugustin Cavalier 	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
377753c7e08SAugustin Cavalier 		if (dir != IEEE80211_FC1_DIR_NODS)
378753c7e08SAugustin Cavalier 			bssid = wh->i_addr1;
379753c7e08SAugustin Cavalier 		else if (type == IEEE80211_FC0_TYPE_CTL)
380753c7e08SAugustin Cavalier 			bssid = wh->i_addr1;
381753c7e08SAugustin Cavalier 		else {
382753c7e08SAugustin Cavalier 			if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
383753c7e08SAugustin Cavalier 				IEEE80211_DISCARD_MAC(vap,
384753c7e08SAugustin Cavalier 				    IEEE80211_MSG_ANY, ni->ni_macaddr,
385753c7e08SAugustin Cavalier 				    NULL, "too short (2): len %u",
386753c7e08SAugustin Cavalier 				    m->m_pkthdr.len);
387753c7e08SAugustin Cavalier 				vap->iv_stats.is_rx_tooshort++;
388753c7e08SAugustin Cavalier 				goto out;
389753c7e08SAugustin Cavalier 			}
390753c7e08SAugustin Cavalier 			bssid = wh->i_addr3;
391753c7e08SAugustin Cavalier 		}
392753c7e08SAugustin Cavalier 		/*
393753c7e08SAugustin Cavalier 		 * Validate the bssid.
394753c7e08SAugustin Cavalier 		 */
395753c7e08SAugustin Cavalier 		if (!(type == IEEE80211_FC0_TYPE_MGT &&
396753c7e08SAugustin Cavalier 		     (subtype == IEEE80211_FC0_SUBTYPE_BEACON ||
397753c7e08SAugustin Cavalier 		      subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) &&
398753c7e08SAugustin Cavalier 		    !IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) &&
399753c7e08SAugustin Cavalier 		    !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
400753c7e08SAugustin Cavalier 			/* not interested in */
401753c7e08SAugustin Cavalier 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
402753c7e08SAugustin Cavalier 			    bssid, NULL, "%s", "not to bss");
403753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_wrongbss++;
404753c7e08SAugustin Cavalier 			goto out;
405753c7e08SAugustin Cavalier 		}
406753c7e08SAugustin Cavalier 		/*
407753c7e08SAugustin Cavalier 		 * Data frame, cons up a node when it doesn't
408753c7e08SAugustin Cavalier 		 * exist. This should probably done after an ACL check.
409753c7e08SAugustin Cavalier 		 */
410753c7e08SAugustin Cavalier 		if (type == IEEE80211_FC0_TYPE_DATA &&
411753c7e08SAugustin Cavalier 		    ni == vap->iv_bss &&
412753c7e08SAugustin Cavalier 		    !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
413753c7e08SAugustin Cavalier 			/*
414753c7e08SAugustin Cavalier 			 * Beware of frames that come in too early; we
415753c7e08SAugustin Cavalier 			 * can receive broadcast frames and creating sta
416753c7e08SAugustin Cavalier 			 * entries will blow up because there is no bss
417753c7e08SAugustin Cavalier 			 * channel yet.
418753c7e08SAugustin Cavalier 			 */
419753c7e08SAugustin Cavalier 			if (vap->iv_state != IEEE80211_S_RUN) {
420753c7e08SAugustin Cavalier 				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
421753c7e08SAugustin Cavalier 				    wh, "data", "not in RUN state (%s)",
422753c7e08SAugustin Cavalier 				    ieee80211_state_name[vap->iv_state]);
423753c7e08SAugustin Cavalier 				vap->iv_stats.is_rx_badstate++;
424753c7e08SAugustin Cavalier 				goto err;
425753c7e08SAugustin Cavalier 			}
426753c7e08SAugustin Cavalier 			/*
4278244a9baSAugustin Cavalier 			 * Fake up a node for this newly discovered member
4288244a9baSAugustin Cavalier 			 * of the IBSS.
4298244a9baSAugustin Cavalier 			 *
4308244a9baSAugustin Cavalier 			 * Note: This doesn't "upgrade" the node to 11n;
4318244a9baSAugustin Cavalier 			 * that will happen after a probe request/response
4328244a9baSAugustin Cavalier 			 * exchange.
433753c7e08SAugustin Cavalier 			 */
434753c7e08SAugustin Cavalier 			ni = ieee80211_fakeup_adhoc_node(vap, wh->i_addr2);
435753c7e08SAugustin Cavalier 			if (ni == NULL) {
436753c7e08SAugustin Cavalier 				/* NB: stat kept for alloc failure */
437753c7e08SAugustin Cavalier 				goto err;
438753c7e08SAugustin Cavalier 			}
439753c7e08SAugustin Cavalier 		}
440753c7e08SAugustin Cavalier 		IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
441753c7e08SAugustin Cavalier 		ni->ni_noise = nf;
442753c7e08SAugustin Cavalier 		if (IEEE80211_HAS_SEQ(type, subtype) &&
443753c7e08SAugustin Cavalier 		    IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
444753c7e08SAugustin Cavalier 			uint8_t tid = ieee80211_gettid(wh);
445753c7e08SAugustin Cavalier 			if (IEEE80211_QOS_HAS_SEQ(wh) &&
446753c7e08SAugustin Cavalier 			    TID_TO_WME_AC(tid) >= WME_AC_VI)
447753c7e08SAugustin Cavalier 				ic->ic_wme.wme_hipri_traffic++;
4488244a9baSAugustin Cavalier 			if (! ieee80211_check_rxseq(ni, wh, bssid, rxs))
449753c7e08SAugustin Cavalier 				goto out;
450753c7e08SAugustin Cavalier 		}
451753c7e08SAugustin Cavalier 	}
452753c7e08SAugustin Cavalier 
453753c7e08SAugustin Cavalier 	switch (type) {
454753c7e08SAugustin Cavalier 	case IEEE80211_FC0_TYPE_DATA:
455753c7e08SAugustin Cavalier 		hdrspace = ieee80211_hdrspace(ic, wh);
456753c7e08SAugustin Cavalier 		if (m->m_len < hdrspace &&
457753c7e08SAugustin Cavalier 		    (m = m_pullup(m, hdrspace)) == NULL) {
458753c7e08SAugustin Cavalier 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
459753c7e08SAugustin Cavalier 			    ni->ni_macaddr, NULL,
460753c7e08SAugustin Cavalier 			    "data too short: expecting %u", hdrspace);
461753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_tooshort++;
462753c7e08SAugustin Cavalier 			goto out;		/* XXX */
463753c7e08SAugustin Cavalier 		}
464753c7e08SAugustin Cavalier 		if (dir != IEEE80211_FC1_DIR_NODS) {
465753c7e08SAugustin Cavalier 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
466753c7e08SAugustin Cavalier 			    wh, "data", "incorrect dir 0x%x", dir);
467753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_wrongdir++;
468753c7e08SAugustin Cavalier 			goto out;
469753c7e08SAugustin Cavalier 		}
470753c7e08SAugustin Cavalier 		/* XXX no power-save support */
471753c7e08SAugustin Cavalier 
472753c7e08SAugustin Cavalier 		/*
473753c7e08SAugustin Cavalier 		 * Handle A-MPDU re-ordering.  If the frame is to be
474753c7e08SAugustin Cavalier 		 * processed directly then ieee80211_ampdu_reorder
475753c7e08SAugustin Cavalier 		 * will return 0; otherwise it has consumed the mbuf
476753c7e08SAugustin Cavalier 		 * and we should do nothing more with it.
477753c7e08SAugustin Cavalier 		 */
478753c7e08SAugustin Cavalier 		if ((m->m_flags & M_AMPDU) &&
4798244a9baSAugustin Cavalier 		    ieee80211_ampdu_reorder(ni, m, rxs) != 0) {
480753c7e08SAugustin Cavalier 			m = NULL;
481753c7e08SAugustin Cavalier 			goto out;
482753c7e08SAugustin Cavalier 		}
483753c7e08SAugustin Cavalier 	resubmit_ampdu:
484753c7e08SAugustin Cavalier 
485753c7e08SAugustin Cavalier 		/*
486753c7e08SAugustin Cavalier 		 * Handle privacy requirements.  Note that we
487753c7e08SAugustin Cavalier 		 * must not be preempted from here until after
488753c7e08SAugustin Cavalier 		 * we (potentially) call ieee80211_crypto_demic;
489753c7e08SAugustin Cavalier 		 * otherwise we may violate assumptions in the
490753c7e08SAugustin Cavalier 		 * crypto cipher modules used to do delayed update
491753c7e08SAugustin Cavalier 		 * of replay sequence numbers.
492753c7e08SAugustin Cavalier 		 */
49386021fd4SAugustin Cavalier 		if (is_hw_decrypted || IEEE80211_IS_PROTECTED(wh)) {
494753c7e08SAugustin Cavalier 			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
495753c7e08SAugustin Cavalier 				/*
496753c7e08SAugustin Cavalier 				 * Discard encrypted frames when privacy is off.
497753c7e08SAugustin Cavalier 				 */
498753c7e08SAugustin Cavalier 				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
499753c7e08SAugustin Cavalier 				    wh, "WEP", "%s", "PRIVACY off");
500753c7e08SAugustin Cavalier 				vap->iv_stats.is_rx_noprivacy++;
501753c7e08SAugustin Cavalier 				IEEE80211_NODE_STAT(ni, rx_noprivacy);
502753c7e08SAugustin Cavalier 				goto out;
503753c7e08SAugustin Cavalier 			}
5048244a9baSAugustin Cavalier 			if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) {
505753c7e08SAugustin Cavalier 				/* NB: stats+msgs handled in crypto_decap */
506753c7e08SAugustin Cavalier 				IEEE80211_NODE_STAT(ni, rx_wepfail);
507753c7e08SAugustin Cavalier 				goto out;
508753c7e08SAugustin Cavalier 			}
509753c7e08SAugustin Cavalier 			wh = mtod(m, struct ieee80211_frame *);
510753c7e08SAugustin Cavalier 			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
5118244a9baSAugustin Cavalier 			has_decrypted = 1;
512753c7e08SAugustin Cavalier 		} else {
513753c7e08SAugustin Cavalier 			/* XXX M_WEP and IEEE80211_F_PRIVACY */
514753c7e08SAugustin Cavalier 			key = NULL;
515753c7e08SAugustin Cavalier 		}
516753c7e08SAugustin Cavalier 
517753c7e08SAugustin Cavalier 		/*
518753c7e08SAugustin Cavalier 		 * Save QoS bits for use below--before we strip the header.
519753c7e08SAugustin Cavalier 		 */
52086021fd4SAugustin Cavalier 		if (subtype == IEEE80211_FC0_SUBTYPE_QOS_DATA)
5215cad57c4SJérôme Duval 			qos = ieee80211_getqos(wh)[0];
5225cad57c4SJérôme Duval 		else
523753c7e08SAugustin Cavalier 			qos = 0;
524753c7e08SAugustin Cavalier 
525753c7e08SAugustin Cavalier 		/*
526753c7e08SAugustin Cavalier 		 * Next up, any fragmentation.
527753c7e08SAugustin Cavalier 		 */
528753c7e08SAugustin Cavalier 		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
52986021fd4SAugustin Cavalier 			m = ieee80211_defrag(ni, m, hdrspace, has_decrypted);
530753c7e08SAugustin Cavalier 			if (m == NULL) {
531753c7e08SAugustin Cavalier 				/* Fragment dropped or frame not complete yet */
532753c7e08SAugustin Cavalier 				goto out;
533753c7e08SAugustin Cavalier 			}
534753c7e08SAugustin Cavalier 		}
535753c7e08SAugustin Cavalier 		wh = NULL;		/* no longer valid, catch any uses */
536753c7e08SAugustin Cavalier 
537753c7e08SAugustin Cavalier 		/*
538753c7e08SAugustin Cavalier 		 * Next strip any MSDU crypto bits.
539753c7e08SAugustin Cavalier 		 */
5408244a9baSAugustin Cavalier 		if (!ieee80211_crypto_demic(vap, key, m, 0)) {
541753c7e08SAugustin Cavalier 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
542753c7e08SAugustin Cavalier 			    ni->ni_macaddr, "data", "%s", "demic error");
543753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_demicfail++;
544753c7e08SAugustin Cavalier 			IEEE80211_NODE_STAT(ni, rx_demicfail);
545753c7e08SAugustin Cavalier 			goto out;
546753c7e08SAugustin Cavalier 		}
547753c7e08SAugustin Cavalier 
548753c7e08SAugustin Cavalier 		/* copy to listener after decrypt */
549753c7e08SAugustin Cavalier 		if (ieee80211_radiotap_active_vap(vap))
550753c7e08SAugustin Cavalier 			ieee80211_radiotap_rx(vap, m);
551753c7e08SAugustin Cavalier 		need_tap = 0;
552753c7e08SAugustin Cavalier 
553753c7e08SAugustin Cavalier 		/*
554753c7e08SAugustin Cavalier 		 * Finally, strip the 802.11 header.
555753c7e08SAugustin Cavalier 		 */
55686021fd4SAugustin Cavalier 		m = ieee80211_decap(vap, m, hdrspace, qos);
557753c7e08SAugustin Cavalier 		if (m == NULL) {
558753c7e08SAugustin Cavalier 			/* XXX mask bit to check for both */
559753c7e08SAugustin Cavalier 			/* don't count Null data frames as errors */
560753c7e08SAugustin Cavalier 			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
561753c7e08SAugustin Cavalier 			    subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
562753c7e08SAugustin Cavalier 				goto out;
563753c7e08SAugustin Cavalier 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
564753c7e08SAugustin Cavalier 			    ni->ni_macaddr, "data", "%s", "decap error");
565753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_decap++;
566753c7e08SAugustin Cavalier 			IEEE80211_NODE_STAT(ni, rx_decap);
567753c7e08SAugustin Cavalier 			goto err;
568753c7e08SAugustin Cavalier 		}
56986021fd4SAugustin Cavalier 		if (!(qos & IEEE80211_QOS_AMSDU))
570753c7e08SAugustin Cavalier 			eh = mtod(m, struct ether_header *);
57186021fd4SAugustin Cavalier 		else
57286021fd4SAugustin Cavalier 			eh = NULL;
573753c7e08SAugustin Cavalier 		if (!ieee80211_node_is_authorized(ni)) {
574753c7e08SAugustin Cavalier 			/*
575753c7e08SAugustin Cavalier 			 * Deny any non-PAE frames received prior to
576753c7e08SAugustin Cavalier 			 * authorization.  For open/shared-key
577753c7e08SAugustin Cavalier 			 * authentication the port is mark authorized
578753c7e08SAugustin Cavalier 			 * after authentication completes.  For 802.1x
579753c7e08SAugustin Cavalier 			 * the port is not marked authorized by the
580753c7e08SAugustin Cavalier 			 * authenticator until the handshake has completed.
581753c7e08SAugustin Cavalier 			 */
58286021fd4SAugustin Cavalier 			if (eh == NULL ||
58386021fd4SAugustin Cavalier 			    eh->ether_type != htons(ETHERTYPE_PAE)) {
584753c7e08SAugustin Cavalier 				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
58586021fd4SAugustin Cavalier 				    ni->ni_macaddr, "data", "unauthorized or "
58686021fd4SAugustin Cavalier 				    "unknown port: ether type 0x%x len %u",
58786021fd4SAugustin Cavalier 				    eh == NULL ? -1 : eh->ether_type,
58886021fd4SAugustin Cavalier 				    m->m_pkthdr.len);
589753c7e08SAugustin Cavalier 				vap->iv_stats.is_rx_unauth++;
590753c7e08SAugustin Cavalier 				IEEE80211_NODE_STAT(ni, rx_unauth);
591753c7e08SAugustin Cavalier 				goto err;
592753c7e08SAugustin Cavalier 			}
593753c7e08SAugustin Cavalier 		} else {
594753c7e08SAugustin Cavalier 			/*
595753c7e08SAugustin Cavalier 			 * When denying unencrypted frames, discard
596753c7e08SAugustin Cavalier 			 * any non-PAE frames received without encryption.
597753c7e08SAugustin Cavalier 			 */
598753c7e08SAugustin Cavalier 			if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
5998244a9baSAugustin Cavalier 			    ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) &&
6008244a9baSAugustin Cavalier 			    (is_hw_decrypted == 0) &&
60186021fd4SAugustin Cavalier 			    (eh == NULL ||
60286021fd4SAugustin Cavalier 			     eh->ether_type != htons(ETHERTYPE_PAE))) {
603753c7e08SAugustin Cavalier 				/*
604753c7e08SAugustin Cavalier 				 * Drop unencrypted frames.
605753c7e08SAugustin Cavalier 				 */
606753c7e08SAugustin Cavalier 				vap->iv_stats.is_rx_unencrypted++;
607753c7e08SAugustin Cavalier 				IEEE80211_NODE_STAT(ni, rx_unencrypted);
608753c7e08SAugustin Cavalier 				goto out;
609753c7e08SAugustin Cavalier 			}
610753c7e08SAugustin Cavalier 		}
611753c7e08SAugustin Cavalier 		/* XXX require HT? */
612753c7e08SAugustin Cavalier 		if (qos & IEEE80211_QOS_AMSDU) {
613753c7e08SAugustin Cavalier 			m = ieee80211_decap_amsdu(ni, m);
614753c7e08SAugustin Cavalier 			if (m == NULL)
615753c7e08SAugustin Cavalier 				return IEEE80211_FC0_TYPE_DATA;
616753c7e08SAugustin Cavalier 		} else {
617753c7e08SAugustin Cavalier #ifdef IEEE80211_SUPPORT_SUPERG
618753c7e08SAugustin Cavalier 			m = ieee80211_decap_fastframe(vap, ni, m);
619753c7e08SAugustin Cavalier 			if (m == NULL)
620753c7e08SAugustin Cavalier 				return IEEE80211_FC0_TYPE_DATA;
621753c7e08SAugustin Cavalier #endif
622753c7e08SAugustin Cavalier 		}
623753c7e08SAugustin Cavalier 		if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL)
624753c7e08SAugustin Cavalier 			ieee80211_deliver_data(ni->ni_wdsvap, ni, m);
625753c7e08SAugustin Cavalier 		else
626753c7e08SAugustin Cavalier 			ieee80211_deliver_data(vap, ni, m);
627753c7e08SAugustin Cavalier 		return IEEE80211_FC0_TYPE_DATA;
628753c7e08SAugustin Cavalier 
629753c7e08SAugustin Cavalier 	case IEEE80211_FC0_TYPE_MGT:
630753c7e08SAugustin Cavalier 		vap->iv_stats.is_rx_mgmt++;
631753c7e08SAugustin Cavalier 		IEEE80211_NODE_STAT(ni, rx_mgmt);
632753c7e08SAugustin Cavalier 		if (dir != IEEE80211_FC1_DIR_NODS) {
633753c7e08SAugustin Cavalier 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
634753c7e08SAugustin Cavalier 			    wh, "data", "incorrect dir 0x%x", dir);
635753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_wrongdir++;
636753c7e08SAugustin Cavalier 			goto err;
637753c7e08SAugustin Cavalier 		}
638753c7e08SAugustin Cavalier 		if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
639753c7e08SAugustin Cavalier 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
640753c7e08SAugustin Cavalier 			    ni->ni_macaddr, "mgt", "too short: len %u",
641753c7e08SAugustin Cavalier 			    m->m_pkthdr.len);
642753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_tooshort++;
643753c7e08SAugustin Cavalier 			goto out;
644753c7e08SAugustin Cavalier 		}
645753c7e08SAugustin Cavalier #ifdef IEEE80211_DEBUG
646753c7e08SAugustin Cavalier 		if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
647753c7e08SAugustin Cavalier 		    ieee80211_msg_dumppkts(vap)) {
648753c7e08SAugustin Cavalier 			if_printf(ifp, "received %s from %s rssi %d\n",
649753c7e08SAugustin Cavalier 			    ieee80211_mgt_subtype_name(subtype),
650753c7e08SAugustin Cavalier 			    ether_sprintf(wh->i_addr2), rssi);
651753c7e08SAugustin Cavalier 		}
652753c7e08SAugustin Cavalier #endif
65386021fd4SAugustin Cavalier 		if (IEEE80211_IS_PROTECTED(wh)) {
654753c7e08SAugustin Cavalier 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
655753c7e08SAugustin Cavalier 			    wh, NULL, "%s", "WEP set but not permitted");
656753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
657753c7e08SAugustin Cavalier 			goto out;
658753c7e08SAugustin Cavalier 		}
659753c7e08SAugustin Cavalier 		vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
660753c7e08SAugustin Cavalier 		goto out;
661753c7e08SAugustin Cavalier 
662753c7e08SAugustin Cavalier 	case IEEE80211_FC0_TYPE_CTL:
663753c7e08SAugustin Cavalier 		vap->iv_stats.is_rx_ctl++;
664753c7e08SAugustin Cavalier 		IEEE80211_NODE_STAT(ni, rx_ctrl);
665753c7e08SAugustin Cavalier 		vap->iv_recv_ctl(ni, m, subtype);
666753c7e08SAugustin Cavalier 		goto out;
667753c7e08SAugustin Cavalier 
668753c7e08SAugustin Cavalier 	default:
669753c7e08SAugustin Cavalier 		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
670753c7e08SAugustin Cavalier 		    wh, "bad", "frame type 0x%x", type);
671753c7e08SAugustin Cavalier 		/* should not come here */
672753c7e08SAugustin Cavalier 		break;
673753c7e08SAugustin Cavalier 	}
674753c7e08SAugustin Cavalier err:
675753c7e08SAugustin Cavalier 	if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
676753c7e08SAugustin Cavalier out:
677753c7e08SAugustin Cavalier 	if (m != NULL) {
678753c7e08SAugustin Cavalier 		if (need_tap && ieee80211_radiotap_active_vap(vap))
679753c7e08SAugustin Cavalier 			ieee80211_radiotap_rx(vap, m);
680753c7e08SAugustin Cavalier 		m_freem(m);
681753c7e08SAugustin Cavalier 	}
682753c7e08SAugustin Cavalier 	return type;
683753c7e08SAugustin Cavalier }
684753c7e08SAugustin Cavalier 
685753c7e08SAugustin Cavalier static int
is11bclient(const uint8_t * rates,const uint8_t * xrates)686753c7e08SAugustin Cavalier is11bclient(const uint8_t *rates, const uint8_t *xrates)
687753c7e08SAugustin Cavalier {
688753c7e08SAugustin Cavalier 	static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11);
689753c7e08SAugustin Cavalier 	int i;
690753c7e08SAugustin Cavalier 
691753c7e08SAugustin Cavalier 	/* NB: the 11b clients we care about will not have xrates */
692753c7e08SAugustin Cavalier 	if (xrates != NULL || rates == NULL)
693753c7e08SAugustin Cavalier 		return 0;
694753c7e08SAugustin Cavalier 	for (i = 0; i < rates[1]; i++) {
695753c7e08SAugustin Cavalier 		int r = rates[2+i] & IEEE80211_RATE_VAL;
696753c7e08SAugustin Cavalier 		if (r > 2*11 || ((1<<r) & brates) == 0)
697753c7e08SAugustin Cavalier 			return 0;
698753c7e08SAugustin Cavalier 	}
699753c7e08SAugustin Cavalier 	return 1;
700753c7e08SAugustin Cavalier }
701753c7e08SAugustin Cavalier 
702753c7e08SAugustin Cavalier static void
adhoc_recv_mgmt(struct ieee80211_node * ni,struct mbuf * m0,int subtype,const struct ieee80211_rx_stats * rxs,int rssi,int nf)703753c7e08SAugustin Cavalier adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
704753c7e08SAugustin Cavalier 	int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
705753c7e08SAugustin Cavalier {
706753c7e08SAugustin Cavalier 	struct ieee80211vap *vap = ni->ni_vap;
707753c7e08SAugustin Cavalier 	struct ieee80211com *ic = ni->ni_ic;
708753c7e08SAugustin Cavalier 	struct ieee80211_channel *rxchan = ic->ic_curchan;
709753c7e08SAugustin Cavalier 	struct ieee80211_frame *wh;
710753c7e08SAugustin Cavalier 	uint8_t *frm, *efrm;
711753c7e08SAugustin Cavalier 	uint8_t *ssid, *rates, *xrates;
712753c7e08SAugustin Cavalier #if 0
713753c7e08SAugustin Cavalier 	int ht_state_change = 0;
714753c7e08SAugustin Cavalier #endif
715753c7e08SAugustin Cavalier 
716753c7e08SAugustin Cavalier 	wh = mtod(m0, struct ieee80211_frame *);
717753c7e08SAugustin Cavalier 	frm = (uint8_t *)&wh[1];
718753c7e08SAugustin Cavalier 	efrm = mtod(m0, uint8_t *) + m0->m_len;
7195cad57c4SJérôme Duval 
7205cad57c4SJérôme Duval 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_DEBUG,
7215cad57c4SJérôme Duval 	    "%s: recv mgmt frame, addr2=%6D, ni=%p (%6D) fc=%.02x %.02x\n",
7225cad57c4SJérôme Duval 	    __func__,
7235cad57c4SJérôme Duval 	    wh->i_addr2, ":",
7245cad57c4SJérôme Duval 	    ni,
7255cad57c4SJérôme Duval 	    ni->ni_macaddr, ":",
7265cad57c4SJérôme Duval 	    wh->i_fc[0],
7275cad57c4SJérôme Duval 	    wh->i_fc[1]);
728753c7e08SAugustin Cavalier 	switch (subtype) {
729753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
730753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_BEACON: {
731753c7e08SAugustin Cavalier 		struct ieee80211_scanparams scan;
732753c7e08SAugustin Cavalier 		struct ieee80211_channel *c;
733753c7e08SAugustin Cavalier 		/*
734753c7e08SAugustin Cavalier 		 * We process beacon/probe response
735753c7e08SAugustin Cavalier 		 * frames to discover neighbors.
736753c7e08SAugustin Cavalier 		 */
737753c7e08SAugustin Cavalier 		if (rxs != NULL) {
738753c7e08SAugustin Cavalier 			c = ieee80211_lookup_channel_rxstatus(vap, rxs);
739753c7e08SAugustin Cavalier 			if (c != NULL)
740753c7e08SAugustin Cavalier 				rxchan = c;
741753c7e08SAugustin Cavalier 		}
742753c7e08SAugustin Cavalier 		if (ieee80211_parse_beacon(ni, m0, rxchan, &scan) != 0)
743753c7e08SAugustin Cavalier 			return;
744753c7e08SAugustin Cavalier 		/*
745753c7e08SAugustin Cavalier 		 * Count frame now that we know it's to be processed.
746753c7e08SAugustin Cavalier 		 */
747753c7e08SAugustin Cavalier 		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
748753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_beacon++;		/* XXX remove */
749753c7e08SAugustin Cavalier 			IEEE80211_NODE_STAT(ni, rx_beacons);
750753c7e08SAugustin Cavalier 		} else
751753c7e08SAugustin Cavalier 			IEEE80211_NODE_STAT(ni, rx_proberesp);
752753c7e08SAugustin Cavalier 		/*
753753c7e08SAugustin Cavalier 		 * If scanning, just pass information to the scan module.
754753c7e08SAugustin Cavalier 		 */
755753c7e08SAugustin Cavalier 		if (ic->ic_flags & IEEE80211_F_SCAN) {
756753c7e08SAugustin Cavalier 			if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
757753c7e08SAugustin Cavalier 				/*
758753c7e08SAugustin Cavalier 				 * Actively scanning a channel marked passive;
759753c7e08SAugustin Cavalier 				 * send a probe request now that we know there
760753c7e08SAugustin Cavalier 				 * is 802.11 traffic present.
761753c7e08SAugustin Cavalier 				 *
762753c7e08SAugustin Cavalier 				 * XXX check if the beacon we recv'd gives
763753c7e08SAugustin Cavalier 				 * us what we need and suppress the probe req
764753c7e08SAugustin Cavalier 				 */
765*da3ca318SAugustin Cavalier 				ieee80211_probe_curchan(vap, true);
766753c7e08SAugustin Cavalier 				ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
767753c7e08SAugustin Cavalier 			}
768753c7e08SAugustin Cavalier 			ieee80211_add_scan(vap, rxchan, &scan, wh,
769753c7e08SAugustin Cavalier 			    subtype, rssi, nf);
770753c7e08SAugustin Cavalier 			return;
771753c7e08SAugustin Cavalier 		}
772753c7e08SAugustin Cavalier 		if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
773753c7e08SAugustin Cavalier 			if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
774753c7e08SAugustin Cavalier 				/*
775753c7e08SAugustin Cavalier 				 * Create a new entry in the neighbor table.
7768244a9baSAugustin Cavalier 				 *
7778244a9baSAugustin Cavalier 				 * XXX TODO:
7788244a9baSAugustin Cavalier 				 *
7798244a9baSAugustin Cavalier 				 * Here we're not scanning; so if we have an
7808244a9baSAugustin Cavalier 				 * SSID then make sure it matches our SSID.
7818244a9baSAugustin Cavalier 				 * Otherwise this code will match on all IBSS
7828244a9baSAugustin Cavalier 				 * beacons/probe requests for all SSIDs,
7838244a9baSAugustin Cavalier 				 * filling the node table with nodes that
7848244a9baSAugustin Cavalier 				 * aren't ours.
785753c7e08SAugustin Cavalier 				 */
7868244a9baSAugustin Cavalier 				if (ieee80211_ibss_node_check_new(ni, &scan)) {
787753c7e08SAugustin Cavalier 					ni = ieee80211_add_neighbor(vap, wh, &scan);
7888244a9baSAugustin Cavalier 					/*
7898244a9baSAugustin Cavalier 					 * Send a probe request so we announce 11n
7908244a9baSAugustin Cavalier 					 * capabilities.
7918244a9baSAugustin Cavalier 					 */
7928244a9baSAugustin Cavalier 					ieee80211_send_probereq(ni, /* node */
7938244a9baSAugustin Cavalier 					    vap->iv_myaddr, /* SA */
7948244a9baSAugustin Cavalier 					    ni->ni_macaddr, /* DA */
7958244a9baSAugustin Cavalier 					    vap->iv_bss->ni_bssid, /* BSSID */
7968244a9baSAugustin Cavalier 					    vap->iv_bss->ni_essid,
7978244a9baSAugustin Cavalier 					    vap->iv_bss->ni_esslen); /* SSID */
7988244a9baSAugustin Cavalier 				} else
7998244a9baSAugustin Cavalier 					ni = NULL;
8008244a9baSAugustin Cavalier 
8015cad57c4SJérôme Duval 				/*
8025cad57c4SJérôme Duval 				 * Send a probe request so we announce 11n
8035cad57c4SJérôme Duval 				 * capabilities.
8045cad57c4SJérôme Duval 				 *
8055cad57c4SJérôme Duval 				 * Don't do this if we're scanning.
8065cad57c4SJérôme Duval 				 */
8075cad57c4SJérôme Duval 				if (! (ic->ic_flags & IEEE80211_F_SCAN))
8085cad57c4SJérôme Duval 					ieee80211_send_probereq(ni, /* node */
8095cad57c4SJérôme Duval 						vap->iv_myaddr, /* SA */
8105cad57c4SJérôme Duval 						ni->ni_macaddr, /* DA */
8115cad57c4SJérôme Duval 						vap->iv_bss->ni_bssid, /* BSSID */
8125cad57c4SJérôme Duval 						vap->iv_bss->ni_essid,
8135cad57c4SJérôme Duval 						vap->iv_bss->ni_esslen); /* SSID */
8145cad57c4SJérôme Duval 
815753c7e08SAugustin Cavalier 			} else if (ni->ni_capinfo == 0) {
816753c7e08SAugustin Cavalier 				/*
817753c7e08SAugustin Cavalier 				 * Update faked node created on transmit.
818753c7e08SAugustin Cavalier 				 * Note this also updates the tsf.
819753c7e08SAugustin Cavalier 				 */
820753c7e08SAugustin Cavalier 				ieee80211_init_neighbor(ni, wh, &scan);
8218244a9baSAugustin Cavalier 
8228244a9baSAugustin Cavalier 				/*
8238244a9baSAugustin Cavalier 				 * Send a probe request so we announce 11n
8248244a9baSAugustin Cavalier 				 * capabilities.
8258244a9baSAugustin Cavalier 				 */
8268244a9baSAugustin Cavalier 				ieee80211_send_probereq(ni, /* node */
8278244a9baSAugustin Cavalier 					vap->iv_myaddr, /* SA */
8288244a9baSAugustin Cavalier 					ni->ni_macaddr, /* DA */
8298244a9baSAugustin Cavalier 					vap->iv_bss->ni_bssid, /* BSSID */
8308244a9baSAugustin Cavalier 					vap->iv_bss->ni_essid,
8318244a9baSAugustin Cavalier 					vap->iv_bss->ni_esslen); /* SSID */
832753c7e08SAugustin Cavalier 			} else {
833753c7e08SAugustin Cavalier 				/*
834753c7e08SAugustin Cavalier 				 * Record tsf for potential resync.
835753c7e08SAugustin Cavalier 				 */
836753c7e08SAugustin Cavalier 				memcpy(ni->ni_tstamp.data, scan.tstamp,
837753c7e08SAugustin Cavalier 					sizeof(ni->ni_tstamp));
838753c7e08SAugustin Cavalier 			}
839753c7e08SAugustin Cavalier 			/*
840753c7e08SAugustin Cavalier 			 * This isn't enabled yet - otherwise it would
841753c7e08SAugustin Cavalier 			 * update the HT parameters and channel width
842753c7e08SAugustin Cavalier 			 * from any node, which could lead to lots of
843753c7e08SAugustin Cavalier 			 * strange behaviour if the 11n nodes aren't
844753c7e08SAugustin Cavalier 			 * exactly configured to match.
845753c7e08SAugustin Cavalier 			 */
846753c7e08SAugustin Cavalier #if 0
847753c7e08SAugustin Cavalier 			if (scan.htcap != NULL && scan.htinfo != NULL &&
848753c7e08SAugustin Cavalier 			    (vap->iv_flags_ht & IEEE80211_FHT_HT)) {
8498244a9baSAugustin Cavalier 				ieee80211_ht_updateparams(ni,
8508244a9baSAugustin Cavalier 				    scan.htcap, scan.htinfo));
8518244a9baSAugustin Cavalier 				if (ieee80211_ht_updateparams_final(ni,
852753c7e08SAugustin Cavalier 				    scan.htcap, scan.htinfo))
853753c7e08SAugustin Cavalier 					ht_state_change = 1;
854753c7e08SAugustin Cavalier 			}
8558244a9baSAugustin Cavalier 
8568244a9baSAugustin Cavalier 			/* XXX same for VHT? */
857753c7e08SAugustin Cavalier #endif
858753c7e08SAugustin Cavalier 			if (ni != NULL) {
859753c7e08SAugustin Cavalier 				IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
860753c7e08SAugustin Cavalier 				ni->ni_noise = nf;
861753c7e08SAugustin Cavalier 			}
862753c7e08SAugustin Cavalier 			/*
863753c7e08SAugustin Cavalier 			 * Same here - the channel width change should
864753c7e08SAugustin Cavalier 			 * be applied to the specific peer node, not
865753c7e08SAugustin Cavalier 			 * to the ic.  Ie, the interface configuration
866753c7e08SAugustin Cavalier 			 * should stay in its current channel width;
867753c7e08SAugustin Cavalier 			 * but it should change the rate control and
868753c7e08SAugustin Cavalier 			 * any queued frames for the given node only.
869753c7e08SAugustin Cavalier 			 *
870753c7e08SAugustin Cavalier 			 * Since there's no (current) way to inform
871753c7e08SAugustin Cavalier 			 * the driver that a channel width change has
872753c7e08SAugustin Cavalier 			 * occurred for a single node, just stub this
873753c7e08SAugustin Cavalier 			 * out.
874753c7e08SAugustin Cavalier 			 */
875753c7e08SAugustin Cavalier #if 0
876753c7e08SAugustin Cavalier 			if (ht_state_change)
877753c7e08SAugustin Cavalier 				ieee80211_update_chw(ic);
878753c7e08SAugustin Cavalier #endif
879753c7e08SAugustin Cavalier 		}
880753c7e08SAugustin Cavalier 		break;
881753c7e08SAugustin Cavalier 	}
882753c7e08SAugustin Cavalier 
883753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
884753c7e08SAugustin Cavalier 		if (vap->iv_state != IEEE80211_S_RUN) {
885753c7e08SAugustin Cavalier 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
886753c7e08SAugustin Cavalier 			    wh, NULL, "wrong state %s",
887753c7e08SAugustin Cavalier 			    ieee80211_state_name[vap->iv_state]);
888753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_mgtdiscard++;
889753c7e08SAugustin Cavalier 			return;
890753c7e08SAugustin Cavalier 		}
891753c7e08SAugustin Cavalier 		if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
892753c7e08SAugustin Cavalier 			/* frame must be directed */
893753c7e08SAugustin Cavalier 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
894753c7e08SAugustin Cavalier 			    wh, NULL, "%s", "not unicast");
895753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_mgtdiscard++;	/* XXX stat */
896753c7e08SAugustin Cavalier 			return;
897753c7e08SAugustin Cavalier 		}
898753c7e08SAugustin Cavalier 
899753c7e08SAugustin Cavalier 		/*
900753c7e08SAugustin Cavalier 		 * prreq frame format
901753c7e08SAugustin Cavalier 		 *	[tlv] ssid
902753c7e08SAugustin Cavalier 		 *	[tlv] supported rates
903753c7e08SAugustin Cavalier 		 *	[tlv] extended supported rates
904753c7e08SAugustin Cavalier 		 */
905753c7e08SAugustin Cavalier 		ssid = rates = xrates = NULL;
906753c7e08SAugustin Cavalier 		while (efrm - frm > 1) {
907753c7e08SAugustin Cavalier 			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
908753c7e08SAugustin Cavalier 			switch (*frm) {
909753c7e08SAugustin Cavalier 			case IEEE80211_ELEMID_SSID:
910753c7e08SAugustin Cavalier 				ssid = frm;
911753c7e08SAugustin Cavalier 				break;
912753c7e08SAugustin Cavalier 			case IEEE80211_ELEMID_RATES:
913753c7e08SAugustin Cavalier 				rates = frm;
914753c7e08SAugustin Cavalier 				break;
915753c7e08SAugustin Cavalier 			case IEEE80211_ELEMID_XRATES:
916753c7e08SAugustin Cavalier 				xrates = frm;
917753c7e08SAugustin Cavalier 				break;
918753c7e08SAugustin Cavalier 			}
919753c7e08SAugustin Cavalier 			frm += frm[1] + 2;
920753c7e08SAugustin Cavalier 		}
921753c7e08SAugustin Cavalier 		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
922753c7e08SAugustin Cavalier 		if (xrates != NULL)
923753c7e08SAugustin Cavalier 			IEEE80211_VERIFY_ELEMENT(xrates,
924753c7e08SAugustin Cavalier 				IEEE80211_RATE_MAXSIZE - rates[1], return);
925753c7e08SAugustin Cavalier 		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
926753c7e08SAugustin Cavalier 		IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return);
927753c7e08SAugustin Cavalier 		if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
928753c7e08SAugustin Cavalier 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
929753c7e08SAugustin Cavalier 			    wh, NULL,
930753c7e08SAugustin Cavalier 			    "%s", "no ssid with ssid suppression enabled");
931753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/
932753c7e08SAugustin Cavalier 			return;
933753c7e08SAugustin Cavalier 		}
934753c7e08SAugustin Cavalier 
935753c7e08SAugustin Cavalier 		/* XXX find a better class or define it's own */
936753c7e08SAugustin Cavalier 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
937753c7e08SAugustin Cavalier 		    "%s", "recv probe req");
938753c7e08SAugustin Cavalier 		/*
939753c7e08SAugustin Cavalier 		 * Some legacy 11b clients cannot hack a complete
940753c7e08SAugustin Cavalier 		 * probe response frame.  When the request includes
941753c7e08SAugustin Cavalier 		 * only a bare-bones rate set, communicate this to
942753c7e08SAugustin Cavalier 		 * the transmit side.
943753c7e08SAugustin Cavalier 		 */
944753c7e08SAugustin Cavalier 		ieee80211_send_proberesp(vap, wh->i_addr2,
945753c7e08SAugustin Cavalier 		    is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0);
9468244a9baSAugustin Cavalier 
9478244a9baSAugustin Cavalier 		/*
9488244a9baSAugustin Cavalier 		 * Note: we don't benefit from stashing the probe request
9498244a9baSAugustin Cavalier 		 * IEs away to use for IBSS negotiation, because we
9508244a9baSAugustin Cavalier 		 * typically don't get all of the IEs.
9518244a9baSAugustin Cavalier 		 */
952753c7e08SAugustin Cavalier 		break;
953753c7e08SAugustin Cavalier 
954753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_ACTION:
955753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_ACTION_NOACK:
956753c7e08SAugustin Cavalier 		if ((ni == vap->iv_bss) &&
957753c7e08SAugustin Cavalier 		    !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
958753c7e08SAugustin Cavalier 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
959753c7e08SAugustin Cavalier 			    wh, NULL, "%s", "unknown node");
960753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_mgtdiscard++;
961753c7e08SAugustin Cavalier 		} else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) &&
962753c7e08SAugustin Cavalier 		    !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
9635cad57c4SJérôme Duval 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_DEBUG,
964753c7e08SAugustin Cavalier 			    wh, NULL, "%s", "not for us");
965753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_mgtdiscard++;
966753c7e08SAugustin Cavalier 		} else if (vap->iv_state != IEEE80211_S_RUN) {
9675cad57c4SJérôme Duval 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_DEBUG,
968753c7e08SAugustin Cavalier 			    wh, NULL, "wrong state %s",
969753c7e08SAugustin Cavalier 			    ieee80211_state_name[vap->iv_state]);
970753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_mgtdiscard++;
971753c7e08SAugustin Cavalier 		} else {
972753c7e08SAugustin Cavalier 			if (ieee80211_parse_action(ni, m0) == 0)
973753c7e08SAugustin Cavalier 				(void)ic->ic_recv_action(ni, wh, frm, efrm);
974753c7e08SAugustin Cavalier 		}
975753c7e08SAugustin Cavalier 		break;
976753c7e08SAugustin Cavalier 
977753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
978753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
979753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
980753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
981753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
982753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_ATIM:
983753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_DISASSOC:
984753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_AUTH:
985753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_DEAUTH:
986753c7e08SAugustin Cavalier 		IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
987753c7e08SAugustin Cavalier 		    wh, NULL, "%s", "not handled");
988753c7e08SAugustin Cavalier 		vap->iv_stats.is_rx_mgtdiscard++;
989753c7e08SAugustin Cavalier 		break;
990753c7e08SAugustin Cavalier 
991753c7e08SAugustin Cavalier 	default:
992753c7e08SAugustin Cavalier 		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
993753c7e08SAugustin Cavalier 		    wh, "mgt", "subtype 0x%x not handled", subtype);
994753c7e08SAugustin Cavalier 		vap->iv_stats.is_rx_badsubtype++;
995753c7e08SAugustin Cavalier 		break;
996753c7e08SAugustin Cavalier 	}
997753c7e08SAugustin Cavalier }
998753c7e08SAugustin Cavalier #undef IEEE80211_VERIFY_LENGTH
999753c7e08SAugustin Cavalier #undef IEEE80211_VERIFY_ELEMENT
1000753c7e08SAugustin Cavalier 
1001753c7e08SAugustin Cavalier static void
ahdemo_recv_mgmt(struct ieee80211_node * ni,struct mbuf * m0,int subtype,const struct ieee80211_rx_stats * rxs,int rssi,int nf)1002753c7e08SAugustin Cavalier ahdemo_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
1003753c7e08SAugustin Cavalier 	int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
1004753c7e08SAugustin Cavalier {
1005753c7e08SAugustin Cavalier 	struct ieee80211vap *vap = ni->ni_vap;
1006753c7e08SAugustin Cavalier 	struct ieee80211com *ic = ni->ni_ic;
1007753c7e08SAugustin Cavalier 
1008753c7e08SAugustin Cavalier 	/*
1009753c7e08SAugustin Cavalier 	 * Process management frames when scanning; useful for doing
1010753c7e08SAugustin Cavalier 	 * a site-survey.
1011753c7e08SAugustin Cavalier 	 */
1012753c7e08SAugustin Cavalier 	if (ic->ic_flags & IEEE80211_F_SCAN)
1013753c7e08SAugustin Cavalier 		adhoc_recv_mgmt(ni, m0, subtype, rxs, rssi, nf);
1014753c7e08SAugustin Cavalier 	else {
101586021fd4SAugustin Cavalier #ifdef IEEE80211_DEBUG
101686021fd4SAugustin Cavalier 		struct ieee80211_frame *wh;
101786021fd4SAugustin Cavalier 
1018753c7e08SAugustin Cavalier 		wh = mtod(m0, struct ieee80211_frame *);
101986021fd4SAugustin Cavalier #endif
1020753c7e08SAugustin Cavalier 		switch (subtype) {
1021753c7e08SAugustin Cavalier 		case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
1022753c7e08SAugustin Cavalier 		case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
1023753c7e08SAugustin Cavalier 		case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
1024753c7e08SAugustin Cavalier 		case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
1025753c7e08SAugustin Cavalier 		case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
1026753c7e08SAugustin Cavalier 		case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
1027753c7e08SAugustin Cavalier 		case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
1028753c7e08SAugustin Cavalier 		case IEEE80211_FC0_SUBTYPE_BEACON:
1029753c7e08SAugustin Cavalier 		case IEEE80211_FC0_SUBTYPE_ATIM:
1030753c7e08SAugustin Cavalier 		case IEEE80211_FC0_SUBTYPE_DISASSOC:
1031753c7e08SAugustin Cavalier 		case IEEE80211_FC0_SUBTYPE_AUTH:
1032753c7e08SAugustin Cavalier 		case IEEE80211_FC0_SUBTYPE_DEAUTH:
1033753c7e08SAugustin Cavalier 		case IEEE80211_FC0_SUBTYPE_ACTION:
1034753c7e08SAugustin Cavalier 		case IEEE80211_FC0_SUBTYPE_ACTION_NOACK:
1035753c7e08SAugustin Cavalier 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1036753c7e08SAugustin Cavalier 			     wh, NULL, "%s", "not handled");
1037753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_mgtdiscard++;
1038753c7e08SAugustin Cavalier 			break;
1039753c7e08SAugustin Cavalier 		default:
1040753c7e08SAugustin Cavalier 			IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
1041753c7e08SAugustin Cavalier 			     wh, "mgt", "subtype 0x%x not handled", subtype);
1042753c7e08SAugustin Cavalier 			vap->iv_stats.is_rx_badsubtype++;
1043753c7e08SAugustin Cavalier 			break;
1044753c7e08SAugustin Cavalier 		}
1045753c7e08SAugustin Cavalier 	}
1046753c7e08SAugustin Cavalier }
1047753c7e08SAugustin Cavalier 
1048753c7e08SAugustin Cavalier static void
adhoc_recv_ctl(struct ieee80211_node * ni,struct mbuf * m,int subtype)1049753c7e08SAugustin Cavalier adhoc_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype)
1050753c7e08SAugustin Cavalier {
1051753c7e08SAugustin Cavalier 
1052753c7e08SAugustin Cavalier 	switch (subtype) {
1053753c7e08SAugustin Cavalier 	case IEEE80211_FC0_SUBTYPE_BAR:
1054753c7e08SAugustin Cavalier 		ieee80211_recv_bar(ni, m);
1055753c7e08SAugustin Cavalier 		break;
1056753c7e08SAugustin Cavalier 	}
1057753c7e08SAugustin Cavalier }
1058