1753c7e08SAugustin Cavalier /*-
286021fd4SAugustin Cavalier * SPDX-License-Identifier: BSD-2-Clause
38244a9baSAugustin Cavalier *
4753c7e08SAugustin Cavalier * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
5753c7e08SAugustin Cavalier * All rights reserved.
6753c7e08SAugustin Cavalier *
7753c7e08SAugustin Cavalier * Redistribution and use in source and binary forms, with or without
8753c7e08SAugustin Cavalier * modification, are permitted provided that the following conditions
9753c7e08SAugustin Cavalier * are met:
10753c7e08SAugustin Cavalier * 1. Redistributions of source code must retain the above copyright
11753c7e08SAugustin Cavalier * notice, this list of conditions and the following disclaimer.
12753c7e08SAugustin Cavalier * 2. Redistributions in binary form must reproduce the above copyright
13753c7e08SAugustin Cavalier * notice, this list of conditions and the following disclaimer in the
14753c7e08SAugustin Cavalier * documentation and/or other materials provided with the distribution.
15753c7e08SAugustin Cavalier *
16753c7e08SAugustin Cavalier * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17753c7e08SAugustin Cavalier * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18753c7e08SAugustin Cavalier * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19753c7e08SAugustin Cavalier * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20753c7e08SAugustin Cavalier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21753c7e08SAugustin Cavalier * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22753c7e08SAugustin Cavalier * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23753c7e08SAugustin Cavalier * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24753c7e08SAugustin Cavalier * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25753c7e08SAugustin Cavalier * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26753c7e08SAugustin Cavalier */
27753c7e08SAugustin Cavalier
28753c7e08SAugustin Cavalier /*
29753c7e08SAugustin Cavalier * IEEE 802.11 WDS mode support.
30753c7e08SAugustin Cavalier */
31753c7e08SAugustin Cavalier #include "opt_inet.h"
32753c7e08SAugustin Cavalier #include "opt_wlan.h"
33753c7e08SAugustin Cavalier
34753c7e08SAugustin Cavalier #include <sys/param.h>
35753c7e08SAugustin Cavalier #include <sys/systm.h>
36753c7e08SAugustin Cavalier #include <sys/mbuf.h>
37753c7e08SAugustin Cavalier #include <sys/malloc.h>
38753c7e08SAugustin Cavalier #include <sys/kernel.h>
39753c7e08SAugustin Cavalier
40753c7e08SAugustin Cavalier #include <sys/socket.h>
41753c7e08SAugustin Cavalier #include <sys/sockio.h>
42753c7e08SAugustin Cavalier #include <sys/endian.h>
43753c7e08SAugustin Cavalier #include <sys/errno.h>
44753c7e08SAugustin Cavalier #include <sys/proc.h>
45753c7e08SAugustin Cavalier #include <sys/sysctl.h>
46753c7e08SAugustin Cavalier
47753c7e08SAugustin Cavalier #include <net/if.h>
48753c7e08SAugustin Cavalier #include <net/if_var.h>
49753c7e08SAugustin Cavalier #include <net/if_media.h>
50753c7e08SAugustin Cavalier #include <net/if_llc.h>
5186021fd4SAugustin Cavalier #include <net/if_private.h>
52753c7e08SAugustin Cavalier #include <net/ethernet.h>
53753c7e08SAugustin Cavalier
54753c7e08SAugustin Cavalier #include <net/bpf.h>
55753c7e08SAugustin Cavalier
56753c7e08SAugustin Cavalier #include <net80211/ieee80211_var.h>
57753c7e08SAugustin Cavalier #include <net80211/ieee80211_wds.h>
58753c7e08SAugustin Cavalier #include <net80211/ieee80211_input.h>
59753c7e08SAugustin Cavalier #ifdef IEEE80211_SUPPORT_SUPERG
60753c7e08SAugustin Cavalier #include <net80211/ieee80211_superg.h>
61753c7e08SAugustin Cavalier #endif
62753c7e08SAugustin Cavalier
63753c7e08SAugustin Cavalier static void wds_vattach(struct ieee80211vap *);
64753c7e08SAugustin Cavalier static int wds_newstate(struct ieee80211vap *, enum ieee80211_state, int);
65753c7e08SAugustin Cavalier static int wds_input(struct ieee80211_node *ni, struct mbuf *m,
66753c7e08SAugustin Cavalier const struct ieee80211_rx_stats *rxs, int, int);
67753c7e08SAugustin Cavalier static void wds_recv_mgmt(struct ieee80211_node *, struct mbuf *, int subtype,
68753c7e08SAugustin Cavalier const struct ieee80211_rx_stats *, int, int);
69753c7e08SAugustin Cavalier
70753c7e08SAugustin Cavalier void
ieee80211_wds_attach(struct ieee80211com * ic)71753c7e08SAugustin Cavalier ieee80211_wds_attach(struct ieee80211com *ic)
72753c7e08SAugustin Cavalier {
73753c7e08SAugustin Cavalier ic->ic_vattach[IEEE80211_M_WDS] = wds_vattach;
74753c7e08SAugustin Cavalier }
75753c7e08SAugustin Cavalier
76753c7e08SAugustin Cavalier void
ieee80211_wds_detach(struct ieee80211com * ic)77753c7e08SAugustin Cavalier ieee80211_wds_detach(struct ieee80211com *ic)
78753c7e08SAugustin Cavalier {
79753c7e08SAugustin Cavalier }
80753c7e08SAugustin Cavalier
81753c7e08SAugustin Cavalier static void
wds_vdetach(struct ieee80211vap * vap)82753c7e08SAugustin Cavalier wds_vdetach(struct ieee80211vap *vap)
83753c7e08SAugustin Cavalier {
84753c7e08SAugustin Cavalier if (vap->iv_bss != NULL) {
85753c7e08SAugustin Cavalier /* XXX locking? */
86753c7e08SAugustin Cavalier if (vap->iv_bss->ni_wdsvap == vap)
87753c7e08SAugustin Cavalier vap->iv_bss->ni_wdsvap = NULL;
88753c7e08SAugustin Cavalier }
89753c7e08SAugustin Cavalier }
90753c7e08SAugustin Cavalier
91753c7e08SAugustin Cavalier static void
wds_vattach(struct ieee80211vap * vap)92753c7e08SAugustin Cavalier wds_vattach(struct ieee80211vap *vap)
93753c7e08SAugustin Cavalier {
94753c7e08SAugustin Cavalier vap->iv_newstate = wds_newstate;
95753c7e08SAugustin Cavalier vap->iv_input = wds_input;
96753c7e08SAugustin Cavalier vap->iv_recv_mgmt = wds_recv_mgmt;
97753c7e08SAugustin Cavalier vap->iv_opdetach = wds_vdetach;
98753c7e08SAugustin Cavalier }
99753c7e08SAugustin Cavalier
100753c7e08SAugustin Cavalier static void
wds_flush(struct ieee80211_node * ni)101753c7e08SAugustin Cavalier wds_flush(struct ieee80211_node *ni)
102753c7e08SAugustin Cavalier {
103753c7e08SAugustin Cavalier struct ieee80211com *ic = ni->ni_ic;
104753c7e08SAugustin Cavalier struct mbuf *m, *next;
105753c7e08SAugustin Cavalier int8_t rssi, nf;
106753c7e08SAugustin Cavalier
107753c7e08SAugustin Cavalier m = ieee80211_ageq_remove(&ic->ic_stageq,
108753c7e08SAugustin Cavalier (void *)(uintptr_t) ieee80211_mac_hash(ic, ni->ni_macaddr));
109753c7e08SAugustin Cavalier if (m == NULL)
110753c7e08SAugustin Cavalier return;
111753c7e08SAugustin Cavalier
112753c7e08SAugustin Cavalier IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_WDS, ni,
113753c7e08SAugustin Cavalier "%s", "flush wds queue");
114753c7e08SAugustin Cavalier ic->ic_node_getsignal(ni, &rssi, &nf);
115753c7e08SAugustin Cavalier for (; m != NULL; m = next) {
116753c7e08SAugustin Cavalier next = m->m_nextpkt;
117753c7e08SAugustin Cavalier m->m_nextpkt = NULL;
118753c7e08SAugustin Cavalier ieee80211_input(ni, m, rssi, nf);
119753c7e08SAugustin Cavalier }
120753c7e08SAugustin Cavalier }
121753c7e08SAugustin Cavalier
122753c7e08SAugustin Cavalier static int
ieee80211_create_wds(struct ieee80211vap * vap,struct ieee80211_channel * chan)123753c7e08SAugustin Cavalier ieee80211_create_wds(struct ieee80211vap *vap, struct ieee80211_channel *chan)
124753c7e08SAugustin Cavalier {
125753c7e08SAugustin Cavalier struct ieee80211com *ic = vap->iv_ic;
126753c7e08SAugustin Cavalier struct ieee80211_node_table *nt = &ic->ic_sta;
127753c7e08SAugustin Cavalier struct ieee80211_node *ni, *obss;
128753c7e08SAugustin Cavalier
129753c7e08SAugustin Cavalier IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
130753c7e08SAugustin Cavalier "%s: creating link to %s on channel %u\n", __func__,
131753c7e08SAugustin Cavalier ether_sprintf(vap->iv_des_bssid), ieee80211_chan2ieee(ic, chan));
132753c7e08SAugustin Cavalier
133753c7e08SAugustin Cavalier /* NB: vap create must specify the bssid for the link */
134753c7e08SAugustin Cavalier KASSERT(vap->iv_flags & IEEE80211_F_DESBSSID, ("no bssid"));
135753c7e08SAugustin Cavalier /* NB: we should only be called on RUN transition */
136753c7e08SAugustin Cavalier KASSERT(vap->iv_state == IEEE80211_S_RUN, ("!RUN state"));
137753c7e08SAugustin Cavalier
138753c7e08SAugustin Cavalier if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) {
139753c7e08SAugustin Cavalier /*
140753c7e08SAugustin Cavalier * Dynamic/non-legacy WDS. Reference the associated
141753c7e08SAugustin Cavalier * station specified by the desired bssid setup at vap
142753c7e08SAugustin Cavalier * create. Point ni_wdsvap at the WDS vap so 4-address
143753c7e08SAugustin Cavalier * frames received through the associated AP vap will
144753c7e08SAugustin Cavalier * be dispatched upward (e.g. to a bridge) as though
145753c7e08SAugustin Cavalier * they arrived on the WDS vap.
146753c7e08SAugustin Cavalier */
147753c7e08SAugustin Cavalier IEEE80211_NODE_LOCK(nt);
148753c7e08SAugustin Cavalier obss = NULL;
149753c7e08SAugustin Cavalier ni = ieee80211_find_node_locked(&ic->ic_sta, vap->iv_des_bssid);
150753c7e08SAugustin Cavalier if (ni == NULL) {
151753c7e08SAugustin Cavalier /*
152753c7e08SAugustin Cavalier * Node went away before we could hookup. This
153753c7e08SAugustin Cavalier * should be ok; no traffic will flow and a leave
154753c7e08SAugustin Cavalier * event will be dispatched that should cause
155753c7e08SAugustin Cavalier * the vap to be destroyed.
156753c7e08SAugustin Cavalier */
157753c7e08SAugustin Cavalier IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
158753c7e08SAugustin Cavalier "%s: station %s went away\n",
159753c7e08SAugustin Cavalier __func__, ether_sprintf(vap->iv_des_bssid));
160753c7e08SAugustin Cavalier /* XXX stat? */
161753c7e08SAugustin Cavalier } else if (ni->ni_wdsvap != NULL) {
162753c7e08SAugustin Cavalier /*
163753c7e08SAugustin Cavalier * Node already setup with a WDS vap; we cannot
164753c7e08SAugustin Cavalier * allow multiple references so disallow. If
165753c7e08SAugustin Cavalier * ni_wdsvap points at us that's ok; we should
166753c7e08SAugustin Cavalier * do nothing anyway.
167753c7e08SAugustin Cavalier */
168753c7e08SAugustin Cavalier /* XXX printf instead? */
169753c7e08SAugustin Cavalier IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
170753c7e08SAugustin Cavalier "%s: station %s in use with %s\n",
171753c7e08SAugustin Cavalier __func__, ether_sprintf(vap->iv_des_bssid),
172753c7e08SAugustin Cavalier ni->ni_wdsvap->iv_ifp->if_xname);
173753c7e08SAugustin Cavalier /* XXX stat? */
174753c7e08SAugustin Cavalier } else {
175753c7e08SAugustin Cavalier /*
176753c7e08SAugustin Cavalier * Committed to new node, setup state.
177753c7e08SAugustin Cavalier */
17886021fd4SAugustin Cavalier obss = vap->iv_update_bss(vap, ni);
179753c7e08SAugustin Cavalier ni->ni_wdsvap = vap;
180753c7e08SAugustin Cavalier }
181753c7e08SAugustin Cavalier IEEE80211_NODE_UNLOCK(nt);
182753c7e08SAugustin Cavalier if (obss != NULL) {
183753c7e08SAugustin Cavalier /* NB: deferred to avoid recursive lock */
184753c7e08SAugustin Cavalier ieee80211_free_node(obss);
185753c7e08SAugustin Cavalier }
186753c7e08SAugustin Cavalier } else {
187753c7e08SAugustin Cavalier /*
188753c7e08SAugustin Cavalier * Legacy WDS vap setup.
189753c7e08SAugustin Cavalier */
190753c7e08SAugustin Cavalier /*
191753c7e08SAugustin Cavalier * The far end does not associate so we just create
192753c7e08SAugustin Cavalier * create a new node and install it as the vap's
193753c7e08SAugustin Cavalier * bss node. We must simulate an association and
194753c7e08SAugustin Cavalier * authorize the port for traffic to flow.
195753c7e08SAugustin Cavalier * XXX check if node already in sta table?
196753c7e08SAugustin Cavalier */
197753c7e08SAugustin Cavalier ni = ieee80211_node_create_wds(vap, vap->iv_des_bssid, chan);
198753c7e08SAugustin Cavalier if (ni != NULL) {
19986021fd4SAugustin Cavalier obss = vap->iv_update_bss(vap, ieee80211_ref_node(ni));
200753c7e08SAugustin Cavalier ni->ni_flags |= IEEE80211_NODE_AREF;
201753c7e08SAugustin Cavalier if (obss != NULL)
202753c7e08SAugustin Cavalier ieee80211_free_node(obss);
203753c7e08SAugustin Cavalier /* give driver a chance to setup state like ni_txrate */
204753c7e08SAugustin Cavalier if (ic->ic_newassoc != NULL)
205753c7e08SAugustin Cavalier ic->ic_newassoc(ni, 1);
206753c7e08SAugustin Cavalier /* tell the authenticator about new station */
207753c7e08SAugustin Cavalier if (vap->iv_auth->ia_node_join != NULL)
208753c7e08SAugustin Cavalier vap->iv_auth->ia_node_join(ni);
209753c7e08SAugustin Cavalier if (ni->ni_authmode != IEEE80211_AUTH_8021X)
210753c7e08SAugustin Cavalier ieee80211_node_authorize(ni);
211753c7e08SAugustin Cavalier
212753c7e08SAugustin Cavalier ieee80211_notify_node_join(ni, 1 /*newassoc*/);
213753c7e08SAugustin Cavalier /* XXX inject l2uf frame */
214753c7e08SAugustin Cavalier }
215753c7e08SAugustin Cavalier }
216753c7e08SAugustin Cavalier
217753c7e08SAugustin Cavalier /*
218753c7e08SAugustin Cavalier * Flush any pending frames now that were setup.
219753c7e08SAugustin Cavalier */
220753c7e08SAugustin Cavalier if (ni != NULL)
221753c7e08SAugustin Cavalier wds_flush(ni);
222753c7e08SAugustin Cavalier return (ni == NULL ? ENOENT : 0);
223753c7e08SAugustin Cavalier }
224753c7e08SAugustin Cavalier
225753c7e08SAugustin Cavalier /*
226753c7e08SAugustin Cavalier * Propagate multicast frames of an ap vap to all DWDS links.
227753c7e08SAugustin Cavalier * The caller is assumed to have verified this frame is multicast.
228753c7e08SAugustin Cavalier */
229753c7e08SAugustin Cavalier void
ieee80211_dwds_mcast(struct ieee80211vap * vap0,struct mbuf * m)230753c7e08SAugustin Cavalier ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m)
231753c7e08SAugustin Cavalier {
232753c7e08SAugustin Cavalier struct ieee80211com *ic = vap0->iv_ic;
233753c7e08SAugustin Cavalier const struct ether_header *eh = mtod(m, const struct ether_header *);
234753c7e08SAugustin Cavalier struct ieee80211_node *ni;
235753c7e08SAugustin Cavalier struct ieee80211vap *vap;
236753c7e08SAugustin Cavalier struct ifnet *ifp;
237753c7e08SAugustin Cavalier struct mbuf *mcopy;
238753c7e08SAugustin Cavalier int err;
239753c7e08SAugustin Cavalier
240753c7e08SAugustin Cavalier KASSERT(ETHER_IS_MULTICAST(eh->ether_dhost),
241753c7e08SAugustin Cavalier ("%s not mcast", ether_sprintf(eh->ether_dhost)));
242753c7e08SAugustin Cavalier
243753c7e08SAugustin Cavalier /* XXX locking */
244753c7e08SAugustin Cavalier TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
245753c7e08SAugustin Cavalier /* only DWDS vaps are interesting */
246753c7e08SAugustin Cavalier if (vap->iv_opmode != IEEE80211_M_WDS ||
247753c7e08SAugustin Cavalier (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY))
248753c7e08SAugustin Cavalier continue;
249753c7e08SAugustin Cavalier /* if it came in this interface, don't send it back out */
250753c7e08SAugustin Cavalier ifp = vap->iv_ifp;
251753c7e08SAugustin Cavalier if (ifp == m->m_pkthdr.rcvif)
252753c7e08SAugustin Cavalier continue;
253753c7e08SAugustin Cavalier /*
254753c7e08SAugustin Cavalier * Duplicate the frame and send it.
255753c7e08SAugustin Cavalier */
25686021fd4SAugustin Cavalier mcopy = m_copypacket(m, IEEE80211_M_NOWAIT);
257753c7e08SAugustin Cavalier if (mcopy == NULL) {
258753c7e08SAugustin Cavalier if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
259753c7e08SAugustin Cavalier /* XXX stat + msg */
260753c7e08SAugustin Cavalier continue;
261753c7e08SAugustin Cavalier }
262753c7e08SAugustin Cavalier ni = ieee80211_find_txnode(vap, eh->ether_dhost);
263753c7e08SAugustin Cavalier if (ni == NULL) {
264753c7e08SAugustin Cavalier /* NB: ieee80211_find_txnode does stat+msg */
265753c7e08SAugustin Cavalier if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
266753c7e08SAugustin Cavalier m_freem(mcopy);
267753c7e08SAugustin Cavalier continue;
268753c7e08SAugustin Cavalier }
269753c7e08SAugustin Cavalier /* calculate priority so drivers can find the tx queue */
270753c7e08SAugustin Cavalier if (ieee80211_classify(ni, mcopy)) {
271753c7e08SAugustin Cavalier IEEE80211_DISCARD_MAC(vap,
272753c7e08SAugustin Cavalier IEEE80211_MSG_OUTPUT | IEEE80211_MSG_WDS,
273753c7e08SAugustin Cavalier eh->ether_dhost, NULL,
274753c7e08SAugustin Cavalier "%s", "classification failure");
275753c7e08SAugustin Cavalier vap->iv_stats.is_tx_classify++;
276753c7e08SAugustin Cavalier if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
277753c7e08SAugustin Cavalier m_freem(mcopy);
278753c7e08SAugustin Cavalier ieee80211_free_node(ni);
279753c7e08SAugustin Cavalier continue;
280753c7e08SAugustin Cavalier }
281753c7e08SAugustin Cavalier
282753c7e08SAugustin Cavalier BPF_MTAP(ifp, m); /* 802.3 tx */
283753c7e08SAugustin Cavalier
284753c7e08SAugustin Cavalier /*
285753c7e08SAugustin Cavalier * Encapsulate the packet in prep for transmission.
286753c7e08SAugustin Cavalier */
287753c7e08SAugustin Cavalier IEEE80211_TX_LOCK(ic);
288753c7e08SAugustin Cavalier mcopy = ieee80211_encap(vap, ni, mcopy);
289753c7e08SAugustin Cavalier if (mcopy == NULL) {
290753c7e08SAugustin Cavalier /* NB: stat+msg handled in ieee80211_encap */
291753c7e08SAugustin Cavalier IEEE80211_TX_UNLOCK(ic);
292753c7e08SAugustin Cavalier ieee80211_free_node(ni);
293753c7e08SAugustin Cavalier continue;
294753c7e08SAugustin Cavalier }
295753c7e08SAugustin Cavalier mcopy->m_flags |= M_MCAST;
29686021fd4SAugustin Cavalier //MPASS((mcopy->m_pkthdr.csum_flags & CSUM_SND_TAG) == 0);
297753c7e08SAugustin Cavalier mcopy->m_pkthdr.rcvif = (void *) ni;
298753c7e08SAugustin Cavalier
299753c7e08SAugustin Cavalier err = ieee80211_parent_xmitpkt(ic, mcopy);
300753c7e08SAugustin Cavalier IEEE80211_TX_UNLOCK(ic);
301753c7e08SAugustin Cavalier if (!err) {
302753c7e08SAugustin Cavalier if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
303753c7e08SAugustin Cavalier if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1);
304753c7e08SAugustin Cavalier if_inc_counter(ifp, IFCOUNTER_OBYTES,
305753c7e08SAugustin Cavalier m->m_pkthdr.len);
306753c7e08SAugustin Cavalier }
307753c7e08SAugustin Cavalier }
308753c7e08SAugustin Cavalier }
309753c7e08SAugustin Cavalier
310753c7e08SAugustin Cavalier /*
311753c7e08SAugustin Cavalier * Handle DWDS discovery on receipt of a 4-address frame in
312753c7e08SAugustin Cavalier * ap mode. Queue the frame and post an event for someone
313753c7e08SAugustin Cavalier * to plumb the necessary WDS vap for this station. Frames
314753c7e08SAugustin Cavalier * received prior to the vap set running will then be reprocessed
315753c7e08SAugustin Cavalier * as if they were just received.
316753c7e08SAugustin Cavalier */
317753c7e08SAugustin Cavalier void
ieee80211_dwds_discover(struct ieee80211_node * ni,struct mbuf * m)318753c7e08SAugustin Cavalier ieee80211_dwds_discover(struct ieee80211_node *ni, struct mbuf *m)
319753c7e08SAugustin Cavalier {
320753c7e08SAugustin Cavalier struct ieee80211com *ic = ni->ni_ic;
321753c7e08SAugustin Cavalier
322753c7e08SAugustin Cavalier /*
323753c7e08SAugustin Cavalier * Save the frame with an aging interval 4 times
324753c7e08SAugustin Cavalier * the listen interval specified by the station.
325753c7e08SAugustin Cavalier * Frames that sit around too long are reclaimed
326753c7e08SAugustin Cavalier * using this information.
327753c7e08SAugustin Cavalier * XXX handle overflow?
328753c7e08SAugustin Cavalier * XXX per/vap beacon interval?
329753c7e08SAugustin Cavalier */
33086021fd4SAugustin Cavalier //MPASS((m->m_pkthdr.csum_flags & CSUM_SND_TAG) == 0);
331753c7e08SAugustin Cavalier m->m_pkthdr.rcvif = (void *)(uintptr_t)
332753c7e08SAugustin Cavalier ieee80211_mac_hash(ic, ni->ni_macaddr);
333753c7e08SAugustin Cavalier (void) ieee80211_ageq_append(&ic->ic_stageq, m,
334753c7e08SAugustin Cavalier ((ni->ni_intval * ic->ic_lintval) << 2) / 1024);
335753c7e08SAugustin Cavalier ieee80211_notify_wds_discover(ni);
336753c7e08SAugustin Cavalier }
337753c7e08SAugustin Cavalier
338753c7e08SAugustin Cavalier /*
339753c7e08SAugustin Cavalier * IEEE80211_M_WDS vap state machine handler.
340753c7e08SAugustin Cavalier */
341753c7e08SAugustin Cavalier static int
wds_newstate(struct ieee80211vap * vap,enum ieee80211_state nstate,int arg)342753c7e08SAugustin Cavalier wds_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
343753c7e08SAugustin Cavalier {
344753c7e08SAugustin Cavalier struct ieee80211com *ic = vap->iv_ic;
345753c7e08SAugustin Cavalier enum ieee80211_state ostate;
346753c7e08SAugustin Cavalier int error;
347753c7e08SAugustin Cavalier
348753c7e08SAugustin Cavalier IEEE80211_LOCK_ASSERT(ic);
349753c7e08SAugustin Cavalier
350753c7e08SAugustin Cavalier ostate = vap->iv_state;
351753c7e08SAugustin Cavalier IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__,
352753c7e08SAugustin Cavalier ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
353753c7e08SAugustin Cavalier vap->iv_state = nstate; /* state transition */
354753c7e08SAugustin Cavalier callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */
355753c7e08SAugustin Cavalier if (ostate != IEEE80211_S_SCAN)
356753c7e08SAugustin Cavalier ieee80211_cancel_scan(vap); /* background scan */
357753c7e08SAugustin Cavalier error = 0;
358753c7e08SAugustin Cavalier switch (nstate) {
359753c7e08SAugustin Cavalier case IEEE80211_S_INIT:
360753c7e08SAugustin Cavalier switch (ostate) {
361753c7e08SAugustin Cavalier case IEEE80211_S_SCAN:
362753c7e08SAugustin Cavalier ieee80211_cancel_scan(vap);
363753c7e08SAugustin Cavalier break;
364753c7e08SAugustin Cavalier default:
365753c7e08SAugustin Cavalier break;
366753c7e08SAugustin Cavalier }
367753c7e08SAugustin Cavalier if (ostate != IEEE80211_S_INIT) {
368753c7e08SAugustin Cavalier /* NB: optimize INIT -> INIT case */
369753c7e08SAugustin Cavalier ieee80211_reset_bss(vap);
370753c7e08SAugustin Cavalier }
371753c7e08SAugustin Cavalier break;
372753c7e08SAugustin Cavalier case IEEE80211_S_SCAN:
373753c7e08SAugustin Cavalier switch (ostate) {
374753c7e08SAugustin Cavalier case IEEE80211_S_INIT:
375753c7e08SAugustin Cavalier ieee80211_check_scan_current(vap);
376753c7e08SAugustin Cavalier break;
377753c7e08SAugustin Cavalier default:
378753c7e08SAugustin Cavalier break;
379753c7e08SAugustin Cavalier }
380753c7e08SAugustin Cavalier break;
381753c7e08SAugustin Cavalier case IEEE80211_S_RUN:
382753c7e08SAugustin Cavalier if (ostate == IEEE80211_S_INIT) {
383753c7e08SAugustin Cavalier /*
384753c7e08SAugustin Cavalier * Already have a channel; bypass the scan
385753c7e08SAugustin Cavalier * and startup immediately.
386753c7e08SAugustin Cavalier */
387753c7e08SAugustin Cavalier error = ieee80211_create_wds(vap, ic->ic_curchan);
388753c7e08SAugustin Cavalier }
389753c7e08SAugustin Cavalier break;
390753c7e08SAugustin Cavalier default:
391753c7e08SAugustin Cavalier break;
392753c7e08SAugustin Cavalier }
393753c7e08SAugustin Cavalier return error;
394753c7e08SAugustin Cavalier }
395753c7e08SAugustin Cavalier
396753c7e08SAugustin Cavalier /*
397753c7e08SAugustin Cavalier * Process a received frame. The node associated with the sender
398753c7e08SAugustin Cavalier * should be supplied. If nothing was found in the node table then
399753c7e08SAugustin Cavalier * the caller is assumed to supply a reference to iv_bss instead.
400753c7e08SAugustin Cavalier * The RSSI and a timestamp are also supplied. The RSSI data is used
401753c7e08SAugustin Cavalier * during AP scanning to select a AP to associate with; it can have
402753c7e08SAugustin Cavalier * any units so long as values have consistent units and higher values
403753c7e08SAugustin Cavalier * mean ``better signal''. The receive timestamp is currently not used
404753c7e08SAugustin Cavalier * by the 802.11 layer.
405753c7e08SAugustin Cavalier */
406753c7e08SAugustin Cavalier static int
wds_input(struct ieee80211_node * ni,struct mbuf * m,const struct ieee80211_rx_stats * rxs,int rssi,int nf)407753c7e08SAugustin Cavalier wds_input(struct ieee80211_node *ni, struct mbuf *m,
408753c7e08SAugustin Cavalier const struct ieee80211_rx_stats *rxs, int rssi, int nf)
409753c7e08SAugustin Cavalier {
410753c7e08SAugustin Cavalier struct ieee80211vap *vap = ni->ni_vap;
411753c7e08SAugustin Cavalier struct ieee80211com *ic = ni->ni_ic;
412753c7e08SAugustin Cavalier struct ifnet *ifp = vap->iv_ifp;
413753c7e08SAugustin Cavalier struct ieee80211_frame *wh;
414753c7e08SAugustin Cavalier struct ieee80211_key *key;
415753c7e08SAugustin Cavalier struct ether_header *eh;
416753c7e08SAugustin Cavalier int hdrspace, need_tap = 1; /* mbuf need to be tapped. */
417753c7e08SAugustin Cavalier uint8_t dir, type, subtype, qos;
4188244a9baSAugustin Cavalier int is_hw_decrypted = 0;
4198244a9baSAugustin Cavalier int has_decrypted = 0;
4208244a9baSAugustin Cavalier
4218244a9baSAugustin Cavalier /*
4228244a9baSAugustin Cavalier * Some devices do hardware decryption all the way through
4238244a9baSAugustin Cavalier * to pretending the frame wasn't encrypted in the first place.
4248244a9baSAugustin Cavalier * So, tag it appropriately so it isn't discarded inappropriately.
4258244a9baSAugustin Cavalier */
4268244a9baSAugustin Cavalier if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED))
4278244a9baSAugustin Cavalier is_hw_decrypted = 1;
428753c7e08SAugustin Cavalier
429753c7e08SAugustin Cavalier if (m->m_flags & M_AMPDU_MPDU) {
430753c7e08SAugustin Cavalier /*
431753c7e08SAugustin Cavalier * Fastpath for A-MPDU reorder q resubmission. Frames
432753c7e08SAugustin Cavalier * w/ M_AMPDU_MPDU marked have already passed through
433753c7e08SAugustin Cavalier * here but were received out of order and been held on
434753c7e08SAugustin Cavalier * the reorder queue. When resubmitted they are marked
435753c7e08SAugustin Cavalier * with the M_AMPDU_MPDU flag and we can bypass most of
436753c7e08SAugustin Cavalier * the normal processing.
437753c7e08SAugustin Cavalier */
438753c7e08SAugustin Cavalier wh = mtod(m, struct ieee80211_frame *);
439753c7e08SAugustin Cavalier type = IEEE80211_FC0_TYPE_DATA;
440753c7e08SAugustin Cavalier dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
44186021fd4SAugustin Cavalier subtype = IEEE80211_FC0_SUBTYPE_QOS_DATA;
442753c7e08SAugustin Cavalier hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
443753c7e08SAugustin Cavalier goto resubmit_ampdu;
444753c7e08SAugustin Cavalier }
445753c7e08SAugustin Cavalier
446753c7e08SAugustin Cavalier KASSERT(ni != NULL, ("null node"));
447753c7e08SAugustin Cavalier
448753c7e08SAugustin Cavalier type = -1; /* undefined */
449753c7e08SAugustin Cavalier
450753c7e08SAugustin Cavalier if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
451753c7e08SAugustin Cavalier IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
452753c7e08SAugustin Cavalier ni->ni_macaddr, NULL,
453753c7e08SAugustin Cavalier "too short (1): len %u", m->m_pkthdr.len);
454753c7e08SAugustin Cavalier vap->iv_stats.is_rx_tooshort++;
455753c7e08SAugustin Cavalier goto out;
456753c7e08SAugustin Cavalier }
457753c7e08SAugustin Cavalier /*
458753c7e08SAugustin Cavalier * Bit of a cheat here, we use a pointer for a 3-address
459753c7e08SAugustin Cavalier * frame format but don't reference fields past outside
460753c7e08SAugustin Cavalier * ieee80211_frame_min w/o first validating the data is
461753c7e08SAugustin Cavalier * present.
462753c7e08SAugustin Cavalier */
463753c7e08SAugustin Cavalier wh = mtod(m, struct ieee80211_frame *);
464753c7e08SAugustin Cavalier
465753c7e08SAugustin Cavalier if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
466753c7e08SAugustin Cavalier ni->ni_inact = ni->ni_inact_reload;
467753c7e08SAugustin Cavalier
468*da3ca318SAugustin Cavalier if (!IEEE80211_IS_FC0_CHECK_VER(wh, IEEE80211_FC0_VERSION_0)) {
469753c7e08SAugustin Cavalier IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
470753c7e08SAugustin Cavalier ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x",
471753c7e08SAugustin Cavalier wh->i_fc[0], wh->i_fc[1]);
472753c7e08SAugustin Cavalier vap->iv_stats.is_rx_badversion++;
473753c7e08SAugustin Cavalier goto err;
474753c7e08SAugustin Cavalier }
475753c7e08SAugustin Cavalier
476753c7e08SAugustin Cavalier dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
477753c7e08SAugustin Cavalier type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
478753c7e08SAugustin Cavalier subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
479753c7e08SAugustin Cavalier
480753c7e08SAugustin Cavalier /* NB: WDS vap's do not scan */
481753c7e08SAugustin Cavalier if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_addr4)) {
482753c7e08SAugustin Cavalier IEEE80211_DISCARD_MAC(vap,
483753c7e08SAugustin Cavalier IEEE80211_MSG_ANY, ni->ni_macaddr, NULL,
484753c7e08SAugustin Cavalier "too short (3): len %u", m->m_pkthdr.len);
485753c7e08SAugustin Cavalier vap->iv_stats.is_rx_tooshort++;
486753c7e08SAugustin Cavalier goto out;
487753c7e08SAugustin Cavalier }
488753c7e08SAugustin Cavalier /* NB: the TA is implicitly verified by finding the wds peer node */
489753c7e08SAugustin Cavalier if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr) &&
490753c7e08SAugustin Cavalier !IEEE80211_ADDR_EQ(wh->i_addr1, ifp->if_broadcastaddr)) {
491753c7e08SAugustin Cavalier /* not interested in */
492753c7e08SAugustin Cavalier IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
493753c7e08SAugustin Cavalier wh->i_addr1, NULL, "%s", "not to bss");
494753c7e08SAugustin Cavalier vap->iv_stats.is_rx_wrongbss++;
495753c7e08SAugustin Cavalier goto out;
496753c7e08SAugustin Cavalier }
497753c7e08SAugustin Cavalier IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
498753c7e08SAugustin Cavalier ni->ni_noise = nf;
499753c7e08SAugustin Cavalier if (IEEE80211_HAS_SEQ(type, subtype)) {
500753c7e08SAugustin Cavalier uint8_t tid = ieee80211_gettid(wh);
501753c7e08SAugustin Cavalier if (IEEE80211_QOS_HAS_SEQ(wh) &&
502753c7e08SAugustin Cavalier TID_TO_WME_AC(tid) >= WME_AC_VI)
503753c7e08SAugustin Cavalier ic->ic_wme.wme_hipri_traffic++;
5048244a9baSAugustin Cavalier if (! ieee80211_check_rxseq(ni, wh, wh->i_addr1, rxs))
505753c7e08SAugustin Cavalier goto out;
506753c7e08SAugustin Cavalier }
507753c7e08SAugustin Cavalier switch (type) {
508753c7e08SAugustin Cavalier case IEEE80211_FC0_TYPE_DATA:
509753c7e08SAugustin Cavalier hdrspace = ieee80211_hdrspace(ic, wh);
510753c7e08SAugustin Cavalier if (m->m_len < hdrspace &&
511753c7e08SAugustin Cavalier (m = m_pullup(m, hdrspace)) == NULL) {
512753c7e08SAugustin Cavalier IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
513753c7e08SAugustin Cavalier ni->ni_macaddr, NULL,
514753c7e08SAugustin Cavalier "data too short: expecting %u", hdrspace);
515753c7e08SAugustin Cavalier vap->iv_stats.is_rx_tooshort++;
516753c7e08SAugustin Cavalier goto out; /* XXX */
517753c7e08SAugustin Cavalier }
518753c7e08SAugustin Cavalier if (dir != IEEE80211_FC1_DIR_DSTODS) {
519753c7e08SAugustin Cavalier IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
520753c7e08SAugustin Cavalier wh, "data", "incorrect dir 0x%x", dir);
521753c7e08SAugustin Cavalier vap->iv_stats.is_rx_wrongdir++;
522753c7e08SAugustin Cavalier goto out;
523753c7e08SAugustin Cavalier }
524753c7e08SAugustin Cavalier /*
525753c7e08SAugustin Cavalier * Only legacy WDS traffic should take this path.
526753c7e08SAugustin Cavalier */
527753c7e08SAugustin Cavalier if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) {
528753c7e08SAugustin Cavalier IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
529753c7e08SAugustin Cavalier wh, "data", "%s", "not legacy wds");
530753c7e08SAugustin Cavalier vap->iv_stats.is_rx_wrongdir++;/*XXX*/
531753c7e08SAugustin Cavalier goto out;
532753c7e08SAugustin Cavalier }
533753c7e08SAugustin Cavalier /*
534753c7e08SAugustin Cavalier * Handle A-MPDU re-ordering. If the frame is to be
535753c7e08SAugustin Cavalier * processed directly then ieee80211_ampdu_reorder
536753c7e08SAugustin Cavalier * will return 0; otherwise it has consumed the mbuf
537753c7e08SAugustin Cavalier * and we should do nothing more with it.
538753c7e08SAugustin Cavalier */
539753c7e08SAugustin Cavalier if ((m->m_flags & M_AMPDU) &&
5408244a9baSAugustin Cavalier ieee80211_ampdu_reorder(ni, m, rxs) != 0) {
541753c7e08SAugustin Cavalier m = NULL;
542753c7e08SAugustin Cavalier goto out;
543753c7e08SAugustin Cavalier }
544753c7e08SAugustin Cavalier resubmit_ampdu:
545753c7e08SAugustin Cavalier
546753c7e08SAugustin Cavalier /*
547753c7e08SAugustin Cavalier * Handle privacy requirements. Note that we
548753c7e08SAugustin Cavalier * must not be preempted from here until after
549753c7e08SAugustin Cavalier * we (potentially) call ieee80211_crypto_demic;
550753c7e08SAugustin Cavalier * otherwise we may violate assumptions in the
551753c7e08SAugustin Cavalier * crypto cipher modules used to do delayed update
552753c7e08SAugustin Cavalier * of replay sequence numbers.
553753c7e08SAugustin Cavalier */
55486021fd4SAugustin Cavalier if (is_hw_decrypted || IEEE80211_IS_PROTECTED(wh)) {
555753c7e08SAugustin Cavalier if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
556753c7e08SAugustin Cavalier /*
557753c7e08SAugustin Cavalier * Discard encrypted frames when privacy is off.
558753c7e08SAugustin Cavalier */
559753c7e08SAugustin Cavalier IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
560753c7e08SAugustin Cavalier wh, "WEP", "%s", "PRIVACY off");
561753c7e08SAugustin Cavalier vap->iv_stats.is_rx_noprivacy++;
562753c7e08SAugustin Cavalier IEEE80211_NODE_STAT(ni, rx_noprivacy);
563753c7e08SAugustin Cavalier goto out;
564753c7e08SAugustin Cavalier }
5658244a9baSAugustin Cavalier if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) {
566753c7e08SAugustin Cavalier /* NB: stats+msgs handled in crypto_decap */
567753c7e08SAugustin Cavalier IEEE80211_NODE_STAT(ni, rx_wepfail);
568753c7e08SAugustin Cavalier goto out;
569753c7e08SAugustin Cavalier }
570753c7e08SAugustin Cavalier wh = mtod(m, struct ieee80211_frame *);
571753c7e08SAugustin Cavalier wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
5728244a9baSAugustin Cavalier has_decrypted = 1;
573753c7e08SAugustin Cavalier } else {
574753c7e08SAugustin Cavalier /* XXX M_WEP and IEEE80211_F_PRIVACY */
575753c7e08SAugustin Cavalier key = NULL;
576753c7e08SAugustin Cavalier }
577753c7e08SAugustin Cavalier
578753c7e08SAugustin Cavalier /*
579753c7e08SAugustin Cavalier * Save QoS bits for use below--before we strip the header.
580753c7e08SAugustin Cavalier */
58186021fd4SAugustin Cavalier if (subtype == IEEE80211_FC0_SUBTYPE_QOS_DATA)
5825cad57c4SJérôme Duval qos = ieee80211_getqos(wh)[0];
5835cad57c4SJérôme Duval else
584753c7e08SAugustin Cavalier qos = 0;
585753c7e08SAugustin Cavalier
586753c7e08SAugustin Cavalier /*
587753c7e08SAugustin Cavalier * Next up, any fragmentation.
588753c7e08SAugustin Cavalier */
589753c7e08SAugustin Cavalier if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
59086021fd4SAugustin Cavalier m = ieee80211_defrag(ni, m, hdrspace, has_decrypted);
591753c7e08SAugustin Cavalier if (m == NULL) {
592753c7e08SAugustin Cavalier /* Fragment dropped or frame not complete yet */
593753c7e08SAugustin Cavalier goto out;
594753c7e08SAugustin Cavalier }
595753c7e08SAugustin Cavalier }
596753c7e08SAugustin Cavalier wh = NULL; /* no longer valid, catch any uses */
597753c7e08SAugustin Cavalier
598753c7e08SAugustin Cavalier /*
599753c7e08SAugustin Cavalier * Next strip any MSDU crypto bits.
600753c7e08SAugustin Cavalier */
6018244a9baSAugustin Cavalier if (!ieee80211_crypto_demic(vap, key, m, 0)) {
602753c7e08SAugustin Cavalier IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
603753c7e08SAugustin Cavalier ni->ni_macaddr, "data", "%s", "demic error");
604753c7e08SAugustin Cavalier vap->iv_stats.is_rx_demicfail++;
605753c7e08SAugustin Cavalier IEEE80211_NODE_STAT(ni, rx_demicfail);
606753c7e08SAugustin Cavalier goto out;
607753c7e08SAugustin Cavalier }
608753c7e08SAugustin Cavalier
609753c7e08SAugustin Cavalier /* copy to listener after decrypt */
610753c7e08SAugustin Cavalier if (ieee80211_radiotap_active_vap(vap))
611753c7e08SAugustin Cavalier ieee80211_radiotap_rx(vap, m);
612753c7e08SAugustin Cavalier need_tap = 0;
613753c7e08SAugustin Cavalier
614753c7e08SAugustin Cavalier /*
615753c7e08SAugustin Cavalier * Finally, strip the 802.11 header.
616753c7e08SAugustin Cavalier */
61786021fd4SAugustin Cavalier m = ieee80211_decap(vap, m, hdrspace, qos);
618753c7e08SAugustin Cavalier if (m == NULL) {
619753c7e08SAugustin Cavalier /* XXX mask bit to check for both */
620753c7e08SAugustin Cavalier /* don't count Null data frames as errors */
621753c7e08SAugustin Cavalier if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
622753c7e08SAugustin Cavalier subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
623753c7e08SAugustin Cavalier goto out;
624753c7e08SAugustin Cavalier IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
625753c7e08SAugustin Cavalier ni->ni_macaddr, "data", "%s", "decap error");
626753c7e08SAugustin Cavalier vap->iv_stats.is_rx_decap++;
627753c7e08SAugustin Cavalier IEEE80211_NODE_STAT(ni, rx_decap);
628753c7e08SAugustin Cavalier goto err;
629753c7e08SAugustin Cavalier }
63086021fd4SAugustin Cavalier if (!(qos & IEEE80211_QOS_AMSDU))
631753c7e08SAugustin Cavalier eh = mtod(m, struct ether_header *);
63286021fd4SAugustin Cavalier else
63386021fd4SAugustin Cavalier eh = NULL;
634753c7e08SAugustin Cavalier if (!ieee80211_node_is_authorized(ni)) {
635753c7e08SAugustin Cavalier /*
636753c7e08SAugustin Cavalier * Deny any non-PAE frames received prior to
637753c7e08SAugustin Cavalier * authorization. For open/shared-key
638753c7e08SAugustin Cavalier * authentication the port is mark authorized
639753c7e08SAugustin Cavalier * after authentication completes. For 802.1x
640753c7e08SAugustin Cavalier * the port is not marked authorized by the
641753c7e08SAugustin Cavalier * authenticator until the handshake has completed.
642753c7e08SAugustin Cavalier */
64386021fd4SAugustin Cavalier if (eh == NULL ||
64486021fd4SAugustin Cavalier eh->ether_type != htons(ETHERTYPE_PAE)) {
645753c7e08SAugustin Cavalier IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
64686021fd4SAugustin Cavalier ni->ni_macaddr, "data", "unauthorized or "
64786021fd4SAugustin Cavalier "unknown port: ether type 0x%x len %u",
64886021fd4SAugustin Cavalier eh == NULL ? -1 : eh->ether_type,
64986021fd4SAugustin Cavalier m->m_pkthdr.len);
650753c7e08SAugustin Cavalier vap->iv_stats.is_rx_unauth++;
651753c7e08SAugustin Cavalier IEEE80211_NODE_STAT(ni, rx_unauth);
652753c7e08SAugustin Cavalier goto err;
653753c7e08SAugustin Cavalier }
654753c7e08SAugustin Cavalier } else {
655753c7e08SAugustin Cavalier /*
656753c7e08SAugustin Cavalier * When denying unencrypted frames, discard
657753c7e08SAugustin Cavalier * any non-PAE frames received without encryption.
658753c7e08SAugustin Cavalier */
659753c7e08SAugustin Cavalier if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
6608244a9baSAugustin Cavalier ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) &&
6618244a9baSAugustin Cavalier (is_hw_decrypted == 0) &&
66286021fd4SAugustin Cavalier (eh == NULL ||
66386021fd4SAugustin Cavalier eh->ether_type != htons(ETHERTYPE_PAE))) {
664753c7e08SAugustin Cavalier /*
665753c7e08SAugustin Cavalier * Drop unencrypted frames.
666753c7e08SAugustin Cavalier */
667753c7e08SAugustin Cavalier vap->iv_stats.is_rx_unencrypted++;
668753c7e08SAugustin Cavalier IEEE80211_NODE_STAT(ni, rx_unencrypted);
669753c7e08SAugustin Cavalier goto out;
670753c7e08SAugustin Cavalier }
671753c7e08SAugustin Cavalier }
672753c7e08SAugustin Cavalier /* XXX require HT? */
673753c7e08SAugustin Cavalier if (qos & IEEE80211_QOS_AMSDU) {
674753c7e08SAugustin Cavalier m = ieee80211_decap_amsdu(ni, m);
675753c7e08SAugustin Cavalier if (m == NULL)
676753c7e08SAugustin Cavalier return IEEE80211_FC0_TYPE_DATA;
677753c7e08SAugustin Cavalier } else {
678753c7e08SAugustin Cavalier #ifdef IEEE80211_SUPPORT_SUPERG
679753c7e08SAugustin Cavalier m = ieee80211_decap_fastframe(vap, ni, m);
680753c7e08SAugustin Cavalier if (m == NULL)
681753c7e08SAugustin Cavalier return IEEE80211_FC0_TYPE_DATA;
682753c7e08SAugustin Cavalier #endif
683753c7e08SAugustin Cavalier }
684753c7e08SAugustin Cavalier ieee80211_deliver_data(vap, ni, m);
685753c7e08SAugustin Cavalier return IEEE80211_FC0_TYPE_DATA;
686753c7e08SAugustin Cavalier
687753c7e08SAugustin Cavalier case IEEE80211_FC0_TYPE_MGT:
688753c7e08SAugustin Cavalier vap->iv_stats.is_rx_mgmt++;
689753c7e08SAugustin Cavalier IEEE80211_NODE_STAT(ni, rx_mgmt);
690753c7e08SAugustin Cavalier if (dir != IEEE80211_FC1_DIR_NODS) {
691753c7e08SAugustin Cavalier IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
692753c7e08SAugustin Cavalier wh, "data", "incorrect dir 0x%x", dir);
693753c7e08SAugustin Cavalier vap->iv_stats.is_rx_wrongdir++;
694753c7e08SAugustin Cavalier goto err;
695753c7e08SAugustin Cavalier }
696753c7e08SAugustin Cavalier if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
697753c7e08SAugustin Cavalier IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
698753c7e08SAugustin Cavalier ni->ni_macaddr, "mgt", "too short: len %u",
699753c7e08SAugustin Cavalier m->m_pkthdr.len);
700753c7e08SAugustin Cavalier vap->iv_stats.is_rx_tooshort++;
701753c7e08SAugustin Cavalier goto out;
702753c7e08SAugustin Cavalier }
703753c7e08SAugustin Cavalier #ifdef IEEE80211_DEBUG
704753c7e08SAugustin Cavalier if (ieee80211_msg_debug(vap) || ieee80211_msg_dumppkts(vap)) {
705753c7e08SAugustin Cavalier if_printf(ifp, "received %s from %s rssi %d\n",
706753c7e08SAugustin Cavalier ieee80211_mgt_subtype_name(subtype),
707753c7e08SAugustin Cavalier ether_sprintf(wh->i_addr2), rssi);
708753c7e08SAugustin Cavalier }
709753c7e08SAugustin Cavalier #endif
71086021fd4SAugustin Cavalier if (IEEE80211_IS_PROTECTED(wh)) {
711753c7e08SAugustin Cavalier IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
712753c7e08SAugustin Cavalier wh, NULL, "%s", "WEP set but not permitted");
713753c7e08SAugustin Cavalier vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
714753c7e08SAugustin Cavalier goto out;
715753c7e08SAugustin Cavalier }
716753c7e08SAugustin Cavalier vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
717753c7e08SAugustin Cavalier goto out;
718753c7e08SAugustin Cavalier
719753c7e08SAugustin Cavalier case IEEE80211_FC0_TYPE_CTL:
720753c7e08SAugustin Cavalier vap->iv_stats.is_rx_ctl++;
721753c7e08SAugustin Cavalier IEEE80211_NODE_STAT(ni, rx_ctrl);
722753c7e08SAugustin Cavalier goto out;
723753c7e08SAugustin Cavalier
724753c7e08SAugustin Cavalier default:
725753c7e08SAugustin Cavalier IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
726753c7e08SAugustin Cavalier wh, "bad", "frame type 0x%x", type);
727753c7e08SAugustin Cavalier /* should not come here */
728753c7e08SAugustin Cavalier break;
729753c7e08SAugustin Cavalier }
730753c7e08SAugustin Cavalier err:
731753c7e08SAugustin Cavalier if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
732753c7e08SAugustin Cavalier out:
733753c7e08SAugustin Cavalier if (m != NULL) {
734753c7e08SAugustin Cavalier if (need_tap && ieee80211_radiotap_active_vap(vap))
735753c7e08SAugustin Cavalier ieee80211_radiotap_rx(vap, m);
736753c7e08SAugustin Cavalier m_freem(m);
737753c7e08SAugustin Cavalier }
738753c7e08SAugustin Cavalier return type;
739753c7e08SAugustin Cavalier }
740753c7e08SAugustin Cavalier
741753c7e08SAugustin Cavalier static void
wds_recv_mgmt(struct ieee80211_node * ni,struct mbuf * m0,int subtype,const struct ieee80211_rx_stats * rxs,int rssi,int nf)742753c7e08SAugustin Cavalier wds_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
743753c7e08SAugustin Cavalier const struct ieee80211_rx_stats *rxs, int rssi, int nf)
744753c7e08SAugustin Cavalier {
745753c7e08SAugustin Cavalier struct ieee80211vap *vap = ni->ni_vap;
746753c7e08SAugustin Cavalier struct ieee80211com *ic = ni->ni_ic;
747753c7e08SAugustin Cavalier struct ieee80211_frame *wh;
748753c7e08SAugustin Cavalier u_int8_t *frm, *efrm;
749753c7e08SAugustin Cavalier
750753c7e08SAugustin Cavalier wh = mtod(m0, struct ieee80211_frame *);
751753c7e08SAugustin Cavalier frm = (u_int8_t *)&wh[1];
752753c7e08SAugustin Cavalier efrm = mtod(m0, u_int8_t *) + m0->m_len;
753753c7e08SAugustin Cavalier switch (subtype) {
754753c7e08SAugustin Cavalier case IEEE80211_FC0_SUBTYPE_ACTION:
755753c7e08SAugustin Cavalier case IEEE80211_FC0_SUBTYPE_ACTION_NOACK:
756753c7e08SAugustin Cavalier if (ni == vap->iv_bss) {
757753c7e08SAugustin Cavalier IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
758753c7e08SAugustin Cavalier wh, NULL, "%s", "unknown node");
759753c7e08SAugustin Cavalier vap->iv_stats.is_rx_mgtdiscard++;
760753c7e08SAugustin Cavalier } else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1)) {
761753c7e08SAugustin Cavalier /* NB: not interested in multicast frames. */
762753c7e08SAugustin Cavalier IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
763753c7e08SAugustin Cavalier wh, NULL, "%s", "not for us");
764753c7e08SAugustin Cavalier vap->iv_stats.is_rx_mgtdiscard++;
765753c7e08SAugustin Cavalier } else if (vap->iv_state != IEEE80211_S_RUN) {
766753c7e08SAugustin Cavalier IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
767753c7e08SAugustin Cavalier wh, NULL, "wrong state %s",
768753c7e08SAugustin Cavalier ieee80211_state_name[vap->iv_state]);
769753c7e08SAugustin Cavalier vap->iv_stats.is_rx_mgtdiscard++;
770753c7e08SAugustin Cavalier } else {
771753c7e08SAugustin Cavalier if (ieee80211_parse_action(ni, m0) == 0)
772753c7e08SAugustin Cavalier (void)ic->ic_recv_action(ni, wh, frm, efrm);
773753c7e08SAugustin Cavalier }
774753c7e08SAugustin Cavalier break;
775753c7e08SAugustin Cavalier
776753c7e08SAugustin Cavalier case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
777753c7e08SAugustin Cavalier case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
778753c7e08SAugustin Cavalier case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
779753c7e08SAugustin Cavalier case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
780753c7e08SAugustin Cavalier case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
781753c7e08SAugustin Cavalier case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
782753c7e08SAugustin Cavalier case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
783753c7e08SAugustin Cavalier case IEEE80211_FC0_SUBTYPE_BEACON:
784753c7e08SAugustin Cavalier case IEEE80211_FC0_SUBTYPE_ATIM:
785753c7e08SAugustin Cavalier case IEEE80211_FC0_SUBTYPE_DISASSOC:
786753c7e08SAugustin Cavalier case IEEE80211_FC0_SUBTYPE_AUTH:
787753c7e08SAugustin Cavalier case IEEE80211_FC0_SUBTYPE_DEAUTH:
788753c7e08SAugustin Cavalier IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
789753c7e08SAugustin Cavalier wh, NULL, "%s", "not handled");
790753c7e08SAugustin Cavalier vap->iv_stats.is_rx_mgtdiscard++;
791753c7e08SAugustin Cavalier break;
792753c7e08SAugustin Cavalier
793753c7e08SAugustin Cavalier default:
794753c7e08SAugustin Cavalier IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
795753c7e08SAugustin Cavalier wh, "mgt", "subtype 0x%x not handled", subtype);
796753c7e08SAugustin Cavalier vap->iv_stats.is_rx_badsubtype++;
797753c7e08SAugustin Cavalier break;
798753c7e08SAugustin Cavalier }
799753c7e08SAugustin Cavalier }
800