ieee80211_freebsd.c revision 178354
1138568Ssam/*-
2178354Ssam * Copyright (c) 2003-2008 Sam Leffler, Errno Consulting
3138568Ssam * All rights reserved.
4138568Ssam *
5138568Ssam * Redistribution and use in source and binary forms, with or without
6138568Ssam * modification, are permitted provided that the following conditions
7138568Ssam * are met:
8138568Ssam * 1. Redistributions of source code must retain the above copyright
9138568Ssam *    notice, this list of conditions and the following disclaimer.
10138568Ssam * 2. Redistributions in binary form must reproduce the above copyright
11138568Ssam *    notice, this list of conditions and the following disclaimer in the
12138568Ssam *    documentation and/or other materials provided with the distribution.
13138568Ssam *
14138568Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15138568Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16138568Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17138568Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18138568Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19138568Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20138568Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21138568Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22138568Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23138568Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24138568Ssam */
25138568Ssam
26138568Ssam#include <sys/cdefs.h>
27138568Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_freebsd.c 178354 2008-04-20 20:35:46Z sam $");
28138568Ssam
29138568Ssam/*
30138568Ssam * IEEE 802.11 support (FreeBSD-specific code)
31138568Ssam */
32178354Ssam#include "opt_wlan.h"
33178354Ssam
34138568Ssam#include <sys/param.h>
35138568Ssam#include <sys/kernel.h>
36138568Ssam#include <sys/systm.h>
37138568Ssam#include <sys/linker.h>
38138568Ssam#include <sys/mbuf.h>
39138568Ssam#include <sys/module.h>
40138568Ssam#include <sys/proc.h>
41138568Ssam#include <sys/sysctl.h>
42138568Ssam
43138568Ssam#include <sys/socket.h>
44138568Ssam
45138568Ssam#include <net/if.h>
46178354Ssam#include <net/if_clone.h>
47138568Ssam#include <net/if_media.h>
48178354Ssam#include <net/if_types.h>
49138568Ssam#include <net/ethernet.h>
50138568Ssam#include <net/route.h>
51138568Ssam
52138568Ssam#include <net80211/ieee80211_var.h>
53138568Ssam
54138568SsamSYSCTL_NODE(_net, OID_AUTO, wlan, CTLFLAG_RD, 0, "IEEE 80211 parameters");
55138568Ssam
56138568Ssam#ifdef IEEE80211_DEBUG
57138568Ssamint	ieee80211_debug = 0;
58138568SsamSYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug,
59138568Ssam	    0, "debugging printfs");
60138568Ssam#endif
61173273Ssamextern int ieee80211_recv_bar_ena;
62173273SsamSYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena,
63173273Ssam	    0, "BAR frame processing (ena/dis)");
64178354Ssamextern int ieee80211_nol_timeout;
65178354SsamSYSCTL_INT(_net_wlan, OID_AUTO, nol_timeout, CTLFLAG_RW,
66178354Ssam	&ieee80211_nol_timeout, 0, "NOL timeout (secs)");
67178354Ssamextern int ieee80211_cac_timeout;
68178354SsamSYSCTL_INT(_net_wlan, OID_AUTO, cac_timeout, CTLFLAG_RW,
69178354Ssam	&ieee80211_cac_timeout, 0, "CAC timeout (secs)");
70138568Ssam
71178354SsamMALLOC_DEFINE(M_80211_COM, "80211com", "802.11 com state");
72178354Ssam
73178354Ssam/*
74178354Ssam * Allocate/free com structure in conjunction with ifnet;
75178354Ssam * these routines are registered with if_register_com_alloc
76178354Ssam * below and are called automatically by the ifnet code
77178354Ssam * when the ifnet of the parent device is created.
78178354Ssam */
79178354Ssamstatic void *
80178354Ssamwlan_alloc(u_char type, struct ifnet *ifp)
81178354Ssam{
82178354Ssam	struct ieee80211com *ic;
83178354Ssam
84178354Ssam	ic = malloc(sizeof(struct ieee80211com), M_80211_COM, M_WAITOK|M_ZERO);
85178354Ssam	ic->ic_ifp = ifp;
86178354Ssam
87178354Ssam	return (ic);
88178354Ssam}
89178354Ssam
90178354Ssamstatic void
91178354Ssamwlan_free(void *ic, u_char type)
92178354Ssam{
93178354Ssam	free(ic, M_80211_COM);
94178354Ssam}
95178354Ssam
96138568Ssamstatic int
97178354Ssamwlan_clone_create(struct if_clone *ifc, int unit, caddr_t params)
98173273Ssam{
99178354Ssam	struct ieee80211_clone_params cp;
100178354Ssam	struct ieee80211vap *vap;
101178354Ssam	struct ieee80211com *ic;
102178354Ssam	struct ifnet *ifp;
103173273Ssam	int error;
104173273Ssam
105178354Ssam	error = copyin(params, &cp, sizeof(cp));
106178354Ssam	if (error)
107178354Ssam		return error;
108178354Ssam	ifp = ifunit(cp.icp_parent);
109178354Ssam	if (ifp == NULL)
110178354Ssam		return ENXIO;
111178354Ssam	if (ifp->if_type != IFT_IEEE80211) {
112178354Ssam		if_printf(ifp, "%s: reject, not an 802.11 device\n", __func__);
113178354Ssam		return EINVAL;
114178354Ssam	}
115178354Ssam	ic = ifp->if_l2com;
116178354Ssam	vap = ic->ic_vap_create(ic, ifc->ifc_name, unit,
117178354Ssam			cp.icp_opmode, cp.icp_flags, cp.icp_bssid,
118178354Ssam			cp.icp_flags & IEEE80211_CLONE_MACADDR ?
119178354Ssam			    cp.icp_macaddr : ic->ic_myaddr);
120178354Ssam	return (vap == NULL ? EIO : 0);
121178354Ssam}
122178354Ssam
123178354Ssamstatic void
124178354Ssamwlan_clone_destroy(struct ifnet *ifp)
125178354Ssam{
126178354Ssam	struct ieee80211vap *vap = ifp->if_softc;
127178354Ssam	struct ieee80211com *ic = vap->iv_ic;
128178354Ssam
129178354Ssam	ic->ic_vap_delete(vap);
130178354Ssam}
131178354SsamIFC_SIMPLE_DECLARE(wlan, 0);
132178354Ssam
133178354Ssamvoid
134178354Ssamieee80211_vap_destroy(struct ieee80211vap *vap)
135178354Ssam{
136178354Ssam	ifc_simple_destroy(&wlan_cloner, vap->iv_ifp);
137178354Ssam}
138178354Ssam
139178354Ssamstatic int
140178354Ssamieee80211_sysctl_msecs_ticks(SYSCTL_HANDLER_ARGS)
141178354Ssam{
142178354Ssam	int msecs = ticks_to_msecs(*(int *)arg1);
143178354Ssam	int error, t;
144178354Ssam
145178354Ssam	error = sysctl_handle_int(oidp, &msecs, 0, req);
146173273Ssam	if (error || !req->newptr)
147173273Ssam		return error;
148178354Ssam	t = msecs_to_ticks(msecs);
149178354Ssam	*(int *)arg1 = (t < 1) ? 1 : t;
150173273Ssam	return 0;
151173273Ssam}
152178354Ssam
153178354Ssam#ifdef IEEE80211_AMPDU_AGE
154178354Ssamextern int ieee80211_ampdu_age;
155178354SsamSYSCTL_PROC(_net_wlan, OID_AUTO, ampdu_age, CTLFLAG_RW,
156178354Ssam	&ieee80211_ampdu_age, 0, ieee80211_sysctl_msecs_ticks, "I",
157178354Ssam	"AMPDU max reorder age (ms)");
158173273Ssam#endif
159178354Ssamextern int ieee80211_addba_timeout;
160178354SsamSYSCTL_PROC(_net_wlan, OID_AUTO, addba_timeout, CTLFLAG_RW,
161178354Ssam	&ieee80211_addba_timeout, 0, ieee80211_sysctl_msecs_ticks, "I",
162178354Ssam	"ADDBA request timeout (ms)");
163178354Ssamextern int ieee80211_addba_backoff;
164178354SsamSYSCTL_PROC(_net_wlan, OID_AUTO, addba_backoff, CTLFLAG_RW,
165178354Ssam	&ieee80211_addba_backoff, 0, ieee80211_sysctl_msecs_ticks, "I",
166178354Ssam	"ADDBA request backoff (ms)");
167178354Ssamextern int ieee80211_addba_maxtries;
168178354SsamSYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLFLAG_RW,
169178354Ssam	&ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff");
170173273Ssam
171173273Ssamstatic int
172138568Ssamieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS)
173138568Ssam{
174138568Ssam	int inact = (*(int *)arg1) * IEEE80211_INACT_WAIT;
175138568Ssam	int error;
176138568Ssam
177138568Ssam	error = sysctl_handle_int(oidp, &inact, 0, req);
178138568Ssam	if (error || !req->newptr)
179138568Ssam		return error;
180138568Ssam	*(int *)arg1 = inact / IEEE80211_INACT_WAIT;
181138568Ssam	return 0;
182138568Ssam}
183138568Ssam
184138568Ssamstatic int
185138568Ssamieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS)
186138568Ssam{
187138568Ssam	struct ieee80211com *ic = arg1;
188138568Ssam	const char *name = ic->ic_ifp->if_xname;
189138568Ssam
190138568Ssam	return SYSCTL_OUT(req, name, strlen(name));
191138568Ssam}
192138568Ssam
193138568Ssamvoid
194138568Ssamieee80211_sysctl_attach(struct ieee80211com *ic)
195138568Ssam{
196178354Ssam}
197178354Ssam
198178354Ssamvoid
199178354Ssamieee80211_sysctl_detach(struct ieee80211com *ic)
200178354Ssam{
201178354Ssam}
202178354Ssam
203178354Ssamvoid
204178354Ssamieee80211_sysctl_vattach(struct ieee80211vap *vap)
205178354Ssam{
206178354Ssam	struct ifnet *ifp = vap->iv_ifp;
207138568Ssam	struct sysctl_ctx_list *ctx;
208138568Ssam	struct sysctl_oid *oid;
209138568Ssam	char num[14];			/* sufficient for 32 bits */
210138568Ssam
211138568Ssam	MALLOC(ctx, struct sysctl_ctx_list *, sizeof(struct sysctl_ctx_list),
212138568Ssam		M_DEVBUF, M_NOWAIT | M_ZERO);
213138568Ssam	if (ctx == NULL) {
214178354Ssam		if_printf(ifp, "%s: cannot allocate sysctl context!\n",
215138568Ssam			__func__);
216138568Ssam		return;
217138568Ssam	}
218138568Ssam	sysctl_ctx_init(ctx);
219178354Ssam	snprintf(num, sizeof(num), "%u", ifp->if_dunit);
220138568Ssam	oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan),
221138568Ssam		OID_AUTO, num, CTLFLAG_RD, NULL, "");
222138568Ssam	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
223178354Ssam		"%parent", CTLFLAG_RD, vap->iv_ic, 0,
224178354Ssam		ieee80211_sysctl_parent, "A", "parent device");
225178354Ssam	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
226178354Ssam		"driver_caps", CTLFLAG_RW, &vap->iv_caps, 0,
227178354Ssam		"driver capabilities");
228138568Ssam#ifdef IEEE80211_DEBUG
229178354Ssam	vap->iv_debug = ieee80211_debug;
230138568Ssam	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
231178354Ssam		"debug", CTLFLAG_RW, &vap->iv_debug, 0,
232138568Ssam		"control debugging printfs");
233138568Ssam#endif
234178354Ssam	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
235178354Ssam		"bmiss_max", CTLFLAG_RW, &vap->iv_bmiss_max, 0,
236178354Ssam		"consecutive beacon misses before scanning");
237138568Ssam	/* XXX inherit from tunables */
238138568Ssam	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
239178354Ssam		"inact_run", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_run, 0,
240138568Ssam		ieee80211_sysctl_inact, "I",
241138568Ssam		"station inactivity timeout (sec)");
242138568Ssam	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
243178354Ssam		"inact_probe", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_probe, 0,
244138568Ssam		ieee80211_sysctl_inact, "I",
245138568Ssam		"station inactivity probe timeout (sec)");
246138568Ssam	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
247178354Ssam		"inact_auth", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_auth, 0,
248138568Ssam		ieee80211_sysctl_inact, "I",
249138568Ssam		"station authentication timeout (sec)");
250138568Ssam	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
251178354Ssam		"inact_init", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_init, 0,
252138568Ssam		ieee80211_sysctl_inact, "I",
253138568Ssam		"station initial state timeout (sec)");
254178354Ssam	if (vap->iv_htcaps & IEEE80211_HTC_HT) {
255178354Ssam		SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
256178354Ssam			"ampdu_mintraffic_bk", CTLFLAG_RW,
257178354Ssam			&vap->iv_ampdu_mintraffic[WME_AC_BK], 0,
258178354Ssam			"BK traffic tx aggr threshold (pps)");
259178354Ssam		SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
260178354Ssam			"ampdu_mintraffic_be", CTLFLAG_RW,
261178354Ssam			&vap->iv_ampdu_mintraffic[WME_AC_BE], 0,
262178354Ssam			"BE traffic tx aggr threshold (pps)");
263178354Ssam		SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
264178354Ssam			"ampdu_mintraffic_vo", CTLFLAG_RW,
265178354Ssam			&vap->iv_ampdu_mintraffic[WME_AC_VO], 0,
266178354Ssam			"VO traffic tx aggr threshold (pps)");
267178354Ssam		SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
268178354Ssam			"ampdu_mintraffic_vi", CTLFLAG_RW,
269178354Ssam			&vap->iv_ampdu_mintraffic[WME_AC_VI], 0,
270178354Ssam			"VI traffic tx aggr threshold (pps)");
271178354Ssam	}
272178354Ssam	vap->iv_sysctl = ctx;
273178354Ssam	vap->iv_oid = oid;
274138568Ssam}
275138568Ssam
276138568Ssamvoid
277178354Ssamieee80211_sysctl_vdetach(struct ieee80211vap *vap)
278138568Ssam{
279138568Ssam
280178354Ssam	if (vap->iv_sysctl != NULL) {
281178354Ssam		sysctl_ctx_free(vap->iv_sysctl);
282178354Ssam		FREE(vap->iv_sysctl, M_DEVBUF);
283178354Ssam		vap->iv_sysctl = NULL;
284138568Ssam	}
285138568Ssam}
286138568Ssam
287138568Ssamint
288138568Ssamieee80211_node_dectestref(struct ieee80211_node *ni)
289138568Ssam{
290138568Ssam	/* XXX need equivalent of atomic_dec_and_test */
291138568Ssam	atomic_subtract_int(&ni->ni_refcnt, 1);
292138568Ssam	return atomic_cmpset_int(&ni->ni_refcnt, 0, 1);
293138568Ssam}
294138568Ssam
295165894Ssamvoid
296165894Ssamieee80211_drain_ifq(struct ifqueue *ifq)
297165894Ssam{
298165894Ssam	struct ieee80211_node *ni;
299165894Ssam	struct mbuf *m;
300165894Ssam
301165894Ssam	for (;;) {
302165894Ssam		IF_DEQUEUE(ifq, m);
303165894Ssam		if (m == NULL)
304165894Ssam			break;
305165894Ssam
306165894Ssam		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
307165894Ssam		KASSERT(ni != NULL, ("frame w/o node"));
308165894Ssam		ieee80211_free_node(ni);
309165894Ssam		m->m_pkthdr.rcvif = NULL;
310165894Ssam
311165894Ssam		m_freem(m);
312165894Ssam	}
313165894Ssam}
314165894Ssam
315178354Ssamvoid
316178354Ssamieee80211_flush_ifq(struct ifqueue *ifq, struct ieee80211vap *vap)
317178354Ssam{
318178354Ssam	struct ieee80211_node *ni;
319178354Ssam	struct mbuf *m, **mprev;
320178354Ssam
321178354Ssam	IF_LOCK(ifq);
322178354Ssam	mprev = &ifq->ifq_head;
323178354Ssam	while ((m = *mprev) != NULL) {
324178354Ssam		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
325178354Ssam		if (ni != NULL && ni->ni_vap == vap) {
326178354Ssam			*mprev = m->m_nextpkt;		/* remove from list */
327178354Ssam			ifq->ifq_len--;
328178354Ssam
329178354Ssam			m_freem(m);
330178354Ssam			ieee80211_free_node(ni);	/* reclaim ref */
331178354Ssam		} else
332178354Ssam			mprev = &m->m_nextpkt;
333178354Ssam	}
334178354Ssam	/* recalculate tail ptr */
335178354Ssam	m = ifq->ifq_head;
336178354Ssam	for (; m != NULL && m->m_nextpkt != NULL; m = m->m_nextpkt)
337178354Ssam		;
338178354Ssam	ifq->ifq_tail = m;
339178354Ssam	IF_UNLOCK(ifq);
340178354Ssam}
341178354Ssam
342138568Ssam/*
343170530Ssam * As above, for mbufs allocated with m_gethdr/MGETHDR
344170530Ssam * or initialized by M_COPY_PKTHDR.
345170530Ssam */
346170530Ssam#define	MC_ALIGN(m, len)						\
347170530Ssamdo {									\
348170530Ssam	(m)->m_data += (MCLBYTES - (len)) &~ (sizeof(long) - 1);	\
349170530Ssam} while (/* CONSTCOND */ 0)
350170530Ssam
351170530Ssam/*
352138568Ssam * Allocate and setup a management frame of the specified
353138568Ssam * size.  We return the mbuf and a pointer to the start
354138568Ssam * of the contiguous data area that's been reserved based
355138568Ssam * on the packet length.  The data area is forced to 32-bit
356138568Ssam * alignment and the buffer length to a multiple of 4 bytes.
357138568Ssam * This is done mainly so beacon frames (that require this)
358138568Ssam * can use this interface too.
359138568Ssam */
360138568Ssamstruct mbuf *
361170530Ssamieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen)
362138568Ssam{
363138568Ssam	struct mbuf *m;
364138568Ssam	u_int len;
365138568Ssam
366138568Ssam	/*
367138568Ssam	 * NB: we know the mbuf routines will align the data area
368138568Ssam	 *     so we don't need to do anything special.
369138568Ssam	 */
370170530Ssam	len = roundup2(headroom + pktlen, 4);
371138568Ssam	KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len));
372138568Ssam	if (len < MINCLSIZE) {
373151967Sandre		m = m_gethdr(M_NOWAIT, MT_DATA);
374138568Ssam		/*
375138568Ssam		 * Align the data in case additional headers are added.
376138568Ssam		 * This should only happen when a WEP header is added
377138568Ssam		 * which only happens for shared key authentication mgt
378138568Ssam		 * frames which all fit in MHLEN.
379138568Ssam		 */
380138568Ssam		if (m != NULL)
381138568Ssam			MH_ALIGN(m, len);
382170530Ssam	} else {
383151967Sandre		m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
384170530Ssam		if (m != NULL)
385170530Ssam			MC_ALIGN(m, len);
386170530Ssam	}
387138568Ssam	if (m != NULL) {
388171984Ssephe		m->m_data += headroom;
389138568Ssam		*frm = m->m_data;
390138568Ssam	}
391138568Ssam	return m;
392138568Ssam}
393138568Ssam
394170530Ssamint
395170530Ssamieee80211_add_callback(struct mbuf *m,
396170530Ssam	void (*func)(struct ieee80211_node *, void *, int), void *arg)
397170530Ssam{
398170530Ssam	struct m_tag *mtag;
399170530Ssam	struct ieee80211_cb *cb;
400170530Ssam
401170530Ssam	mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_CALLBACK,
402170530Ssam			sizeof(struct ieee80211_cb), M_NOWAIT);
403170530Ssam	if (mtag == NULL)
404170530Ssam		return 0;
405170530Ssam
406170530Ssam	cb = (struct ieee80211_cb *)(mtag+1);
407170530Ssam	cb->func = func;
408170530Ssam	cb->arg = arg;
409170530Ssam	m_tag_prepend(m, mtag);
410170530Ssam	m->m_flags |= M_TXCB;
411170530Ssam	return 1;
412170530Ssam}
413170530Ssam
414170530Ssamvoid
415170530Ssamieee80211_process_callback(struct ieee80211_node *ni,
416170530Ssam	struct mbuf *m, int status)
417170530Ssam{
418170530Ssam	struct m_tag *mtag;
419170530Ssam
420170530Ssam	mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_CALLBACK, NULL);
421170530Ssam	if (mtag != NULL) {
422170530Ssam		struct ieee80211_cb *cb = (struct ieee80211_cb *)(mtag+1);
423170530Ssam		cb->func(ni, cb->arg, status);
424170530Ssam	}
425170530Ssam}
426170530Ssam
427138568Ssam#include <sys/libkern.h>
428138568Ssam
429138568Ssamvoid
430138568Ssamget_random_bytes(void *p, size_t n)
431138568Ssam{
432170530Ssam	uint8_t *dp = p;
433138568Ssam
434138568Ssam	while (n > 0) {
435170530Ssam		uint32_t v = arc4random();
436170530Ssam		size_t nb = n > sizeof(uint32_t) ? sizeof(uint32_t) : n;
437170530Ssam		bcopy(&v, dp, n > sizeof(uint32_t) ? sizeof(uint32_t) : n);
438170530Ssam		dp += sizeof(uint32_t), n -= nb;
439138568Ssam	}
440138568Ssam}
441138568Ssam
442178354Ssam/*
443178354Ssam * Helper function for events that pass just a single mac address.
444178354Ssam */
445178354Ssamstatic void
446178354Ssamnotify_macaddr(struct ifnet *ifp, int op, const uint8_t mac[IEEE80211_ADDR_LEN])
447138568Ssam{
448138568Ssam	struct ieee80211_join_event iev;
449138568Ssam
450144302Ssam	memset(&iev, 0, sizeof(iev));
451178354Ssam	IEEE80211_ADDR_COPY(iev.iev_addr, mac);
452178354Ssam	rt_ieee80211msg(ifp, op, &iev, sizeof(iev));
453178354Ssam}
454178354Ssam
455178354Ssamvoid
456178354Ssamieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc)
457178354Ssam{
458178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
459178354Ssam	struct ifnet *ifp = vap->iv_ifp;
460178354Ssam
461178354Ssam	IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode join",
462178354Ssam	    (ni == vap->iv_bss) ? "bss " : "");
463178354Ssam
464178354Ssam	if (ni == vap->iv_bss) {
465178354Ssam		notify_macaddr(ifp, newassoc ?
466178354Ssam		    RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC, ni->ni_bssid);
467138568Ssam		if_link_state_change(ifp, LINK_STATE_UP);
468144302Ssam	} else {
469178354Ssam		notify_macaddr(ifp, newassoc ?
470178354Ssam		    RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN, ni->ni_macaddr);
471138568Ssam	}
472138568Ssam}
473138568Ssam
474138568Ssamvoid
475178354Ssamieee80211_notify_node_leave(struct ieee80211_node *ni)
476138568Ssam{
477178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
478178354Ssam	struct ifnet *ifp = vap->iv_ifp;
479138568Ssam
480178354Ssam	IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode leave",
481178354Ssam	    (ni == vap->iv_bss) ? "bss " : "");
482178354Ssam
483178354Ssam	if (ni == vap->iv_bss) {
484138568Ssam		rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0);
485138568Ssam		if_link_state_change(ifp, LINK_STATE_DOWN);
486138568Ssam	} else {
487138568Ssam		/* fire off wireless event station leaving */
488178354Ssam		notify_macaddr(ifp, RTM_IEEE80211_LEAVE, ni->ni_macaddr);
489138568Ssam	}
490138568Ssam}
491138568Ssam
492138568Ssamvoid
493178354Ssamieee80211_notify_scan_done(struct ieee80211vap *vap)
494138568Ssam{
495178354Ssam	struct ifnet *ifp = vap->iv_ifp;
496138568Ssam
497178354Ssam	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", "notify scan done");
498138568Ssam
499138568Ssam	/* dispatch wireless event indicating scan completed */
500138568Ssam	rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0);
501138568Ssam}
502138568Ssam
503138568Ssamvoid
504178354Ssamieee80211_notify_replay_failure(struct ieee80211vap *vap,
505138568Ssam	const struct ieee80211_frame *wh, const struct ieee80211_key *k,
506138568Ssam	u_int64_t rsc)
507138568Ssam{
508178354Ssam	struct ifnet *ifp = vap->iv_ifp;
509138568Ssam
510178354Ssam	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
511178354Ssam	    "%s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>",
512178354Ssam	    k->wk_cipher->ic_name, (intmax_t) rsc,
513178354Ssam	    (intmax_t) k->wk_keyrsc[IEEE80211_NONQOS_TID],
514148863Ssam	    k->wk_keyix, k->wk_rxkeyix);
515138568Ssam
516138568Ssam	if (ifp != NULL) {		/* NB: for cipher test modules */
517138568Ssam		struct ieee80211_replay_event iev;
518138568Ssam
519138568Ssam		IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1);
520138568Ssam		IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2);
521138568Ssam		iev.iev_cipher = k->wk_cipher->ic_cipher;
522148863Ssam		if (k->wk_rxkeyix != IEEE80211_KEYIX_NONE)
523148863Ssam			iev.iev_keyix = k->wk_rxkeyix;
524148863Ssam		else
525148863Ssam			iev.iev_keyix = k->wk_keyix;
526178354Ssam		iev.iev_keyrsc = k->wk_keyrsc[0];	/* XXX need tid */
527138568Ssam		iev.iev_rsc = rsc;
528138568Ssam		rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev));
529138568Ssam	}
530138568Ssam}
531138568Ssam
532138568Ssamvoid
533178354Ssamieee80211_notify_michael_failure(struct ieee80211vap *vap,
534138568Ssam	const struct ieee80211_frame *wh, u_int keyix)
535138568Ssam{
536178354Ssam	struct ifnet *ifp = vap->iv_ifp;
537138568Ssam
538178354Ssam	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
539178354Ssam	    "michael MIC verification failed <keyix %u>", keyix);
540178354Ssam	vap->iv_stats.is_rx_tkipmic++;
541138568Ssam
542138568Ssam	if (ifp != NULL) {		/* NB: for cipher test modules */
543138568Ssam		struct ieee80211_michael_event iev;
544138568Ssam
545138568Ssam		IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1);
546138568Ssam		IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2);
547138568Ssam		iev.iev_cipher = IEEE80211_CIPHER_TKIP;
548138568Ssam		iev.iev_keyix = keyix;
549138568Ssam		rt_ieee80211msg(ifp, RTM_IEEE80211_MICHAEL, &iev, sizeof(iev));
550138568Ssam	}
551138568Ssam}
552138568Ssam
553138568Ssamvoid
554178354Ssamieee80211_notify_wds_discover(struct ieee80211_node *ni)
555178354Ssam{
556178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
557178354Ssam	struct ifnet *ifp = vap->iv_ifp;
558178354Ssam
559178354Ssam	notify_macaddr(ifp, RTM_IEEE80211_WDS, ni->ni_macaddr);
560178354Ssam}
561178354Ssam
562178354Ssamvoid
563178354Ssamieee80211_notify_csa(struct ieee80211com *ic,
564178354Ssam	const struct ieee80211_channel *c, int mode, int count)
565178354Ssam{
566178354Ssam	struct ifnet *ifp = ic->ic_ifp;
567178354Ssam	struct ieee80211_csa_event iev;
568178354Ssam
569178354Ssam	memset(&iev, 0, sizeof(iev));
570178354Ssam	iev.iev_flags = c->ic_flags;
571178354Ssam	iev.iev_freq = c->ic_freq;
572178354Ssam	iev.iev_ieee = c->ic_ieee;
573178354Ssam	iev.iev_mode = mode;
574178354Ssam	iev.iev_count = count;
575178354Ssam	rt_ieee80211msg(ifp, RTM_IEEE80211_CSA, &iev, sizeof(iev));
576178354Ssam}
577178354Ssam
578178354Ssamvoid
579178354Ssamieee80211_notify_radar(struct ieee80211com *ic,
580178354Ssam	const struct ieee80211_channel *c)
581178354Ssam{
582178354Ssam	struct ifnet *ifp = ic->ic_ifp;
583178354Ssam	struct ieee80211_radar_event iev;
584178354Ssam
585178354Ssam	memset(&iev, 0, sizeof(iev));
586178354Ssam	iev.iev_flags = c->ic_flags;
587178354Ssam	iev.iev_freq = c->ic_freq;
588178354Ssam	iev.iev_ieee = c->ic_ieee;
589178354Ssam	rt_ieee80211msg(ifp, RTM_IEEE80211_RADAR, &iev, sizeof(iev));
590178354Ssam}
591178354Ssam
592178354Ssamvoid
593178354Ssamieee80211_notify_cac(struct ieee80211com *ic,
594178354Ssam	const struct ieee80211_channel *c, enum ieee80211_notify_cac_event type)
595178354Ssam{
596178354Ssam	struct ifnet *ifp = ic->ic_ifp;
597178354Ssam	struct ieee80211_cac_event iev;
598178354Ssam
599178354Ssam	memset(&iev, 0, sizeof(iev));
600178354Ssam	iev.iev_flags = c->ic_flags;
601178354Ssam	iev.iev_freq = c->ic_freq;
602178354Ssam	iev.iev_ieee = c->ic_ieee;
603178354Ssam	iev.iev_type = type;
604178354Ssam	rt_ieee80211msg(ifp, RTM_IEEE80211_CAC, &iev, sizeof(iev));
605178354Ssam}
606178354Ssam
607178354Ssamvoid
608178354Ssamieee80211_notify_node_deauth(struct ieee80211_node *ni)
609178354Ssam{
610178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
611178354Ssam	struct ifnet *ifp = vap->iv_ifp;
612178354Ssam
613178354Ssam	IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node deauth");
614178354Ssam
615178354Ssam	notify_macaddr(ifp, RTM_IEEE80211_DEAUTH, ni->ni_macaddr);
616178354Ssam}
617178354Ssam
618178354Ssamvoid
619178354Ssamieee80211_notify_node_auth(struct ieee80211_node *ni)
620178354Ssam{
621178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
622178354Ssam	struct ifnet *ifp = vap->iv_ifp;
623178354Ssam
624178354Ssam	IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node auth");
625178354Ssam
626178354Ssam	notify_macaddr(ifp, RTM_IEEE80211_AUTH, ni->ni_macaddr);
627178354Ssam}
628178354Ssam
629178354Ssamvoid
630178354Ssamieee80211_notify_country(struct ieee80211vap *vap,
631178354Ssam	const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t cc[2])
632178354Ssam{
633178354Ssam	struct ifnet *ifp = vap->iv_ifp;
634178354Ssam	struct ieee80211_country_event iev;
635178354Ssam
636178354Ssam	memset(&iev, 0, sizeof(iev));
637178354Ssam	IEEE80211_ADDR_COPY(iev.iev_addr, bssid);
638178354Ssam	iev.iev_cc[0] = cc[0];
639178354Ssam	iev.iev_cc[1] = cc[1];
640178354Ssam	rt_ieee80211msg(ifp, RTM_IEEE80211_COUNTRY, &iev, sizeof(iev));
641178354Ssam}
642178354Ssam
643178354Ssamvoid
644178354Ssamieee80211_notify_radio(struct ieee80211com *ic, int state)
645178354Ssam{
646178354Ssam	struct ifnet *ifp = ic->ic_ifp;
647178354Ssam	struct ieee80211_radio_event iev;
648178354Ssam
649178354Ssam	memset(&iev, 0, sizeof(iev));
650178354Ssam	iev.iev_state = state;
651178354Ssam	rt_ieee80211msg(ifp, RTM_IEEE80211_RADIO, &iev, sizeof(iev));
652178354Ssam}
653178354Ssam
654178354Ssamvoid
655138568Ssamieee80211_load_module(const char *modname)
656138568Ssam{
657159590Sjhb
658138777Ssam#ifdef notyet
659159590Sjhb	(void)kern_kldload(curthread, modname, NULL);
660138777Ssam#else
661138777Ssam	printf("%s: load the %s module by hand for now.\n", __func__, modname);
662138777Ssam#endif
663138568Ssam}
664138568Ssam
665138568Ssam/*
666138568Ssam * Module glue.
667138568Ssam *
668138568Ssam * NB: the module name is "wlan" for compatibility with NetBSD.
669138568Ssam */
670138568Ssamstatic int
671138568Ssamwlan_modevent(module_t mod, int type, void *unused)
672138568Ssam{
673138568Ssam	switch (type) {
674138568Ssam	case MOD_LOAD:
675138568Ssam		if (bootverbose)
676138568Ssam			printf("wlan: <802.11 Link Layer>\n");
677178354Ssam		if_clone_attach(&wlan_cloner);
678178354Ssam		if_register_com_alloc(IFT_IEEE80211, wlan_alloc, wlan_free);
679138568Ssam		return 0;
680138568Ssam	case MOD_UNLOAD:
681178354Ssam		if_deregister_com_alloc(IFT_IEEE80211);
682178354Ssam		if_clone_detach(&wlan_cloner);
683138568Ssam		return 0;
684138568Ssam	}
685138568Ssam	return EINVAL;
686138568Ssam}
687138568Ssam
688138568Ssamstatic moduledata_t wlan_mod = {
689138568Ssam	"wlan",
690138568Ssam	wlan_modevent,
691138568Ssam	0
692138568Ssam};
693138568SsamDECLARE_MODULE(wlan, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
694138568SsamMODULE_VERSION(wlan, 1);
695138568SsamMODULE_DEPEND(wlan, ether, 1, 1, 1);
696