ieee80211_freebsd.c revision 170360
155682Smarkm/*-
255682Smarkm * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting
355682Smarkm * All rights reserved.
455682Smarkm *
555682Smarkm * Redistribution and use in source and binary forms, with or without
655682Smarkm * modification, are permitted provided that the following conditions
755682Smarkm * are met:
855682Smarkm * 1. Redistributions of source code must retain the above copyright
955682Smarkm *    notice, this list of conditions and the following disclaimer.
1055682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer in the
1255682Smarkm *    documentation and/or other materials provided with the distribution.
1355682Smarkm *
1455682Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1555682Smarkm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1655682Smarkm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1755682Smarkm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1855682Smarkm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1955682Smarkm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2055682Smarkm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2155682Smarkm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2255682Smarkm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2355682Smarkm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2455682Smarkm */
2555682Smarkm
2655682Smarkm#include <sys/cdefs.h>
2755682Smarkm__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_freebsd.c 170360 2007-06-06 04:56:04Z sam $");
2855682Smarkm
2955682Smarkm/*
3055682Smarkm * IEEE 802.11 support (FreeBSD-specific code)
3155682Smarkm */
3255682Smarkm#include <sys/param.h>
3355682Smarkm#include <sys/kernel.h>
3455682Smarkm#include <sys/systm.h>
3555682Smarkm#include <sys/linker.h>
3657416Smarkm#include <sys/mbuf.h>
3755682Smarkm#include <sys/module.h>
3857416Smarkm#include <sys/proc.h>
3957416Smarkm#include <sys/sysctl.h>
4055682Smarkm
4155682Smarkm#include <sys/socket.h>
4255682Smarkm
4355682Smarkm#include <net/if.h>
4455682Smarkm#include <net/if_media.h>
4555682Smarkm#include <net/ethernet.h>
4655682Smarkm#include <net/route.h>
4755682Smarkm
4855682Smarkm#include <net80211/ieee80211_var.h>
4955682Smarkm
5055682SmarkmSYSCTL_NODE(_net, OID_AUTO, wlan, CTLFLAG_RD, 0, "IEEE 80211 parameters");
5155682Smarkm
5255682Smarkm#ifdef IEEE80211_DEBUG
5355682Smarkmint	ieee80211_debug = 0;
5455682SmarkmSYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug,
5555682Smarkm	    0, "debugging printfs");
5655682Smarkm#endif
5755682Smarkm
5855682Smarkmstatic int
5955682Smarkmieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS)
6055682Smarkm{
6155682Smarkm	int inact = (*(int *)arg1) * IEEE80211_INACT_WAIT;
6255682Smarkm	int error;
6355682Smarkm
6455682Smarkm	error = sysctl_handle_int(oidp, &inact, 0, req);
6555682Smarkm	if (error || !req->newptr)
6655682Smarkm		return error;
6755682Smarkm	*(int *)arg1 = inact / IEEE80211_INACT_WAIT;
6855682Smarkm	return 0;
6955682Smarkm}
7055682Smarkm
7155682Smarkmstatic int
7255682Smarkmieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS)
7355682Smarkm{
7455682Smarkm	struct ieee80211com *ic = arg1;
7555682Smarkm	const char *name = ic->ic_ifp->if_xname;
7655682Smarkm
7755682Smarkm	return SYSCTL_OUT(req, name, strlen(name));
7855682Smarkm}
7955682Smarkm
8055682Smarkmvoid
8155682Smarkmieee80211_sysctl_attach(struct ieee80211com *ic)
8255682Smarkm{
8355682Smarkm	struct sysctl_ctx_list *ctx;
8455682Smarkm	struct sysctl_oid *oid;
8555682Smarkm	char num[14];			/* sufficient for 32 bits */
8655682Smarkm
8755682Smarkm	MALLOC(ctx, struct sysctl_ctx_list *, sizeof(struct sysctl_ctx_list),
8855682Smarkm		M_DEVBUF, M_NOWAIT | M_ZERO);
8955682Smarkm	if (ctx == NULL) {
9055682Smarkm		if_printf(ic->ic_ifp, "%s: cannot allocate sysctl context!\n",
9155682Smarkm			__func__);
9255682Smarkm		return;
9355682Smarkm	}
9455682Smarkm	sysctl_ctx_init(ctx);
9555682Smarkm	snprintf(num, sizeof(num), "%u", ic->ic_vap);
9655682Smarkm	oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan),
9755682Smarkm		OID_AUTO, num, CTLFLAG_RD, NULL, "");
9855682Smarkm	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
9955682Smarkm		"%parent", CTLFLAG_RD, ic, 0, ieee80211_sysctl_parent, "A",
10055682Smarkm		"parent device");
10155682Smarkm#ifdef IEEE80211_DEBUG
10255682Smarkm	ic->ic_debug = ieee80211_debug;
10355682Smarkm	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
10455682Smarkm		"debug", CTLFLAG_RW, &ic->ic_debug, 0,
10555682Smarkm		"control debugging printfs");
10655682Smarkm#endif
10755682Smarkm	/* XXX inherit from tunables */
10855682Smarkm	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
10955682Smarkm		"inact_run", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_run, 0,
11055682Smarkm		ieee80211_sysctl_inact, "I",
11155682Smarkm		"station inactivity timeout (sec)");
11255682Smarkm	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
11355682Smarkm		"inact_probe", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_probe, 0,
11455682Smarkm		ieee80211_sysctl_inact, "I",
11555682Smarkm		"station inactivity probe timeout (sec)");
11655682Smarkm	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
11755682Smarkm		"inact_auth", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_auth, 0,
11855682Smarkm		ieee80211_sysctl_inact, "I",
11955682Smarkm		"station authentication timeout (sec)");
12055682Smarkm	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
12155682Smarkm		"inact_init", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_init, 0,
12255682Smarkm		ieee80211_sysctl_inact, "I",
12355682Smarkm		"station initial state timeout (sec)");
12455682Smarkm	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
12555682Smarkm		"driver_caps", CTLFLAG_RW, &ic->ic_caps, 0,
12655682Smarkm		"driver capabilities");
12755682Smarkm	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
12855682Smarkm		"bmiss_max", CTLFLAG_RW, &ic->ic_bmiss_max, 0,
12955682Smarkm		"consecutive beacon misses before scanning");
13055682Smarkm	ic->ic_sysctl = ctx;
13155682Smarkm}
13255682Smarkm
13355682Smarkmvoid
13455682Smarkmieee80211_sysctl_detach(struct ieee80211com *ic)
13555682Smarkm{
13655682Smarkm
13755682Smarkm	if (ic->ic_sysctl != NULL) {
13855682Smarkm		sysctl_ctx_free(ic->ic_sysctl);
13955682Smarkm		ic->ic_sysctl = NULL;
14055682Smarkm	}
14155682Smarkm}
14255682Smarkm
14355682Smarkmint
14455682Smarkmieee80211_node_dectestref(struct ieee80211_node *ni)
14555682Smarkm{
14655682Smarkm	/* XXX need equivalent of atomic_dec_and_test */
14755682Smarkm	atomic_subtract_int(&ni->ni_refcnt, 1);
14855682Smarkm	return atomic_cmpset_int(&ni->ni_refcnt, 0, 1);
14955682Smarkm}
15055682Smarkm
15155682Smarkmvoid
15255682Smarkmieee80211_drain_ifq(struct ifqueue *ifq)
15355682Smarkm{
15455682Smarkm	struct ieee80211_node *ni;
15555682Smarkm	struct mbuf *m;
156
157	for (;;) {
158		IF_DEQUEUE(ifq, m);
159		if (m == NULL)
160			break;
161
162		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
163		KASSERT(ni != NULL, ("frame w/o node"));
164		ieee80211_free_node(ni);
165		m->m_pkthdr.rcvif = NULL;
166
167		m_freem(m);
168	}
169}
170
171/*
172 * Allocate and setup a management frame of the specified
173 * size.  We return the mbuf and a pointer to the start
174 * of the contiguous data area that's been reserved based
175 * on the packet length.  The data area is forced to 32-bit
176 * alignment and the buffer length to a multiple of 4 bytes.
177 * This is done mainly so beacon frames (that require this)
178 * can use this interface too.
179 */
180struct mbuf *
181ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen)
182{
183	struct mbuf *m;
184	u_int len;
185
186	/*
187	 * NB: we know the mbuf routines will align the data area
188	 *     so we don't need to do anything special.
189	 */
190	/* XXX 4-address frame? */
191	len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4);
192	KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len));
193	if (len < MINCLSIZE) {
194		m = m_gethdr(M_NOWAIT, MT_DATA);
195		/*
196		 * Align the data in case additional headers are added.
197		 * This should only happen when a WEP header is added
198		 * which only happens for shared key authentication mgt
199		 * frames which all fit in MHLEN.
200		 */
201		if (m != NULL)
202			MH_ALIGN(m, len);
203	} else
204		m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
205	if (m != NULL) {
206		m->m_data += sizeof(struct ieee80211_frame);
207		*frm = m->m_data;
208	}
209	return m;
210}
211
212#include <sys/libkern.h>
213
214void
215get_random_bytes(void *p, size_t n)
216{
217	u_int8_t *dp = p;
218
219	while (n > 0) {
220		u_int32_t v = arc4random();
221		size_t nb = n > sizeof(u_int32_t) ? sizeof(u_int32_t) : n;
222		bcopy(&v, dp, n > sizeof(u_int32_t) ? sizeof(u_int32_t) : n);
223		dp += sizeof(u_int32_t), n -= nb;
224	}
225}
226
227void
228ieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int newassoc)
229{
230	struct ifnet *ifp = ic->ic_ifp;
231	struct ieee80211_join_event iev;
232
233	memset(&iev, 0, sizeof(iev));
234	if (ni == ic->ic_bss) {
235		IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_bssid);
236		rt_ieee80211msg(ifp, newassoc ?
237			RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC,
238			&iev, sizeof(iev));
239		if_link_state_change(ifp, LINK_STATE_UP);
240	} else {
241		IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
242		rt_ieee80211msg(ifp, newassoc ?
243			RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN,
244			&iev, sizeof(iev));
245	}
246}
247
248void
249ieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
250{
251	struct ifnet *ifp = ic->ic_ifp;
252	struct ieee80211_leave_event iev;
253
254	if (ni == ic->ic_bss) {
255		rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0);
256		if_link_state_change(ifp, LINK_STATE_DOWN);
257	} else {
258		/* fire off wireless event station leaving */
259		memset(&iev, 0, sizeof(iev));
260		IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
261		rt_ieee80211msg(ifp, RTM_IEEE80211_LEAVE, &iev, sizeof(iev));
262	}
263}
264
265void
266ieee80211_notify_scan_done(struct ieee80211com *ic)
267{
268	struct ifnet *ifp = ic->ic_ifp;
269
270	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s\n", "notify scan done");
271
272	/* dispatch wireless event indicating scan completed */
273	rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0);
274}
275
276void
277ieee80211_notify_replay_failure(struct ieee80211com *ic,
278	const struct ieee80211_frame *wh, const struct ieee80211_key *k,
279	u_int64_t rsc)
280{
281	struct ifnet *ifp = ic->ic_ifp;
282
283	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
284	    "[%s] %s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>\n",
285	    ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name,
286	    (intmax_t) rsc, (intmax_t) k->wk_keyrsc,
287	    k->wk_keyix, k->wk_rxkeyix);
288
289	if (ifp != NULL) {		/* NB: for cipher test modules */
290		struct ieee80211_replay_event iev;
291
292		IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1);
293		IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2);
294		iev.iev_cipher = k->wk_cipher->ic_cipher;
295		if (k->wk_rxkeyix != IEEE80211_KEYIX_NONE)
296			iev.iev_keyix = k->wk_rxkeyix;
297		else
298			iev.iev_keyix = k->wk_keyix;
299		iev.iev_keyrsc = k->wk_keyrsc;
300		iev.iev_rsc = rsc;
301		rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev));
302	}
303}
304
305void
306ieee80211_notify_michael_failure(struct ieee80211com *ic,
307	const struct ieee80211_frame *wh, u_int keyix)
308{
309	struct ifnet *ifp = ic->ic_ifp;
310
311	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
312		"[%s] michael MIC verification failed <keyix %u>\n",
313	       ether_sprintf(wh->i_addr2), keyix);
314	ic->ic_stats.is_rx_tkipmic++;
315
316	if (ifp != NULL) {		/* NB: for cipher test modules */
317		struct ieee80211_michael_event iev;
318
319		IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1);
320		IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2);
321		iev.iev_cipher = IEEE80211_CIPHER_TKIP;
322		iev.iev_keyix = keyix;
323		rt_ieee80211msg(ifp, RTM_IEEE80211_MICHAEL, &iev, sizeof(iev));
324	}
325}
326
327void
328ieee80211_load_module(const char *modname)
329{
330
331#ifdef notyet
332	(void)kern_kldload(curthread, modname, NULL);
333#else
334	printf("%s: load the %s module by hand for now.\n", __func__, modname);
335#endif
336}
337
338/*
339 * Module glue.
340 *
341 * NB: the module name is "wlan" for compatibility with NetBSD.
342 */
343static int
344wlan_modevent(module_t mod, int type, void *unused)
345{
346	switch (type) {
347	case MOD_LOAD:
348		if (bootverbose)
349			printf("wlan: <802.11 Link Layer>\n");
350		return 0;
351	case MOD_UNLOAD:
352		return 0;
353	}
354	return EINVAL;
355}
356
357static moduledata_t wlan_mod = {
358	"wlan",
359	wlan_modevent,
360	0
361};
362DECLARE_MODULE(wlan, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
363MODULE_VERSION(wlan, 1);
364MODULE_DEPEND(wlan, ether, 1, 1, 1);
365