1116742Ssam/*-
2116904Ssam * Copyright (c) 2001 Atsushi Onoe
3186904Ssam * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
4116742Ssam * All rights reserved.
5116742Ssam *
6116742Ssam * Redistribution and use in source and binary forms, with or without
7116742Ssam * modification, are permitted provided that the following conditions
8116742Ssam * are met:
9116742Ssam * 1. Redistributions of source code must retain the above copyright
10116904Ssam *    notice, this list of conditions and the following disclaimer.
11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright
12116904Ssam *    notice, this list of conditions and the following disclaimer in the
13116904Ssam *    documentation and/or other materials provided with the distribution.
14116742Ssam *
15116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25116742Ssam */
26116742Ssam
27116742Ssam#include <sys/cdefs.h>
28116742Ssam__FBSDID("$FreeBSD$");
29116742Ssam
30178354Ssam#include "opt_wlan.h"
31178354Ssam
32116742Ssam#include <sys/param.h>
33138568Ssam#include <sys/systm.h>
34116742Ssam#include <sys/mbuf.h>
35116742Ssam#include <sys/malloc.h>
36138568Ssam#include <sys/endian.h>
37142283Ssam#include <sys/kernel.h>
38138568Ssam
39116742Ssam#include <sys/socket.h>
40178354Ssam
41178354Ssam#include <net/ethernet.h>
42116742Ssam#include <net/if.h>
43178354Ssam#include <net/if_llc.h>
44116742Ssam#include <net/if_media.h>
45138568Ssam#include <net/if_vlan_var.h>
46116742Ssam
47116742Ssam#include <net80211/ieee80211_var.h>
48178354Ssam#include <net80211/ieee80211_input.h>
49195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH
50195618Srpaulo#include <net80211/ieee80211_mesh.h>
51195618Srpaulo#endif
52116742Ssam
53116742Ssam#include <net/bpf.h>
54116742Ssam
55178354Ssam#ifdef INET
56178354Ssam#include <netinet/in.h>
57178354Ssam#include <net/ethernet.h>
58178354Ssam#endif
59116742Ssam
60220445Sadrianstatic void
61220445Sadrianieee80211_process_mimo(struct ieee80211_node *ni, struct ieee80211_rx_stats *rx)
62220445Sadrian{
63220445Sadrian	int i;
64220445Sadrian
65220445Sadrian	/* Verify the required MIMO bits are set */
66220445Sadrian	if ((rx->r_flags & (IEEE80211_R_C_CHAIN | IEEE80211_R_C_NF | IEEE80211_R_C_RSSI)) !=
67220445Sadrian	    (IEEE80211_R_C_CHAIN | IEEE80211_R_C_NF | IEEE80211_R_C_RSSI))
68220445Sadrian		return;
69220445Sadrian
70220445Sadrian	/* XXX This assumes the MIMO radios have both ctl and ext chains */
71220445Sadrian	for (i = 0; i < MIN(rx->c_chain, IEEE80211_MAX_CHAINS); i++) {
72220445Sadrian		IEEE80211_RSSI_LPF(ni->ni_mimo_rssi_ctl[i], rx->c_rssi_ctl[i]);
73220445Sadrian		IEEE80211_RSSI_LPF(ni->ni_mimo_rssi_ext[i], rx->c_rssi_ext[i]);
74220445Sadrian	}
75220445Sadrian
76220445Sadrian	/* XXX This also assumes the MIMO radios have both ctl and ext chains */
77220445Sadrian	for(i = 0; i < MIN(rx->c_chain, IEEE80211_MAX_CHAINS); i++) {
78220445Sadrian		ni->ni_mimo_noise_ctl[i] = rx->c_nf_ctl[i];
79220445Sadrian		ni->ni_mimo_noise_ext[i] = rx->c_nf_ext[i];
80220445Sadrian	}
81220445Sadrian	ni->ni_mimo_chains = rx->c_chain;
82220445Sadrian}
83220445Sadrian
84144616Ssamint
85220445Sadrianieee80211_input_mimo(struct ieee80211_node *ni, struct mbuf *m,
86220445Sadrian    struct ieee80211_rx_stats *rx)
87220445Sadrian{
88220445Sadrian	/* XXX should assert IEEE80211_R_NF and IEEE80211_R_RSSI are set */
89220445Sadrian	ieee80211_process_mimo(ni, rx);
90220445Sadrian	return ieee80211_input(ni, m, rx->rssi, rx->nf);
91220445Sadrian}
92220445Sadrian
93220445Sadrianint
94192468Ssamieee80211_input_all(struct ieee80211com *ic, struct mbuf *m, int rssi, int nf)
95116742Ssam{
96220445Sadrian	struct ieee80211_rx_stats rx;
97220445Sadrian
98220445Sadrian	rx.r_flags = IEEE80211_R_NF | IEEE80211_R_RSSI;
99220445Sadrian	rx.nf = nf;
100220445Sadrian	rx.rssi = rssi;
101220445Sadrian	return ieee80211_input_mimo_all(ic, m, &rx);
102220445Sadrian}
103220445Sadrian
104220445Sadrianint
105220445Sadrianieee80211_input_mimo_all(struct ieee80211com *ic, struct mbuf *m,
106220445Sadrian    struct ieee80211_rx_stats *rx)
107220445Sadrian{
108178354Ssam	struct ieee80211vap *vap;
109178354Ssam	int type = -1;
110116742Ssam
111193292Ssam	m->m_flags |= M_BCAST;		/* NB: mark for bpf tap'ing */
112193292Ssam
113178354Ssam	/* XXX locking */
114178354Ssam	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
115178354Ssam		struct ieee80211_node *ni;
116178354Ssam		struct mbuf *mcopy;
117170530Ssam
118193288Ssam		/* NB: could check for IFF_UP but this is cheaper */
119193288Ssam		if (vap->iv_state == IEEE80211_S_INIT)
120193288Ssam			continue;
121138568Ssam		/*
122178354Ssam		 * WDS vap's only receive directed traffic from the
123178354Ssam		 * station at the ``far end''.  That traffic should
124178354Ssam		 * be passed through the AP vap the station is associated
125178354Ssam		 * to--so don't spam them with mcast frames.
126170530Ssam		 */
127178354Ssam		if (vap->iv_opmode == IEEE80211_M_WDS)
128178354Ssam			continue;
129178354Ssam		if (TAILQ_NEXT(vap, iv_next) != NULL) {
130138568Ssam			/*
131178354Ssam			 * Packet contents are changed by ieee80211_decap
132178354Ssam			 * so do a deep copy of the packet.
133138568Ssam			 */
134243882Sglebius			mcopy = m_dup(m, M_NOWAIT);
135178354Ssam			if (mcopy == NULL) {
136178354Ssam				/* XXX stat+msg */
137178354Ssam				continue;
138138568Ssam			}
139138568Ssam		} else {
140178354Ssam			mcopy = m;
141178354Ssam			m = NULL;
142138568Ssam		}
143178354Ssam		ni = ieee80211_ref_node(vap->iv_bss);
144220445Sadrian		type = ieee80211_input_mimo(ni, mcopy, rx);
145178354Ssam		ieee80211_free_node(ni);
146116742Ssam	}
147178354Ssam	if (m != NULL)			/* no vaps, reclaim mbuf */
148116742Ssam		m_freem(m);
149144616Ssam	return type;
150116742Ssam}
151116742Ssam
152138568Ssam/*
153189987Srpaulo * This function reassembles fragments.
154178354Ssam *
155178354Ssam * XXX should handle 3 concurrent reassemblies per-spec.
156138568Ssam */
157178354Ssamstruct mbuf *
158178354Ssamieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace)
159116742Ssam{
160178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
161138568Ssam	struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
162138568Ssam	struct ieee80211_frame *lwh;
163170530Ssam	uint16_t rxseq;
164170530Ssam	uint8_t fragno;
165170530Ssam	uint8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG;
166138568Ssam	struct mbuf *mfrag;
167138568Ssam
168138568Ssam	KASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?"));
169138568Ssam
170170530Ssam	rxseq = le16toh(*(uint16_t *)wh->i_seq);
171138568Ssam	fragno = rxseq & IEEE80211_SEQ_FRAG_MASK;
172138568Ssam
173138568Ssam	/* Quick way out, if there's nothing to defragment */
174138568Ssam	if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL)
175138568Ssam		return m;
176138568Ssam
177138568Ssam	/*
178138568Ssam	 * Remove frag to insure it doesn't get reaped by timer.
179138568Ssam	 */
180138568Ssam	if (ni->ni_table == NULL) {
181138568Ssam		/*
182138568Ssam		 * Should never happen.  If the node is orphaned (not in
183138568Ssam		 * the table) then input packets should not reach here.
184138568Ssam		 * Otherwise, a concurrent request that yanks the table
185138568Ssam		 * should be blocked by other interlocking and/or by first
186138568Ssam		 * shutting the driver down.  Regardless, be defensive
187138568Ssam		 * here and just bail
188138568Ssam		 */
189138568Ssam		/* XXX need msg+stat */
190138568Ssam		m_freem(m);
191138568Ssam		return NULL;
192138568Ssam	}
193138568Ssam	IEEE80211_NODE_LOCK(ni->ni_table);
194138568Ssam	mfrag = ni->ni_rxfrag[0];
195138568Ssam	ni->ni_rxfrag[0] = NULL;
196138568Ssam	IEEE80211_NODE_UNLOCK(ni->ni_table);
197138568Ssam
198138568Ssam	/*
199138568Ssam	 * Validate new fragment is in order and
200138568Ssam	 * related to the previous ones.
201138568Ssam	 */
202138568Ssam	if (mfrag != NULL) {
203170530Ssam		uint16_t last_rxseq;
204138568Ssam
205138568Ssam		lwh = mtod(mfrag, struct ieee80211_frame *);
206170530Ssam		last_rxseq = le16toh(*(uint16_t *)lwh->i_seq);
207138568Ssam		/* NB: check seq # and frag together */
208138568Ssam		if (rxseq != last_rxseq+1 ||
209138568Ssam		    !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) ||
210138568Ssam		    !IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) {
211138568Ssam			/*
212138568Ssam			 * Unrelated fragment or no space for it,
213138568Ssam			 * clear current fragments.
214138568Ssam			 */
215138568Ssam			m_freem(mfrag);
216138568Ssam			mfrag = NULL;
217138568Ssam		}
218138568Ssam	}
219138568Ssam
220138568Ssam 	if (mfrag == NULL) {
221138568Ssam		if (fragno != 0) {		/* !first fragment, discard */
222178354Ssam			vap->iv_stats.is_rx_defrag++;
223138568Ssam			IEEE80211_NODE_STAT(ni, rx_defrag);
224138568Ssam			m_freem(m);
225138568Ssam			return NULL;
226138568Ssam		}
227138568Ssam		mfrag = m;
228138568Ssam	} else {				/* concatenate */
229147252Ssam		m_adj(m, hdrspace);		/* strip header */
230138568Ssam		m_cat(mfrag, m);
231138568Ssam		/* NB: m_cat doesn't update the packet header */
232138568Ssam		mfrag->m_pkthdr.len += m->m_pkthdr.len;
233138568Ssam		/* track last seqnum and fragno */
234138568Ssam		lwh = mtod(mfrag, struct ieee80211_frame *);
235170530Ssam		*(uint16_t *) lwh->i_seq = *(uint16_t *) wh->i_seq;
236138568Ssam	}
237138568Ssam	if (more_frag) {			/* more to come, save */
238142283Ssam		ni->ni_rxfragstamp = ticks;
239138568Ssam		ni->ni_rxfrag[0] = mfrag;
240138568Ssam		mfrag = NULL;
241138568Ssam	}
242138568Ssam	return mfrag;
243138568Ssam}
244138568Ssam
245170530Ssamvoid
246178354Ssamieee80211_deliver_data(struct ieee80211vap *vap,
247148293Ssam	struct ieee80211_node *ni, struct mbuf *m)
248148293Ssam{
249148293Ssam	struct ether_header *eh = mtod(m, struct ether_header *);
250178354Ssam	struct ifnet *ifp = vap->iv_ifp;
251148293Ssam
252193292Ssam	/* clear driver/net80211 flags before passing up */
253254523Sandre	m->m_flags &= ~(M_MCAST | M_BCAST);
254254640Sadrian#if __FreeBSD_version >= 1000046
255254523Sandre	m_clrprotoflags(m);
256254640Sadrian#endif
257193292Ssam
258178354Ssam	/* NB: see hostap_deliver_data, this path doesn't handle hostap */
259178354Ssam	KASSERT(vap->iv_opmode != IEEE80211_M_HOSTAP, ("gack, hostap"));
260161145Ssam	/*
261161145Ssam	 * Do accounting.
262161145Ssam	 */
263161145Ssam	ifp->if_ipackets++;
264161145Ssam	IEEE80211_NODE_STAT(ni, rx_data);
265161145Ssam	IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
266161145Ssam	if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
267161145Ssam		m->m_flags |= M_MCAST;		/* XXX M_BCAST? */
268161145Ssam		IEEE80211_NODE_STAT(ni, rx_mcast);
269161145Ssam	} else
270161145Ssam		IEEE80211_NODE_STAT(ni, rx_ucast);
271178354Ssam	m->m_pkthdr.rcvif = ifp;
272161145Ssam
273178354Ssam	if (ni->ni_vlan != 0) {
274178354Ssam		/* attach vlan tag */
275178354Ssam		m->m_pkthdr.ether_vtag = ni->ni_vlan;
276178354Ssam		m->m_flags |= M_VLANTAG;
277148293Ssam	}
278178354Ssam	ifp->if_input(ifp, m);
279148293Ssam}
280148293Ssam
281178354Ssamstruct mbuf *
282178354Ssamieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen)
283138568Ssam{
284195618Srpaulo	struct ieee80211_qosframe_addr4 wh;
285116742Ssam	struct ether_header *eh;
286116742Ssam	struct llc *llc;
287116742Ssam
288195784Srpaulo	KASSERT(hdrlen <= sizeof(wh),
289195784Srpaulo	    ("hdrlen %d > max %zd", hdrlen, sizeof(wh)));
290195784Srpaulo
291147252Ssam	if (m->m_len < hdrlen + sizeof(*llc) &&
292147252Ssam	    (m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) {
293195784Srpaulo		vap->iv_stats.is_rx_tooshort++;
294195784Srpaulo		/* XXX msg */
295138568Ssam		return NULL;
296116742Ssam	}
297147252Ssam	memcpy(&wh, mtod(m, caddr_t), hdrlen);
298147252Ssam	llc = (struct llc *)(mtod(m, caddr_t) + hdrlen);
299116742Ssam	if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP &&
300116742Ssam	    llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 &&
301181196Ssam	    llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0 &&
302181196Ssam	    /* NB: preserve AppleTalk frames that have a native SNAP hdr */
303181196Ssam	    !(llc->llc_snap.ether_type == htons(ETHERTYPE_AARP) ||
304181196Ssam	      llc->llc_snap.ether_type == htons(ETHERTYPE_IPX))) {
305164805Ssam		m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh));
306116742Ssam		llc = NULL;
307116742Ssam	} else {
308147252Ssam		m_adj(m, hdrlen - sizeof(*eh));
309116742Ssam	}
310116742Ssam	eh = mtod(m, struct ether_header *);
311116742Ssam	switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) {
312116742Ssam	case IEEE80211_FC1_DIR_NODS:
313116742Ssam		IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1);
314116742Ssam		IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2);
315116742Ssam		break;
316116742Ssam	case IEEE80211_FC1_DIR_TODS:
317116742Ssam		IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3);
318116742Ssam		IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2);
319116742Ssam		break;
320116742Ssam	case IEEE80211_FC1_DIR_FROMDS:
321116742Ssam		IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1);
322116742Ssam		IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3);
323116742Ssam		break;
324116742Ssam	case IEEE80211_FC1_DIR_DSTODS:
325147252Ssam		IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3);
326147252Ssam		IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr4);
327147252Ssam		break;
328116742Ssam	}
329246710Sglebius#ifndef __NO_STRICT_ALIGNMENT
330170530Ssam	if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) {
331195757Ssam		m = ieee80211_realign(vap, m, sizeof(*eh));
332195757Ssam		if (m == NULL)
333195757Ssam			return NULL;
334116742Ssam	}
335246710Sglebius#endif /* !__NO_STRICT_ALIGNMENT */
336116742Ssam	if (llc != NULL) {
337116742Ssam		eh = mtod(m, struct ether_header *);
338116742Ssam		eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
339116742Ssam	}
340116742Ssam	return m;
341116742Ssam}
342116742Ssam
343116742Ssam/*
344170530Ssam * Decap a frame encapsulated in a fast-frame/A-MSDU.
345170530Ssam */
346170530Ssamstruct mbuf *
347170530Ssamieee80211_decap1(struct mbuf *m, int *framelen)
348170530Ssam{
349170530Ssam#define	FF_LLC_SIZE	(sizeof(struct ether_header) + sizeof(struct llc))
350170530Ssam	struct ether_header *eh;
351170530Ssam	struct llc *llc;
352170530Ssam
353170530Ssam	/*
354170530Ssam	 * The frame has an 802.3 header followed by an 802.2
355170530Ssam	 * LLC header.  The encapsulated frame length is in the
356170530Ssam	 * first header type field; save that and overwrite it
357170530Ssam	 * with the true type field found in the second.  Then
358170530Ssam	 * copy the 802.3 header up to where it belongs and
359170530Ssam	 * adjust the mbuf contents to remove the void.
360170530Ssam	 */
361170530Ssam	if (m->m_len < FF_LLC_SIZE && (m = m_pullup(m, FF_LLC_SIZE)) == NULL)
362170530Ssam		return NULL;
363170530Ssam	eh = mtod(m, struct ether_header *);	/* 802.3 header is first */
364170530Ssam	llc = (struct llc *)&eh[1];		/* 802.2 header follows */
365170530Ssam	*framelen = ntohs(eh->ether_type)	/* encap'd frame size */
366170530Ssam		  + sizeof(struct ether_header) - sizeof(struct llc);
367170530Ssam	eh->ether_type = llc->llc_un.type_snap.ether_type;
368170530Ssam	ovbcopy(eh, mtod(m, uint8_t *) + sizeof(struct llc),
369170530Ssam		sizeof(struct ether_header));
370170530Ssam	m_adj(m, sizeof(struct llc));
371170530Ssam	return m;
372170530Ssam#undef FF_LLC_SIZE
373170530Ssam}
374170530Ssam
375170530Ssam/*
376116742Ssam * Install received rate set information in the node's state block.
377116742Ssam */
378148299Ssamint
379148299Ssamieee80211_setup_rates(struct ieee80211_node *ni,
380170530Ssam	const uint8_t *rates, const uint8_t *xrates, int flags)
381116742Ssam{
382178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
383116742Ssam	struct ieee80211_rateset *rs = &ni->ni_rates;
384116742Ssam
385116742Ssam	memset(rs, 0, sizeof(*rs));
386116742Ssam	rs->rs_nrates = rates[1];
387116742Ssam	memcpy(rs->rs_rates, rates + 2, rs->rs_nrates);
388116742Ssam	if (xrates != NULL) {
389170530Ssam		uint8_t nxrates;
390116742Ssam		/*
391116742Ssam		 * Tack on 11g extended supported rate element.
392116742Ssam		 */
393116742Ssam		nxrates = xrates[1];
394116742Ssam		if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) {
395116742Ssam			nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates;
396178354Ssam			IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE, ni,
397178354Ssam			    "extended rate set too large; only using "
398178354Ssam			    "%u of %u rates", nxrates, xrates[1]);
399178354Ssam			vap->iv_stats.is_rx_rstoobig++;
400116742Ssam		}
401116742Ssam		memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
402116742Ssam		rs->rs_nrates += nxrates;
403116742Ssam	}
404167442Ssam	return ieee80211_fix_rate(ni, rs, flags);
405116742Ssam}
406116742Ssam
407147211Ssam/*
408147211Ssam * Send a management frame error response to the specified
409147211Ssam * station.  If ni is associated with the station then use
410147211Ssam * it; otherwise allocate a temporary node suitable for
411147211Ssam * transmitting the frame and then free the reference so
412147211Ssam * it will go away as soon as the frame has been transmitted.
413147211Ssam */
414178354Ssamvoid
415178354Ssamieee80211_send_error(struct ieee80211_node *ni,
416178354Ssam	const uint8_t mac[IEEE80211_ADDR_LEN], int subtype, int arg)
417147211Ssam{
418178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
419147211Ssam	int istmp;
420147211Ssam
421178354Ssam	if (ni == vap->iv_bss) {
422178354Ssam		if (vap->iv_state != IEEE80211_S_RUN) {
423178354Ssam			/*
424178354Ssam			 * XXX hack until we get rid of this routine.
425178354Ssam			 * We can be called prior to the vap reaching
426178354Ssam			 * run state under certain conditions in which
427178354Ssam			 * case iv_bss->ni_chan will not be setup.
428178354Ssam			 * Check for this explicitly and and just ignore
429178354Ssam			 * the request.
430178354Ssam			 */
431178354Ssam			return;
432178354Ssam		}
433178354Ssam		ni = ieee80211_tmp_node(vap, mac);
434147211Ssam		if (ni == NULL) {
435147211Ssam			/* XXX msg */
436147211Ssam			return;
437147211Ssam		}
438147211Ssam		istmp = 1;
439147211Ssam	} else
440147211Ssam		istmp = 0;
441178354Ssam	IEEE80211_SEND_MGMT(ni, subtype, arg);
442147211Ssam	if (istmp)
443147211Ssam		ieee80211_free_node(ni);
444147211Ssam}
445147211Ssam
446178354Ssamint
447178354Ssamieee80211_alloc_challenge(struct ieee80211_node *ni)
448138568Ssam{
449138568Ssam	if (ni->ni_challenge == NULL)
450186302Ssam		ni->ni_challenge = (uint32_t *) malloc(IEEE80211_CHALLENGE_LEN,
451170530Ssam		    M_80211_NODE, M_NOWAIT);
452138568Ssam	if (ni->ni_challenge == NULL) {
453178354Ssam		IEEE80211_NOTE(ni->ni_vap,
454178354Ssam		    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni,
455178354Ssam		    "%s", "shared key challenge alloc failed");
456138568Ssam		/* XXX statistic */
457138568Ssam	}
458138568Ssam	return (ni->ni_challenge != NULL);
459138568Ssam}
460138568Ssam
461156359Ssam/*
462178354Ssam * Parse a Beacon or ProbeResponse frame and return the
463178354Ssam * useful information in an ieee80211_scanparams structure.
464178354Ssam * Status is set to 0 if no problems were found; otherwise
465178354Ssam * a bitmask of IEEE80211_BPARSE_* items is returned that
466178354Ssam * describes the problems detected.
467156359Ssam */
468178354Ssamint
469178354Ssamieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m,
470178354Ssam	struct ieee80211_scanparams *scan)
471156359Ssam{
472178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
473156359Ssam	struct ieee80211com *ic = ni->ni_ic;
474116742Ssam	struct ieee80211_frame *wh;
475170530Ssam	uint8_t *frm, *efrm;
476116742Ssam
477178354Ssam	wh = mtod(m, struct ieee80211_frame *);
478170530Ssam	frm = (uint8_t *)&wh[1];
479178354Ssam	efrm = mtod(m, uint8_t *) + m->m_len;
480178354Ssam	scan->status = 0;
481178354Ssam	/*
482178354Ssam	 * beacon/probe response frame format
483178354Ssam	 *	[8] time stamp
484178354Ssam	 *	[2] beacon interval
485178354Ssam	 *	[2] capability information
486178354Ssam	 *	[tlv] ssid
487178354Ssam	 *	[tlv] supported rates
488178354Ssam	 *	[tlv] country information
489193439Ssam	 *	[tlv] channel switch announcement (CSA)
490178354Ssam	 *	[tlv] parameter set (FH/DS)
491178354Ssam	 *	[tlv] erp information
492178354Ssam	 *	[tlv] extended supported rates
493178354Ssam	 *	[tlv] WME
494178354Ssam	 *	[tlv] WPA or RSN
495178354Ssam	 *	[tlv] HT capabilities
496178354Ssam	 *	[tlv] HT information
497178354Ssam	 *	[tlv] Atheros capabilities
498195618Srpaulo	 *	[tlv] Mesh ID
499195618Srpaulo	 *	[tlv] Mesh Configuration
500178354Ssam	 */
501178354Ssam	IEEE80211_VERIFY_LENGTH(efrm - frm, 12,
502178354Ssam	    return (scan->status = IEEE80211_BPARSE_BADIELEN));
503178354Ssam	memset(scan, 0, sizeof(*scan));
504178354Ssam	scan->tstamp  = frm;				frm += 8;
505178354Ssam	scan->bintval = le16toh(*(uint16_t *)frm);	frm += 2;
506178354Ssam	scan->capinfo = le16toh(*(uint16_t *)frm);	frm += 2;
507178354Ssam	scan->bchan = ieee80211_chan2ieee(ic, ic->ic_curchan);
508178354Ssam	scan->chan = scan->bchan;
509178354Ssam	scan->ies = frm;
510178354Ssam	scan->ies_len = efrm - frm;
511116742Ssam
512178354Ssam	while (efrm - frm > 1) {
513178354Ssam		IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2,
514178354Ssam		    return (scan->status = IEEE80211_BPARSE_BADIELEN));
515178354Ssam		switch (*frm) {
516178354Ssam		case IEEE80211_ELEMID_SSID:
517178354Ssam			scan->ssid = frm;
518178354Ssam			break;
519178354Ssam		case IEEE80211_ELEMID_RATES:
520178354Ssam			scan->rates = frm;
521178354Ssam			break;
522178354Ssam		case IEEE80211_ELEMID_COUNTRY:
523178354Ssam			scan->country = frm;
524178354Ssam			break;
525193439Ssam		case IEEE80211_ELEMID_CSA:
526193439Ssam			scan->csa = frm;
527193439Ssam			break;
528227331Sadrian		case IEEE80211_ELEMID_QUIET:
529227331Sadrian			scan->quiet = frm;
530227331Sadrian			break;
531178354Ssam		case IEEE80211_ELEMID_FHPARMS:
532178354Ssam			if (ic->ic_phytype == IEEE80211_T_FH) {
533178354Ssam				scan->fhdwell = LE_READ_2(&frm[2]);
534178354Ssam				scan->chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
535178354Ssam				scan->fhindex = frm[6];
536116742Ssam			}
537178354Ssam			break;
538178354Ssam		case IEEE80211_ELEMID_DSPARMS:
539116742Ssam			/*
540178354Ssam			 * XXX hack this since depending on phytype
541178354Ssam			 * is problematic for multi-mode devices.
542116742Ssam			 */
543178354Ssam			if (ic->ic_phytype != IEEE80211_T_FH)
544178354Ssam				scan->chan = frm[2];
545178354Ssam			break;
546178354Ssam		case IEEE80211_ELEMID_TIM:
547178354Ssam			/* XXX ATIM? */
548178354Ssam			scan->tim = frm;
549178354Ssam			scan->timoff = frm - mtod(m, uint8_t *);
550178354Ssam			break;
551178354Ssam		case IEEE80211_ELEMID_IBSSPARMS:
552178354Ssam		case IEEE80211_ELEMID_CFPARMS:
553184270Ssam		case IEEE80211_ELEMID_PWRCNSTR:
554178354Ssam			/* NB: avoid debugging complaints */
555178354Ssam			break;
556178354Ssam		case IEEE80211_ELEMID_XRATES:
557178354Ssam			scan->xrates = frm;
558178354Ssam			break;
559178354Ssam		case IEEE80211_ELEMID_ERP:
560178354Ssam			if (frm[1] != 1) {
561178354Ssam				IEEE80211_DISCARD_IE(vap,
562178354Ssam				    IEEE80211_MSG_ELEMID, wh, "ERP",
563178354Ssam				    "bad len %u", frm[1]);
564178354Ssam				vap->iv_stats.is_rx_elem_toobig++;
565116742Ssam				break;
566116742Ssam			}
567178354Ssam			scan->erp = frm[2] | 0x100;
568178354Ssam			break;
569178354Ssam		case IEEE80211_ELEMID_HTCAP:
570178354Ssam			scan->htcap = frm;
571178354Ssam			break;
572178354Ssam		case IEEE80211_ELEMID_RSN:
573178354Ssam			scan->rsn = frm;
574178354Ssam			break;
575178354Ssam		case IEEE80211_ELEMID_HTINFO:
576178354Ssam			scan->htinfo = frm;
577178354Ssam			break;
578196316Srpaulo#ifdef IEEE80211_SUPPORT_MESH
579195618Srpaulo		case IEEE80211_ELEMID_MESHID:
580195618Srpaulo			scan->meshid = frm;
581195618Srpaulo			break;
582195618Srpaulo		case IEEE80211_ELEMID_MESHCONF:
583195618Srpaulo			scan->meshconf = frm;
584195618Srpaulo			break;
585195618Srpaulo#endif
586178354Ssam		case IEEE80211_ELEMID_VENDOR:
587178354Ssam			if (iswpaoui(frm))
588178354Ssam				scan->wpa = frm;
589178354Ssam			else if (iswmeparam(frm) || iswmeinfo(frm))
590178354Ssam				scan->wme = frm;
591190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG
592178354Ssam			else if (isatherosoui(frm))
593178354Ssam				scan->ath = frm;
594190391Ssam#endif
595186904Ssam#ifdef IEEE80211_SUPPORT_TDMA
596186904Ssam			else if (istdmaoui(frm))
597186904Ssam				scan->tdma = frm;
598186904Ssam#endif
599193655Ssam			else if (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) {
600138568Ssam				/*
601178354Ssam				 * Accept pre-draft HT ie's if the
602178354Ssam				 * standard ones have not been seen.
603138568Ssam				 */
604178354Ssam				if (ishtcapoui(frm)) {
605178354Ssam					if (scan->htcap == NULL)
606178354Ssam						scan->htcap = frm;
607178354Ssam				} else if (ishtinfooui(frm)) {
608178354Ssam					if (scan->htinfo == NULL)
609178354Ssam						scan->htcap = frm;
610170530Ssam				}
611116742Ssam			}
612170530Ssam			break;
613178354Ssam		default:
614178354Ssam			IEEE80211_DISCARD_IE(vap, IEEE80211_MSG_ELEMID,
615178354Ssam			    wh, "unhandled",
616178354Ssam			    "id %u, len %u", *frm, frm[1]);
617178354Ssam			vap->iv_stats.is_rx_elem_unknown++;
618170530Ssam			break;
619170530Ssam		}
620178354Ssam		frm += frm[1] + 2;
621178354Ssam	}
622178354Ssam	IEEE80211_VERIFY_ELEMENT(scan->rates, IEEE80211_RATE_MAXSIZE,
623178354Ssam	    scan->status |= IEEE80211_BPARSE_RATES_INVALID);
624178354Ssam	if (scan->rates != NULL && scan->xrates != NULL) {
625167436Ssam		/*
626178354Ssam		 * NB: don't process XRATES if RATES is missing.  This
627178354Ssam		 * avoids a potential null ptr deref and should be ok
628178354Ssam		 * as the return code will already note RATES is missing
629178354Ssam		 * (so callers shouldn't otherwise process the frame).
630167436Ssam		 */
631178354Ssam		IEEE80211_VERIFY_ELEMENT(scan->xrates,
632178354Ssam		    IEEE80211_RATE_MAXSIZE - scan->rates[1],
633178354Ssam		    scan->status |= IEEE80211_BPARSE_XRATES_INVALID);
634178354Ssam	}
635178354Ssam	IEEE80211_VERIFY_ELEMENT(scan->ssid, IEEE80211_NWID_LEN,
636178354Ssam	    scan->status |= IEEE80211_BPARSE_SSID_INVALID);
637178354Ssam	if (scan->chan != scan->bchan && ic->ic_phytype != IEEE80211_T_FH) {
638147794Ssam		/*
639178354Ssam		 * Frame was received on a channel different from the
640178354Ssam		 * one indicated in the DS params element id;
641178354Ssam		 * silently discard it.
642173273Ssam		 *
643178354Ssam		 * NB: this can happen due to signal leakage.
644178354Ssam		 *     But we should take it for FH phy because
645178354Ssam		 *     the rssi value should be correct even for
646178354Ssam		 *     different hop pattern in FH.
647173273Ssam		 */
648178354Ssam		IEEE80211_DISCARD(vap,
649178354Ssam		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
650178354Ssam		    wh, NULL, "for off-channel %u", scan->chan);
651178354Ssam		vap->iv_stats.is_rx_chanmismatch++;
652178354Ssam		scan->status |= IEEE80211_BPARSE_OFFCHAN;
653116742Ssam	}
654178354Ssam	if (!(IEEE80211_BINTVAL_MIN <= scan->bintval &&
655178354Ssam	      scan->bintval <= IEEE80211_BINTVAL_MAX)) {
656178354Ssam		IEEE80211_DISCARD(vap,
657178354Ssam		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
658232269Sadrian		    wh, NULL, "bogus beacon interval (%d TU)",
659232269Sadrian		    (int) scan->bintval);
660178354Ssam		vap->iv_stats.is_rx_badbintval++;
661178354Ssam		scan->status |= IEEE80211_BPARSE_BINTVAL_INVALID;
662178354Ssam	}
663178354Ssam	if (scan->country != NULL) {
664116742Ssam		/*
665178354Ssam		 * Validate we have at least enough data to extract
666178354Ssam		 * the country code.  Not sure if we should return an
667178354Ssam		 * error instead of discarding the IE; consider this
668178354Ssam		 * being lenient as we don't depend on the data for
669178354Ssam		 * correct operation.
670116742Ssam		 */
671178354Ssam		IEEE80211_VERIFY_LENGTH(scan->country[1], 3 * sizeof(uint8_t),
672178354Ssam		    scan->country = NULL);
673116742Ssam	}
674193439Ssam	if (scan->csa != NULL) {
675193439Ssam		/*
676193439Ssam		 * Validate Channel Switch Announcement; this must
677193439Ssam		 * be the correct length or we toss the frame.
678193439Ssam		 */
679193439Ssam		IEEE80211_VERIFY_LENGTH(scan->csa[1], 3 * sizeof(uint8_t),
680193439Ssam		    scan->status |= IEEE80211_BPARSE_CSA_INVALID);
681193439Ssam	}
682178354Ssam	/*
683178354Ssam	 * Process HT ie's.  This is complicated by our
684178354Ssam	 * accepting both the standard ie's and the pre-draft
685178354Ssam	 * vendor OUI ie's that some vendors still use/require.
686178354Ssam	 */
687178354Ssam	if (scan->htcap != NULL) {
688178354Ssam		IEEE80211_VERIFY_LENGTH(scan->htcap[1],
689178354Ssam		     scan->htcap[0] == IEEE80211_ELEMID_VENDOR ?
690178354Ssam			 4 + sizeof(struct ieee80211_ie_htcap)-2 :
691178354Ssam			 sizeof(struct ieee80211_ie_htcap)-2,
692178354Ssam		     scan->htcap = NULL);
693178354Ssam	}
694178354Ssam	if (scan->htinfo != NULL) {
695178354Ssam		IEEE80211_VERIFY_LENGTH(scan->htinfo[1],
696178354Ssam		     scan->htinfo[0] == IEEE80211_ELEMID_VENDOR ?
697178354Ssam			 4 + sizeof(struct ieee80211_ie_htinfo)-2 :
698178354Ssam			 sizeof(struct ieee80211_ie_htinfo)-2,
699178354Ssam		     scan->htinfo = NULL);
700178354Ssam	}
701178354Ssam	return scan->status;
702178354Ssam}
703116742Ssam
704178354Ssam/*
705178354Ssam * Parse an Action frame.  Return 0 on success, non-zero on failure.
706178354Ssam */
707178354Ssamint
708178354Ssamieee80211_parse_action(struct ieee80211_node *ni, struct mbuf *m)
709178354Ssam{
710178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
711178354Ssam	const struct ieee80211_action *ia;
712178354Ssam	struct ieee80211_frame *wh;
713178354Ssam	uint8_t *frm, *efrm;
714138568Ssam
715178354Ssam	/*
716178354Ssam	 * action frame format:
717178354Ssam	 *	[1] category
718178354Ssam	 *	[1] action
719178354Ssam	 *	[tlv] parameters
720178354Ssam	 */
721178354Ssam	wh = mtod(m, struct ieee80211_frame *);
722178354Ssam	frm = (u_int8_t *)&wh[1];
723178354Ssam	efrm = mtod(m, u_int8_t *) + m->m_len;
724178354Ssam	IEEE80211_VERIFY_LENGTH(efrm - frm,
725178354Ssam		sizeof(struct ieee80211_action), return EINVAL);
726178354Ssam	ia = (const struct ieee80211_action *) frm;
727147215Ssam
728178354Ssam	vap->iv_stats.is_rx_action++;
729178354Ssam	IEEE80211_NODE_STAT(ni, rx_action);
730116742Ssam
731178354Ssam	/* verify frame payloads but defer processing */
732178354Ssam	switch (ia->ia_category) {
733178354Ssam	case IEEE80211_ACTION_CAT_BA:
734178354Ssam		switch (ia->ia_action) {
735178354Ssam		case IEEE80211_ACTION_BA_ADDBA_REQUEST:
736178354Ssam			IEEE80211_VERIFY_LENGTH(efrm - frm,
737178354Ssam			    sizeof(struct ieee80211_action_ba_addbarequest),
738178354Ssam			    return EINVAL);
739167431Ssam			break;
740178354Ssam		case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
741178354Ssam			IEEE80211_VERIFY_LENGTH(efrm - frm,
742178354Ssam			    sizeof(struct ieee80211_action_ba_addbaresponse),
743178354Ssam			    return EINVAL);
744116742Ssam			break;
745178354Ssam		case IEEE80211_ACTION_BA_DELBA:
746178354Ssam			IEEE80211_VERIFY_LENGTH(efrm - frm,
747178354Ssam			    sizeof(struct ieee80211_action_ba_delba),
748178354Ssam			    return EINVAL);
749116742Ssam			break;
750116742Ssam		}
751116742Ssam		break;
752178354Ssam	case IEEE80211_ACTION_CAT_HT:
753178354Ssam		switch (ia->ia_action) {
754178354Ssam		case IEEE80211_ACTION_HT_TXCHWIDTH:
755178354Ssam			IEEE80211_VERIFY_LENGTH(efrm - frm,
756178354Ssam			    sizeof(struct ieee80211_action_ht_txchwidth),
757178354Ssam			    return EINVAL);
758170530Ssam			break;
759178354Ssam		case IEEE80211_ACTION_HT_MIMOPWRSAVE:
760178354Ssam			IEEE80211_VERIFY_LENGTH(efrm - frm,
761178354Ssam			    sizeof(struct ieee80211_action_ht_mimopowersave),
762178354Ssam			    return EINVAL);
763170530Ssam			break;
764170530Ssam		}
765170530Ssam		break;
766232484Sglebius#ifdef IEEE80211_SUPPORT_MESH
767232479Sadrian	case IEEE80211_ACTION_CAT_MESH:
768232479Sadrian		switch (ia->ia_action) {
769232479Sadrian		case IEEE80211_ACTION_MESH_LMETRIC:
770232479Sadrian			/*
771232479Sadrian			 * XXX: verification is true only if we are using
772232479Sadrian			 * Airtime link metric (default)
773232479Sadrian			 */
774232479Sadrian			IEEE80211_VERIFY_LENGTH(efrm - frm,
775232479Sadrian			    sizeof(struct ieee80211_meshlmetric_ie),
776232479Sadrian			    return EINVAL);
777232479Sadrian			break;
778232479Sadrian		case IEEE80211_ACTION_MESH_HWMP:
779232479Sadrian			/* verify something */
780232479Sadrian			break;
781232479Sadrian		case IEEE80211_ACTION_MESH_GANN:
782246504Smonthadar			IEEE80211_VERIFY_LENGTH(efrm - frm,
783246504Smonthadar			    sizeof(struct ieee80211_meshgann_ie),
784246504Smonthadar			    return EINVAL);
785246504Smonthadar			break;
786232479Sadrian		case IEEE80211_ACTION_MESH_CC:
787232479Sadrian		case IEEE80211_ACTION_MESH_MCCA_SREQ:
788232479Sadrian		case IEEE80211_ACTION_MESH_MCCA_SREP:
789232479Sadrian		case IEEE80211_ACTION_MESH_MCCA_AREQ:
790232479Sadrian		case IEEE80211_ACTION_MESH_MCCA_ADVER:
791232479Sadrian		case IEEE80211_ACTION_MESH_MCCA_TRDOWN:
792232479Sadrian		case IEEE80211_ACTION_MESH_TBTT_REQ:
793232479Sadrian		case IEEE80211_ACTION_MESH_TBTT_RES:
794232479Sadrian			/* reject these early on, not implemented */
795232479Sadrian			IEEE80211_DISCARD(vap,
796232479Sadrian			    IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
797232479Sadrian			    wh, NULL, "not implemented yet, act=0x%02X",
798232479Sadrian			    ia->ia_action);
799232479Sadrian			return EINVAL;
800232479Sadrian		}
801232479Sadrian		break;
802234874Smonthadar	case IEEE80211_ACTION_CAT_SELF_PROT:
803234874Smonthadar		/* If TA or RA group address discard silently */
804234874Smonthadar		if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
805234874Smonthadar			IEEE80211_IS_MULTICAST(wh->i_addr2))
806234874Smonthadar			return EINVAL;
807234874Smonthadar		/*
808234874Smonthadar		 * XXX: Should we verify complete length now or it is
809234874Smonthadar		 * to varying in sizes?
810234874Smonthadar		 */
811234874Smonthadar		switch (ia->ia_action) {
812234874Smonthadar		case IEEE80211_ACTION_MESHPEERING_CONFIRM:
813234874Smonthadar		case IEEE80211_ACTION_MESHPEERING_CLOSE:
814234874Smonthadar			/* is not a peering candidate (yet) */
815234874Smonthadar			if (ni == vap->iv_bss)
816234874Smonthadar				return EINVAL;
817234874Smonthadar			break;
818234874Smonthadar		}
819234874Smonthadar		break;
820232484Sglebius#endif
821170530Ssam	}
822178354Ssam	return 0;
823116742Ssam}
824138568Ssam
825178354Ssam#ifdef IEEE80211_DEBUG
826138568Ssam/*
827178354Ssam * Debugging support.
828138568Ssam */
829178354Ssamvoid
830178354Ssamieee80211_ssid_mismatch(struct ieee80211vap *vap, const char *tag,
831178354Ssam	uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid)
832138568Ssam{
833178354Ssam	printf("[%s] discard %s frame, ssid mismatch: ",
834178354Ssam		ether_sprintf(mac), tag);
835178354Ssam	ieee80211_print_essid(ssid + 2, ssid[1]);
836178354Ssam	printf("\n");
837138568Ssam}
838138568Ssam
839138568Ssam/*
840138568Ssam * Return the bssid of a frame.
841138568Ssam */
842170530Ssamstatic const uint8_t *
843205986Srpauloieee80211_getbssid(const struct ieee80211vap *vap,
844205986Srpaulo	const struct ieee80211_frame *wh)
845138568Ssam{
846178354Ssam	if (vap->iv_opmode == IEEE80211_M_STA)
847138568Ssam		return wh->i_addr2;
848138568Ssam	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS)
849138568Ssam		return wh->i_addr1;
850138568Ssam	if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
851138568Ssam		return wh->i_addr1;
852138568Ssam	return wh->i_addr3;
853138568Ssam}
854138568Ssam
855178354Ssam#include <machine/stdarg.h>
856178354Ssam
857148288Ssamvoid
858205986Srpauloieee80211_note(const struct ieee80211vap *vap, const char *fmt, ...)
859148288Ssam{
860148288Ssam	char buf[128];		/* XXX */
861148288Ssam	va_list ap;
862148288Ssam
863148288Ssam	va_start(ap, fmt);
864148288Ssam	vsnprintf(buf, sizeof(buf), fmt, ap);
865148288Ssam	va_end(ap);
866148288Ssam
867178354Ssam	if_printf(vap->iv_ifp, "%s", buf);	/* NB: no \n */
868148288Ssam}
869148288Ssam
870148288Ssamvoid
871205986Srpauloieee80211_note_frame(const struct ieee80211vap *vap,
872148288Ssam	const struct ieee80211_frame *wh,
873148288Ssam	const char *fmt, ...)
874148288Ssam{
875148288Ssam	char buf[128];		/* XXX */
876148288Ssam	va_list ap;
877148288Ssam
878148288Ssam	va_start(ap, fmt);
879148288Ssam	vsnprintf(buf, sizeof(buf), fmt, ap);
880148288Ssam	va_end(ap);
881178354Ssam	if_printf(vap->iv_ifp, "[%s] %s\n",
882178354Ssam		ether_sprintf(ieee80211_getbssid(vap, wh)), buf);
883148288Ssam}
884148288Ssam
885148288Ssamvoid
886205986Srpauloieee80211_note_mac(const struct ieee80211vap *vap,
887170530Ssam	const uint8_t mac[IEEE80211_ADDR_LEN],
888148288Ssam	const char *fmt, ...)
889148288Ssam{
890148288Ssam	char buf[128];		/* XXX */
891148288Ssam	va_list ap;
892148288Ssam
893148288Ssam	va_start(ap, fmt);
894148288Ssam	vsnprintf(buf, sizeof(buf), fmt, ap);
895148288Ssam	va_end(ap);
896178354Ssam	if_printf(vap->iv_ifp, "[%s] %s\n", ether_sprintf(mac), buf);
897148288Ssam}
898148288Ssam
899167441Ssamvoid
900205986Srpauloieee80211_discard_frame(const struct ieee80211vap *vap,
901138568Ssam	const struct ieee80211_frame *wh,
902138568Ssam	const char *type, const char *fmt, ...)
903138568Ssam{
904138568Ssam	va_list ap;
905138568Ssam
906178354Ssam	if_printf(vap->iv_ifp, "[%s] discard ",
907178354Ssam		ether_sprintf(ieee80211_getbssid(vap, wh)));
908178354Ssam	if (type == NULL) {
909178354Ssam		printf("%s frame, ", ieee80211_mgt_subtype_name[
910178354Ssam			(wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) >>
911178354Ssam			IEEE80211_FC0_SUBTYPE_SHIFT]);
912178354Ssam	} else
913148277Ssam		printf("%s frame, ", type);
914138568Ssam	va_start(ap, fmt);
915138568Ssam	vprintf(fmt, ap);
916138568Ssam	va_end(ap);
917138568Ssam	printf("\n");
918138568Ssam}
919138568Ssam
920167441Ssamvoid
921205986Srpauloieee80211_discard_ie(const struct ieee80211vap *vap,
922138568Ssam	const struct ieee80211_frame *wh,
923138568Ssam	const char *type, const char *fmt, ...)
924138568Ssam{
925138568Ssam	va_list ap;
926138568Ssam
927178354Ssam	if_printf(vap->iv_ifp, "[%s] discard ",
928178354Ssam		ether_sprintf(ieee80211_getbssid(vap, wh)));
929138568Ssam	if (type != NULL)
930148277Ssam		printf("%s information element, ", type);
931138568Ssam	else
932148277Ssam		printf("information element, ");
933138568Ssam	va_start(ap, fmt);
934138568Ssam	vprintf(fmt, ap);
935138568Ssam	va_end(ap);
936138568Ssam	printf("\n");
937138568Ssam}
938138568Ssam
939167441Ssamvoid
940205986Srpauloieee80211_discard_mac(const struct ieee80211vap *vap,
941170530Ssam	const uint8_t mac[IEEE80211_ADDR_LEN],
942138568Ssam	const char *type, const char *fmt, ...)
943138568Ssam{
944138568Ssam	va_list ap;
945138568Ssam
946178354Ssam	if_printf(vap->iv_ifp, "[%s] discard ", ether_sprintf(mac));
947138568Ssam	if (type != NULL)
948148277Ssam		printf("%s frame, ", type);
949138568Ssam	else
950148277Ssam		printf("frame, ");
951138568Ssam	va_start(ap, fmt);
952138568Ssam	vprintf(fmt, ap);
953138568Ssam	va_end(ap);
954138568Ssam	printf("\n");
955138568Ssam}
956138568Ssam#endif /* IEEE80211_DEBUG */
957