Commit | Line | Data |
---|---|---|
32176cfd RP |
1 | /*- |
2 | * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * | |
14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
32176cfd RP |
24 | */ |
25 | ||
085ff963 MD |
26 | #include <sys/cdefs.h> |
27 | #ifdef __FreeBSD__ | |
28 | __FBSDID("$FreeBSD$"); | |
29 | #endif | |
30 | ||
32176cfd RP |
31 | /* |
32 | * IEEE 802.11 Station mode support. | |
33 | */ | |
34 | #include "opt_inet.h" | |
35 | #include "opt_wlan.h" | |
36 | ||
37 | #include <sys/param.h> | |
38 | #include <sys/systm.h> | |
39 | #include <sys/mbuf.h> | |
40 | #include <sys/malloc.h> | |
41 | #include <sys/kernel.h> | |
42 | ||
43 | #include <sys/socket.h> | |
44 | #include <sys/sockio.h> | |
45 | #include <sys/endian.h> | |
46 | #include <sys/errno.h> | |
47 | #include <sys/proc.h> | |
48 | #include <sys/sysctl.h> | |
49 | ||
50 | #include <net/if.h> | |
51 | #include <net/if_media.h> | |
52 | #include <net/if_llc.h> | |
085ff963 MD |
53 | #include <net/if_dl.h> |
54 | #include <net/if_var.h> | |
32176cfd | 55 | #include <net/ethernet.h> |
32176cfd RP |
56 | |
57 | #include <net/bpf.h> | |
58 | ||
59 | #include <netproto/802_11/ieee80211_var.h> | |
60 | #include <netproto/802_11/ieee80211_sta.h> | |
61 | #include <netproto/802_11/ieee80211_input.h> | |
62 | #ifdef IEEE80211_SUPPORT_SUPERG | |
63 | #include <netproto/802_11/ieee80211_superg.h> | |
64 | #endif | |
085ff963 MD |
65 | #include <netproto/802_11/ieee80211_ratectl.h> |
66 | #include <netproto/802_11/ieee80211_sta.h> | |
32176cfd RP |
67 | |
68 | #define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) | |
69 | ||
70 | static void sta_vattach(struct ieee80211vap *); | |
71 | static void sta_beacon_miss(struct ieee80211vap *); | |
72 | static int sta_newstate(struct ieee80211vap *, enum ieee80211_state, int); | |
4f898719 IV |
73 | static int sta_input(struct ieee80211_node *, struct mbuf *, |
74 | const struct ieee80211_rx_stats *, int, int); | |
32176cfd | 75 | static void sta_recv_mgmt(struct ieee80211_node *, struct mbuf *, |
4f898719 | 76 | int subtype, const struct ieee80211_rx_stats *, int rssi, int nf); |
32176cfd RP |
77 | static void sta_recv_ctl(struct ieee80211_node *, struct mbuf *, int subtype); |
78 | ||
79 | void | |
80 | ieee80211_sta_attach(struct ieee80211com *ic) | |
81 | { | |
82 | ic->ic_vattach[IEEE80211_M_STA] = sta_vattach; | |
83 | } | |
84 | ||
85 | void | |
86 | ieee80211_sta_detach(struct ieee80211com *ic) | |
87 | { | |
88 | } | |
89 | ||
90 | static void | |
91 | sta_vdetach(struct ieee80211vap *vap) | |
92 | { | |
93 | } | |
94 | ||
95 | static void | |
96 | sta_vattach(struct ieee80211vap *vap) | |
97 | { | |
98 | vap->iv_newstate = sta_newstate; | |
99 | vap->iv_input = sta_input; | |
100 | vap->iv_recv_mgmt = sta_recv_mgmt; | |
101 | vap->iv_recv_ctl = sta_recv_ctl; | |
102 | vap->iv_opdetach = sta_vdetach; | |
103 | vap->iv_bmiss = sta_beacon_miss; | |
104 | } | |
105 | ||
106 | /* | |
107 | * Handle a beacon miss event. The common code filters out | |
108 | * spurious events that can happen when scanning and/or before | |
109 | * reaching RUN state. | |
110 | */ | |
111 | static void | |
112 | sta_beacon_miss(struct ieee80211vap *vap) | |
113 | { | |
114 | struct ieee80211com *ic = vap->iv_ic; | |
115 | ||
085ff963 MD |
116 | IEEE80211_LOCK_ASSERT(ic); |
117 | ||
32176cfd RP |
118 | KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning")); |
119 | KASSERT(vap->iv_state >= IEEE80211_S_RUN, | |
120 | ("wrong state %s", ieee80211_state_name[vap->iv_state])); | |
121 | ||
122 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, | |
123 | "beacon miss, mode %s state %s\n", | |
124 | ieee80211_opmode_name[vap->iv_opmode], | |
125 | ieee80211_state_name[vap->iv_state]); | |
126 | ||
127 | if (vap->iv_state == IEEE80211_S_CSA) { | |
128 | /* | |
129 | * A Channel Switch is pending; assume we missed the | |
130 | * beacon that would've completed the process and just | |
131 | * force the switch. If we made a mistake we'll not | |
132 | * find the AP on the new channel and fall back to a | |
133 | * normal scan. | |
134 | */ | |
135 | ieee80211_csa_completeswitch(ic); | |
136 | return; | |
137 | } | |
138 | if (++vap->iv_bmiss_count < vap->iv_bmiss_max) { | |
139 | /* | |
140 | * Send a directed probe req before falling back to a | |
141 | * scan; if we receive a response ic_bmiss_count will | |
142 | * be reset. Some cards mistakenly report beacon miss | |
143 | * so this avoids the expensive scan if the ap is | |
144 | * still there. | |
145 | */ | |
146 | ieee80211_send_probereq(vap->iv_bss, vap->iv_myaddr, | |
147 | vap->iv_bss->ni_bssid, vap->iv_bss->ni_bssid, | |
148 | vap->iv_bss->ni_essid, vap->iv_bss->ni_esslen); | |
149 | return; | |
150 | } | |
085ff963 MD |
151 | |
152 | callout_stop(&vap->iv_swbmiss); | |
32176cfd RP |
153 | vap->iv_bmiss_count = 0; |
154 | vap->iv_stats.is_beacon_miss++; | |
155 | if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { | |
156 | #ifdef IEEE80211_SUPPORT_SUPERG | |
157 | struct ieee80211com *ic = vap->iv_ic; | |
158 | ||
159 | /* | |
160 | * If we receive a beacon miss interrupt when using | |
161 | * dynamic turbo, attempt to switch modes before | |
162 | * reassociating. | |
163 | */ | |
164 | if (IEEE80211_ATH_CAP(vap, vap->iv_bss, IEEE80211_NODE_TURBOP)) | |
165 | ieee80211_dturbo_switch(vap, | |
166 | ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO); | |
167 | #endif | |
168 | /* | |
169 | * Try to reassociate before scanning for a new ap. | |
170 | */ | |
171 | ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1); | |
172 | } else { | |
173 | /* | |
174 | * Somebody else is controlling state changes (e.g. | |
175 | * a user-mode app) don't do anything that would | |
176 | * confuse them; just drop into scan mode so they'll | |
177 | * notified of the state change and given control. | |
178 | */ | |
179 | ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); | |
180 | } | |
181 | } | |
182 | ||
183 | /* | |
184 | * Handle deauth with reason. We retry only for | |
185 | * the cases where we might succeed. Otherwise | |
186 | * we downgrade the ap and scan. | |
187 | */ | |
188 | static void | |
189 | sta_authretry(struct ieee80211vap *vap, struct ieee80211_node *ni, int reason) | |
190 | { | |
191 | switch (reason) { | |
192 | case IEEE80211_STATUS_SUCCESS: /* NB: MLME assoc */ | |
193 | case IEEE80211_STATUS_TIMEOUT: | |
194 | case IEEE80211_REASON_ASSOC_EXPIRE: | |
195 | case IEEE80211_REASON_NOT_AUTHED: | |
196 | case IEEE80211_REASON_NOT_ASSOCED: | |
197 | case IEEE80211_REASON_ASSOC_LEAVE: | |
198 | case IEEE80211_REASON_ASSOC_NOT_AUTHED: | |
199 | IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); | |
200 | break; | |
201 | default: | |
202 | ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, reason); | |
203 | if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) | |
204 | ieee80211_check_scan_current(vap); | |
205 | break; | |
206 | } | |
207 | } | |
208 | ||
4f655ef5 MD |
209 | static void |
210 | sta_swbmiss_start(struct ieee80211vap *vap) | |
211 | { | |
212 | ||
213 | if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) { | |
214 | /* | |
215 | * Start s/w beacon miss timer for devices w/o | |
216 | * hardware support. We fudge a bit here since | |
217 | * we're doing this in software. | |
218 | */ | |
219 | vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS( | |
220 | 2 * vap->iv_bmissthreshold * vap->iv_bss->ni_intval); | |
221 | vap->iv_swbmiss_count = 0; | |
222 | callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, | |
223 | ieee80211_swbmiss, vap); | |
224 | } | |
225 | } | |
226 | ||
32176cfd RP |
227 | /* |
228 | * IEEE80211_M_STA vap state machine handler. | |
229 | * This routine handles the main states in the 802.11 protocol. | |
230 | */ | |
231 | static int | |
232 | sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) | |
233 | { | |
234 | struct ieee80211com *ic = vap->iv_ic; | |
235 | struct ieee80211_node *ni; | |
236 | enum ieee80211_state ostate; | |
085ff963 MD |
237 | |
238 | IEEE80211_LOCK_ASSERT(ic); | |
239 | ||
32176cfd RP |
240 | ostate = vap->iv_state; |
241 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", | |
242 | __func__, ieee80211_state_name[ostate], | |
243 | ieee80211_state_name[nstate], arg); | |
244 | vap->iv_state = nstate; /* state transition */ | |
085ff963 | 245 | callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */ |
32176cfd RP |
246 | if (ostate != IEEE80211_S_SCAN) |
247 | ieee80211_cancel_scan(vap); /* background scan */ | |
248 | ni = vap->iv_bss; /* NB: no reference held */ | |
249 | if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) | |
250 | callout_stop(&vap->iv_swbmiss); | |
251 | switch (nstate) { | |
252 | case IEEE80211_S_INIT: | |
253 | switch (ostate) { | |
254 | case IEEE80211_S_SLEEP: | |
255 | /* XXX wakeup */ | |
d98a0bcf | 256 | /* XXX driver hook to wakeup the hardware? */ |
32176cfd RP |
257 | case IEEE80211_S_RUN: |
258 | IEEE80211_SEND_MGMT(ni, | |
259 | IEEE80211_FC0_SUBTYPE_DISASSOC, | |
260 | IEEE80211_REASON_ASSOC_LEAVE); | |
261 | ieee80211_sta_leave(ni); | |
262 | break; | |
263 | case IEEE80211_S_ASSOC: | |
264 | IEEE80211_SEND_MGMT(ni, | |
265 | IEEE80211_FC0_SUBTYPE_DEAUTH, | |
266 | IEEE80211_REASON_AUTH_LEAVE); | |
267 | break; | |
268 | case IEEE80211_S_SCAN: | |
269 | ieee80211_cancel_scan(vap); | |
270 | break; | |
271 | default: | |
4f655ef5 | 272 | break; |
32176cfd RP |
273 | } |
274 | if (ostate != IEEE80211_S_INIT) { | |
275 | /* NB: optimize INIT -> INIT case */ | |
276 | ieee80211_reset_bss(vap); | |
277 | } | |
278 | if (vap->iv_auth->ia_detach != NULL) | |
279 | vap->iv_auth->ia_detach(vap); | |
280 | break; | |
281 | case IEEE80211_S_SCAN: | |
282 | switch (ostate) { | |
283 | case IEEE80211_S_INIT: | |
284 | /* | |
285 | * Initiate a scan. We can come here as a result | |
286 | * of an IEEE80211_IOC_SCAN_REQ too in which case | |
287 | * the vap will be marked with IEEE80211_FEXT_SCANREQ | |
288 | * and the scan request parameters will be present | |
289 | * in iv_scanreq. Otherwise we do the default. | |
290 | */ | |
291 | if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { | |
292 | ieee80211_check_scan(vap, | |
293 | vap->iv_scanreq_flags, | |
294 | vap->iv_scanreq_duration, | |
295 | vap->iv_scanreq_mindwell, | |
296 | vap->iv_scanreq_maxdwell, | |
297 | vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); | |
298 | vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; | |
299 | } else | |
300 | ieee80211_check_scan_current(vap); | |
301 | break; | |
302 | case IEEE80211_S_SCAN: | |
303 | case IEEE80211_S_AUTH: | |
304 | case IEEE80211_S_ASSOC: | |
305 | /* | |
306 | * These can happen either because of a timeout | |
307 | * on an assoc/auth response or because of a | |
308 | * change in state that requires a reset. For | |
309 | * the former we're called with a non-zero arg | |
310 | * that is the cause for the failure; pass this | |
311 | * to the scan code so it can update state. | |
312 | * Otherwise trigger a new scan unless we're in | |
313 | * manual roaming mode in which case an application | |
314 | * must issue an explicit scan request. | |
315 | */ | |
316 | if (arg != 0) | |
317 | ieee80211_scan_assoc_fail(vap, | |
318 | vap->iv_bss->ni_macaddr, arg); | |
319 | if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) | |
320 | ieee80211_check_scan_current(vap); | |
321 | break; | |
d98a0bcf MD |
322 | case IEEE80211_S_SLEEP: /* beacon miss */ |
323 | /* | |
324 | * XXX if in sleep we need to wakeup the hardware. | |
325 | */ | |
326 | /* FALLTHROUGH */ | |
32176cfd RP |
327 | case IEEE80211_S_RUN: /* beacon miss */ |
328 | /* | |
329 | * Beacon miss. Notify user space and if not | |
330 | * under control of a user application (roaming | |
331 | * manual) kick off a scan to re-connect. | |
332 | */ | |
d98a0bcf | 333 | |
32176cfd RP |
334 | ieee80211_sta_leave(ni); |
335 | if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) | |
336 | ieee80211_check_scan_current(vap); | |
337 | break; | |
338 | default: | |
339 | goto invalid; | |
340 | } | |
341 | break; | |
342 | case IEEE80211_S_AUTH: | |
343 | switch (ostate) { | |
344 | case IEEE80211_S_INIT: | |
345 | case IEEE80211_S_SCAN: | |
346 | IEEE80211_SEND_MGMT(ni, | |
347 | IEEE80211_FC0_SUBTYPE_AUTH, 1); | |
348 | break; | |
349 | case IEEE80211_S_AUTH: | |
350 | case IEEE80211_S_ASSOC: | |
351 | switch (arg & 0xff) { | |
352 | case IEEE80211_FC0_SUBTYPE_AUTH: | |
353 | /* ??? */ | |
354 | IEEE80211_SEND_MGMT(ni, | |
355 | IEEE80211_FC0_SUBTYPE_AUTH, 2); | |
356 | break; | |
357 | case IEEE80211_FC0_SUBTYPE_DEAUTH: | |
358 | sta_authretry(vap, ni, arg>>8); | |
359 | break; | |
360 | } | |
361 | break; | |
4f655ef5 | 362 | case IEEE80211_S_SLEEP: |
32176cfd RP |
363 | case IEEE80211_S_RUN: |
364 | switch (arg & 0xff) { | |
365 | case IEEE80211_FC0_SUBTYPE_AUTH: | |
366 | IEEE80211_SEND_MGMT(ni, | |
367 | IEEE80211_FC0_SUBTYPE_AUTH, 2); | |
4f655ef5 | 368 | vap->iv_state = IEEE80211_S_RUN; /* stay RUN */ |
32176cfd RP |
369 | break; |
370 | case IEEE80211_FC0_SUBTYPE_DEAUTH: | |
371 | ieee80211_sta_leave(ni); | |
372 | if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { | |
373 | /* try to reauth */ | |
374 | IEEE80211_SEND_MGMT(ni, | |
375 | IEEE80211_FC0_SUBTYPE_AUTH, 1); | |
376 | } | |
377 | break; | |
378 | } | |
379 | break; | |
380 | default: | |
381 | goto invalid; | |
382 | } | |
383 | break; | |
384 | case IEEE80211_S_ASSOC: | |
385 | switch (ostate) { | |
386 | case IEEE80211_S_AUTH: | |
387 | case IEEE80211_S_ASSOC: | |
388 | IEEE80211_SEND_MGMT(ni, | |
389 | IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); | |
390 | break; | |
391 | case IEEE80211_S_SLEEP: /* cannot happen */ | |
392 | case IEEE80211_S_RUN: | |
393 | ieee80211_sta_leave(ni); | |
394 | if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { | |
395 | IEEE80211_SEND_MGMT(ni, arg ? | |
396 | IEEE80211_FC0_SUBTYPE_REASSOC_REQ : | |
397 | IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); | |
398 | } | |
399 | break; | |
400 | default: | |
401 | goto invalid; | |
402 | } | |
403 | break; | |
404 | case IEEE80211_S_RUN: | |
405 | if (vap->iv_flags & IEEE80211_F_WPA) { | |
406 | /* XXX validate prerequisites */ | |
407 | } | |
408 | switch (ostate) { | |
409 | case IEEE80211_S_RUN: | |
410 | case IEEE80211_S_CSA: | |
411 | break; | |
412 | case IEEE80211_S_AUTH: /* when join is done in fw */ | |
413 | case IEEE80211_S_ASSOC: | |
414 | #ifdef IEEE80211_DEBUG | |
415 | if (ieee80211_msg_debug(vap)) { | |
1e290df3 | 416 | ieee80211_note(vap, "%s with %s ssid ", |
32176cfd RP |
417 | (vap->iv_opmode == IEEE80211_M_STA ? |
418 | "associated" : "synchronized"), | |
085ff963 | 419 | ether_sprintf(ni->ni_bssid)); |
32176cfd RP |
420 | ieee80211_print_essid(vap->iv_bss->ni_essid, |
421 | ni->ni_esslen); | |
422 | /* XXX MCS/HT */ | |
6168f72e | 423 | kprintf(" channel %d start %uMb\n", |
32176cfd RP |
424 | ieee80211_chan2ieee(ic, ic->ic_curchan), |
425 | IEEE80211_RATE2MBS(ni->ni_txrate)); | |
426 | } | |
427 | #endif | |
428 | ieee80211_scan_assoc_success(vap, ni->ni_macaddr); | |
429 | ieee80211_notify_node_join(ni, | |
430 | arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); | |
431 | break; | |
432 | case IEEE80211_S_SLEEP: | |
d98a0bcf MD |
433 | /* Wake up from sleep */ |
434 | vap->iv_sta_ps(vap, 0); | |
32176cfd RP |
435 | break; |
436 | default: | |
437 | goto invalid; | |
438 | } | |
439 | ieee80211_sync_curchan(ic); | |
4f655ef5 MD |
440 | if (ostate != IEEE80211_S_RUN) |
441 | sta_swbmiss_start(vap); | |
32176cfd RP |
442 | /* |
443 | * When 802.1x is not in use mark the port authorized | |
444 | * at this point so traffic can flow. | |
445 | */ | |
446 | if (ni->ni_authmode != IEEE80211_AUTH_8021X) | |
447 | ieee80211_node_authorize(ni); | |
448 | /* | |
449 | * Fake association when joining an existing bss. | |
d98a0bcf MD |
450 | * |
451 | * Don't do this if we're doing SLEEP->RUN. | |
32176cfd | 452 | */ |
d98a0bcf MD |
453 | if (ic->ic_newassoc != NULL && ostate != IEEE80211_S_SLEEP) |
454 | ic->ic_newassoc(vap->iv_bss, (ostate != IEEE80211_S_RUN)); | |
32176cfd RP |
455 | break; |
456 | case IEEE80211_S_CSA: | |
457 | if (ostate != IEEE80211_S_RUN) | |
458 | goto invalid; | |
459 | break; | |
460 | case IEEE80211_S_SLEEP: | |
4f655ef5 | 461 | sta_swbmiss_start(vap); |
085ff963 | 462 | vap->iv_sta_ps(vap, 1); |
32176cfd RP |
463 | break; |
464 | default: | |
465 | invalid: | |
466 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, | |
467 | "%s: unexpected state transition %s -> %s\n", __func__, | |
468 | ieee80211_state_name[ostate], ieee80211_state_name[nstate]); | |
469 | break; | |
470 | } | |
471 | return 0; | |
472 | } | |
473 | ||
474 | /* | |
475 | * Return non-zero if the frame is an echo of a multicast | |
476 | * frame sent by ourself. The dir is known to be DSTODS. | |
477 | */ | |
478 | static __inline int | |
479 | isdstods_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh) | |
480 | { | |
481 | #define QWH4(wh) ((const struct ieee80211_qosframe_addr4 *)wh) | |
482 | #define WH4(wh) ((const struct ieee80211_frame_addr4 *)wh) | |
483 | const uint8_t *sa; | |
484 | ||
485 | KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode")); | |
486 | ||
487 | if (!IEEE80211_IS_MULTICAST(wh->i_addr3)) | |
488 | return 0; | |
489 | sa = IEEE80211_QOS_HAS_SEQ(wh) ? QWH4(wh)->i_addr4 : WH4(wh)->i_addr4; | |
490 | return IEEE80211_ADDR_EQ(sa, vap->iv_myaddr); | |
491 | #undef WH4 | |
492 | #undef QWH4 | |
493 | } | |
494 | ||
495 | /* | |
496 | * Return non-zero if the frame is an echo of a multicast | |
497 | * frame sent by ourself. The dir is known to be FROMDS. | |
498 | */ | |
499 | static __inline int | |
500 | isfromds_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh) | |
501 | { | |
502 | KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode")); | |
503 | ||
504 | if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) | |
505 | return 0; | |
506 | return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr); | |
507 | } | |
508 | ||
509 | /* | |
510 | * Decide if a received management frame should be | |
511 | * printed when debugging is enabled. This filters some | |
512 | * of the less interesting frames that come frequently | |
513 | * (e.g. beacons). | |
514 | */ | |
515 | static __inline int | |
516 | doprint(struct ieee80211vap *vap, int subtype) | |
517 | { | |
518 | switch (subtype) { | |
519 | case IEEE80211_FC0_SUBTYPE_BEACON: | |
520 | return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN); | |
521 | case IEEE80211_FC0_SUBTYPE_PROBE_REQ: | |
522 | return 0; | |
523 | } | |
524 | return 1; | |
525 | } | |
526 | ||
527 | /* | |
528 | * Process a received frame. The node associated with the sender | |
529 | * should be supplied. If nothing was found in the node table then | |
530 | * the caller is assumed to supply a reference to iv_bss instead. | |
531 | * The RSSI and a timestamp are also supplied. The RSSI data is used | |
532 | * during AP scanning to select a AP to associate with; it can have | |
533 | * any units so long as values have consistent units and higher values | |
534 | * mean ``better signal''. The receive timestamp is currently not used | |
535 | * by the 802.11 layer. | |
536 | */ | |
537 | static int | |
4f898719 IV |
538 | sta_input(struct ieee80211_node *ni, struct mbuf *m, |
539 | const struct ieee80211_rx_stats *rxs, int rssi, int nf) | |
32176cfd | 540 | { |
32176cfd RP |
541 | struct ieee80211vap *vap = ni->ni_vap; |
542 | struct ieee80211com *ic = ni->ni_ic; | |
543 | struct ifnet *ifp = vap->iv_ifp; | |
544 | struct ieee80211_frame *wh; | |
545 | struct ieee80211_key *key; | |
546 | struct ether_header *eh; | |
547 | int hdrspace, need_tap = 1; /* mbuf need to be tapped. */ | |
548 | uint8_t dir, type, subtype, qos; | |
549 | uint8_t *bssid; | |
32176cfd RP |
550 | |
551 | if (m->m_flags & M_AMPDU_MPDU) { | |
552 | /* | |
553 | * Fastpath for A-MPDU reorder q resubmission. Frames | |
554 | * w/ M_AMPDU_MPDU marked have already passed through | |
555 | * here but were received out of order and been held on | |
556 | * the reorder queue. When resubmitted they are marked | |
557 | * with the M_AMPDU_MPDU flag and we can bypass most of | |
558 | * the normal processing. | |
559 | */ | |
560 | wh = mtod(m, struct ieee80211_frame *); | |
561 | type = IEEE80211_FC0_TYPE_DATA; | |
562 | dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; | |
563 | subtype = IEEE80211_FC0_SUBTYPE_QOS; | |
564 | hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ | |
565 | goto resubmit_ampdu; | |
566 | } | |
567 | ||
568 | KASSERT(ni != NULL, ("null node")); | |
569 | ni->ni_inact = ni->ni_inact_reload; | |
570 | ||
571 | type = -1; /* undefined */ | |
572 | ||
573 | if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { | |
574 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, | |
575 | ni->ni_macaddr, NULL, | |
576 | "too short (1): len %u", m->m_pkthdr.len); | |
577 | vap->iv_stats.is_rx_tooshort++; | |
578 | goto out; | |
579 | } | |
580 | /* | |
581 | * Bit of a cheat here, we use a pointer for a 3-address | |
582 | * frame format but don't reference fields past outside | |
583 | * ieee80211_frame_min w/o first validating the data is | |
584 | * present. | |
585 | */ | |
586 | wh = mtod(m, struct ieee80211_frame *); | |
587 | ||
588 | if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != | |
589 | IEEE80211_FC0_VERSION_0) { | |
590 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, | |
591 | ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x", | |
592 | wh->i_fc[0], wh->i_fc[1]); | |
593 | vap->iv_stats.is_rx_badversion++; | |
594 | goto err; | |
595 | } | |
596 | ||
597 | dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; | |
598 | type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; | |
599 | subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; | |
600 | if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { | |
601 | bssid = wh->i_addr2; | |
602 | if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) { | |
603 | /* not interested in */ | |
604 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, | |
605 | bssid, NULL, "%s", "not to bss"); | |
606 | vap->iv_stats.is_rx_wrongbss++; | |
607 | goto out; | |
608 | } | |
085ff963 MD |
609 | |
610 | /* | |
611 | * Some devices may be in a promiscuous mode | |
612 | * where they receive frames for multiple station | |
613 | * addresses. | |
614 | * | |
615 | * If we receive a data frame that isn't | |
616 | * destined to our VAP MAC, drop it. | |
617 | * | |
618 | * XXX TODO: This is only enforced when not scanning; | |
619 | * XXX it assumes a software-driven scan will put the NIC | |
620 | * XXX into a "no data frames" mode before setting this | |
621 | * XXX flag. Otherwise it may be possible that we'll still | |
622 | * XXX process data frames whilst scanning. | |
623 | */ | |
624 | if ((! IEEE80211_IS_MULTICAST(wh->i_addr1)) | |
625 | && (! IEEE80211_ADDR_EQ(wh->i_addr1, IF_LLADDR(ifp)))) { | |
626 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, | |
4f655ef5 | 627 | #if defined(__DragonFly__) |
f92fae3f SW |
628 | bssid, NULL, "not to cur sta: lladdr=%s, addr1=%s", |
629 | ether_sprintf(IF_LLADDR(ifp)), | |
630 | ether_sprintf(wh->i_addr1)); | |
4f655ef5 MD |
631 | #else |
632 | bssid, NULL, "not to cur sta: lladdr=%6D, addr1=%6D", | |
633 | IF_LLADDR(ifp), ":", wh->i_addr1, ":"); | |
634 | #endif | |
085ff963 MD |
635 | vap->iv_stats.is_rx_wrongbss++; |
636 | goto out; | |
637 | } | |
638 | ||
32176cfd RP |
639 | IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); |
640 | ni->ni_noise = nf; | |
4f898719 IV |
641 | if ( IEEE80211_HAS_SEQ(type, subtype) && |
642 | !IEEE80211_IS_MULTICAST(wh->i_addr1)) { | |
32176cfd RP |
643 | uint8_t tid = ieee80211_gettid(wh); |
644 | if (IEEE80211_QOS_HAS_SEQ(wh) && | |
645 | TID_TO_WME_AC(tid) >= WME_AC_VI) | |
646 | ic->ic_wme.wme_hipri_traffic++; | |
4f655ef5 | 647 | if (! ieee80211_check_rxseq(ni, wh, bssid)) |
32176cfd | 648 | goto out; |
32176cfd RP |
649 | } |
650 | } | |
651 | ||
652 | switch (type) { | |
653 | case IEEE80211_FC0_TYPE_DATA: | |
654 | hdrspace = ieee80211_hdrspace(ic, wh); | |
655 | if (m->m_len < hdrspace && | |
656 | (m = m_pullup(m, hdrspace)) == NULL) { | |
657 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, | |
658 | ni->ni_macaddr, NULL, | |
659 | "data too short: expecting %u", hdrspace); | |
660 | vap->iv_stats.is_rx_tooshort++; | |
661 | goto out; /* XXX */ | |
662 | } | |
663 | /* | |
664 | * Handle A-MPDU re-ordering. If the frame is to be | |
665 | * processed directly then ieee80211_ampdu_reorder | |
666 | * will return 0; otherwise it has consumed the mbuf | |
667 | * and we should do nothing more with it. | |
668 | */ | |
669 | if ((m->m_flags & M_AMPDU) && | |
670 | (dir == IEEE80211_FC1_DIR_FROMDS || | |
671 | dir == IEEE80211_FC1_DIR_DSTODS) && | |
672 | ieee80211_ampdu_reorder(ni, m) != 0) { | |
673 | m = NULL; | |
674 | goto out; | |
675 | } | |
676 | resubmit_ampdu: | |
677 | if (dir == IEEE80211_FC1_DIR_FROMDS) { | |
678 | if ((ifp->if_flags & IFF_SIMPLEX) && | |
679 | isfromds_mcastecho(vap, wh)) { | |
680 | /* | |
681 | * In IEEE802.11 network, multicast | |
682 | * packets sent from "me" are broadcast | |
683 | * from the AP; silently discard for | |
684 | * SIMPLEX interface. | |
685 | */ | |
686 | IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, | |
687 | wh, "data", "%s", "multicast echo"); | |
688 | vap->iv_stats.is_rx_mcastecho++; | |
689 | goto out; | |
690 | } | |
691 | if ((vap->iv_flags & IEEE80211_F_DWDS) && | |
692 | IEEE80211_IS_MULTICAST(wh->i_addr1)) { | |
693 | /* | |
694 | * DWDS sta's must drop 3-address mcast frames | |
695 | * as they will be sent separately as a 4-addr | |
696 | * frame. Accepting the 3-addr frame will | |
697 | * confuse the bridge into thinking the sending | |
698 | * sta is located at the end of WDS link. | |
699 | */ | |
700 | IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, | |
701 | "3-address data", "%s", "DWDS enabled"); | |
702 | vap->iv_stats.is_rx_mcastecho++; | |
703 | goto out; | |
704 | } | |
705 | } else if (dir == IEEE80211_FC1_DIR_DSTODS) { | |
706 | if ((vap->iv_flags & IEEE80211_F_DWDS) == 0) { | |
707 | IEEE80211_DISCARD(vap, | |
708 | IEEE80211_MSG_INPUT, wh, "4-address data", | |
709 | "%s", "DWDS not enabled"); | |
710 | vap->iv_stats.is_rx_wrongdir++; | |
711 | goto out; | |
712 | } | |
713 | if ((ifp->if_flags & IFF_SIMPLEX) && | |
714 | isdstods_mcastecho(vap, wh)) { | |
715 | /* | |
716 | * In IEEE802.11 network, multicast | |
717 | * packets sent from "me" are broadcast | |
718 | * from the AP; silently discard for | |
719 | * SIMPLEX interface. | |
720 | */ | |
721 | IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, | |
722 | "4-address data", "%s", "multicast echo"); | |
723 | vap->iv_stats.is_rx_mcastecho++; | |
724 | goto out; | |
725 | } | |
726 | } else { | |
727 | IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, | |
728 | "data", "incorrect dir 0x%x", dir); | |
729 | vap->iv_stats.is_rx_wrongdir++; | |
730 | goto out; | |
731 | } | |
732 | ||
733 | /* | |
734 | * Handle privacy requirements. Note that we | |
735 | * must not be preempted from here until after | |
736 | * we (potentially) call ieee80211_crypto_demic; | |
737 | * otherwise we may violate assumptions in the | |
738 | * crypto cipher modules used to do delayed update | |
739 | * of replay sequence numbers. | |
740 | */ | |
085ff963 | 741 | if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { |
32176cfd RP |
742 | if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { |
743 | /* | |
744 | * Discard encrypted frames when privacy is off. | |
745 | */ | |
746 | IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, | |
747 | wh, "WEP", "%s", "PRIVACY off"); | |
748 | vap->iv_stats.is_rx_noprivacy++; | |
749 | IEEE80211_NODE_STAT(ni, rx_noprivacy); | |
750 | goto out; | |
751 | } | |
752 | key = ieee80211_crypto_decap(ni, m, hdrspace); | |
753 | if (key == NULL) { | |
754 | /* NB: stats+msgs handled in crypto_decap */ | |
755 | IEEE80211_NODE_STAT(ni, rx_wepfail); | |
756 | goto out; | |
757 | } | |
758 | wh = mtod(m, struct ieee80211_frame *); | |
085ff963 | 759 | wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; |
32176cfd RP |
760 | } else { |
761 | /* XXX M_WEP and IEEE80211_F_PRIVACY */ | |
762 | key = NULL; | |
763 | } | |
764 | ||
765 | /* | |
766 | * Save QoS bits for use below--before we strip the header. | |
767 | */ | |
768 | if (subtype == IEEE80211_FC0_SUBTYPE_QOS) { | |
769 | qos = (dir == IEEE80211_FC1_DIR_DSTODS) ? | |
770 | ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] : | |
771 | ((struct ieee80211_qosframe *)wh)->i_qos[0]; | |
772 | } else | |
773 | qos = 0; | |
774 | ||
775 | /* | |
776 | * Next up, any fragmentation. | |
777 | */ | |
778 | if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { | |
779 | m = ieee80211_defrag(ni, m, hdrspace); | |
780 | if (m == NULL) { | |
781 | /* Fragment dropped or frame not complete yet */ | |
782 | goto out; | |
783 | } | |
784 | } | |
785 | wh = NULL; /* no longer valid, catch any uses */ | |
786 | ||
787 | /* | |
788 | * Next strip any MSDU crypto bits. | |
789 | */ | |
790 | if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) { | |
791 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, | |
792 | ni->ni_macaddr, "data", "%s", "demic error"); | |
793 | vap->iv_stats.is_rx_demicfail++; | |
794 | IEEE80211_NODE_STAT(ni, rx_demicfail); | |
795 | goto out; | |
796 | } | |
797 | ||
798 | /* copy to listener after decrypt */ | |
799 | if (ieee80211_radiotap_active_vap(vap)) | |
800 | ieee80211_radiotap_rx(vap, m); | |
801 | need_tap = 0; | |
802 | ||
803 | /* | |
804 | * Finally, strip the 802.11 header. | |
805 | */ | |
806 | m = ieee80211_decap(vap, m, hdrspace); | |
807 | if (m == NULL) { | |
808 | /* XXX mask bit to check for both */ | |
809 | /* don't count Null data frames as errors */ | |
810 | if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || | |
811 | subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) | |
812 | goto out; | |
813 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, | |
814 | ni->ni_macaddr, "data", "%s", "decap error"); | |
815 | vap->iv_stats.is_rx_decap++; | |
816 | IEEE80211_NODE_STAT(ni, rx_decap); | |
817 | goto err; | |
818 | } | |
819 | eh = mtod(m, struct ether_header *); | |
820 | if (!ieee80211_node_is_authorized(ni)) { | |
821 | /* | |
822 | * Deny any non-PAE frames received prior to | |
823 | * authorization. For open/shared-key | |
824 | * authentication the port is mark authorized | |
825 | * after authentication completes. For 802.1x | |
826 | * the port is not marked authorized by the | |
827 | * authenticator until the handshake has completed. | |
828 | */ | |
829 | if (eh->ether_type != htons(ETHERTYPE_PAE)) { | |
830 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, | |
831 | eh->ether_shost, "data", | |
832 | "unauthorized port: ether type 0x%x len %u", | |
833 | eh->ether_type, m->m_pkthdr.len); | |
834 | vap->iv_stats.is_rx_unauth++; | |
835 | IEEE80211_NODE_STAT(ni, rx_unauth); | |
836 | goto err; | |
837 | } | |
838 | } else { | |
839 | /* | |
840 | * When denying unencrypted frames, discard | |
841 | * any non-PAE frames received without encryption. | |
842 | */ | |
843 | if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && | |
844 | (key == NULL && (m->m_flags & M_WEP) == 0) && | |
845 | eh->ether_type != htons(ETHERTYPE_PAE)) { | |
846 | /* | |
847 | * Drop unencrypted frames. | |
848 | */ | |
849 | vap->iv_stats.is_rx_unencrypted++; | |
850 | IEEE80211_NODE_STAT(ni, rx_unencrypted); | |
851 | goto out; | |
852 | } | |
853 | } | |
854 | /* XXX require HT? */ | |
855 | if (qos & IEEE80211_QOS_AMSDU) { | |
856 | m = ieee80211_decap_amsdu(ni, m); | |
857 | if (m == NULL) | |
858 | return IEEE80211_FC0_TYPE_DATA; | |
859 | } else { | |
860 | #ifdef IEEE80211_SUPPORT_SUPERG | |
861 | m = ieee80211_decap_fastframe(vap, ni, m); | |
862 | if (m == NULL) | |
863 | return IEEE80211_FC0_TYPE_DATA; | |
864 | #endif | |
865 | } | |
866 | ieee80211_deliver_data(vap, ni, m); | |
867 | return IEEE80211_FC0_TYPE_DATA; | |
868 | ||
869 | case IEEE80211_FC0_TYPE_MGT: | |
870 | vap->iv_stats.is_rx_mgmt++; | |
871 | IEEE80211_NODE_STAT(ni, rx_mgmt); | |
872 | if (dir != IEEE80211_FC1_DIR_NODS) { | |
873 | IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, | |
874 | wh, "data", "incorrect dir 0x%x", dir); | |
875 | vap->iv_stats.is_rx_wrongdir++; | |
876 | goto err; | |
877 | } | |
878 | if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { | |
879 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, | |
880 | ni->ni_macaddr, "mgt", "too short: len %u", | |
881 | m->m_pkthdr.len); | |
882 | vap->iv_stats.is_rx_tooshort++; | |
883 | goto out; | |
884 | } | |
885 | #ifdef IEEE80211_DEBUG | |
886 | if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) || | |
887 | ieee80211_msg_dumppkts(vap)) { | |
1e290df3 | 888 | if_printf(ifp, "received %s from %s rssi %d\n", |
4f655ef5 | 889 | ieee80211_mgt_subtype_name(subtype), |
085ff963 | 890 | ether_sprintf(wh->i_addr2), rssi); |
32176cfd RP |
891 | } |
892 | #endif | |
085ff963 | 893 | if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { |
32176cfd RP |
894 | if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) { |
895 | /* | |
896 | * Only shared key auth frames with a challenge | |
897 | * should be encrypted, discard all others. | |
898 | */ | |
899 | IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, | |
4f655ef5 | 900 | wh, ieee80211_mgt_subtype_name(subtype), |
32176cfd RP |
901 | "%s", "WEP set but not permitted"); |
902 | vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ | |
903 | goto out; | |
904 | } | |
905 | if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { | |
906 | /* | |
907 | * Discard encrypted frames when privacy is off. | |
908 | */ | |
909 | IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, | |
910 | wh, "mgt", "%s", "WEP set but PRIVACY off"); | |
911 | vap->iv_stats.is_rx_noprivacy++; | |
912 | goto out; | |
913 | } | |
914 | hdrspace = ieee80211_hdrspace(ic, wh); | |
915 | key = ieee80211_crypto_decap(ni, m, hdrspace); | |
916 | if (key == NULL) { | |
917 | /* NB: stats+msgs handled in crypto_decap */ | |
918 | goto out; | |
919 | } | |
920 | wh = mtod(m, struct ieee80211_frame *); | |
085ff963 | 921 | wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; |
32176cfd | 922 | } |
4f898719 | 923 | vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf); |
32176cfd RP |
924 | goto out; |
925 | ||
926 | case IEEE80211_FC0_TYPE_CTL: | |
927 | vap->iv_stats.is_rx_ctl++; | |
928 | IEEE80211_NODE_STAT(ni, rx_ctrl); | |
929 | vap->iv_recv_ctl(ni, m, subtype); | |
930 | goto out; | |
931 | ||
932 | default: | |
933 | IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, | |
934 | wh, NULL, "bad frame type 0x%x", type); | |
935 | /* should not come here */ | |
936 | break; | |
937 | } | |
938 | err: | |
4f655ef5 | 939 | if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); |
32176cfd RP |
940 | out: |
941 | if (m != NULL) { | |
942 | if (need_tap && ieee80211_radiotap_active_vap(vap)) | |
943 | ieee80211_radiotap_rx(vap, m); | |
944 | m_freem(m); | |
945 | } | |
946 | return type; | |
32176cfd RP |
947 | } |
948 | ||
949 | static void | |
950 | sta_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh, | |
951 | int rssi, int nf, uint16_t seq, uint16_t status) | |
952 | { | |
953 | struct ieee80211vap *vap = ni->ni_vap; | |
954 | ||
955 | if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { | |
956 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, | |
957 | ni->ni_macaddr, "open auth", | |
958 | "bad sta auth mode %u", ni->ni_authmode); | |
959 | vap->iv_stats.is_rx_bad_auth++; /* XXX */ | |
960 | return; | |
961 | } | |
962 | if (vap->iv_state != IEEE80211_S_AUTH || | |
963 | seq != IEEE80211_AUTH_OPEN_RESPONSE) { | |
964 | vap->iv_stats.is_rx_bad_auth++; | |
965 | return; | |
966 | } | |
967 | if (status != 0) { | |
968 | IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, | |
969 | ni, "open auth failed (reason %d)", status); | |
970 | vap->iv_stats.is_rx_auth_fail++; | |
971 | vap->iv_stats.is_rx_authfail_code = status; | |
972 | ieee80211_new_state(vap, IEEE80211_S_SCAN, | |
973 | IEEE80211_SCAN_FAIL_STATUS); | |
974 | } else | |
975 | ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); | |
976 | } | |
977 | ||
978 | static void | |
979 | sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh, | |
980 | uint8_t *frm, uint8_t *efrm, int rssi, int nf, | |
981 | uint16_t seq, uint16_t status) | |
982 | { | |
983 | struct ieee80211vap *vap = ni->ni_vap; | |
984 | uint8_t *challenge; | |
085ff963 | 985 | int estatus; |
32176cfd RP |
986 | |
987 | /* | |
988 | * NB: this can happen as we allow pre-shared key | |
989 | * authentication to be enabled w/o wep being turned | |
990 | * on so that configuration of these can be done | |
991 | * in any order. It may be better to enforce the | |
992 | * ordering in which case this check would just be | |
993 | * for sanity/consistency. | |
994 | */ | |
995 | if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { | |
996 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, | |
997 | ni->ni_macaddr, "shared key auth", | |
998 | "%s", " PRIVACY is disabled"); | |
085ff963 | 999 | estatus = IEEE80211_STATUS_ALG; |
32176cfd RP |
1000 | goto bad; |
1001 | } | |
1002 | /* | |
1003 | * Pre-shared key authentication is evil; accept | |
1004 | * it only if explicitly configured (it is supported | |
1005 | * mainly for compatibility with clients like OS X). | |
1006 | */ | |
1007 | if (ni->ni_authmode != IEEE80211_AUTH_AUTO && | |
1008 | ni->ni_authmode != IEEE80211_AUTH_SHARED) { | |
1009 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, | |
1010 | ni->ni_macaddr, "shared key auth", | |
1011 | "bad sta auth mode %u", ni->ni_authmode); | |
1012 | vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */ | |
085ff963 | 1013 | estatus = IEEE80211_STATUS_ALG; |
32176cfd RP |
1014 | goto bad; |
1015 | } | |
1016 | ||
1017 | challenge = NULL; | |
1018 | if (frm + 1 < efrm) { | |
1019 | if ((frm[1] + 2) > (efrm - frm)) { | |
1020 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, | |
1021 | ni->ni_macaddr, "shared key auth", | |
2c7ccc4a | 1022 | "ie %d/%ld too long", |
085ff963 | 1023 | frm[0], (frm[1] + 2) - (efrm - frm)); |
32176cfd | 1024 | vap->iv_stats.is_rx_bad_auth++; |
085ff963 | 1025 | estatus = IEEE80211_STATUS_CHALLENGE; |
32176cfd RP |
1026 | goto bad; |
1027 | } | |
1028 | if (*frm == IEEE80211_ELEMID_CHALLENGE) | |
1029 | challenge = frm; | |
1030 | frm += frm[1] + 2; | |
1031 | } | |
1032 | switch (seq) { | |
1033 | case IEEE80211_AUTH_SHARED_CHALLENGE: | |
1034 | case IEEE80211_AUTH_SHARED_RESPONSE: | |
1035 | if (challenge == NULL) { | |
1036 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, | |
1037 | ni->ni_macaddr, "shared key auth", | |
1038 | "%s", "no challenge"); | |
1039 | vap->iv_stats.is_rx_bad_auth++; | |
085ff963 | 1040 | estatus = IEEE80211_STATUS_CHALLENGE; |
32176cfd RP |
1041 | goto bad; |
1042 | } | |
1043 | if (challenge[1] != IEEE80211_CHALLENGE_LEN) { | |
1044 | IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, | |
1045 | ni->ni_macaddr, "shared key auth", | |
1046 | "bad challenge len %d", challenge[1]); | |
1047 | vap->iv_stats.is_rx_bad_auth++; | |
085ff963 | 1048 | estatus = IEEE80211_STATUS_CHALLENGE; |
32176cfd RP |
1049 | goto bad; |
1050 | } | |
1051 | default: | |
1052 | break; | |
1053 | } | |
1054 | if (vap->iv_state != IEEE80211_S_AUTH) | |
1055 | return; | |
1056 | switch (seq) { | |
1057 | case IEEE80211_AUTH_SHARED_PASS: | |
1058 | if (ni->ni_challenge != NULL) { | |
4f655ef5 | 1059 | IEEE80211_FREE(ni->ni_challenge, M_80211_NODE); |
32176cfd RP |
1060 | ni->ni_challenge = NULL; |
1061 | } | |
1062 | if (status != 0) { | |
1063 | IEEE80211_NOTE_FRAME(vap, | |
1064 | IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, wh, | |
1065 | "shared key auth failed (reason %d)", status); | |
1066 | vap->iv_stats.is_rx_auth_fail++; | |
1067 | vap->iv_stats.is_rx_authfail_code = status; | |
1068 | return; | |
1069 | } | |
1070 | ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); | |
1071 | break; | |
1072 | case IEEE80211_AUTH_SHARED_CHALLENGE: | |
1073 | if (!ieee80211_alloc_challenge(ni)) | |
1074 | return; | |
1075 | /* XXX could optimize by passing recvd challenge */ | |
1076 | memcpy(ni->ni_challenge, &challenge[2], challenge[1]); | |
1077 | IEEE80211_SEND_MGMT(ni, | |
1078 | IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); | |
1079 | break; | |
1080 | default: | |
1081 | IEEE80211_DISCARD(vap, IEEE80211_MSG_AUTH, | |
1082 | wh, "shared key auth", "bad seq %d", seq); | |
1083 | vap->iv_stats.is_rx_bad_auth++; | |
1084 | return; | |
1085 | } | |
1086 | return; | |
1087 | bad: | |
1088 | /* | |
1089 | * Kick the state machine. This short-circuits | |
1090 | * using the mgt frame timeout to trigger the | |
1091 | * state transition. | |
1092 | */ | |
1093 | if (vap->iv_state == IEEE80211_S_AUTH) | |
1094 | ieee80211_new_state(vap, IEEE80211_S_SCAN, | |
1095 | IEEE80211_SCAN_FAIL_STATUS); | |
1096 | } | |
1097 | ||
085ff963 | 1098 | int |
32176cfd RP |
1099 | ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm, |
1100 | const struct ieee80211_frame *wh) | |
1101 | { | |
1102 | #define MS(_v, _f) (((_v) & _f) >> _f##_S) | |
1103 | struct ieee80211_wme_state *wme = &vap->iv_ic->ic_wme; | |
1104 | u_int len = frm[1], qosinfo; | |
1105 | int i; | |
1106 | ||
1107 | if (len < sizeof(struct ieee80211_wme_param)-2) { | |
1108 | IEEE80211_DISCARD_IE(vap, | |
1109 | IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME, | |
1110 | wh, "WME", "too short, len %u", len); | |
1111 | return -1; | |
1112 | } | |
1113 | qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)]; | |
1114 | qosinfo &= WME_QOSINFO_COUNT; | |
1115 | /* XXX do proper check for wraparound */ | |
1116 | if (qosinfo == wme->wme_wmeChanParams.cap_info) | |
1117 | return 0; | |
1118 | frm += __offsetof(struct ieee80211_wme_param, params_acParams); | |
1119 | for (i = 0; i < WME_NUM_AC; i++) { | |
1120 | struct wmeParams *wmep = | |
1121 | &wme->wme_wmeChanParams.cap_wmeParams[i]; | |
1122 | /* NB: ACI not used */ | |
1123 | wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM); | |
1124 | wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN); | |
1125 | wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN); | |
1126 | wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX); | |
4f655ef5 | 1127 | wmep->wmep_txopLimit = le16dec(frm+2); |
32176cfd RP |
1128 | frm += 4; |
1129 | } | |
1130 | wme->wme_wmeChanParams.cap_info = qosinfo; | |
1131 | return 1; | |
1132 | #undef MS | |
1133 | } | |
1134 | ||
1135 | /* | |
1136 | * Process 11h Channel Switch Announcement (CSA) ie. If this | |
1137 | * is the first CSA then initiate the switch. Otherwise we | |
1138 | * track state and trigger completion and/or cancel of the switch. | |
1139 | * XXX should be public for IBSS use | |
1140 | */ | |
1141 | static void | |
1142 | ieee80211_parse_csaparams(struct ieee80211vap *vap, uint8_t *frm, | |
1143 | const struct ieee80211_frame *wh) | |
1144 | { | |
1145 | struct ieee80211com *ic = vap->iv_ic; | |
1146 | const struct ieee80211_csa_ie *csa = | |
1147 | (const struct ieee80211_csa_ie *) frm; | |
1148 | ||
1149 | KASSERT(vap->iv_state >= IEEE80211_S_RUN, | |
1150 | ("state %s", ieee80211_state_name[vap->iv_state])); | |
1151 | ||
1152 | if (csa->csa_mode > 1) { | |
1153 | IEEE80211_DISCARD_IE(vap, | |
1154 | IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH, | |
1155 | wh, "CSA", "invalid mode %u", csa->csa_mode); | |
1156 | return; | |
1157 | } | |
085ff963 | 1158 | IEEE80211_LOCK(ic); |
32176cfd RP |
1159 | if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0) { |
1160 | /* | |
1161 | * Convert the channel number to a channel reference. We | |
1162 | * try first to preserve turbo attribute of the current | |
1163 | * channel then fallback. Note this will not work if the | |
1164 | * CSA specifies a channel that requires a band switch (e.g. | |
1165 | * 11a => 11g). This is intentional as 11h is defined only | |
1166 | * for 5GHz/11a and because the switch does not involve a | |
1167 | * reassociation, protocol state (capabilities, negotated | |
1168 | * rates, etc) may/will be wrong. | |
1169 | */ | |
1170 | struct ieee80211_channel *c = | |
1171 | ieee80211_find_channel_byieee(ic, csa->csa_newchan, | |
1172 | (ic->ic_bsschan->ic_flags & IEEE80211_CHAN_ALLTURBO)); | |
1173 | if (c == NULL) { | |
1174 | c = ieee80211_find_channel_byieee(ic, | |
1175 | csa->csa_newchan, | |
1176 | (ic->ic_bsschan->ic_flags & IEEE80211_CHAN_ALL)); | |
1177 | if (c == NULL) { | |
1178 | IEEE80211_DISCARD_IE(vap, | |
1179 | IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH, | |
1180 | wh, "CSA", "invalid channel %u", | |
1181 | csa->csa_newchan); | |
1182 | goto done; | |
1183 | } | |
1184 | } | |
1185 | #if IEEE80211_CSA_COUNT_MIN > 0 | |
1186 | if (csa->csa_count < IEEE80211_CSA_COUNT_MIN) { | |
1187 | /* | |
1188 | * Require at least IEEE80211_CSA_COUNT_MIN count to | |
1189 | * reduce the risk of being redirected by a fabricated | |
1190 | * CSA. If a valid CSA is dropped we'll still get a | |
1191 | * beacon miss when the AP leaves the channel so we'll | |
1192 | * eventually follow to the new channel. | |
1193 | * | |
1194 | * NOTE: this violates the 11h spec that states that | |
1195 | * count may be any value and if 0 then a switch | |
1196 | * should happen asap. | |
1197 | */ | |
1198 | IEEE80211_DISCARD_IE(vap, | |
1199 | IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH, | |
1200 | wh, "CSA", "count %u too small, must be >= %u", | |
1201 | csa->csa_count, IEEE80211_CSA_COUNT_MIN); | |
1202 | goto done; | |
1203 | } | |
1204 | #endif | |
1205 | ieee80211_csa_startswitch(ic, c, csa->csa_mode, csa->csa_count); | |
1206 | } else { | |
1207 | /* | |
1208 | * Validate this ie against the initial CSA. We require | |
1209 | * mode and channel not change and the count must be | |
1210 | * monotonically decreasing. This may be pointless and | |
1211 | * canceling the switch as a result may be too paranoid but | |
1212 | * in the worst case if we drop out of CSA because of this | |
1213 | * and the AP does move then we'll just end up taking a | |
1214 | * beacon miss and scan to find the AP. | |
1215 | * | |
1216 | * XXX may want <= on count as we also process ProbeResp | |
1217 | * frames and those may come in w/ the same count as the | |
1218 | * previous beacon; but doing so leaves us open to a stuck | |
1219 | * count until we add a dead-man timer | |
1220 | */ | |
1221 | if (!(csa->csa_count < ic->ic_csa_count && | |
1222 | csa->csa_mode == ic->ic_csa_mode && | |
1223 | csa->csa_newchan == ieee80211_chan2ieee(ic, ic->ic_csa_newchan))) { | |
1224 | IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_DOTH, wh, | |
1225 | "CSA ie mismatch, initial ie <%d,%d,%d>, " | |
1226 | "this ie <%d,%d,%d>", ic->ic_csa_mode, | |
2c7ccc4a | 1227 | ic->ic_csa_newchan->ic_ieee, ic->ic_csa_count, |
32176cfd RP |
1228 | csa->csa_mode, csa->csa_newchan, csa->csa_count); |
1229 | ieee80211_csa_cancelswitch(ic); | |
1230 | } else { | |
1231 | if (csa->csa_count <= 1) | |
1232 | ieee80211_csa_completeswitch(ic); | |
1233 | else | |
1234 | ic->ic_csa_count = csa->csa_count; | |
1235 | } | |
1236 | } | |
1237 | done: | |
085ff963 | 1238 | IEEE80211_UNLOCK(ic); |
32176cfd RP |
1239 | } |
1240 | ||
1241 | /* | |
1242 | * Return non-zero if a background scan may be continued: | |
1243 | * o bg scan is active | |
1244 | * o no channel switch is pending | |
1245 | * o there has not been any traffic recently | |
3aca8a44 | 1246 | * o no full-offload scan support (no need for explicitly continuing scan then) |
32176cfd RP |
1247 | * |
1248 | * Note we do not check if there is an administrative enable; | |
1249 | * this is only done to start the scan. We assume that any | |
1250 | * change in state will be accompanied by a request to cancel | |
1251 | * active scans which will otherwise cause this test to fail. | |
1252 | */ | |
1253 | static __inline int | |
1254 | contbgscan(struct ieee80211vap *vap) | |
1255 | { | |
1256 | struct ieee80211com *ic = vap->iv_ic; | |
1257 | ||
1258 | return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) && | |
1259 | (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 && | |
3aca8a44 | 1260 | !(vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) && |
32176cfd | 1261 | vap->iv_state == IEEE80211_S_RUN && /* XXX? */ |
4f655ef5 | 1262 | ieee80211_time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)); |
32176cfd RP |
1263 | } |
1264 | ||
1265 | /* | |
1266 | * Return non-zero if a backgrond scan may be started: | |
1267 | * o bg scanning is administratively enabled | |
1268 | * o no channel switch is pending | |
1269 | * o we are not boosted on a dynamic turbo channel | |
1270 | * o there has not been a scan recently | |
3aca8a44 | 1271 | * o there has not been any traffic recently (don't check if full-offload scan) |
32176cfd RP |
1272 | */ |
1273 | static __inline int | |
1274 | startbgscan(struct ieee80211vap *vap) | |
1275 | { | |
1276 | struct ieee80211com *ic = vap->iv_ic; | |
1277 | ||
1278 | return ((vap->iv_flags & IEEE80211_F_BGSCAN) && | |
1279 | (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 && | |
1280 | #ifdef IEEE80211_SUPPORT_SUPERG | |
1281 | !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) && | |
1282 | #endif | |
4f655ef5 | 1283 | ieee80211_time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) && |
3aca8a44 IV |
1284 | ((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) || |
1285 | ieee80211_time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle))); | |
32176cfd RP |
1286 | } |
1287 | ||
1288 | static void | |
4f898719 IV |
1289 | sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, |
1290 | const struct ieee80211_rx_stats *rxs, | |
1291 | int rssi, int nf) | |
32176cfd RP |
1292 | { |
1293 | #define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) | |
1294 | #define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP) | |
1295 | struct ieee80211vap *vap = ni->ni_vap; | |
1296 | struct ieee80211com *ic = ni->ni_ic; | |
4f898719 | 1297 | struct ieee80211_channel *rxchan = ic->ic_curchan; |
32176cfd RP |
1298 | struct ieee80211_frame *wh; |
1299 | uint8_t *frm, *efrm; | |
1300 | uint8_t *rates, *xrates, *wme, *htcap, *htinfo; | |
1301 | uint8_t rate; | |
085ff963 | 1302 | int ht_state_change = 0; |
32176cfd RP |
1303 | |
1304 | wh = mtod(m0, struct ieee80211_frame *); | |
1305 | frm = (uint8_t *)&wh[1]; | |
1306 | efrm = mtod(m0, uint8_t *) + m0->m_len; | |
1307 | switch (subtype) { | |
1308 | case IEEE80211_FC0_SUBTYPE_PROBE_RESP: | |
1309 | case IEEE80211_FC0_SUBTYPE_BEACON: { | |
1310 | struct ieee80211_scanparams scan; | |
4f898719 | 1311 | struct ieee80211_channel *c; |
32176cfd RP |
1312 | /* |
1313 | * We process beacon/probe response frames: | |
1314 | * o when scanning, or | |
1315 | * o station mode when associated (to collect state | |
1316 | * updates such as 802.11g slot time) | |
1317 | * Frames otherwise received are discarded. | |
1318 | */ | |
1319 | if (!((ic->ic_flags & IEEE80211_F_SCAN) || ni->ni_associd)) { | |
1320 | vap->iv_stats.is_rx_mgtdiscard++; | |
1321 | return; | |
1322 | } | |
4f655ef5 | 1323 | |
4f898719 IV |
1324 | /* Override RX channel as appropriate */ |
1325 | if (rxs != NULL) { | |
1326 | c = ieee80211_lookup_channel_rxstatus(vap, rxs); | |
1327 | if (c != NULL) | |
1328 | rxchan = c; | |
1329 | } | |
4f655ef5 | 1330 | |
32176cfd | 1331 | /* XXX probe response in sta mode when !scanning? */ |
4f898719 | 1332 | if (ieee80211_parse_beacon(ni, m0, rxchan, &scan) != 0) { |
085ff963 MD |
1333 | if (! (ic->ic_flags & IEEE80211_F_SCAN)) |
1334 | vap->iv_stats.is_beacon_bad++; | |
32176cfd | 1335 | return; |
085ff963 MD |
1336 | } |
1337 | ||
32176cfd RP |
1338 | /* |
1339 | * Count frame now that we know it's to be processed. | |
1340 | */ | |
1341 | if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { | |
1342 | vap->iv_stats.is_rx_beacon++; /* XXX remove */ | |
1343 | IEEE80211_NODE_STAT(ni, rx_beacons); | |
1344 | } else | |
1345 | IEEE80211_NODE_STAT(ni, rx_proberesp); | |
1346 | /* | |
1347 | * When operating in station mode, check for state updates. | |
1348 | * Be careful to ignore beacons received while doing a | |
1349 | * background scan. We consider only 11g/WMM stuff right now. | |
1350 | */ | |
1351 | if (ni->ni_associd != 0 && | |
1352 | ((ic->ic_flags & IEEE80211_F_SCAN) == 0 || | |
1353 | IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) { | |
1354 | /* record tsf of last beacon */ | |
1355 | memcpy(ni->ni_tstamp.data, scan.tstamp, | |
1356 | sizeof(ni->ni_tstamp)); | |
1357 | /* count beacon frame for s/w bmiss handling */ | |
1358 | vap->iv_swbmiss_count++; | |
1359 | vap->iv_bmiss_count = 0; | |
1360 | if (ni->ni_erp != scan.erp) { | |
1361 | IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, | |
1362 | wh->i_addr2, | |
1363 | "erp change: was 0x%x, now 0x%x", | |
1364 | ni->ni_erp, scan.erp); | |
1365 | if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && | |
1366 | (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) | |
1367 | ic->ic_flags |= IEEE80211_F_USEPROT; | |
1368 | else | |
1369 | ic->ic_flags &= ~IEEE80211_F_USEPROT; | |
1370 | ni->ni_erp = scan.erp; | |
1371 | /* XXX statistic */ | |
1372 | /* XXX driver notification */ | |
1373 | } | |
1374 | if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) { | |
1375 | IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, | |
1376 | wh->i_addr2, | |
1377 | "capabilities change: was 0x%x, now 0x%x", | |
1378 | ni->ni_capinfo, scan.capinfo); | |
1379 | /* | |
1380 | * NB: we assume short preamble doesn't | |
1381 | * change dynamically | |
1382 | */ | |
1383 | ieee80211_set_shortslottime(ic, | |
1384 | IEEE80211_IS_CHAN_A(ic->ic_bsschan) || | |
1385 | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); | |
1386 | ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME) | |
1387 | | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME); | |
1388 | /* XXX statistic */ | |
1389 | } | |
1390 | if (scan.wme != NULL && | |
1391 | (ni->ni_flags & IEEE80211_NODE_QOS) && | |
1392 | ieee80211_parse_wmeparams(vap, scan.wme, wh) > 0) | |
1393 | ieee80211_wme_updateparams(vap); | |
1394 | #ifdef IEEE80211_SUPPORT_SUPERG | |
1395 | if (scan.ath != NULL) | |
1396 | ieee80211_parse_athparams(ni, scan.ath, wh); | |
1397 | #endif | |
1398 | if (scan.htcap != NULL && scan.htinfo != NULL && | |
1399 | (vap->iv_flags_ht & IEEE80211_FHT_HT)) { | |
32176cfd | 1400 | /* XXX state changes? */ |
085ff963 MD |
1401 | if (ieee80211_ht_updateparams(ni, |
1402 | scan.htcap, scan.htinfo)) | |
1403 | ht_state_change = 1; | |
32176cfd | 1404 | } |
085ff963 MD |
1405 | if (scan.quiet) |
1406 | ic->ic_set_quiet(ni, scan.quiet); | |
d98a0bcf | 1407 | |
32176cfd RP |
1408 | if (scan.tim != NULL) { |
1409 | struct ieee80211_tim_ie *tim = | |
1410 | (struct ieee80211_tim_ie *) scan.tim; | |
d98a0bcf MD |
1411 | /* |
1412 | * XXX Check/debug this code; see if it's about | |
1413 | * the right time to force the VAP awake if we | |
1414 | * receive a frame destined for us? | |
1415 | */ | |
32176cfd RP |
1416 | int aid = IEEE80211_AID(ni->ni_associd); |
1417 | int ix = aid / NBBY; | |
1418 | int min = tim->tim_bitctl &~ 1; | |
1419 | int max = tim->tim_len + min - 4; | |
4f898719 | 1420 | int tim_ucast = 0, tim_mcast = 0; |
d98a0bcf MD |
1421 | |
1422 | /* | |
1423 | * Only do this for unicast traffic in the TIM | |
1424 | * The multicast traffic notification for | |
1425 | * the scan notification stuff should occur | |
1426 | * differently. | |
1427 | */ | |
1428 | if (min <= ix && ix <= max && | |
085ff963 | 1429 | isset(tim->tim_bitmap - min, aid)) { |
4f898719 | 1430 | tim_ucast = 1; |
d98a0bcf MD |
1431 | } |
1432 | ||
1433 | /* | |
4f898719 | 1434 | * Do a separate notification |
d98a0bcf MD |
1435 | * for the multicast bit being set. |
1436 | */ | |
d98a0bcf | 1437 | if (tim->tim_bitctl & 1) { |
4f898719 IV |
1438 | tim_mcast = 1; |
1439 | } | |
1440 | ||
1441 | /* | |
1442 | * If the TIM indicates there's traffic for | |
1443 | * us then get us out of STA mode powersave. | |
1444 | */ | |
1445 | if (tim_ucast == 1) { | |
1446 | ||
1447 | /* | |
1448 | * Wake us out of SLEEP state if we're | |
1449 | * in it; and if we're doing bgscan | |
1450 | * then wake us out of STA powersave. | |
1451 | */ | |
d98a0bcf | 1452 | ieee80211_sta_tim_notify(vap, 1); |
4f898719 IV |
1453 | |
1454 | /* | |
1455 | * This is preventing us from | |
1456 | * continuing a bgscan; because it | |
1457 | * tricks the contbgscan() | |
1458 | * routine to think there's always | |
1459 | * traffic for us. | |
1460 | * | |
1461 | * I think we need both an RX and | |
1462 | * TX ic_lastdata field. | |
1463 | */ | |
32176cfd | 1464 | ic->ic_lastdata = ticks; |
32176cfd | 1465 | } |
085ff963 | 1466 | |
32176cfd RP |
1467 | ni->ni_dtim_count = tim->tim_count; |
1468 | ni->ni_dtim_period = tim->tim_period; | |
1469 | } | |
1470 | if (scan.csa != NULL && | |
1471 | (vap->iv_flags & IEEE80211_F_DOTH)) | |
1472 | ieee80211_parse_csaparams(vap, scan.csa, wh); | |
1473 | else if (ic->ic_flags & IEEE80211_F_CSAPENDING) { | |
1474 | /* | |
1475 | * No CSA ie or 11h disabled, but a channel | |
1476 | * switch is pending; drop out so we aren't | |
1477 | * stuck in CSA state. If the AP really is | |
1478 | * moving we'll get a beacon miss and scan. | |
1479 | */ | |
085ff963 | 1480 | IEEE80211_LOCK(ic); |
32176cfd | 1481 | ieee80211_csa_cancelswitch(ic); |
085ff963 | 1482 | IEEE80211_UNLOCK(ic); |
32176cfd RP |
1483 | } |
1484 | /* | |
1485 | * If scanning, pass the info to the scan module. | |
1486 | * Otherwise, check if it's the right time to do | |
1487 | * a background scan. Background scanning must | |
1488 | * be enabled and we must not be operating in the | |
1489 | * turbo phase of dynamic turbo mode. Then, | |
1490 | * it's been a while since the last background | |
1491 | * scan and if no data frames have come through | |
1492 | * recently, kick off a scan. Note that this | |
1493 | * is the mechanism by which a background scan | |
1494 | * is started _and_ continued each time we | |
1495 | * return on-channel to receive a beacon from | |
1496 | * our ap. | |
1497 | */ | |
1498 | if (ic->ic_flags & IEEE80211_F_SCAN) { | |
4f898719 IV |
1499 | ieee80211_add_scan(vap, rxchan, |
1500 | &scan, wh, subtype, rssi, nf); | |
32176cfd RP |
1501 | } else if (contbgscan(vap)) { |
1502 | ieee80211_bg_scan(vap, 0); | |
1503 | } else if (startbgscan(vap)) { | |
1504 | vap->iv_stats.is_scan_bg++; | |
1505 | #if 0 | |
1506 | /* wakeup if we are sleeing */ | |
1507 | ieee80211_set_pwrsave(vap, 0); | |
1508 | #endif | |
1509 | ieee80211_bg_scan(vap, 0); | |
1510 | } | |
d98a0bcf MD |
1511 | |
1512 | /* | |
1513 | * Put the station to sleep if we haven't seen | |
1514 | * traffic in a while. | |
1515 | */ | |
085ff963 | 1516 | IEEE80211_LOCK(ic); |
d98a0bcf | 1517 | ieee80211_sta_ps_timer_check(vap); |
085ff963 | 1518 | IEEE80211_UNLOCK(ic); |
d98a0bcf | 1519 | |
085ff963 MD |
1520 | /* |
1521 | * If we've had a channel width change (eg HT20<->HT40) | |
1522 | * then schedule a delayed driver notification. | |
1523 | */ | |
1524 | if (ht_state_change) | |
1525 | ieee80211_update_chw(ic); | |
32176cfd RP |
1526 | return; |
1527 | } | |
1528 | /* | |
1529 | * If scanning, just pass information to the scan module. | |
1530 | */ | |
1531 | if (ic->ic_flags & IEEE80211_F_SCAN) { | |
1532 | if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) { | |
1533 | /* | |
1534 | * Actively scanning a channel marked passive; | |
1535 | * send a probe request now that we know there | |
1536 | * is 802.11 traffic present. | |
1537 | * | |
1538 | * XXX check if the beacon we recv'd gives | |
1539 | * us what we need and suppress the probe req | |
1540 | */ | |
1541 | ieee80211_probe_curchan(vap, 1); | |
1542 | ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; | |
1543 | } | |
4f898719 IV |
1544 | ieee80211_add_scan(vap, rxchan, &scan, wh, |
1545 | subtype, rssi, nf); | |
32176cfd RP |
1546 | return; |
1547 | } | |
1548 | break; | |
1549 | } | |
1550 | ||
1551 | case IEEE80211_FC0_SUBTYPE_AUTH: { | |
1552 | uint16_t algo, seq, status; | |
1553 | /* | |
1554 | * auth frame format | |
1555 | * [2] algorithm | |
1556 | * [2] sequence | |
1557 | * [2] status | |
1558 | * [tlv*] challenge | |
1559 | */ | |
1560 | IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); | |
1561 | algo = le16toh(*(uint16_t *)frm); | |
1562 | seq = le16toh(*(uint16_t *)(frm + 2)); | |
1563 | status = le16toh(*(uint16_t *)(frm + 4)); | |
1564 | IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2, | |
1565 | "recv auth frame with algorithm %d seq %d", algo, seq); | |
1566 | ||
1567 | if (vap->iv_flags & IEEE80211_F_COUNTERM) { | |
1568 | IEEE80211_DISCARD(vap, | |
1569 | IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO, | |
1570 | wh, "auth", "%s", "TKIP countermeasures enabled"); | |
1571 | vap->iv_stats.is_rx_auth_countermeasures++; | |
1572 | if (vap->iv_opmode == IEEE80211_M_HOSTAP) { | |
1573 | ieee80211_send_error(ni, wh->i_addr2, | |
1574 | IEEE80211_FC0_SUBTYPE_AUTH, | |
1575 | IEEE80211_REASON_MIC_FAILURE); | |
1576 | } | |
1577 | return; | |
1578 | } | |
1579 | if (algo == IEEE80211_AUTH_ALG_SHARED) | |
1580 | sta_auth_shared(ni, wh, frm + 6, efrm, rssi, nf, | |
1581 | seq, status); | |
1582 | else if (algo == IEEE80211_AUTH_ALG_OPEN) | |
1583 | sta_auth_open(ni, wh, rssi, nf, seq, status); | |
1584 | else { | |
1585 | IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, | |
1586 | wh, "auth", "unsupported alg %d", algo); | |
1587 | vap->iv_stats.is_rx_auth_unsupported++; | |
1588 | return; | |
1589 | } | |
1590 | break; | |
1591 | } | |
1592 | ||
1593 | case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: | |
1594 | case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { | |
1595 | uint16_t capinfo, associd; | |
1596 | uint16_t status; | |
1597 | ||
1598 | if (vap->iv_state != IEEE80211_S_ASSOC) { | |
1599 | vap->iv_stats.is_rx_mgtdiscard++; | |
1600 | return; | |
1601 | } | |
1602 | ||
1603 | /* | |
1604 | * asresp frame format | |
1605 | * [2] capability information | |
1606 | * [2] status | |
1607 | * [2] association ID | |
1608 | * [tlv] supported rates | |
1609 | * [tlv] extended supported rates | |
1610 | * [tlv] WME | |
1611 | * [tlv] HT capabilities | |
1612 | * [tlv] HT info | |
1613 | */ | |
1614 | IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); | |
1615 | ni = vap->iv_bss; | |
1616 | capinfo = le16toh(*(uint16_t *)frm); | |
1617 | frm += 2; | |
1618 | status = le16toh(*(uint16_t *)frm); | |
1619 | frm += 2; | |
1620 | if (status != 0) { | |
1621 | IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, | |
1622 | wh->i_addr2, "%sassoc failed (reason %d)", | |
1623 | ISREASSOC(subtype) ? "re" : "", status); | |
1624 | vap->iv_stats.is_rx_auth_fail++; /* XXX */ | |
1625 | return; | |
1626 | } | |
1627 | associd = le16toh(*(uint16_t *)frm); | |
1628 | frm += 2; | |
1629 | ||
1630 | rates = xrates = wme = htcap = htinfo = NULL; | |
1631 | while (efrm - frm > 1) { | |
1632 | IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); | |
1633 | switch (*frm) { | |
1634 | case IEEE80211_ELEMID_RATES: | |
1635 | rates = frm; | |
1636 | break; | |
1637 | case IEEE80211_ELEMID_XRATES: | |
1638 | xrates = frm; | |
1639 | break; | |
1640 | case IEEE80211_ELEMID_HTCAP: | |
1641 | htcap = frm; | |
1642 | break; | |
1643 | case IEEE80211_ELEMID_HTINFO: | |
1644 | htinfo = frm; | |
1645 | break; | |
1646 | case IEEE80211_ELEMID_VENDOR: | |
1647 | if (iswmeoui(frm)) | |
1648 | wme = frm; | |
1649 | else if (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) { | |
1650 | /* | |
1651 | * Accept pre-draft HT ie's if the | |
1652 | * standard ones have not been seen. | |
1653 | */ | |
1654 | if (ishtcapoui(frm)) { | |
1655 | if (htcap == NULL) | |
1656 | htcap = frm; | |
1657 | } else if (ishtinfooui(frm)) { | |
1658 | if (htinfo == NULL) | |
085ff963 | 1659 | htinfo = frm; |
32176cfd RP |
1660 | } |
1661 | } | |
1662 | /* XXX Atheros OUI support */ | |
1663 | break; | |
1664 | } | |
1665 | frm += frm[1] + 2; | |
1666 | } | |
1667 | ||
1668 | IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); | |
1669 | if (xrates != NULL) | |
1670 | IEEE80211_VERIFY_ELEMENT(xrates, | |
1671 | IEEE80211_RATE_MAXSIZE - rates[1], return); | |
1672 | rate = ieee80211_setup_rates(ni, rates, xrates, | |
1673 | IEEE80211_F_JOIN | | |
1674 | IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | | |
1675 | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); | |
1676 | if (rate & IEEE80211_RATE_BASIC) { | |
1677 | IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, | |
1678 | wh->i_addr2, | |
1679 | "%sassoc failed (rate set mismatch)", | |
1680 | ISREASSOC(subtype) ? "re" : ""); | |
1681 | vap->iv_stats.is_rx_assoc_norate++; | |
1682 | ieee80211_new_state(vap, IEEE80211_S_SCAN, | |
1683 | IEEE80211_SCAN_FAIL_STATUS); | |
1684 | return; | |
1685 | } | |
1686 | ||
1687 | ni->ni_capinfo = capinfo; | |
1688 | ni->ni_associd = associd; | |
1689 | if (ni->ni_jointime == 0) | |
cec73927 | 1690 | ni->ni_jointime = time_uptime; |
32176cfd RP |
1691 | if (wme != NULL && |
1692 | ieee80211_parse_wmeparams(vap, wme, wh) >= 0) { | |
1693 | ni->ni_flags |= IEEE80211_NODE_QOS; | |
1694 | ieee80211_wme_updateparams(vap); | |
1695 | } else | |
1696 | ni->ni_flags &= ~IEEE80211_NODE_QOS; | |
1697 | /* | |
1698 | * Setup HT state according to the negotiation. | |
1699 | * | |
1700 | * NB: shouldn't need to check if HT use is enabled but some | |
1701 | * ap's send back HT ie's even when we don't indicate we | |
1702 | * are HT capable in our AssocReq. | |
1703 | */ | |
1704 | if (htcap != NULL && htinfo != NULL && | |
1705 | (vap->iv_flags_ht & IEEE80211_FHT_HT)) { | |
1706 | ieee80211_ht_node_init(ni); | |
1707 | ieee80211_ht_updateparams(ni, htcap, htinfo); | |
1708 | ieee80211_setup_htrates(ni, htcap, | |
1709 | IEEE80211_F_JOIN | IEEE80211_F_DOBRS); | |
1710 | ieee80211_setup_basic_htrates(ni, htinfo); | |
1711 | ieee80211_node_setuptxparms(ni); | |
4fbce6bd | 1712 | ieee80211_ratectl_node_init(ni); |
32176cfd | 1713 | } |
4f655ef5 MD |
1714 | |
1715 | /* | |
1716 | * Always initialise FF/superg state; we can use this | |
1717 | * for doing A-MSDU encapsulation as well. | |
1718 | */ | |
1719 | #ifdef IEEE80211_SUPPORT_SUPERG | |
1720 | ieee80211_ff_node_init(ni); | |
1721 | #endif | |
1722 | ||
32176cfd RP |
1723 | /* |
1724 | * Configure state now that we are associated. | |
1725 | * | |
1726 | * XXX may need different/additional driver callbacks? | |
1727 | */ | |
1728 | if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || | |
1729 | (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { | |
1730 | ic->ic_flags |= IEEE80211_F_SHPREAMBLE; | |
1731 | ic->ic_flags &= ~IEEE80211_F_USEBARKER; | |
1732 | } else { | |
1733 | ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; | |
1734 | ic->ic_flags |= IEEE80211_F_USEBARKER; | |
1735 | } | |
1736 | ieee80211_set_shortslottime(ic, | |
1737 | IEEE80211_IS_CHAN_A(ic->ic_curchan) || | |
1738 | (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); | |
1739 | /* | |
1740 | * Honor ERP protection. | |
1741 | * | |
1742 | * NB: ni_erp should zero for non-11g operation. | |
1743 | */ | |
1744 | if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && | |
1745 | (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) | |
1746 | ic->ic_flags |= IEEE80211_F_USEPROT; | |
1747 | else | |
1748 | ic->ic_flags &= ~IEEE80211_F_USEPROT; | |
1749 | IEEE80211_NOTE_MAC(vap, | |
1750 | IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, wh->i_addr2, | |
1751 | "%sassoc success at aid %d: %s preamble, %s slot time%s%s%s%s%s%s%s%s", | |
1752 | ISREASSOC(subtype) ? "re" : "", | |
1753 | IEEE80211_NODE_AID(ni), | |
1754 | ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", | |
1755 | ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", | |
1756 | ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "", | |
1757 | ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "", | |
1758 | ni->ni_flags & IEEE80211_NODE_HT ? | |
1759 | (ni->ni_chw == 40 ? ", HT40" : ", HT20") : "", | |
1760 | ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "", | |
1761 | ni->ni_flags & IEEE80211_NODE_MIMO_RTS ? " (+SMPS-DYN)" : | |
1762 | ni->ni_flags & IEEE80211_NODE_MIMO_PS ? " (+SMPS)" : "", | |
1763 | ni->ni_flags & IEEE80211_NODE_RIFS ? " (+RIFS)" : "", | |
1764 | IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ? | |
1765 | ", fast-frames" : "", | |
1766 | IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ? | |
1767 | ", turbo" : "" | |
1768 | ); | |
1769 | ieee80211_new_state(vap, IEEE80211_S_RUN, subtype); | |
1770 | break; | |
1771 | } | |
1772 | ||
1773 | case IEEE80211_FC0_SUBTYPE_DEAUTH: { | |
1774 | uint16_t reason; | |
1775 | ||
1776 | if (vap->iv_state == IEEE80211_S_SCAN) { | |
1777 | vap->iv_stats.is_rx_mgtdiscard++; | |
1778 | return; | |
1779 | } | |
1780 | if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) { | |
1781 | /* NB: can happen when in promiscuous mode */ | |
1782 | vap->iv_stats.is_rx_mgtdiscard++; | |
1783 | break; | |
1784 | } | |
1785 | ||
1786 | /* | |
1787 | * deauth frame format | |
1788 | * [2] reason | |
1789 | */ | |
1790 | IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); | |
1791 | reason = le16toh(*(uint16_t *)frm); | |
1792 | ||
1793 | vap->iv_stats.is_rx_deauth++; | |
1794 | vap->iv_stats.is_rx_deauth_code = reason; | |
1795 | IEEE80211_NODE_STAT(ni, rx_deauth); | |
1796 | ||
1797 | IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, | |
4f655ef5 MD |
1798 | "recv deauthenticate (reason: %d (%s))", reason, |
1799 | ieee80211_reason_to_string(reason)); | |
32176cfd RP |
1800 | ieee80211_new_state(vap, IEEE80211_S_AUTH, |
1801 | (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH); | |
1802 | break; | |
1803 | } | |
1804 | ||
1805 | case IEEE80211_FC0_SUBTYPE_DISASSOC: { | |
1806 | uint16_t reason; | |
1807 | ||
1808 | if (vap->iv_state != IEEE80211_S_RUN && | |
1809 | vap->iv_state != IEEE80211_S_ASSOC && | |
1810 | vap->iv_state != IEEE80211_S_AUTH) { | |
1811 | vap->iv_stats.is_rx_mgtdiscard++; | |
1812 | return; | |
1813 | } | |
1814 | if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) { | |
1815 | /* NB: can happen when in promiscuous mode */ | |
1816 | vap->iv_stats.is_rx_mgtdiscard++; | |
1817 | break; | |
1818 | } | |
1819 | ||
1820 | /* | |
1821 | * disassoc frame format | |
1822 | * [2] reason | |
1823 | */ | |
1824 | IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); | |
1825 | reason = le16toh(*(uint16_t *)frm); | |
1826 | ||
1827 | vap->iv_stats.is_rx_disassoc++; | |
1828 | vap->iv_stats.is_rx_disassoc_code = reason; | |
1829 | IEEE80211_NODE_STAT(ni, rx_disassoc); | |
1830 | ||
1831 | IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, | |
4f655ef5 MD |
1832 | "recv disassociate (reason: %d (%s))", reason, |
1833 | ieee80211_reason_to_string(reason)); | |
32176cfd RP |
1834 | ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); |
1835 | break; | |
1836 | } | |
1837 | ||
1838 | case IEEE80211_FC0_SUBTYPE_ACTION: | |
085ff963 MD |
1839 | case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: |
1840 | if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) && | |
1841 | !IEEE80211_IS_MULTICAST(wh->i_addr1)) { | |
1842 | IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, | |
1843 | wh, NULL, "%s", "not for us"); | |
1844 | vap->iv_stats.is_rx_mgtdiscard++; | |
1845 | } else if (vap->iv_state != IEEE80211_S_RUN) { | |
1846 | IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, | |
1847 | wh, NULL, "wrong state %s", | |
1848 | ieee80211_state_name[vap->iv_state]); | |
32176cfd | 1849 | vap->iv_stats.is_rx_mgtdiscard++; |
085ff963 MD |
1850 | } else { |
1851 | if (ieee80211_parse_action(ni, m0) == 0) | |
1852 | (void)ic->ic_recv_action(ni, wh, frm, efrm); | |
1853 | } | |
32176cfd RP |
1854 | break; |
1855 | ||
32176cfd RP |
1856 | case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: |
1857 | case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: | |
085ff963 | 1858 | case IEEE80211_FC0_SUBTYPE_PROBE_REQ: |
4f655ef5 | 1859 | case IEEE80211_FC0_SUBTYPE_TIMING_ADV: |
085ff963 MD |
1860 | case IEEE80211_FC0_SUBTYPE_ATIM: |
1861 | IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, | |
1862 | wh, NULL, "%s", "not handled"); | |
32176cfd | 1863 | vap->iv_stats.is_rx_mgtdiscard++; |
085ff963 MD |
1864 | break; |
1865 | ||
32176cfd RP |
1866 | default: |
1867 | IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, | |
085ff963 | 1868 | wh, "mgt", "subtype 0x%x not handled", subtype); |
32176cfd RP |
1869 | vap->iv_stats.is_rx_badsubtype++; |
1870 | break; | |
1871 | } | |
1872 | #undef ISREASSOC | |
1873 | #undef ISPROBE | |
1874 | } | |
1875 | ||
1876 | static void | |
085ff963 | 1877 | sta_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype) |
32176cfd | 1878 | { |
085ff963 MD |
1879 | switch (subtype) { |
1880 | case IEEE80211_FC0_SUBTYPE_BAR: | |
1881 | ieee80211_recv_bar(ni, m); | |
1882 | break; | |
1883 | } | |
32176cfd | 1884 | } |