xref: /freebsd/sys/net80211/ieee80211_freebsd.c (revision 1b6167d23905ef392a200c3cf17c0b59d5461bb9)
18a1b9b6aSSam Leffler /*-
2ae8b7333SSam Leffler  * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting
38a1b9b6aSSam Leffler  * All rights reserved.
48a1b9b6aSSam Leffler  *
58a1b9b6aSSam Leffler  * Redistribution and use in source and binary forms, with or without
68a1b9b6aSSam Leffler  * modification, are permitted provided that the following conditions
78a1b9b6aSSam Leffler  * are met:
88a1b9b6aSSam Leffler  * 1. Redistributions of source code must retain the above copyright
98a1b9b6aSSam Leffler  *    notice, this list of conditions and the following disclaimer.
108a1b9b6aSSam Leffler  * 2. Redistributions in binary form must reproduce the above copyright
118a1b9b6aSSam Leffler  *    notice, this list of conditions and the following disclaimer in the
128a1b9b6aSSam Leffler  *    documentation and/or other materials provided with the distribution.
138a1b9b6aSSam Leffler  *
148a1b9b6aSSam Leffler  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
158a1b9b6aSSam Leffler  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
168a1b9b6aSSam Leffler  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
178a1b9b6aSSam Leffler  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
188a1b9b6aSSam Leffler  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
198a1b9b6aSSam Leffler  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
208a1b9b6aSSam Leffler  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
218a1b9b6aSSam Leffler  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
228a1b9b6aSSam Leffler  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
238a1b9b6aSSam Leffler  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
248a1b9b6aSSam Leffler  */
258a1b9b6aSSam Leffler 
268a1b9b6aSSam Leffler #include <sys/cdefs.h>
278a1b9b6aSSam Leffler __FBSDID("$FreeBSD$");
288a1b9b6aSSam Leffler 
298a1b9b6aSSam Leffler /*
308a1b9b6aSSam Leffler  * IEEE 802.11 support (FreeBSD-specific code)
318a1b9b6aSSam Leffler  */
328a1b9b6aSSam Leffler #include <sys/param.h>
338a1b9b6aSSam Leffler #include <sys/kernel.h>
348a1b9b6aSSam Leffler #include <sys/systm.h>
358a1b9b6aSSam Leffler #include <sys/linker.h>
368a1b9b6aSSam Leffler #include <sys/mbuf.h>
378a1b9b6aSSam Leffler #include <sys/module.h>
388a1b9b6aSSam Leffler #include <sys/proc.h>
398a1b9b6aSSam Leffler #include <sys/sysctl.h>
408a1b9b6aSSam Leffler 
418a1b9b6aSSam Leffler #include <sys/socket.h>
428a1b9b6aSSam Leffler 
438a1b9b6aSSam Leffler #include <net/if.h>
448a1b9b6aSSam Leffler #include <net/if_media.h>
458a1b9b6aSSam Leffler #include <net/ethernet.h>
468a1b9b6aSSam Leffler #include <net/route.h>
478a1b9b6aSSam Leffler 
488a1b9b6aSSam Leffler #include <net80211/ieee80211_var.h>
498a1b9b6aSSam Leffler 
508a1b9b6aSSam Leffler SYSCTL_NODE(_net, OID_AUTO, wlan, CTLFLAG_RD, 0, "IEEE 80211 parameters");
518a1b9b6aSSam Leffler 
528a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG
538a1b9b6aSSam Leffler int	ieee80211_debug = 0;
548a1b9b6aSSam Leffler SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug,
558a1b9b6aSSam Leffler 	    0, "debugging printfs");
568a1b9b6aSSam Leffler #endif
571b6167d2SSam Leffler extern int ieee80211_recv_bar_ena;
581b6167d2SSam Leffler SYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena,
591b6167d2SSam Leffler 	    0, "BAR frame processing (ena/dis)");
601b6167d2SSam Leffler 
611b6167d2SSam Leffler #ifdef IEEE80211_AMPDU_AGE
621b6167d2SSam Leffler static int
631b6167d2SSam Leffler ieee80211_sysctl_ampdu_age(SYSCTL_HANDLER_ARGS)
641b6167d2SSam Leffler {
651b6167d2SSam Leffler 	extern int ieee80211_ampdu_age;
661b6167d2SSam Leffler 	int ampdu_age = ticks_to_msecs(ieee80211_ampdu_age);
671b6167d2SSam Leffler 	int error;
681b6167d2SSam Leffler 
691b6167d2SSam Leffler 	error = sysctl_handle_int(oidp, &ampdu_age, 0, req);
701b6167d2SSam Leffler 	if (error || !req->newptr)
711b6167d2SSam Leffler 		return error;
721b6167d2SSam Leffler 	ieee80211_ampdu_age = msecs_to_ticks(ampdu_age);
731b6167d2SSam Leffler 	return 0;
741b6167d2SSam Leffler }
751b6167d2SSam Leffler SYSCTL_PROC(_net_wlan, OID_AUTO, "ampdu_age", CTLFLAG_RW, NULL, 0,
761b6167d2SSam Leffler 	ieee80211_sysctl_ampdu_age, "A", "AMPDU max reorder age (ms)");
771b6167d2SSam Leffler #endif
788a1b9b6aSSam Leffler 
798a1b9b6aSSam Leffler static int
808a1b9b6aSSam Leffler ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS)
818a1b9b6aSSam Leffler {
828a1b9b6aSSam Leffler 	int inact = (*(int *)arg1) * IEEE80211_INACT_WAIT;
838a1b9b6aSSam Leffler 	int error;
848a1b9b6aSSam Leffler 
858a1b9b6aSSam Leffler 	error = sysctl_handle_int(oidp, &inact, 0, req);
868a1b9b6aSSam Leffler 	if (error || !req->newptr)
878a1b9b6aSSam Leffler 		return error;
888a1b9b6aSSam Leffler 	*(int *)arg1 = inact / IEEE80211_INACT_WAIT;
898a1b9b6aSSam Leffler 	return 0;
908a1b9b6aSSam Leffler }
918a1b9b6aSSam Leffler 
928a1b9b6aSSam Leffler static int
938a1b9b6aSSam Leffler ieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS)
948a1b9b6aSSam Leffler {
958a1b9b6aSSam Leffler 	struct ieee80211com *ic = arg1;
968a1b9b6aSSam Leffler 	const char *name = ic->ic_ifp->if_xname;
978a1b9b6aSSam Leffler 
988a1b9b6aSSam Leffler 	return SYSCTL_OUT(req, name, strlen(name));
998a1b9b6aSSam Leffler }
1008a1b9b6aSSam Leffler 
1018a1b9b6aSSam Leffler void
1028a1b9b6aSSam Leffler ieee80211_sysctl_attach(struct ieee80211com *ic)
1038a1b9b6aSSam Leffler {
1048a1b9b6aSSam Leffler 	struct sysctl_ctx_list *ctx;
1058a1b9b6aSSam Leffler 	struct sysctl_oid *oid;
1068a1b9b6aSSam Leffler 	char num[14];			/* sufficient for 32 bits */
1078a1b9b6aSSam Leffler 
1088a1b9b6aSSam Leffler 	MALLOC(ctx, struct sysctl_ctx_list *, sizeof(struct sysctl_ctx_list),
1098a1b9b6aSSam Leffler 		M_DEVBUF, M_NOWAIT | M_ZERO);
1108a1b9b6aSSam Leffler 	if (ctx == NULL) {
1118a1b9b6aSSam Leffler 		if_printf(ic->ic_ifp, "%s: cannot allocate sysctl context!\n",
1128a1b9b6aSSam Leffler 			__func__);
1138a1b9b6aSSam Leffler 		return;
1148a1b9b6aSSam Leffler 	}
1158a1b9b6aSSam Leffler 	sysctl_ctx_init(ctx);
1168a1b9b6aSSam Leffler 	snprintf(num, sizeof(num), "%u", ic->ic_vap);
1178a1b9b6aSSam Leffler 	oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan),
1188a1b9b6aSSam Leffler 		OID_AUTO, num, CTLFLAG_RD, NULL, "");
1198a1b9b6aSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1208a1b9b6aSSam Leffler 		"%parent", CTLFLAG_RD, ic, 0, ieee80211_sysctl_parent, "A",
1218a1b9b6aSSam Leffler 		"parent device");
1228a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG
1238a1b9b6aSSam Leffler 	ic->ic_debug = ieee80211_debug;
1248a1b9b6aSSam Leffler 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1258a1b9b6aSSam Leffler 		"debug", CTLFLAG_RW, &ic->ic_debug, 0,
1268a1b9b6aSSam Leffler 		"control debugging printfs");
1278a1b9b6aSSam Leffler #endif
1288a1b9b6aSSam Leffler 	/* XXX inherit from tunables */
1298a1b9b6aSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
130e5a96ac7SSam Leffler 		"inact_run", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_run, 0,
1318a1b9b6aSSam Leffler 		ieee80211_sysctl_inact, "I",
1328a1b9b6aSSam Leffler 		"station inactivity timeout (sec)");
1338a1b9b6aSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1348a1b9b6aSSam Leffler 		"inact_probe", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_probe, 0,
1358a1b9b6aSSam Leffler 		ieee80211_sysctl_inact, "I",
1368a1b9b6aSSam Leffler 		"station inactivity probe timeout (sec)");
1378a1b9b6aSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1388a1b9b6aSSam Leffler 		"inact_auth", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_auth, 0,
1398a1b9b6aSSam Leffler 		ieee80211_sysctl_inact, "I",
1408a1b9b6aSSam Leffler 		"station authentication timeout (sec)");
1418a1b9b6aSSam Leffler 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
1428a1b9b6aSSam Leffler 		"inact_init", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_init, 0,
1438a1b9b6aSSam Leffler 		ieee80211_sysctl_inact, "I",
1448a1b9b6aSSam Leffler 		"station initial state timeout (sec)");
145da615e95SSam Leffler 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
146da615e95SSam Leffler 		"driver_caps", CTLFLAG_RW, &ic->ic_caps, 0,
147da615e95SSam Leffler 		"driver capabilities");
148e701e041SSam Leffler 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
149e701e041SSam Leffler 		"bmiss_max", CTLFLAG_RW, &ic->ic_bmiss_max, 0,
150e701e041SSam Leffler 		"consecutive beacon misses before scanning");
1518a1b9b6aSSam Leffler 	ic->ic_sysctl = ctx;
1528a1b9b6aSSam Leffler }
1538a1b9b6aSSam Leffler 
1548a1b9b6aSSam Leffler void
1558a1b9b6aSSam Leffler ieee80211_sysctl_detach(struct ieee80211com *ic)
1568a1b9b6aSSam Leffler {
1578a1b9b6aSSam Leffler 
1588a1b9b6aSSam Leffler 	if (ic->ic_sysctl != NULL) {
1598a1b9b6aSSam Leffler 		sysctl_ctx_free(ic->ic_sysctl);
16098b33550SSepherosa Ziehau 		FREE(ic->ic_sysctl, M_DEVBUF);
1618a1b9b6aSSam Leffler 		ic->ic_sysctl = NULL;
1628a1b9b6aSSam Leffler 	}
1638a1b9b6aSSam Leffler }
1648a1b9b6aSSam Leffler 
1658a1b9b6aSSam Leffler int
1668a1b9b6aSSam Leffler ieee80211_node_dectestref(struct ieee80211_node *ni)
1678a1b9b6aSSam Leffler {
1688a1b9b6aSSam Leffler 	/* XXX need equivalent of atomic_dec_and_test */
1698a1b9b6aSSam Leffler 	atomic_subtract_int(&ni->ni_refcnt, 1);
1708a1b9b6aSSam Leffler 	return atomic_cmpset_int(&ni->ni_refcnt, 0, 1);
1718a1b9b6aSSam Leffler }
1728a1b9b6aSSam Leffler 
173915f1482SSam Leffler void
174915f1482SSam Leffler ieee80211_drain_ifq(struct ifqueue *ifq)
175915f1482SSam Leffler {
176915f1482SSam Leffler 	struct ieee80211_node *ni;
177915f1482SSam Leffler 	struct mbuf *m;
178915f1482SSam Leffler 
179915f1482SSam Leffler 	for (;;) {
180915f1482SSam Leffler 		IF_DEQUEUE(ifq, m);
181915f1482SSam Leffler 		if (m == NULL)
182915f1482SSam Leffler 			break;
183915f1482SSam Leffler 
184915f1482SSam Leffler 		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
185915f1482SSam Leffler 		KASSERT(ni != NULL, ("frame w/o node"));
186915f1482SSam Leffler 		ieee80211_free_node(ni);
187915f1482SSam Leffler 		m->m_pkthdr.rcvif = NULL;
188915f1482SSam Leffler 
189915f1482SSam Leffler 		m_freem(m);
190915f1482SSam Leffler 	}
191915f1482SSam Leffler }
192915f1482SSam Leffler 
1938a1b9b6aSSam Leffler /*
19468e8e04eSSam Leffler  * As above, for mbufs allocated with m_gethdr/MGETHDR
19568e8e04eSSam Leffler  * or initialized by M_COPY_PKTHDR.
19668e8e04eSSam Leffler  */
19768e8e04eSSam Leffler #define	MC_ALIGN(m, len)						\
19868e8e04eSSam Leffler do {									\
19968e8e04eSSam Leffler 	(m)->m_data += (MCLBYTES - (len)) &~ (sizeof(long) - 1);	\
20068e8e04eSSam Leffler } while (/* CONSTCOND */ 0)
20168e8e04eSSam Leffler 
20268e8e04eSSam Leffler /*
2038a1b9b6aSSam Leffler  * Allocate and setup a management frame of the specified
2048a1b9b6aSSam Leffler  * size.  We return the mbuf and a pointer to the start
2058a1b9b6aSSam Leffler  * of the contiguous data area that's been reserved based
2068a1b9b6aSSam Leffler  * on the packet length.  The data area is forced to 32-bit
2078a1b9b6aSSam Leffler  * alignment and the buffer length to a multiple of 4 bytes.
2088a1b9b6aSSam Leffler  * This is done mainly so beacon frames (that require this)
2098a1b9b6aSSam Leffler  * can use this interface too.
2108a1b9b6aSSam Leffler  */
2118a1b9b6aSSam Leffler struct mbuf *
21268e8e04eSSam Leffler ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen)
2138a1b9b6aSSam Leffler {
2148a1b9b6aSSam Leffler 	struct mbuf *m;
2158a1b9b6aSSam Leffler 	u_int len;
2168a1b9b6aSSam Leffler 
2178a1b9b6aSSam Leffler 	/*
2188a1b9b6aSSam Leffler 	 * NB: we know the mbuf routines will align the data area
2198a1b9b6aSSam Leffler 	 *     so we don't need to do anything special.
2208a1b9b6aSSam Leffler 	 */
22168e8e04eSSam Leffler 	len = roundup2(headroom + pktlen, 4);
2228a1b9b6aSSam Leffler 	KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len));
2238a1b9b6aSSam Leffler 	if (len < MINCLSIZE) {
22434333b16SAndre Oppermann 		m = m_gethdr(M_NOWAIT, MT_DATA);
2258a1b9b6aSSam Leffler 		/*
2268a1b9b6aSSam Leffler 		 * Align the data in case additional headers are added.
2278a1b9b6aSSam Leffler 		 * This should only happen when a WEP header is added
2288a1b9b6aSSam Leffler 		 * which only happens for shared key authentication mgt
2298a1b9b6aSSam Leffler 		 * frames which all fit in MHLEN.
2308a1b9b6aSSam Leffler 		 */
2318a1b9b6aSSam Leffler 		if (m != NULL)
2328a1b9b6aSSam Leffler 			MH_ALIGN(m, len);
23368e8e04eSSam Leffler 	} else {
23434333b16SAndre Oppermann 		m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
23568e8e04eSSam Leffler 		if (m != NULL)
23668e8e04eSSam Leffler 			MC_ALIGN(m, len);
23768e8e04eSSam Leffler 	}
2388a1b9b6aSSam Leffler 	if (m != NULL) {
23998b33550SSepherosa Ziehau 		m->m_data += headroom;
2408a1b9b6aSSam Leffler 		*frm = m->m_data;
2418a1b9b6aSSam Leffler 	}
2428a1b9b6aSSam Leffler 	return m;
2438a1b9b6aSSam Leffler }
2448a1b9b6aSSam Leffler 
24568e8e04eSSam Leffler int
24668e8e04eSSam Leffler ieee80211_add_callback(struct mbuf *m,
24768e8e04eSSam Leffler 	void (*func)(struct ieee80211_node *, void *, int), void *arg)
24868e8e04eSSam Leffler {
24968e8e04eSSam Leffler 	struct m_tag *mtag;
25068e8e04eSSam Leffler 	struct ieee80211_cb *cb;
25168e8e04eSSam Leffler 
25268e8e04eSSam Leffler 	mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_CALLBACK,
25368e8e04eSSam Leffler 			sizeof(struct ieee80211_cb), M_NOWAIT);
25468e8e04eSSam Leffler 	if (mtag == NULL)
25568e8e04eSSam Leffler 		return 0;
25668e8e04eSSam Leffler 
25768e8e04eSSam Leffler 	cb = (struct ieee80211_cb *)(mtag+1);
25868e8e04eSSam Leffler 	cb->func = func;
25968e8e04eSSam Leffler 	cb->arg = arg;
26068e8e04eSSam Leffler 	m_tag_prepend(m, mtag);
26168e8e04eSSam Leffler 	m->m_flags |= M_TXCB;
26268e8e04eSSam Leffler 	return 1;
26368e8e04eSSam Leffler }
26468e8e04eSSam Leffler 
26568e8e04eSSam Leffler void
26668e8e04eSSam Leffler ieee80211_process_callback(struct ieee80211_node *ni,
26768e8e04eSSam Leffler 	struct mbuf *m, int status)
26868e8e04eSSam Leffler {
26968e8e04eSSam Leffler 	struct m_tag *mtag;
27068e8e04eSSam Leffler 
27168e8e04eSSam Leffler 	mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_CALLBACK, NULL);
27268e8e04eSSam Leffler 	if (mtag != NULL) {
27368e8e04eSSam Leffler 		struct ieee80211_cb *cb = (struct ieee80211_cb *)(mtag+1);
27468e8e04eSSam Leffler 		cb->func(ni, cb->arg, status);
27568e8e04eSSam Leffler 	}
27668e8e04eSSam Leffler }
27768e8e04eSSam Leffler 
2788a1b9b6aSSam Leffler #include <sys/libkern.h>
2798a1b9b6aSSam Leffler 
2808a1b9b6aSSam Leffler void
2818a1b9b6aSSam Leffler get_random_bytes(void *p, size_t n)
2828a1b9b6aSSam Leffler {
28368e8e04eSSam Leffler 	uint8_t *dp = p;
2848a1b9b6aSSam Leffler 
2858a1b9b6aSSam Leffler 	while (n > 0) {
28668e8e04eSSam Leffler 		uint32_t v = arc4random();
28768e8e04eSSam Leffler 		size_t nb = n > sizeof(uint32_t) ? sizeof(uint32_t) : n;
28868e8e04eSSam Leffler 		bcopy(&v, dp, n > sizeof(uint32_t) ? sizeof(uint32_t) : n);
28968e8e04eSSam Leffler 		dp += sizeof(uint32_t), n -= nb;
2908a1b9b6aSSam Leffler 	}
2918a1b9b6aSSam Leffler }
2928a1b9b6aSSam Leffler 
2938a1b9b6aSSam Leffler void
2948a1b9b6aSSam Leffler ieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int newassoc)
2958a1b9b6aSSam Leffler {
2968a1b9b6aSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
2978a1b9b6aSSam Leffler 	struct ieee80211_join_event iev;
2988a1b9b6aSSam Leffler 
2998a1b9b6aSSam Leffler 	memset(&iev, 0, sizeof(iev));
3000fc5fe12SSam Leffler 	if (ni == ic->ic_bss) {
3018a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_bssid);
3028a1b9b6aSSam Leffler 		rt_ieee80211msg(ifp, newassoc ?
3038a1b9b6aSSam Leffler 			RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC,
3048a1b9b6aSSam Leffler 			&iev, sizeof(iev));
3058a1b9b6aSSam Leffler 		if_link_state_change(ifp, LINK_STATE_UP);
3060fc5fe12SSam Leffler 	} else {
3078a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
3080fc5fe12SSam Leffler 		rt_ieee80211msg(ifp, newassoc ?
3090fc5fe12SSam Leffler 			RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN,
3100fc5fe12SSam Leffler 			&iev, sizeof(iev));
3118a1b9b6aSSam Leffler 	}
3128a1b9b6aSSam Leffler }
3138a1b9b6aSSam Leffler 
3148a1b9b6aSSam Leffler void
3158a1b9b6aSSam Leffler ieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
3168a1b9b6aSSam Leffler {
3178a1b9b6aSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
3188a1b9b6aSSam Leffler 	struct ieee80211_leave_event iev;
3198a1b9b6aSSam Leffler 
3208a1b9b6aSSam Leffler 	if (ni == ic->ic_bss) {
3218a1b9b6aSSam Leffler 		rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0);
3228a1b9b6aSSam Leffler 		if_link_state_change(ifp, LINK_STATE_DOWN);
3238a1b9b6aSSam Leffler 	} else {
3248a1b9b6aSSam Leffler 		/* fire off wireless event station leaving */
3258a1b9b6aSSam Leffler 		memset(&iev, 0, sizeof(iev));
3268a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
3278a1b9b6aSSam Leffler 		rt_ieee80211msg(ifp, RTM_IEEE80211_LEAVE, &iev, sizeof(iev));
3288a1b9b6aSSam Leffler 	}
3298a1b9b6aSSam Leffler }
3308a1b9b6aSSam Leffler 
3318a1b9b6aSSam Leffler void
3328a1b9b6aSSam Leffler ieee80211_notify_scan_done(struct ieee80211com *ic)
3338a1b9b6aSSam Leffler {
3348a1b9b6aSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
3358a1b9b6aSSam Leffler 
336f1bf5adeSSam Leffler 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s\n", "notify scan done");
3378a1b9b6aSSam Leffler 
3388a1b9b6aSSam Leffler 	/* dispatch wireless event indicating scan completed */
3398a1b9b6aSSam Leffler 	rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0);
3408a1b9b6aSSam Leffler }
3418a1b9b6aSSam Leffler 
3428a1b9b6aSSam Leffler void
3438a1b9b6aSSam Leffler ieee80211_notify_replay_failure(struct ieee80211com *ic,
3448a1b9b6aSSam Leffler 	const struct ieee80211_frame *wh, const struct ieee80211_key *k,
3458a1b9b6aSSam Leffler 	u_int64_t rsc)
3468a1b9b6aSSam Leffler {
3478a1b9b6aSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
3488a1b9b6aSSam Leffler 
3498a1b9b6aSSam Leffler 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
350c1225b52SSam Leffler 	    "[%s] %s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>\n",
3518a1b9b6aSSam Leffler 	    ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name,
352c1225b52SSam Leffler 	    (intmax_t) rsc, (intmax_t) k->wk_keyrsc,
353c1225b52SSam Leffler 	    k->wk_keyix, k->wk_rxkeyix);
3548a1b9b6aSSam Leffler 
3558a1b9b6aSSam Leffler 	if (ifp != NULL) {		/* NB: for cipher test modules */
3568a1b9b6aSSam Leffler 		struct ieee80211_replay_event iev;
3578a1b9b6aSSam Leffler 
3588a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1);
3598a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2);
3608a1b9b6aSSam Leffler 		iev.iev_cipher = k->wk_cipher->ic_cipher;
361c1225b52SSam Leffler 		if (k->wk_rxkeyix != IEEE80211_KEYIX_NONE)
362c1225b52SSam Leffler 			iev.iev_keyix = k->wk_rxkeyix;
363c1225b52SSam Leffler 		else
3648a1b9b6aSSam Leffler 			iev.iev_keyix = k->wk_keyix;
3658a1b9b6aSSam Leffler 		iev.iev_keyrsc = k->wk_keyrsc;
3668a1b9b6aSSam Leffler 		iev.iev_rsc = rsc;
3678a1b9b6aSSam Leffler 		rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev));
3688a1b9b6aSSam Leffler 	}
3698a1b9b6aSSam Leffler }
3708a1b9b6aSSam Leffler 
3718a1b9b6aSSam Leffler void
3728a1b9b6aSSam Leffler ieee80211_notify_michael_failure(struct ieee80211com *ic,
3738a1b9b6aSSam Leffler 	const struct ieee80211_frame *wh, u_int keyix)
3748a1b9b6aSSam Leffler {
3758a1b9b6aSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
3768a1b9b6aSSam Leffler 
3778a1b9b6aSSam Leffler 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
378da615e95SSam Leffler 		"[%s] michael MIC verification failed <keyix %u>\n",
3798a1b9b6aSSam Leffler 	       ether_sprintf(wh->i_addr2), keyix);
3808a1b9b6aSSam Leffler 	ic->ic_stats.is_rx_tkipmic++;
3818a1b9b6aSSam Leffler 
3828a1b9b6aSSam Leffler 	if (ifp != NULL) {		/* NB: for cipher test modules */
3838a1b9b6aSSam Leffler 		struct ieee80211_michael_event iev;
3848a1b9b6aSSam Leffler 
3858a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1);
3868a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2);
3878a1b9b6aSSam Leffler 		iev.iev_cipher = IEEE80211_CIPHER_TKIP;
3888a1b9b6aSSam Leffler 		iev.iev_keyix = keyix;
3898a1b9b6aSSam Leffler 		rt_ieee80211msg(ifp, RTM_IEEE80211_MICHAEL, &iev, sizeof(iev));
3908a1b9b6aSSam Leffler 	}
3918a1b9b6aSSam Leffler }
3928a1b9b6aSSam Leffler 
3938a1b9b6aSSam Leffler void
3948a1b9b6aSSam Leffler ieee80211_load_module(const char *modname)
3958a1b9b6aSSam Leffler {
3968a1b9b6aSSam Leffler 
397edd32c2dSJohn Baldwin #ifdef notyet
398edd32c2dSJohn Baldwin 	(void)kern_kldload(curthread, modname, NULL);
399472f08caSSam Leffler #else
400472f08caSSam Leffler 	printf("%s: load the %s module by hand for now.\n", __func__, modname);
401472f08caSSam Leffler #endif
4028a1b9b6aSSam Leffler }
4038a1b9b6aSSam Leffler 
4048a1b9b6aSSam Leffler /*
4058a1b9b6aSSam Leffler  * Module glue.
4068a1b9b6aSSam Leffler  *
4078a1b9b6aSSam Leffler  * NB: the module name is "wlan" for compatibility with NetBSD.
4088a1b9b6aSSam Leffler  */
4098a1b9b6aSSam Leffler static int
4108a1b9b6aSSam Leffler wlan_modevent(module_t mod, int type, void *unused)
4118a1b9b6aSSam Leffler {
4128a1b9b6aSSam Leffler 	switch (type) {
4138a1b9b6aSSam Leffler 	case MOD_LOAD:
4148a1b9b6aSSam Leffler 		if (bootverbose)
4158a1b9b6aSSam Leffler 			printf("wlan: <802.11 Link Layer>\n");
4168a1b9b6aSSam Leffler 		return 0;
4178a1b9b6aSSam Leffler 	case MOD_UNLOAD:
4188a1b9b6aSSam Leffler 		return 0;
4198a1b9b6aSSam Leffler 	}
4208a1b9b6aSSam Leffler 	return EINVAL;
4218a1b9b6aSSam Leffler }
4228a1b9b6aSSam Leffler 
4238a1b9b6aSSam Leffler static moduledata_t wlan_mod = {
4248a1b9b6aSSam Leffler 	"wlan",
4258a1b9b6aSSam Leffler 	wlan_modevent,
4268a1b9b6aSSam Leffler 	0
4278a1b9b6aSSam Leffler };
4288a1b9b6aSSam Leffler DECLARE_MODULE(wlan, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
4298a1b9b6aSSam Leffler MODULE_VERSION(wlan, 1);
4308a1b9b6aSSam Leffler MODULE_DEPEND(wlan, ether, 1, 1, 1);
431