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