ieee80211_wds.c revision 283855
118334Speter/*-
2132718Skan * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
3169689Skan * All rights reserved.
418334Speter *
590075Sobrien * Redistribution and use in source and binary forms, with or without
618334Speter * modification, are permitted provided that the following conditions
790075Sobrien * are met:
890075Sobrien * 1. Redistributions of source code must retain the above copyright
990075Sobrien *    notice, this list of conditions and the following disclaimer.
1090075Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1118334Speter *    notice, this list of conditions and the following disclaimer in the
1290075Sobrien *    documentation and/or other materials provided with the distribution.
1390075Sobrien *
1490075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1590075Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1618334Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1718334Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1890075Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19169689Skan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20169689Skan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2118334Speter * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2218334Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2318334Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2418334Speter */
2518334Speter
2690075Sobrien#include <sys/cdefs.h>
2790075Sobrien#ifdef __FreeBSD__
2818334Speter__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_wds.c 283855 2015-05-31 23:29:04Z ae $");
2990075Sobrien#endif
3090075Sobrien
3118334Speter/*
3290075Sobrien * IEEE 802.11 WDS mode support.
3390075Sobrien */
3418334Speter#include "opt_inet.h"
3590075Sobrien#include "opt_wlan.h"
3690075Sobrien
3718334Speter#include <sys/param.h>
3890075Sobrien#include <sys/systm.h>
3990075Sobrien#include <sys/mbuf.h>
4018334Speter#include <sys/malloc.h>
4190075Sobrien#include <sys/kernel.h>
4290075Sobrien
4390075Sobrien#include <sys/socket.h>
4418334Speter#include <sys/sockio.h>
4590075Sobrien#include <sys/endian.h>
4690075Sobrien#include <sys/errno.h>
4718334Speter#include <sys/proc.h>
4890075Sobrien#include <sys/sysctl.h>
4918334Speter
5090075Sobrien#include <net/if.h>
5118334Speter#include <net/if_media.h>
5290075Sobrien#include <net/if_llc.h>
5318334Speter#include <net/ethernet.h>
5490075Sobrien
5590075Sobrien#include <net/bpf.h>
5618334Speter
57169689Skan#include <net80211/ieee80211_var.h>
5818334Speter#include <net80211/ieee80211_wds.h>
5990075Sobrien#include <net80211/ieee80211_input.h>
60169689Skan#ifdef IEEE80211_SUPPORT_SUPERG
6118334Speter#include <net80211/ieee80211_superg.h>
6290075Sobrien#endif
6390075Sobrien
6490075Sobrienstatic void wds_vattach(struct ieee80211vap *);
6590075Sobrienstatic int wds_newstate(struct ieee80211vap *, enum ieee80211_state, int);
6690075Sobrienstatic	int wds_input(struct ieee80211_node *ni, struct mbuf *m, int, int);
6790075Sobrienstatic void wds_recv_mgmt(struct ieee80211_node *, struct mbuf *,
6890075Sobrien	    int subtype, int, int);
6990075Sobrien
7090075Sobrienvoid
7190075Sobrienieee80211_wds_attach(struct ieee80211com *ic)
7290075Sobrien{
7390075Sobrien	ic->ic_vattach[IEEE80211_M_WDS] = wds_vattach;
7490075Sobrien}
7590075Sobrien
7618334Spetervoid
7718334Speterieee80211_wds_detach(struct ieee80211com *ic)
7818334Speter{
7918334Speter}
8018334Speter
8118334Speterstatic void
8290075Sobrienwds_vdetach(struct ieee80211vap *vap)
8390075Sobrien{
8490075Sobrien	if (vap->iv_bss != NULL) {
8518334Speter		/* XXX locking? */
86132718Skan		if (vap->iv_bss->ni_wdsvap == vap)
8750397Sobrien			vap->iv_bss->ni_wdsvap = NULL;
88132718Skan	}
89132718Skan}
9018334Speter
9190075Sobrienstatic void
9290075Sobrienwds_vattach(struct ieee80211vap *vap)
9318334Speter{
9490075Sobrien	vap->iv_newstate = wds_newstate;
9590075Sobrien	vap->iv_input = wds_input;
9690075Sobrien	vap->iv_recv_mgmt = wds_recv_mgmt;
9718334Speter	vap->iv_opdetach = wds_vdetach;
9818334Speter}
9918334Speter
100132718Skanstatic void
101132718Skanwds_flush(struct ieee80211_node *ni)
10218334Speter{
10318334Speter	struct ieee80211com *ic = ni->ni_ic;
10418334Speter	struct mbuf *m, *next;
10518334Speter	int8_t rssi, nf;
10618334Speter
10718334Speter	m = ieee80211_ageq_remove(&ic->ic_stageq,
10818334Speter	    (void *)(uintptr_t) ieee80211_mac_hash(ic, ni->ni_macaddr));
10918334Speter	if (m == NULL)
11018334Speter		return;
11118334Speter
11218334Speter	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_WDS, ni,
11318334Speter	    "%s", "flush wds queue");
11490075Sobrien	ic->ic_node_getsignal(ni, &rssi, &nf);
11590075Sobrien	for (; m != NULL; m = next) {
11690075Sobrien		next = m->m_nextpkt;
11790075Sobrien		m->m_nextpkt = NULL;
11890075Sobrien		ieee80211_input(ni, m, rssi, nf);
11990075Sobrien	}
12090075Sobrien}
12190075Sobrien
12290075Sobrienstatic int
12390075Sobrienieee80211_create_wds(struct ieee80211vap *vap, struct ieee80211_channel *chan)
12490075Sobrien{
12590075Sobrien	struct ieee80211com *ic = vap->iv_ic;
12690075Sobrien	struct ieee80211_node_table *nt = &ic->ic_sta;
12790075Sobrien	struct ieee80211_node *ni, *obss;
12890075Sobrien
12990075Sobrien	IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
13090075Sobrien	     "%s: creating link to %s on channel %u\n", __func__,
13190075Sobrien	     ether_sprintf(vap->iv_des_bssid), ieee80211_chan2ieee(ic, chan));
13290075Sobrien
13390075Sobrien	/* NB: vap create must specify the bssid for the link */
13490075Sobrien	KASSERT(vap->iv_flags & IEEE80211_F_DESBSSID, ("no bssid"));
13590075Sobrien	/* NB: we should only be called on RUN transition */
13690075Sobrien	KASSERT(vap->iv_state == IEEE80211_S_RUN, ("!RUN state"));
13790075Sobrien
13890075Sobrien	if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) {
13990075Sobrien		/*
14090075Sobrien		 * Dynamic/non-legacy WDS.  Reference the associated
14190075Sobrien		 * station specified by the desired bssid setup at vap
14290075Sobrien		 * create.  Point ni_wdsvap at the WDS vap so 4-address
14390075Sobrien		 * frames received through the associated AP vap will
14490075Sobrien		 * be dispatched upward (e.g. to a bridge) as though
14590075Sobrien		 * they arrived on the WDS vap.
14690075Sobrien		 */
14790075Sobrien		IEEE80211_NODE_LOCK(nt);
14890075Sobrien		obss = NULL;
14990075Sobrien		ni = ieee80211_find_node_locked(&ic->ic_sta, vap->iv_des_bssid);
15090075Sobrien		if (ni == NULL) {
15118334Speter			/*
15218334Speter			 * Node went away before we could hookup.  This
15318334Speter			 * should be ok; no traffic will flow and a leave
15418334Speter			 * event will be dispatched that should cause
15518334Speter			 * the vap to be destroyed.
15690075Sobrien			 */
15790075Sobrien			IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
15890075Sobrien			    "%s: station %s went away\n",
15918334Speter			    __func__, ether_sprintf(vap->iv_des_bssid));
16018334Speter			/* XXX stat? */
161169689Skan		} else if (ni->ni_wdsvap != NULL) {
16290075Sobrien			/*
16318334Speter			 * Node already setup with a WDS vap; we cannot
16418334Speter			 * allow multiple references so disallow.  If
16518334Speter			 * ni_wdsvap points at us that's ok; we should
16690075Sobrien			 * do nothing anyway.
16790075Sobrien			 */
16890075Sobrien			/* XXX printf instead? */
16918334Speter			IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
17018334Speter			    "%s: station %s in use with %s\n",
17190075Sobrien			    __func__, ether_sprintf(vap->iv_des_bssid),
17218334Speter			    ni->ni_wdsvap->iv_ifp->if_xname);
17390075Sobrien			/* XXX stat? */
17418334Speter		} else {
175132718Skan			/*
176132718Skan			 * Committed to new node, setup state.
177132718Skan			 */
178132718Skan			obss = vap->iv_bss;
179132718Skan			vap->iv_bss = ni;
180132718Skan			ni->ni_wdsvap = vap;
181132718Skan		}
182132718Skan		IEEE80211_NODE_UNLOCK(nt);
183132718Skan		if (obss != NULL) {
184132718Skan			/* NB: deferred to avoid recursive lock */
185132718Skan			ieee80211_free_node(obss);
186132718Skan		}
187132718Skan	} else {
188132718Skan		/*
189132718Skan		 * Legacy WDS vap setup.
190169689Skan		 */
191169689Skan		/*
192169689Skan		 * The far end does not associate so we just create
193169689Skan		 * create a new node and install it as the vap's
19452284Sobrien		 * bss node.  We must simulate an association and
195169689Skan		 * authorize the port for traffic to flow.
196169689Skan		 * XXX check if node already in sta table?
197169689Skan		 */
198169689Skan		ni = ieee80211_node_create_wds(vap, vap->iv_des_bssid, chan);
199169689Skan		if (ni != NULL) {
20052284Sobrien			obss = vap->iv_bss;
201169689Skan			vap->iv_bss = ieee80211_ref_node(ni);
202169689Skan			ni->ni_flags |= IEEE80211_NODE_AREF;
203169689Skan			if (obss != NULL)
204169689Skan				ieee80211_free_node(obss);
20552284Sobrien			/* give driver a chance to setup state like ni_txrate */
206169689Skan			if (ic->ic_newassoc != NULL)
207169689Skan				ic->ic_newassoc(ni, 1);
20852284Sobrien			/* tell the authenticator about new station */
209169689Skan			if (vap->iv_auth->ia_node_join != NULL)
210169689Skan				vap->iv_auth->ia_node_join(ni);
21152284Sobrien			if (ni->ni_authmode != IEEE80211_AUTH_8021X)
212169689Skan				ieee80211_node_authorize(ni);
213169689Skan
214169689Skan			ieee80211_notify_node_join(ni, 1 /*newassoc*/);
215169689Skan			/* XXX inject l2uf frame */
216169689Skan		}
217169689Skan	}
218169689Skan
219169689Skan	/*
22018334Speter	 * Flush any pending frames now that were setup.
221132718Skan	 */
22218334Speter	if (ni != NULL)
22318334Speter		wds_flush(ni);
22490075Sobrien	return (ni == NULL ? ENOENT : 0);
22518334Speter}
22618334Speter
22750397Sobrien/*
228132718Skan * Propagate multicast frames of an ap vap to all DWDS links.
229132718Skan * The caller is assumed to have verified this frame is multicast.
23050397Sobrien */
23190075Sobrienvoid
23218334Speterieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m)
23390075Sobrien{
23490075Sobrien	struct ieee80211com *ic = vap0->iv_ic;
23590075Sobrien	const struct ether_header *eh = mtod(m, const struct ether_header *);
23690075Sobrien	struct ieee80211_node *ni;
23718334Speter	struct ieee80211vap *vap;
23818334Speter	struct ifnet *ifp;
23918334Speter	struct mbuf *mcopy;
24018334Speter	int err;
24118334Speter
24218334Speter	KASSERT(ETHER_IS_MULTICAST(eh->ether_dhost),
24318334Speter	    ("%s not mcast", ether_sprintf(eh->ether_dhost)));
24490075Sobrien
24518334Speter	/* XXX locking */
246132718Skan	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
247169689Skan		/* only DWDS vaps are interesting */
24818334Speter		if (vap->iv_opmode != IEEE80211_M_WDS ||
24918334Speter		    (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY))
25018334Speter			continue;
251132718Skan		/* if it came in this interface, don't send it back out */
25218334Speter		ifp = vap->iv_ifp;
25390075Sobrien		if (ifp == m->m_pkthdr.rcvif)
25418334Speter			continue;
25590075Sobrien		/*
25690075Sobrien		 * Duplicate the frame and send it.
25790075Sobrien		 */
25818334Speter		mcopy = m_copypacket(m, M_NOWAIT);
25990075Sobrien		if (mcopy == NULL) {
26018334Speter			ifp->if_oerrors++;
26190075Sobrien			/* XXX stat + msg */
26290075Sobrien			continue;
26390075Sobrien		}
26490075Sobrien		ni = ieee80211_find_txnode(vap, eh->ether_dhost);
26590075Sobrien		if (ni == NULL) {
26690075Sobrien			/* NB: ieee80211_find_txnode does stat+msg */
26790075Sobrien			ifp->if_oerrors++;
26890075Sobrien			m_freem(mcopy);
26990075Sobrien			continue;
27090075Sobrien		}
27190075Sobrien		/* calculate priority so drivers can find the tx queue */
27290075Sobrien		if (ieee80211_classify(ni, mcopy)) {
27318334Speter			IEEE80211_DISCARD_MAC(vap,
27490075Sobrien			    IEEE80211_MSG_OUTPUT | IEEE80211_MSG_WDS,
27590075Sobrien			    eh->ether_dhost, NULL,
27618334Speter			    "%s", "classification failure");
27790075Sobrien			vap->iv_stats.is_tx_classify++;
278132718Skan			ifp->if_oerrors++;
27990075Sobrien			m_freem(mcopy);
28090075Sobrien			ieee80211_free_node(ni);
28190075Sobrien			continue;
28290075Sobrien		}
28390075Sobrien
28490075Sobrien		BPF_MTAP(ifp, m);		/* 802.3 tx */
28590075Sobrien
28618334Speter		/*
28790075Sobrien		 * Encapsulate the packet in prep for transmission.
28890075Sobrien		 */
28918334Speter		mcopy = ieee80211_encap(vap, ni, mcopy);
29090075Sobrien		if (mcopy == NULL) {
29190075Sobrien			/* NB: stat+msg handled in ieee80211_encap */
29218334Speter			ieee80211_free_node(ni);
29318334Speter			continue;
294132718Skan		}
29590075Sobrien		mcopy->m_flags |= M_MCAST;
29618334Speter		mcopy->m_pkthdr.rcvif = (void *) ni;
29790075Sobrien
29890075Sobrien		err = ieee80211_parent_xmitpkt(ic, mcopy);
299169689Skan		if (err) {
30090075Sobrien			/* NB: IFQ_HANDOFF reclaims mbuf */
30118334Speter			ifp->if_oerrors++;
30290075Sobrien			ieee80211_free_node(ni);
30318334Speter		} else {
30490075Sobrien			ifp->if_opackets++;
30590075Sobrien			if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1);
30690075Sobrien			if_inc_counter(ifp, IFCOUNTER_OBYTES,
30790075Sobrien			    m->m_pkthdr.len);
30890075Sobrien		}
30990075Sobrien	}
31018334Speter}
31190075Sobrien
31290075Sobrien/*
31390075Sobrien * Handle DWDS discovery on receipt of a 4-address frame in
31490075Sobrien * ap mode.  Queue the frame and post an event for someone
31590075Sobrien * to plumb the necessary WDS vap for this station.  Frames
31618334Speter * received prior to the vap set running will then be reprocessed
31718334Speter * as if they were just received.
31890075Sobrien */
31918334Spetervoid
32090075Sobrienieee80211_dwds_discover(struct ieee80211_node *ni, struct mbuf *m)
32190075Sobrien{
32290075Sobrien	struct ieee80211com *ic = ni->ni_ic;
32390075Sobrien
32490075Sobrien	/*
32590075Sobrien	 * Save the frame with an aging interval 4 times
32690075Sobrien	 * the listen interval specified by the station.
32718334Speter	 * Frames that sit around too long are reclaimed
32818334Speter	 * using this information.
32990075Sobrien	 * XXX handle overflow?
33090075Sobrien	 * XXX per/vap beacon interval?
33190075Sobrien	 */
332132718Skan	m->m_pkthdr.rcvif = (void *)(uintptr_t)
333132718Skan	    ieee80211_mac_hash(ic, ni->ni_macaddr);
334132718Skan	(void) ieee80211_ageq_append(&ic->ic_stageq, m,
335132718Skan	    ((ni->ni_intval * ic->ic_lintval) << 2) / 1024);
336132718Skan	ieee80211_notify_wds_discover(ni);
33790075Sobrien}
33890075Sobrien
33918334Speter/*
34090075Sobrien * IEEE80211_M_WDS vap state machine handler.
34190075Sobrien */
342132718Skanstatic int
343132718Skanwds_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
344132718Skan{
345132718Skan	struct ieee80211com *ic = vap->iv_ic;
346132718Skan	struct ieee80211_node *ni;
347132718Skan	enum ieee80211_state ostate;
34890075Sobrien	int error;
34990075Sobrien
35018334Speter	IEEE80211_LOCK_ASSERT(ic);
35196263Sobrien
35296263Sobrien	ostate = vap->iv_state;
35396263Sobrien	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__,
35496263Sobrien		ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
35596263Sobrien	vap->iv_state = nstate;			/* state transition */
35696263Sobrien	callout_stop(&vap->iv_mgtsend);		/* XXX callout_drain */
35796263Sobrien	if (ostate != IEEE80211_S_SCAN)
35890075Sobrien		ieee80211_cancel_scan(vap);	/* background scan */
35990075Sobrien	ni = vap->iv_bss;			/* NB: no reference held */
36090075Sobrien	error = 0;
36190075Sobrien	switch (nstate) {
36218334Speter	case IEEE80211_S_INIT:
36390075Sobrien		switch (ostate) {
364132718Skan		case IEEE80211_S_SCAN:
365132718Skan			ieee80211_cancel_scan(vap);
366132718Skan			break;
367132718Skan		default:
368132718Skan			break;
36918334Speter		}
37090075Sobrien		if (ostate != IEEE80211_S_INIT) {
37190075Sobrien			/* NB: optimize INIT -> INIT case */
372132718Skan			ieee80211_reset_bss(vap);
373132718Skan		}
374132718Skan		break;
375132718Skan	case IEEE80211_S_SCAN:
376132718Skan		switch (ostate) {
377132718Skan		case IEEE80211_S_INIT:
37890075Sobrien			ieee80211_check_scan_current(vap);
379132718Skan			break;
380132718Skan		default:
381132718Skan			break;
382132718Skan		}
383132718Skan		break;
38490075Sobrien	case IEEE80211_S_RUN:
38590075Sobrien		if (ostate == IEEE80211_S_INIT) {
386169689Skan			/*
38790075Sobrien			 * Already have a channel; bypass the scan
38818334Speter			 * and startup immediately.
38990075Sobrien			 */
39090075Sobrien			error = ieee80211_create_wds(vap, ic->ic_curchan);
39190075Sobrien		}
39290075Sobrien		break;
39390075Sobrien	default:
39490075Sobrien		break;
39590075Sobrien	}
39690075Sobrien	return error;
39790075Sobrien}
39890075Sobrien
39990075Sobrien/*
40090075Sobrien * Process a received frame.  The node associated with the sender
40190075Sobrien * should be supplied.  If nothing was found in the node table then
40290075Sobrien * the caller is assumed to supply a reference to iv_bss instead.
40318334Speter * The RSSI and a timestamp are also supplied.  The RSSI data is used
40490075Sobrien * during AP scanning to select a AP to associate with; it can have
40590075Sobrien * any units so long as values have consistent units and higher values
406132718Skan * mean ``better signal''.  The receive timestamp is currently not used
40790075Sobrien * by the 802.11 layer.
40890075Sobrien */
409132718Skanstatic int
41090075Sobrienwds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
411132718Skan{
412132718Skan#define	HAS_SEQ(type)	((type & 0x4) == 0)
413132718Skan	struct ieee80211vap *vap = ni->ni_vap;
414132718Skan	struct ieee80211com *ic = ni->ni_ic;
41590075Sobrien	struct ifnet *ifp = vap->iv_ifp;
41690075Sobrien	struct ieee80211_frame *wh;
41790075Sobrien	struct ieee80211_key *key;
41818334Speter	struct ether_header *eh;
41990075Sobrien	int hdrspace, need_tap = 1;	/* mbuf need to be tapped. */
42090075Sobrien	uint8_t dir, type, subtype, qos;
42190075Sobrien	uint16_t rxseq;
42218334Speter
42318334Speter	if (m->m_flags & M_AMPDU_MPDU) {
42418334Speter		/*
42518334Speter		 * Fastpath for A-MPDU reorder q resubmission.  Frames
42618334Speter		 * w/ M_AMPDU_MPDU marked have already passed through
42718334Speter		 * here but were received out of order and been held on
42818334Speter		 * the reorder queue.  When resubmitted they are marked
42918334Speter		 * with the M_AMPDU_MPDU flag and we can bypass most of
430132718Skan		 * the normal processing.
431132718Skan		 */
43218334Speter		wh = mtod(m, struct ieee80211_frame *);
43390075Sobrien		type = IEEE80211_FC0_TYPE_DATA;
43490075Sobrien		dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
43518334Speter		subtype = IEEE80211_FC0_SUBTYPE_QOS;
43618334Speter		hdrspace = ieee80211_hdrspace(ic, wh);	/* XXX optimize? */
43718334Speter		goto resubmit_ampdu;
43818334Speter	}
43918334Speter
44018334Speter	KASSERT(ni != NULL, ("null node"));
44118334Speter
44218334Speter	type = -1;			/* undefined */
44318334Speter
44418334Speter	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
44518334Speter		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
44618334Speter		    ni->ni_macaddr, NULL,
44718334Speter		    "too short (1): len %u", m->m_pkthdr.len);
44890075Sobrien		vap->iv_stats.is_rx_tooshort++;
44990075Sobrien		goto out;
45090075Sobrien	}
45118334Speter	/*
45218334Speter	 * Bit of a cheat here, we use a pointer for a 3-address
45390075Sobrien	 * frame format but don't reference fields past outside
45418334Speter	 * ieee80211_frame_min w/o first validating the data is
45590075Sobrien	 * present.
45690075Sobrien	 */
45790075Sobrien	wh = mtod(m, struct ieee80211_frame *);
45818334Speter
45990075Sobrien	if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
46090075Sobrien		ni->ni_inact = ni->ni_inact_reload;
46190075Sobrien
46290075Sobrien	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
46390075Sobrien	    IEEE80211_FC0_VERSION_0) {
46490075Sobrien		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
46590075Sobrien		    ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x",
46690075Sobrien		    wh->i_fc[0], wh->i_fc[1]);
46790075Sobrien		vap->iv_stats.is_rx_badversion++;
46890075Sobrien		goto err;
46918334Speter	}
47018334Speter
47118334Speter	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
47218334Speter	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
47318334Speter	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
47418334Speter
47518334Speter	/* NB: WDS vap's do not scan */
47618334Speter	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_addr4)) {
47790075Sobrien		IEEE80211_DISCARD_MAC(vap,
47890075Sobrien		    IEEE80211_MSG_ANY, ni->ni_macaddr, NULL,
47990075Sobrien		    "too short (3): len %u", m->m_pkthdr.len);
48018334Speter		vap->iv_stats.is_rx_tooshort++;
48118334Speter		goto out;
48290075Sobrien	}
48318334Speter	/* NB: the TA is implicitly verified by finding the wds peer node */
48490075Sobrien	if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr) &&
48590075Sobrien	    !IEEE80211_ADDR_EQ(wh->i_addr1, ifp->if_broadcastaddr)) {
48690075Sobrien		/* not interested in */
48718334Speter		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
48890075Sobrien		    wh->i_addr1, NULL, "%s", "not to bss");
48990075Sobrien		vap->iv_stats.is_rx_wrongbss++;
49090075Sobrien		goto out;
49190075Sobrien	}
49290075Sobrien	IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
49390075Sobrien	ni->ni_noise = nf;
49490075Sobrien	if (HAS_SEQ(type)) {
49590075Sobrien		uint8_t tid = ieee80211_gettid(wh);
49690075Sobrien		if (IEEE80211_QOS_HAS_SEQ(wh) &&
49790075Sobrien		    TID_TO_WME_AC(tid) >= WME_AC_VI)
49818334Speter			ic->ic_wme.wme_hipri_traffic++;
49918334Speter		rxseq = le16toh(*(uint16_t *)wh->i_seq);
50018334Speter		if (! ieee80211_check_rxseq(ni, wh)) {
50118334Speter			/* duplicate, discard */
50218334Speter			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
50318334Speter			    wh->i_addr1, "duplicate",
50418334Speter			    "seqno <%u,%u> fragno <%u,%u> tid %u",
50518334Speter			    rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
50618334Speter			    ni->ni_rxseqs[tid] >> IEEE80211_SEQ_SEQ_SHIFT,
50790075Sobrien			    rxseq & IEEE80211_SEQ_FRAG_MASK,
50890075Sobrien			    ni->ni_rxseqs[tid] & IEEE80211_SEQ_FRAG_MASK,
50990075Sobrien			    tid);
51018334Speter			vap->iv_stats.is_rx_dup++;
51118334Speter			IEEE80211_NODE_STAT(ni, rx_dup);
51290075Sobrien			goto out;
51390075Sobrien		}
51490075Sobrien		ni->ni_rxseqs[tid] = rxseq;
51590075Sobrien	}
51690075Sobrien	switch (type) {
51790075Sobrien	case IEEE80211_FC0_TYPE_DATA:
51890075Sobrien		hdrspace = ieee80211_hdrspace(ic, wh);
51990075Sobrien		if (m->m_len < hdrspace &&
52090075Sobrien		    (m = m_pullup(m, hdrspace)) == NULL) {
52190075Sobrien			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
52290075Sobrien			    ni->ni_macaddr, NULL,
52390075Sobrien			    "data too short: expecting %u", hdrspace);
52490075Sobrien			vap->iv_stats.is_rx_tooshort++;
52590075Sobrien			goto out;		/* XXX */
52618334Speter		}
52790075Sobrien		if (dir != IEEE80211_FC1_DIR_DSTODS) {
52818334Speter			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
52918334Speter			    wh, "data", "incorrect dir 0x%x", dir);
53018334Speter			vap->iv_stats.is_rx_wrongdir++;
53118334Speter			goto out;
53218334Speter		}
53318334Speter		/*
53496263Sobrien		 * Only legacy WDS traffic should take this path.
53518334Speter		 */
53618334Speter		if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) {
53790075Sobrien			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
53818334Speter			    wh, "data", "%s", "not legacy wds");
53918334Speter			vap->iv_stats.is_rx_wrongdir++;/*XXX*/
54018334Speter			goto out;
54190075Sobrien		}
54218334Speter		/*
543132718Skan		 * Handle A-MPDU re-ordering.  If the frame is to be
54450397Sobrien		 * processed directly then ieee80211_ampdu_reorder
54550397Sobrien		 * will return 0; otherwise it has consumed the mbuf
54618334Speter		 * and we should do nothing more with it.
54718334Speter		 */
54818334Speter		if ((m->m_flags & M_AMPDU) &&
54918334Speter		    ieee80211_ampdu_reorder(ni, m) != 0) {
55018334Speter			m = NULL;
55118334Speter			goto out;
55218334Speter		}
55318334Speter	resubmit_ampdu:
55450397Sobrien
55590075Sobrien		/*
55618334Speter		 * Handle privacy requirements.  Note that we
55718334Speter		 * must not be preempted from here until after
55818334Speter		 * we (potentially) call ieee80211_crypto_demic;
55918334Speter		 * otherwise we may violate assumptions in the
56090075Sobrien		 * crypto cipher modules used to do delayed update
56118334Speter		 * of replay sequence numbers.
56218334Speter		 */
56318334Speter		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
56490075Sobrien			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
56590075Sobrien				/*
56690075Sobrien				 * Discard encrypted frames when privacy is off.
56790075Sobrien				 */
568132718Skan				IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
56990075Sobrien				    wh, "WEP", "%s", "PRIVACY off");
57090075Sobrien				vap->iv_stats.is_rx_noprivacy++;
57190075Sobrien				IEEE80211_NODE_STAT(ni, rx_noprivacy);
57290075Sobrien				goto out;
57390075Sobrien			}
57490075Sobrien			key = ieee80211_crypto_decap(ni, m, hdrspace);
57590075Sobrien			if (key == NULL) {
57690075Sobrien				/* NB: stats+msgs handled in crypto_decap */
57790075Sobrien				IEEE80211_NODE_STAT(ni, rx_wepfail);
57890075Sobrien				goto out;
57990075Sobrien			}
58090075Sobrien			wh = mtod(m, struct ieee80211_frame *);
58190075Sobrien			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
58290075Sobrien		} else {
58390075Sobrien			/* XXX M_WEP and IEEE80211_F_PRIVACY */
58490075Sobrien			key = NULL;
58590075Sobrien		}
58690075Sobrien
58790075Sobrien		/*
58890075Sobrien		 * Save QoS bits for use below--before we strip the header.
58990075Sobrien		 */
59090075Sobrien		if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
59190075Sobrien			qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
59290075Sobrien			    ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
59390075Sobrien			    ((struct ieee80211_qosframe *)wh)->i_qos[0];
59490075Sobrien		} else
59590075Sobrien			qos = 0;
59690075Sobrien
59790075Sobrien		/*
59890075Sobrien		 * Next up, any fragmentation.
59990075Sobrien		 */
60090075Sobrien		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
60190075Sobrien			m = ieee80211_defrag(ni, m, hdrspace);
60290075Sobrien			if (m == NULL) {
60390075Sobrien				/* Fragment dropped or frame not complete yet */
60490075Sobrien				goto out;
60590075Sobrien			}
606132718Skan		}
60790075Sobrien		wh = NULL;		/* no longer valid, catch any uses */
60890075Sobrien
60990075Sobrien		/*
61090075Sobrien		 * Next strip any MSDU crypto bits.
61190075Sobrien		 */
61290075Sobrien		if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
61390075Sobrien			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
61490075Sobrien			    ni->ni_macaddr, "data", "%s", "demic error");
61590075Sobrien			vap->iv_stats.is_rx_demicfail++;
61690075Sobrien			IEEE80211_NODE_STAT(ni, rx_demicfail);
61790075Sobrien			goto out;
61890075Sobrien		}
61990075Sobrien
62090075Sobrien		/* copy to listener after decrypt */
62190075Sobrien		if (ieee80211_radiotap_active_vap(vap))
62290075Sobrien			ieee80211_radiotap_rx(vap, m);
62390075Sobrien		need_tap = 0;
62490075Sobrien
62590075Sobrien		/*
62690075Sobrien		 * Finally, strip the 802.11 header.
62790075Sobrien		 */
62890075Sobrien		m = ieee80211_decap(vap, m, hdrspace);
62990075Sobrien		if (m == NULL) {
63090075Sobrien			/* XXX mask bit to check for both */
63190075Sobrien			/* don't count Null data frames as errors */
63290075Sobrien			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
63390075Sobrien			    subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
63490075Sobrien				goto out;
63590075Sobrien			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
63690075Sobrien			    ni->ni_macaddr, "data", "%s", "decap error");
63790075Sobrien			vap->iv_stats.is_rx_decap++;
63890075Sobrien			IEEE80211_NODE_STAT(ni, rx_decap);
63990075Sobrien			goto err;
64090075Sobrien		}
64190075Sobrien		eh = mtod(m, struct ether_header *);
64290075Sobrien		if (!ieee80211_node_is_authorized(ni)) {
64390075Sobrien			/*
64490075Sobrien			 * Deny any non-PAE frames received prior to
64590075Sobrien			 * authorization.  For open/shared-key
64690075Sobrien			 * authentication the port is mark authorized
64790075Sobrien			 * after authentication completes.  For 802.1x
64890075Sobrien			 * the port is not marked authorized by the
64990075Sobrien			 * authenticator until the handshake has completed.
65090075Sobrien			 */
65190075Sobrien			if (eh->ether_type != htons(ETHERTYPE_PAE)) {
65290075Sobrien				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
65390075Sobrien				    eh->ether_shost, "data",
65418334Speter				    "unauthorized port: ether type 0x%x len %u",
65518334Speter				    eh->ether_type, m->m_pkthdr.len);
65618334Speter				vap->iv_stats.is_rx_unauth++;
65718334Speter				IEEE80211_NODE_STAT(ni, rx_unauth);
65818334Speter				goto err;
65918334Speter			}
660132718Skan		} else {
66118334Speter			/*
66290075Sobrien			 * When denying unencrypted frames, discard
66390075Sobrien			 * any non-PAE frames received without encryption.
66418334Speter			 */
66590075Sobrien			if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
66690075Sobrien			    (key == NULL && (m->m_flags & M_WEP) == 0) &&
66718334Speter			    eh->ether_type != htons(ETHERTYPE_PAE)) {
66890075Sobrien				/*
66990075Sobrien				 * Drop unencrypted frames.
67018334Speter				 */
671132718Skan				vap->iv_stats.is_rx_unencrypted++;
672132718Skan				IEEE80211_NODE_STAT(ni, rx_unencrypted);
67390075Sobrien				goto out;
67490075Sobrien			}
675169689Skan		}
67690075Sobrien		/* XXX require HT? */
67790075Sobrien		if (qos & IEEE80211_QOS_AMSDU) {
67890075Sobrien			m = ieee80211_decap_amsdu(ni, m);
67918334Speter			if (m == NULL)
68018334Speter				return IEEE80211_FC0_TYPE_DATA;
68190075Sobrien		} else {
68290075Sobrien#ifdef IEEE80211_SUPPORT_SUPERG
68318334Speter			m = ieee80211_decap_fastframe(vap, ni, m);
68490075Sobrien			if (m == NULL)
68590075Sobrien				return IEEE80211_FC0_TYPE_DATA;
68618334Speter#endif
68790075Sobrien		}
68818334Speter		ieee80211_deliver_data(vap, ni, m);
68918334Speter		return IEEE80211_FC0_TYPE_DATA;
69018334Speter
691169689Skan	case IEEE80211_FC0_TYPE_MGT:
692169689Skan		vap->iv_stats.is_rx_mgmt++;
69396263Sobrien		IEEE80211_NODE_STAT(ni, rx_mgmt);
69418334Speter		if (dir != IEEE80211_FC1_DIR_NODS) {
69518334Speter			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
69690075Sobrien			    wh, "data", "incorrect dir 0x%x", dir);
697169689Skan			vap->iv_stats.is_rx_wrongdir++;
698169689Skan			goto err;
699169689Skan		}
700169689Skan		if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
701169689Skan			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
702169689Skan			    ni->ni_macaddr, "mgt", "too short: len %u",
703169689Skan			    m->m_pkthdr.len);
704169689Skan			vap->iv_stats.is_rx_tooshort++;
705169689Skan			goto out;
706169689Skan		}
70750397Sobrien#ifdef IEEE80211_DEBUG
70850397Sobrien		if (ieee80211_msg_debug(vap) || ieee80211_msg_dumppkts(vap)) {
70950397Sobrien			if_printf(ifp, "received %s from %s rssi %d\n",
71050397Sobrien			    ieee80211_mgt_subtype_name[subtype >>
71118334Speter				IEEE80211_FC0_SUBTYPE_SHIFT],
71218334Speter			    ether_sprintf(wh->i_addr2), rssi);
71318334Speter		}
71418334Speter#endif
71590075Sobrien		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
71690075Sobrien			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
71790075Sobrien			    wh, NULL, "%s", "WEP set but not permitted");
71818334Speter			vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
71990075Sobrien			goto out;
72090075Sobrien		}
72190075Sobrien		vap->iv_recv_mgmt(ni, m, subtype, rssi, nf);
72290075Sobrien		goto out;
72390075Sobrien
72418334Speter	case IEEE80211_FC0_TYPE_CTL:
72590075Sobrien		vap->iv_stats.is_rx_ctl++;
72618334Speter		IEEE80211_NODE_STAT(ni, rx_ctrl);
72718334Speter		goto out;
72818334Speter
72990075Sobrien	default:
73090075Sobrien		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
73118334Speter		    wh, "bad", "frame type 0x%x", type);
73218334Speter		/* should not come here */
73318334Speter		break;
73418334Speter	}
73518334Spetererr:
73618334Speter	ifp->if_ierrors++;
737132718Skanout:
73818334Speter	if (m != NULL) {
73990075Sobrien		if (need_tap && ieee80211_radiotap_active_vap(vap))
74090075Sobrien			ieee80211_radiotap_rx(vap, m);
74190075Sobrien		m_freem(m);
74290075Sobrien	}
74318334Speter	return type;
74490075Sobrien}
74518334Speter
746132718Skanstatic void
747132718Skanwds_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
748132718Skan	int subtype, int rssi, int nf)
749132718Skan{
750132718Skan	struct ieee80211vap *vap = ni->ni_vap;
751132718Skan	struct ieee80211com *ic = ni->ni_ic;
752132718Skan	struct ieee80211_frame *wh;
753132718Skan	u_int8_t *frm, *efrm;
754169689Skan
755169689Skan	wh = mtod(m0, struct ieee80211_frame *);
756169689Skan	frm = (u_int8_t *)&wh[1];
757169689Skan	efrm = mtod(m0, u_int8_t *) + m0->m_len;
758169689Skan	switch (subtype) {
759169689Skan	case IEEE80211_FC0_SUBTYPE_ACTION:
760169689Skan	case IEEE80211_FC0_SUBTYPE_ACTION_NOACK:
761169689Skan		if (ni == vap->iv_bss) {
762169689Skan			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
763169689Skan			    wh, NULL, "%s", "unknown node");
764169689Skan			vap->iv_stats.is_rx_mgtdiscard++;
765169689Skan		} else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1)) {
766169689Skan			/* NB: not interested in multicast frames. */
767169689Skan			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
768132718Skan			    wh, NULL, "%s", "not for us");
769132718Skan			vap->iv_stats.is_rx_mgtdiscard++;
770132718Skan		} else if (vap->iv_state != IEEE80211_S_RUN) {
771132718Skan			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
772132718Skan			    wh, NULL, "wrong state %s",
773132718Skan			    ieee80211_state_name[vap->iv_state]);
774132718Skan			vap->iv_stats.is_rx_mgtdiscard++;
775132718Skan		} else {
776132718Skan			if (ieee80211_parse_action(ni, m0) == 0)
777132718Skan				(void)ic->ic_recv_action(ni, wh, frm, efrm);
778169689Skan		}
779132718Skan		break;
780132718Skan
781132718Skan	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
782132718Skan	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
783132718Skan	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
784132718Skan	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
785132718Skan	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
786132718Skan	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
787132718Skan	case IEEE80211_FC0_SUBTYPE_BEACON:
788132718Skan	case IEEE80211_FC0_SUBTYPE_ATIM:
789132718Skan	case IEEE80211_FC0_SUBTYPE_DISASSOC:
790132718Skan	case IEEE80211_FC0_SUBTYPE_AUTH:
791132718Skan	case IEEE80211_FC0_SUBTYPE_DEAUTH:
792132718Skan		IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
793132718Skan		    wh, NULL, "%s", "not handled");
794132718Skan		vap->iv_stats.is_rx_mgtdiscard++;
795132718Skan		break;
796132718Skan
797132718Skan	default:
798132718Skan		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
799132718Skan		    wh, "mgt", "subtype 0x%x not handled", subtype);
800132718Skan		vap->iv_stats.is_rx_badsubtype++;
801132718Skan		break;
802132718Skan	}
803132718Skan}
804132718Skan