1 |
/* $MidnightBSD$ */ |
2 |
/*- |
3 |
* Copyright (c) 2007-2008 Sam Leffler, Errno Consulting |
4 |
* All rights reserved. |
5 |
* |
6 |
* Redistribution and use in source and binary forms, with or without |
7 |
* modification, are permitted provided that the following conditions |
8 |
* are met: |
9 |
* 1. Redistributions of source code must retain the above copyright |
10 |
* notice, this list of conditions and the following disclaimer. |
11 |
* 2. Redistributions in binary form must reproduce the above copyright |
12 |
* notice, this list of conditions and the following disclaimer in the |
13 |
* documentation and/or other materials provided with the distribution. |
14 |
* |
15 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
16 |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
17 |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
18 |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
19 |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
20 |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
21 |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
22 |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
23 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
24 |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 |
*/ |
26 |
|
27 |
#include <sys/cdefs.h> |
28 |
#ifdef __MidnightBSD__ |
29 |
__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_wds.c 283855 2015-05-31 23:29:04Z ae $"); |
30 |
#endif |
31 |
|
32 |
/* |
33 |
* IEEE 802.11 WDS mode support. |
34 |
*/ |
35 |
#include "opt_inet.h" |
36 |
#include "opt_wlan.h" |
37 |
|
38 |
#include <sys/param.h> |
39 |
#include <sys/systm.h> |
40 |
#include <sys/mbuf.h> |
41 |
#include <sys/malloc.h> |
42 |
#include <sys/kernel.h> |
43 |
|
44 |
#include <sys/socket.h> |
45 |
#include <sys/sockio.h> |
46 |
#include <sys/endian.h> |
47 |
#include <sys/errno.h> |
48 |
#include <sys/proc.h> |
49 |
#include <sys/sysctl.h> |
50 |
|
51 |
#include <net/if.h> |
52 |
#include <net/if_media.h> |
53 |
#include <net/if_llc.h> |
54 |
#include <net/ethernet.h> |
55 |
|
56 |
#include <net/bpf.h> |
57 |
|
58 |
#include <net80211/ieee80211_var.h> |
59 |
#include <net80211/ieee80211_wds.h> |
60 |
#include <net80211/ieee80211_input.h> |
61 |
#ifdef IEEE80211_SUPPORT_SUPERG |
62 |
#include <net80211/ieee80211_superg.h> |
63 |
#endif |
64 |
|
65 |
static void wds_vattach(struct ieee80211vap *); |
66 |
static int wds_newstate(struct ieee80211vap *, enum ieee80211_state, int); |
67 |
static int wds_input(struct ieee80211_node *ni, struct mbuf *m, int, int); |
68 |
static void wds_recv_mgmt(struct ieee80211_node *, struct mbuf *, |
69 |
int subtype, int, int); |
70 |
|
71 |
void |
72 |
ieee80211_wds_attach(struct ieee80211com *ic) |
73 |
{ |
74 |
ic->ic_vattach[IEEE80211_M_WDS] = wds_vattach; |
75 |
} |
76 |
|
77 |
void |
78 |
ieee80211_wds_detach(struct ieee80211com *ic) |
79 |
{ |
80 |
} |
81 |
|
82 |
static void |
83 |
wds_vdetach(struct ieee80211vap *vap) |
84 |
{ |
85 |
if (vap->iv_bss != NULL) { |
86 |
/* XXX locking? */ |
87 |
if (vap->iv_bss->ni_wdsvap == vap) |
88 |
vap->iv_bss->ni_wdsvap = NULL; |
89 |
} |
90 |
} |
91 |
|
92 |
static void |
93 |
wds_vattach(struct ieee80211vap *vap) |
94 |
{ |
95 |
vap->iv_newstate = wds_newstate; |
96 |
vap->iv_input = wds_input; |
97 |
vap->iv_recv_mgmt = wds_recv_mgmt; |
98 |
vap->iv_opdetach = wds_vdetach; |
99 |
} |
100 |
|
101 |
static void |
102 |
wds_flush(struct ieee80211_node *ni) |
103 |
{ |
104 |
struct ieee80211com *ic = ni->ni_ic; |
105 |
struct mbuf *m, *next; |
106 |
int8_t rssi, nf; |
107 |
|
108 |
m = ieee80211_ageq_remove(&ic->ic_stageq, |
109 |
(void *)(uintptr_t) ieee80211_mac_hash(ic, ni->ni_macaddr)); |
110 |
if (m == NULL) |
111 |
return; |
112 |
|
113 |
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_WDS, ni, |
114 |
"%s", "flush wds queue"); |
115 |
ic->ic_node_getsignal(ni, &rssi, &nf); |
116 |
for (; m != NULL; m = next) { |
117 |
next = m->m_nextpkt; |
118 |
m->m_nextpkt = NULL; |
119 |
ieee80211_input(ni, m, rssi, nf); |
120 |
} |
121 |
} |
122 |
|
123 |
static int |
124 |
ieee80211_create_wds(struct ieee80211vap *vap, struct ieee80211_channel *chan) |
125 |
{ |
126 |
struct ieee80211com *ic = vap->iv_ic; |
127 |
struct ieee80211_node_table *nt = &ic->ic_sta; |
128 |
struct ieee80211_node *ni, *obss; |
129 |
|
130 |
IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS, |
131 |
"%s: creating link to %s on channel %u\n", __func__, |
132 |
ether_sprintf(vap->iv_des_bssid), ieee80211_chan2ieee(ic, chan)); |
133 |
|
134 |
/* NB: vap create must specify the bssid for the link */ |
135 |
KASSERT(vap->iv_flags & IEEE80211_F_DESBSSID, ("no bssid")); |
136 |
/* NB: we should only be called on RUN transition */ |
137 |
KASSERT(vap->iv_state == IEEE80211_S_RUN, ("!RUN state")); |
138 |
|
139 |
if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) { |
140 |
/* |
141 |
* Dynamic/non-legacy WDS. Reference the associated |
142 |
* station specified by the desired bssid setup at vap |
143 |
* create. Point ni_wdsvap at the WDS vap so 4-address |
144 |
* frames received through the associated AP vap will |
145 |
* be dispatched upward (e.g. to a bridge) as though |
146 |
* they arrived on the WDS vap. |
147 |
*/ |
148 |
IEEE80211_NODE_LOCK(nt); |
149 |
obss = NULL; |
150 |
ni = ieee80211_find_node_locked(&ic->ic_sta, vap->iv_des_bssid); |
151 |
if (ni == NULL) { |
152 |
/* |
153 |
* Node went away before we could hookup. This |
154 |
* should be ok; no traffic will flow and a leave |
155 |
* event will be dispatched that should cause |
156 |
* the vap to be destroyed. |
157 |
*/ |
158 |
IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS, |
159 |
"%s: station %s went away\n", |
160 |
__func__, ether_sprintf(vap->iv_des_bssid)); |
161 |
/* XXX stat? */ |
162 |
} else if (ni->ni_wdsvap != NULL) { |
163 |
/* |
164 |
* Node already setup with a WDS vap; we cannot |
165 |
* allow multiple references so disallow. If |
166 |
* ni_wdsvap points at us that's ok; we should |
167 |
* do nothing anyway. |
168 |
*/ |
169 |
/* XXX printf instead? */ |
170 |
IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS, |
171 |
"%s: station %s in use with %s\n", |
172 |
__func__, ether_sprintf(vap->iv_des_bssid), |
173 |
ni->ni_wdsvap->iv_ifp->if_xname); |
174 |
/* XXX stat? */ |
175 |
} else { |
176 |
/* |
177 |
* Committed to new node, setup state. |
178 |
*/ |
179 |
obss = vap->iv_bss; |
180 |
vap->iv_bss = ni; |
181 |
ni->ni_wdsvap = vap; |
182 |
} |
183 |
IEEE80211_NODE_UNLOCK(nt); |
184 |
if (obss != NULL) { |
185 |
/* NB: deferred to avoid recursive lock */ |
186 |
ieee80211_free_node(obss); |
187 |
} |
188 |
} else { |
189 |
/* |
190 |
* Legacy WDS vap setup. |
191 |
*/ |
192 |
/* |
193 |
* The far end does not associate so we just create |
194 |
* create a new node and install it as the vap's |
195 |
* bss node. We must simulate an association and |
196 |
* authorize the port for traffic to flow. |
197 |
* XXX check if node already in sta table? |
198 |
*/ |
199 |
ni = ieee80211_node_create_wds(vap, vap->iv_des_bssid, chan); |
200 |
if (ni != NULL) { |
201 |
obss = vap->iv_bss; |
202 |
vap->iv_bss = ieee80211_ref_node(ni); |
203 |
ni->ni_flags |= IEEE80211_NODE_AREF; |
204 |
if (obss != NULL) |
205 |
ieee80211_free_node(obss); |
206 |
/* give driver a chance to setup state like ni_txrate */ |
207 |
if (ic->ic_newassoc != NULL) |
208 |
ic->ic_newassoc(ni, 1); |
209 |
/* tell the authenticator about new station */ |
210 |
if (vap->iv_auth->ia_node_join != NULL) |
211 |
vap->iv_auth->ia_node_join(ni); |
212 |
if (ni->ni_authmode != IEEE80211_AUTH_8021X) |
213 |
ieee80211_node_authorize(ni); |
214 |
|
215 |
ieee80211_notify_node_join(ni, 1 /*newassoc*/); |
216 |
/* XXX inject l2uf frame */ |
217 |
} |
218 |
} |
219 |
|
220 |
/* |
221 |
* Flush any pending frames now that were setup. |
222 |
*/ |
223 |
if (ni != NULL) |
224 |
wds_flush(ni); |
225 |
return (ni == NULL ? ENOENT : 0); |
226 |
} |
227 |
|
228 |
/* |
229 |
* Propagate multicast frames of an ap vap to all DWDS links. |
230 |
* The caller is assumed to have verified this frame is multicast. |
231 |
*/ |
232 |
void |
233 |
ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m) |
234 |
{ |
235 |
struct ieee80211com *ic = vap0->iv_ic; |
236 |
const struct ether_header *eh = mtod(m, const struct ether_header *); |
237 |
struct ieee80211_node *ni; |
238 |
struct ieee80211vap *vap; |
239 |
struct ifnet *ifp; |
240 |
struct mbuf *mcopy; |
241 |
int err; |
242 |
|
243 |
KASSERT(ETHER_IS_MULTICAST(eh->ether_dhost), |
244 |
("%s not mcast", ether_sprintf(eh->ether_dhost))); |
245 |
|
246 |
/* XXX locking */ |
247 |
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
248 |
/* only DWDS vaps are interesting */ |
249 |
if (vap->iv_opmode != IEEE80211_M_WDS || |
250 |
(vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)) |
251 |
continue; |
252 |
/* if it came in this interface, don't send it back out */ |
253 |
ifp = vap->iv_ifp; |
254 |
if (ifp == m->m_pkthdr.rcvif) |
255 |
continue; |
256 |
/* |
257 |
* Duplicate the frame and send it. |
258 |
*/ |
259 |
mcopy = m_copypacket(m, M_NOWAIT); |
260 |
if (mcopy == NULL) { |
261 |
ifp->if_oerrors++; |
262 |
/* XXX stat + msg */ |
263 |
continue; |
264 |
} |
265 |
ni = ieee80211_find_txnode(vap, eh->ether_dhost); |
266 |
if (ni == NULL) { |
267 |
/* NB: ieee80211_find_txnode does stat+msg */ |
268 |
ifp->if_oerrors++; |
269 |
m_freem(mcopy); |
270 |
continue; |
271 |
} |
272 |
/* calculate priority so drivers can find the tx queue */ |
273 |
if (ieee80211_classify(ni, mcopy)) { |
274 |
IEEE80211_DISCARD_MAC(vap, |
275 |
IEEE80211_MSG_OUTPUT | IEEE80211_MSG_WDS, |
276 |
eh->ether_dhost, NULL, |
277 |
"%s", "classification failure"); |
278 |
vap->iv_stats.is_tx_classify++; |
279 |
ifp->if_oerrors++; |
280 |
m_freem(mcopy); |
281 |
ieee80211_free_node(ni); |
282 |
continue; |
283 |
} |
284 |
|
285 |
BPF_MTAP(ifp, m); /* 802.3 tx */ |
286 |
|
287 |
/* |
288 |
* Encapsulate the packet in prep for transmission. |
289 |
*/ |
290 |
mcopy = ieee80211_encap(vap, ni, mcopy); |
291 |
if (mcopy == NULL) { |
292 |
/* NB: stat+msg handled in ieee80211_encap */ |
293 |
ieee80211_free_node(ni); |
294 |
continue; |
295 |
} |
296 |
mcopy->m_flags |= M_MCAST; |
297 |
mcopy->m_pkthdr.rcvif = (void *) ni; |
298 |
|
299 |
err = ieee80211_parent_xmitpkt(ic, mcopy); |
300 |
if (err) { |
301 |
/* NB: IFQ_HANDOFF reclaims mbuf */ |
302 |
ifp->if_oerrors++; |
303 |
ieee80211_free_node(ni); |
304 |
} else { |
305 |
ifp->if_opackets++; |
306 |
if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); |
307 |
if_inc_counter(ifp, IFCOUNTER_OBYTES, |
308 |
m->m_pkthdr.len); |
309 |
} |
310 |
} |
311 |
} |
312 |
|
313 |
/* |
314 |
* Handle DWDS discovery on receipt of a 4-address frame in |
315 |
* ap mode. Queue the frame and post an event for someone |
316 |
* to plumb the necessary WDS vap for this station. Frames |
317 |
* received prior to the vap set running will then be reprocessed |
318 |
* as if they were just received. |
319 |
*/ |
320 |
void |
321 |
ieee80211_dwds_discover(struct ieee80211_node *ni, struct mbuf *m) |
322 |
{ |
323 |
struct ieee80211com *ic = ni->ni_ic; |
324 |
|
325 |
/* |
326 |
* Save the frame with an aging interval 4 times |
327 |
* the listen interval specified by the station. |
328 |
* Frames that sit around too long are reclaimed |
329 |
* using this information. |
330 |
* XXX handle overflow? |
331 |
* XXX per/vap beacon interval? |
332 |
*/ |
333 |
m->m_pkthdr.rcvif = (void *)(uintptr_t) |
334 |
ieee80211_mac_hash(ic, ni->ni_macaddr); |
335 |
(void) ieee80211_ageq_append(&ic->ic_stageq, m, |
336 |
((ni->ni_intval * ic->ic_lintval) << 2) / 1024); |
337 |
ieee80211_notify_wds_discover(ni); |
338 |
} |
339 |
|
340 |
/* |
341 |
* IEEE80211_M_WDS vap state machine handler. |
342 |
*/ |
343 |
static int |
344 |
wds_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) |
345 |
{ |
346 |
struct ieee80211com *ic = vap->iv_ic; |
347 |
struct ieee80211_node *ni; |
348 |
enum ieee80211_state ostate; |
349 |
int error; |
350 |
|
351 |
IEEE80211_LOCK_ASSERT(ic); |
352 |
|
353 |
ostate = vap->iv_state; |
354 |
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, |
355 |
ieee80211_state_name[ostate], ieee80211_state_name[nstate]); |
356 |
vap->iv_state = nstate; /* state transition */ |
357 |
callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */ |
358 |
if (ostate != IEEE80211_S_SCAN) |
359 |
ieee80211_cancel_scan(vap); /* background scan */ |
360 |
ni = vap->iv_bss; /* NB: no reference held */ |
361 |
error = 0; |
362 |
switch (nstate) { |
363 |
case IEEE80211_S_INIT: |
364 |
switch (ostate) { |
365 |
case IEEE80211_S_SCAN: |
366 |
ieee80211_cancel_scan(vap); |
367 |
break; |
368 |
default: |
369 |
break; |
370 |
} |
371 |
if (ostate != IEEE80211_S_INIT) { |
372 |
/* NB: optimize INIT -> INIT case */ |
373 |
ieee80211_reset_bss(vap); |
374 |
} |
375 |
break; |
376 |
case IEEE80211_S_SCAN: |
377 |
switch (ostate) { |
378 |
case IEEE80211_S_INIT: |
379 |
ieee80211_check_scan_current(vap); |
380 |
break; |
381 |
default: |
382 |
break; |
383 |
} |
384 |
break; |
385 |
case IEEE80211_S_RUN: |
386 |
if (ostate == IEEE80211_S_INIT) { |
387 |
/* |
388 |
* Already have a channel; bypass the scan |
389 |
* and startup immediately. |
390 |
*/ |
391 |
error = ieee80211_create_wds(vap, ic->ic_curchan); |
392 |
} |
393 |
break; |
394 |
default: |
395 |
break; |
396 |
} |
397 |
return error; |
398 |
} |
399 |
|
400 |
/* |
401 |
* Process a received frame. The node associated with the sender |
402 |
* should be supplied. If nothing was found in the node table then |
403 |
* the caller is assumed to supply a reference to iv_bss instead. |
404 |
* The RSSI and a timestamp are also supplied. The RSSI data is used |
405 |
* during AP scanning to select a AP to associate with; it can have |
406 |
* any units so long as values have consistent units and higher values |
407 |
* mean ``better signal''. The receive timestamp is currently not used |
408 |
* by the 802.11 layer. |
409 |
*/ |
410 |
static int |
411 |
wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) |
412 |
{ |
413 |
#define HAS_SEQ(type) ((type & 0x4) == 0) |
414 |
struct ieee80211vap *vap = ni->ni_vap; |
415 |
struct ieee80211com *ic = ni->ni_ic; |
416 |
struct ifnet *ifp = vap->iv_ifp; |
417 |
struct ieee80211_frame *wh; |
418 |
struct ieee80211_key *key; |
419 |
struct ether_header *eh; |
420 |
int hdrspace, need_tap = 1; /* mbuf need to be tapped. */ |
421 |
uint8_t dir, type, subtype, qos; |
422 |
uint16_t rxseq; |
423 |
|
424 |
if (m->m_flags & M_AMPDU_MPDU) { |
425 |
/* |
426 |
* Fastpath for A-MPDU reorder q resubmission. Frames |
427 |
* w/ M_AMPDU_MPDU marked have already passed through |
428 |
* here but were received out of order and been held on |
429 |
* the reorder queue. When resubmitted they are marked |
430 |
* with the M_AMPDU_MPDU flag and we can bypass most of |
431 |
* the normal processing. |
432 |
*/ |
433 |
wh = mtod(m, struct ieee80211_frame *); |
434 |
type = IEEE80211_FC0_TYPE_DATA; |
435 |
dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; |
436 |
subtype = IEEE80211_FC0_SUBTYPE_QOS; |
437 |
hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ |
438 |
goto resubmit_ampdu; |
439 |
} |
440 |
|
441 |
KASSERT(ni != NULL, ("null node")); |
442 |
|
443 |
type = -1; /* undefined */ |
444 |
|
445 |
if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { |
446 |
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, |
447 |
ni->ni_macaddr, NULL, |
448 |
"too short (1): len %u", m->m_pkthdr.len); |
449 |
vap->iv_stats.is_rx_tooshort++; |
450 |
goto out; |
451 |
} |
452 |
/* |
453 |
* Bit of a cheat here, we use a pointer for a 3-address |
454 |
* frame format but don't reference fields past outside |
455 |
* ieee80211_frame_min w/o first validating the data is |
456 |
* present. |
457 |
*/ |
458 |
wh = mtod(m, struct ieee80211_frame *); |
459 |
|
460 |
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) |
461 |
ni->ni_inact = ni->ni_inact_reload; |
462 |
|
463 |
if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != |
464 |
IEEE80211_FC0_VERSION_0) { |
465 |
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, |
466 |
ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x", |
467 |
wh->i_fc[0], wh->i_fc[1]); |
468 |
vap->iv_stats.is_rx_badversion++; |
469 |
goto err; |
470 |
} |
471 |
|
472 |
dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; |
473 |
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; |
474 |
subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; |
475 |
|
476 |
/* NB: WDS vap's do not scan */ |
477 |
if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_addr4)) { |
478 |
IEEE80211_DISCARD_MAC(vap, |
479 |
IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, |
480 |
"too short (3): len %u", m->m_pkthdr.len); |
481 |
vap->iv_stats.is_rx_tooshort++; |
482 |
goto out; |
483 |
} |
484 |
/* NB: the TA is implicitly verified by finding the wds peer node */ |
485 |
if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr) && |
486 |
!IEEE80211_ADDR_EQ(wh->i_addr1, ifp->if_broadcastaddr)) { |
487 |
/* not interested in */ |
488 |
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, |
489 |
wh->i_addr1, NULL, "%s", "not to bss"); |
490 |
vap->iv_stats.is_rx_wrongbss++; |
491 |
goto out; |
492 |
} |
493 |
IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); |
494 |
ni->ni_noise = nf; |
495 |
if (HAS_SEQ(type)) { |
496 |
uint8_t tid = ieee80211_gettid(wh); |
497 |
if (IEEE80211_QOS_HAS_SEQ(wh) && |
498 |
TID_TO_WME_AC(tid) >= WME_AC_VI) |
499 |
ic->ic_wme.wme_hipri_traffic++; |
500 |
rxseq = le16toh(*(uint16_t *)wh->i_seq); |
501 |
if (! ieee80211_check_rxseq(ni, wh)) { |
502 |
/* duplicate, discard */ |
503 |
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, |
504 |
wh->i_addr1, "duplicate", |
505 |
"seqno <%u,%u> fragno <%u,%u> tid %u", |
506 |
rxseq >> IEEE80211_SEQ_SEQ_SHIFT, |
507 |
ni->ni_rxseqs[tid] >> IEEE80211_SEQ_SEQ_SHIFT, |
508 |
rxseq & IEEE80211_SEQ_FRAG_MASK, |
509 |
ni->ni_rxseqs[tid] & IEEE80211_SEQ_FRAG_MASK, |
510 |
tid); |
511 |
vap->iv_stats.is_rx_dup++; |
512 |
IEEE80211_NODE_STAT(ni, rx_dup); |
513 |
goto out; |
514 |
} |
515 |
ni->ni_rxseqs[tid] = rxseq; |
516 |
} |
517 |
switch (type) { |
518 |
case IEEE80211_FC0_TYPE_DATA: |
519 |
hdrspace = ieee80211_hdrspace(ic, wh); |
520 |
if (m->m_len < hdrspace && |
521 |
(m = m_pullup(m, hdrspace)) == NULL) { |
522 |
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, |
523 |
ni->ni_macaddr, NULL, |
524 |
"data too short: expecting %u", hdrspace); |
525 |
vap->iv_stats.is_rx_tooshort++; |
526 |
goto out; /* XXX */ |
527 |
} |
528 |
if (dir != IEEE80211_FC1_DIR_DSTODS) { |
529 |
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, |
530 |
wh, "data", "incorrect dir 0x%x", dir); |
531 |
vap->iv_stats.is_rx_wrongdir++; |
532 |
goto out; |
533 |
} |
534 |
/* |
535 |
* Only legacy WDS traffic should take this path. |
536 |
*/ |
537 |
if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) { |
538 |
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, |
539 |
wh, "data", "%s", "not legacy wds"); |
540 |
vap->iv_stats.is_rx_wrongdir++;/*XXX*/ |
541 |
goto out; |
542 |
} |
543 |
/* |
544 |
* Handle A-MPDU re-ordering. If the frame is to be |
545 |
* processed directly then ieee80211_ampdu_reorder |
546 |
* will return 0; otherwise it has consumed the mbuf |
547 |
* and we should do nothing more with it. |
548 |
*/ |
549 |
if ((m->m_flags & M_AMPDU) && |
550 |
ieee80211_ampdu_reorder(ni, m) != 0) { |
551 |
m = NULL; |
552 |
goto out; |
553 |
} |
554 |
resubmit_ampdu: |
555 |
|
556 |
/* |
557 |
* Handle privacy requirements. Note that we |
558 |
* must not be preempted from here until after |
559 |
* we (potentially) call ieee80211_crypto_demic; |
560 |
* otherwise we may violate assumptions in the |
561 |
* crypto cipher modules used to do delayed update |
562 |
* of replay sequence numbers. |
563 |
*/ |
564 |
if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { |
565 |
if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { |
566 |
/* |
567 |
* Discard encrypted frames when privacy is off. |
568 |
*/ |
569 |
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, |
570 |
wh, "WEP", "%s", "PRIVACY off"); |
571 |
vap->iv_stats.is_rx_noprivacy++; |
572 |
IEEE80211_NODE_STAT(ni, rx_noprivacy); |
573 |
goto out; |
574 |
} |
575 |
key = ieee80211_crypto_decap(ni, m, hdrspace); |
576 |
if (key == NULL) { |
577 |
/* NB: stats+msgs handled in crypto_decap */ |
578 |
IEEE80211_NODE_STAT(ni, rx_wepfail); |
579 |
goto out; |
580 |
} |
581 |
wh = mtod(m, struct ieee80211_frame *); |
582 |
wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; |
583 |
} else { |
584 |
/* XXX M_WEP and IEEE80211_F_PRIVACY */ |
585 |
key = NULL; |
586 |
} |
587 |
|
588 |
/* |
589 |
* Save QoS bits for use below--before we strip the header. |
590 |
*/ |
591 |
if (subtype == IEEE80211_FC0_SUBTYPE_QOS) { |
592 |
qos = (dir == IEEE80211_FC1_DIR_DSTODS) ? |
593 |
((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] : |
594 |
((struct ieee80211_qosframe *)wh)->i_qos[0]; |
595 |
} else |
596 |
qos = 0; |
597 |
|
598 |
/* |
599 |
* Next up, any fragmentation. |
600 |
*/ |
601 |
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { |
602 |
m = ieee80211_defrag(ni, m, hdrspace); |
603 |
if (m == NULL) { |
604 |
/* Fragment dropped or frame not complete yet */ |
605 |
goto out; |
606 |
} |
607 |
} |
608 |
wh = NULL; /* no longer valid, catch any uses */ |
609 |
|
610 |
/* |
611 |
* Next strip any MSDU crypto bits. |
612 |
*/ |
613 |
if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) { |
614 |
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, |
615 |
ni->ni_macaddr, "data", "%s", "demic error"); |
616 |
vap->iv_stats.is_rx_demicfail++; |
617 |
IEEE80211_NODE_STAT(ni, rx_demicfail); |
618 |
goto out; |
619 |
} |
620 |
|
621 |
/* copy to listener after decrypt */ |
622 |
if (ieee80211_radiotap_active_vap(vap)) |
623 |
ieee80211_radiotap_rx(vap, m); |
624 |
need_tap = 0; |
625 |
|
626 |
/* |
627 |
* Finally, strip the 802.11 header. |
628 |
*/ |
629 |
m = ieee80211_decap(vap, m, hdrspace); |
630 |
if (m == NULL) { |
631 |
/* XXX mask bit to check for both */ |
632 |
/* don't count Null data frames as errors */ |
633 |
if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || |
634 |
subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) |
635 |
goto out; |
636 |
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, |
637 |
ni->ni_macaddr, "data", "%s", "decap error"); |
638 |
vap->iv_stats.is_rx_decap++; |
639 |
IEEE80211_NODE_STAT(ni, rx_decap); |
640 |
goto err; |
641 |
} |
642 |
eh = mtod(m, struct ether_header *); |
643 |
if (!ieee80211_node_is_authorized(ni)) { |
644 |
/* |
645 |
* Deny any non-PAE frames received prior to |
646 |
* authorization. For open/shared-key |
647 |
* authentication the port is mark authorized |
648 |
* after authentication completes. For 802.1x |
649 |
* the port is not marked authorized by the |
650 |
* authenticator until the handshake has completed. |
651 |
*/ |
652 |
if (eh->ether_type != htons(ETHERTYPE_PAE)) { |
653 |
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, |
654 |
eh->ether_shost, "data", |
655 |
"unauthorized port: ether type 0x%x len %u", |
656 |
eh->ether_type, m->m_pkthdr.len); |
657 |
vap->iv_stats.is_rx_unauth++; |
658 |
IEEE80211_NODE_STAT(ni, rx_unauth); |
659 |
goto err; |
660 |
} |
661 |
} else { |
662 |
/* |
663 |
* When denying unencrypted frames, discard |
664 |
* any non-PAE frames received without encryption. |
665 |
*/ |
666 |
if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && |
667 |
(key == NULL && (m->m_flags & M_WEP) == 0) && |
668 |
eh->ether_type != htons(ETHERTYPE_PAE)) { |
669 |
/* |
670 |
* Drop unencrypted frames. |
671 |
*/ |
672 |
vap->iv_stats.is_rx_unencrypted++; |
673 |
IEEE80211_NODE_STAT(ni, rx_unencrypted); |
674 |
goto out; |
675 |
} |
676 |
} |
677 |
/* XXX require HT? */ |
678 |
if (qos & IEEE80211_QOS_AMSDU) { |
679 |
m = ieee80211_decap_amsdu(ni, m); |
680 |
if (m == NULL) |
681 |
return IEEE80211_FC0_TYPE_DATA; |
682 |
} else { |
683 |
#ifdef IEEE80211_SUPPORT_SUPERG |
684 |
m = ieee80211_decap_fastframe(vap, ni, m); |
685 |
if (m == NULL) |
686 |
return IEEE80211_FC0_TYPE_DATA; |
687 |
#endif |
688 |
} |
689 |
ieee80211_deliver_data(vap, ni, m); |
690 |
return IEEE80211_FC0_TYPE_DATA; |
691 |
|
692 |
case IEEE80211_FC0_TYPE_MGT: |
693 |
vap->iv_stats.is_rx_mgmt++; |
694 |
IEEE80211_NODE_STAT(ni, rx_mgmt); |
695 |
if (dir != IEEE80211_FC1_DIR_NODS) { |
696 |
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, |
697 |
wh, "data", "incorrect dir 0x%x", dir); |
698 |
vap->iv_stats.is_rx_wrongdir++; |
699 |
goto err; |
700 |
} |
701 |
if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { |
702 |
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, |
703 |
ni->ni_macaddr, "mgt", "too short: len %u", |
704 |
m->m_pkthdr.len); |
705 |
vap->iv_stats.is_rx_tooshort++; |
706 |
goto out; |
707 |
} |
708 |
#ifdef IEEE80211_DEBUG |
709 |
if (ieee80211_msg_debug(vap) || ieee80211_msg_dumppkts(vap)) { |
710 |
if_printf(ifp, "received %s from %s rssi %d\n", |
711 |
ieee80211_mgt_subtype_name[subtype >> |
712 |
IEEE80211_FC0_SUBTYPE_SHIFT], |
713 |
ether_sprintf(wh->i_addr2), rssi); |
714 |
} |
715 |
#endif |
716 |
if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { |
717 |
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, |
718 |
wh, NULL, "%s", "WEP set but not permitted"); |
719 |
vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ |
720 |
goto out; |
721 |
} |
722 |
vap->iv_recv_mgmt(ni, m, subtype, rssi, nf); |
723 |
goto out; |
724 |
|
725 |
case IEEE80211_FC0_TYPE_CTL: |
726 |
vap->iv_stats.is_rx_ctl++; |
727 |
IEEE80211_NODE_STAT(ni, rx_ctrl); |
728 |
goto out; |
729 |
|
730 |
default: |
731 |
IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, |
732 |
wh, "bad", "frame type 0x%x", type); |
733 |
/* should not come here */ |
734 |
break; |
735 |
} |
736 |
err: |
737 |
ifp->if_ierrors++; |
738 |
out: |
739 |
if (m != NULL) { |
740 |
if (need_tap && ieee80211_radiotap_active_vap(vap)) |
741 |
ieee80211_radiotap_rx(vap, m); |
742 |
m_freem(m); |
743 |
} |
744 |
return type; |
745 |
} |
746 |
|
747 |
static void |
748 |
wds_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, |
749 |
int subtype, int rssi, int nf) |
750 |
{ |
751 |
struct ieee80211vap *vap = ni->ni_vap; |
752 |
struct ieee80211com *ic = ni->ni_ic; |
753 |
struct ieee80211_frame *wh; |
754 |
u_int8_t *frm, *efrm; |
755 |
|
756 |
wh = mtod(m0, struct ieee80211_frame *); |
757 |
frm = (u_int8_t *)&wh[1]; |
758 |
efrm = mtod(m0, u_int8_t *) + m0->m_len; |
759 |
switch (subtype) { |
760 |
case IEEE80211_FC0_SUBTYPE_ACTION: |
761 |
case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: |
762 |
if (ni == vap->iv_bss) { |
763 |
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, |
764 |
wh, NULL, "%s", "unknown node"); |
765 |
vap->iv_stats.is_rx_mgtdiscard++; |
766 |
} else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1)) { |
767 |
/* NB: not interested in multicast frames. */ |
768 |
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, |
769 |
wh, NULL, "%s", "not for us"); |
770 |
vap->iv_stats.is_rx_mgtdiscard++; |
771 |
} else if (vap->iv_state != IEEE80211_S_RUN) { |
772 |
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, |
773 |
wh, NULL, "wrong state %s", |
774 |
ieee80211_state_name[vap->iv_state]); |
775 |
vap->iv_stats.is_rx_mgtdiscard++; |
776 |
} else { |
777 |
if (ieee80211_parse_action(ni, m0) == 0) |
778 |
(void)ic->ic_recv_action(ni, wh, frm, efrm); |
779 |
} |
780 |
break; |
781 |
|
782 |
case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: |
783 |
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: |
784 |
case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: |
785 |
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: |
786 |
case IEEE80211_FC0_SUBTYPE_PROBE_REQ: |
787 |
case IEEE80211_FC0_SUBTYPE_PROBE_RESP: |
788 |
case IEEE80211_FC0_SUBTYPE_BEACON: |
789 |
case IEEE80211_FC0_SUBTYPE_ATIM: |
790 |
case IEEE80211_FC0_SUBTYPE_DISASSOC: |
791 |
case IEEE80211_FC0_SUBTYPE_AUTH: |
792 |
case IEEE80211_FC0_SUBTYPE_DEAUTH: |
793 |
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, |
794 |
wh, NULL, "%s", "not handled"); |
795 |
vap->iv_stats.is_rx_mgtdiscard++; |
796 |
break; |
797 |
|
798 |
default: |
799 |
IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, |
800 |
wh, "mgt", "subtype 0x%x not handled", subtype); |
801 |
vap->iv_stats.is_rx_badsubtype++; |
802 |
break; |
803 |
} |
804 |
} |