ieee80211_freebsd.c revision 171984
1138568Ssam/*- 2170360Ssam * Copyright (c) 2003-2007 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 171984 2007-08-26 11:32:56Z sephe $"); 28138568Ssam 29138568Ssam/* 30138568Ssam * IEEE 802.11 support (FreeBSD-specific code) 31138568Ssam */ 32138568Ssam#include <sys/param.h> 33138568Ssam#include <sys/kernel.h> 34138568Ssam#include <sys/systm.h> 35138568Ssam#include <sys/linker.h> 36138568Ssam#include <sys/mbuf.h> 37138568Ssam#include <sys/module.h> 38138568Ssam#include <sys/proc.h> 39138568Ssam#include <sys/sysctl.h> 40138568Ssam 41138568Ssam#include <sys/socket.h> 42138568Ssam 43138568Ssam#include <net/if.h> 44138568Ssam#include <net/if_media.h> 45138568Ssam#include <net/ethernet.h> 46138568Ssam#include <net/route.h> 47138568Ssam 48138568Ssam#include <net80211/ieee80211_var.h> 49138568Ssam 50138568SsamSYSCTL_NODE(_net, OID_AUTO, wlan, CTLFLAG_RD, 0, "IEEE 80211 parameters"); 51138568Ssam 52138568Ssam#ifdef IEEE80211_DEBUG 53138568Ssamint ieee80211_debug = 0; 54138568SsamSYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug, 55138568Ssam 0, "debugging printfs"); 56138568Ssam#endif 57138568Ssam 58138568Ssamstatic int 59138568Ssamieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS) 60138568Ssam{ 61138568Ssam int inact = (*(int *)arg1) * IEEE80211_INACT_WAIT; 62138568Ssam int error; 63138568Ssam 64138568Ssam error = sysctl_handle_int(oidp, &inact, 0, req); 65138568Ssam if (error || !req->newptr) 66138568Ssam return error; 67138568Ssam *(int *)arg1 = inact / IEEE80211_INACT_WAIT; 68138568Ssam return 0; 69138568Ssam} 70138568Ssam 71138568Ssamstatic int 72138568Ssamieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS) 73138568Ssam{ 74138568Ssam struct ieee80211com *ic = arg1; 75138568Ssam const char *name = ic->ic_ifp->if_xname; 76138568Ssam 77138568Ssam return SYSCTL_OUT(req, name, strlen(name)); 78138568Ssam} 79138568Ssam 80138568Ssamvoid 81138568Ssamieee80211_sysctl_attach(struct ieee80211com *ic) 82138568Ssam{ 83138568Ssam struct sysctl_ctx_list *ctx; 84138568Ssam struct sysctl_oid *oid; 85138568Ssam char num[14]; /* sufficient for 32 bits */ 86138568Ssam 87138568Ssam MALLOC(ctx, struct sysctl_ctx_list *, sizeof(struct sysctl_ctx_list), 88138568Ssam M_DEVBUF, M_NOWAIT | M_ZERO); 89138568Ssam if (ctx == NULL) { 90138568Ssam if_printf(ic->ic_ifp, "%s: cannot allocate sysctl context!\n", 91138568Ssam __func__); 92138568Ssam return; 93138568Ssam } 94138568Ssam sysctl_ctx_init(ctx); 95138568Ssam snprintf(num, sizeof(num), "%u", ic->ic_vap); 96138568Ssam oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan), 97138568Ssam OID_AUTO, num, CTLFLAG_RD, NULL, ""); 98138568Ssam SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 99138568Ssam "%parent", CTLFLAG_RD, ic, 0, ieee80211_sysctl_parent, "A", 100138568Ssam "parent device"); 101138568Ssam#ifdef IEEE80211_DEBUG 102138568Ssam ic->ic_debug = ieee80211_debug; 103138568Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 104138568Ssam "debug", CTLFLAG_RW, &ic->ic_debug, 0, 105138568Ssam "control debugging printfs"); 106138568Ssam#endif 107138568Ssam /* XXX inherit from tunables */ 108138568Ssam SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 109140633Ssam "inact_run", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_run, 0, 110138568Ssam ieee80211_sysctl_inact, "I", 111138568Ssam "station inactivity timeout (sec)"); 112138568Ssam SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 113138568Ssam "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_probe, 0, 114138568Ssam ieee80211_sysctl_inact, "I", 115138568Ssam "station inactivity probe timeout (sec)"); 116138568Ssam SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 117138568Ssam "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_auth, 0, 118138568Ssam ieee80211_sysctl_inact, "I", 119138568Ssam "station authentication timeout (sec)"); 120138568Ssam SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 121138568Ssam "inact_init", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_init, 0, 122138568Ssam ieee80211_sysctl_inact, "I", 123138568Ssam "station initial state timeout (sec)"); 124139512Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 125139512Ssam "driver_caps", CTLFLAG_RW, &ic->ic_caps, 0, 126139512Ssam "driver capabilities"); 127153349Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 128153349Ssam "bmiss_max", CTLFLAG_RW, &ic->ic_bmiss_max, 0, 129153349Ssam "consecutive beacon misses before scanning"); 130138568Ssam ic->ic_sysctl = ctx; 131138568Ssam} 132138568Ssam 133138568Ssamvoid 134138568Ssamieee80211_sysctl_detach(struct ieee80211com *ic) 135138568Ssam{ 136138568Ssam 137138568Ssam if (ic->ic_sysctl != NULL) { 138138568Ssam sysctl_ctx_free(ic->ic_sysctl); 139171984Ssephe FREE(ic->ic_sysctl, M_DEVBUF); 140138568Ssam ic->ic_sysctl = NULL; 141138568Ssam } 142138568Ssam} 143138568Ssam 144138568Ssamint 145138568Ssamieee80211_node_dectestref(struct ieee80211_node *ni) 146138568Ssam{ 147138568Ssam /* XXX need equivalent of atomic_dec_and_test */ 148138568Ssam atomic_subtract_int(&ni->ni_refcnt, 1); 149138568Ssam return atomic_cmpset_int(&ni->ni_refcnt, 0, 1); 150138568Ssam} 151138568Ssam 152165894Ssamvoid 153165894Ssamieee80211_drain_ifq(struct ifqueue *ifq) 154165894Ssam{ 155165894Ssam struct ieee80211_node *ni; 156165894Ssam struct mbuf *m; 157165894Ssam 158165894Ssam for (;;) { 159165894Ssam IF_DEQUEUE(ifq, m); 160165894Ssam if (m == NULL) 161165894Ssam break; 162165894Ssam 163165894Ssam ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; 164165894Ssam KASSERT(ni != NULL, ("frame w/o node")); 165165894Ssam ieee80211_free_node(ni); 166165894Ssam m->m_pkthdr.rcvif = NULL; 167165894Ssam 168165894Ssam m_freem(m); 169165894Ssam } 170165894Ssam} 171165894Ssam 172138568Ssam/* 173170530Ssam * As above, for mbufs allocated with m_gethdr/MGETHDR 174170530Ssam * or initialized by M_COPY_PKTHDR. 175170530Ssam */ 176170530Ssam#define MC_ALIGN(m, len) \ 177170530Ssamdo { \ 178170530Ssam (m)->m_data += (MCLBYTES - (len)) &~ (sizeof(long) - 1); \ 179170530Ssam} while (/* CONSTCOND */ 0) 180170530Ssam 181170530Ssam/* 182138568Ssam * Allocate and setup a management frame of the specified 183138568Ssam * size. We return the mbuf and a pointer to the start 184138568Ssam * of the contiguous data area that's been reserved based 185138568Ssam * on the packet length. The data area is forced to 32-bit 186138568Ssam * alignment and the buffer length to a multiple of 4 bytes. 187138568Ssam * This is done mainly so beacon frames (that require this) 188138568Ssam * can use this interface too. 189138568Ssam */ 190138568Ssamstruct mbuf * 191170530Ssamieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen) 192138568Ssam{ 193138568Ssam struct mbuf *m; 194138568Ssam u_int len; 195138568Ssam 196138568Ssam /* 197138568Ssam * NB: we know the mbuf routines will align the data area 198138568Ssam * so we don't need to do anything special. 199138568Ssam */ 200170530Ssam len = roundup2(headroom + pktlen, 4); 201138568Ssam KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len)); 202138568Ssam if (len < MINCLSIZE) { 203151967Sandre m = m_gethdr(M_NOWAIT, MT_DATA); 204138568Ssam /* 205138568Ssam * Align the data in case additional headers are added. 206138568Ssam * This should only happen when a WEP header is added 207138568Ssam * which only happens for shared key authentication mgt 208138568Ssam * frames which all fit in MHLEN. 209138568Ssam */ 210138568Ssam if (m != NULL) 211138568Ssam MH_ALIGN(m, len); 212170530Ssam } else { 213151967Sandre m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 214170530Ssam if (m != NULL) 215170530Ssam MC_ALIGN(m, len); 216170530Ssam } 217138568Ssam if (m != NULL) { 218171984Ssephe m->m_data += headroom; 219138568Ssam *frm = m->m_data; 220138568Ssam } 221138568Ssam return m; 222138568Ssam} 223138568Ssam 224170530Ssamint 225170530Ssamieee80211_add_callback(struct mbuf *m, 226170530Ssam void (*func)(struct ieee80211_node *, void *, int), void *arg) 227170530Ssam{ 228170530Ssam struct m_tag *mtag; 229170530Ssam struct ieee80211_cb *cb; 230170530Ssam 231170530Ssam mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_CALLBACK, 232170530Ssam sizeof(struct ieee80211_cb), M_NOWAIT); 233170530Ssam if (mtag == NULL) 234170530Ssam return 0; 235170530Ssam 236170530Ssam cb = (struct ieee80211_cb *)(mtag+1); 237170530Ssam cb->func = func; 238170530Ssam cb->arg = arg; 239170530Ssam m_tag_prepend(m, mtag); 240170530Ssam m->m_flags |= M_TXCB; 241170530Ssam return 1; 242170530Ssam} 243170530Ssam 244170530Ssamvoid 245170530Ssamieee80211_process_callback(struct ieee80211_node *ni, 246170530Ssam struct mbuf *m, int status) 247170530Ssam{ 248170530Ssam struct m_tag *mtag; 249170530Ssam 250170530Ssam mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_CALLBACK, NULL); 251170530Ssam if (mtag != NULL) { 252170530Ssam struct ieee80211_cb *cb = (struct ieee80211_cb *)(mtag+1); 253170530Ssam cb->func(ni, cb->arg, status); 254170530Ssam } 255170530Ssam} 256170530Ssam 257138568Ssam#include <sys/libkern.h> 258138568Ssam 259138568Ssamvoid 260138568Ssamget_random_bytes(void *p, size_t n) 261138568Ssam{ 262170530Ssam uint8_t *dp = p; 263138568Ssam 264138568Ssam while (n > 0) { 265170530Ssam uint32_t v = arc4random(); 266170530Ssam size_t nb = n > sizeof(uint32_t) ? sizeof(uint32_t) : n; 267170530Ssam bcopy(&v, dp, n > sizeof(uint32_t) ? sizeof(uint32_t) : n); 268170530Ssam dp += sizeof(uint32_t), n -= nb; 269138568Ssam } 270138568Ssam} 271138568Ssam 272138568Ssamvoid 273138568Ssamieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int newassoc) 274138568Ssam{ 275138568Ssam struct ifnet *ifp = ic->ic_ifp; 276138568Ssam struct ieee80211_join_event iev; 277138568Ssam 278144302Ssam memset(&iev, 0, sizeof(iev)); 279138568Ssam if (ni == ic->ic_bss) { 280138568Ssam IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_bssid); 281138568Ssam rt_ieee80211msg(ifp, newassoc ? 282138568Ssam RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC, 283138568Ssam &iev, sizeof(iev)); 284138568Ssam if_link_state_change(ifp, LINK_STATE_UP); 285144302Ssam } else { 286138568Ssam IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr); 287144302Ssam rt_ieee80211msg(ifp, newassoc ? 288144302Ssam RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN, 289144302Ssam &iev, sizeof(iev)); 290138568Ssam } 291138568Ssam} 292138568Ssam 293138568Ssamvoid 294138568Ssamieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) 295138568Ssam{ 296138568Ssam struct ifnet *ifp = ic->ic_ifp; 297138568Ssam struct ieee80211_leave_event iev; 298138568Ssam 299138568Ssam if (ni == ic->ic_bss) { 300138568Ssam rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0); 301138568Ssam if_link_state_change(ifp, LINK_STATE_DOWN); 302138568Ssam } else { 303138568Ssam /* fire off wireless event station leaving */ 304138568Ssam memset(&iev, 0, sizeof(iev)); 305138568Ssam IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr); 306138568Ssam rt_ieee80211msg(ifp, RTM_IEEE80211_LEAVE, &iev, sizeof(iev)); 307138568Ssam } 308138568Ssam} 309138568Ssam 310138568Ssamvoid 311138568Ssamieee80211_notify_scan_done(struct ieee80211com *ic) 312138568Ssam{ 313138568Ssam struct ifnet *ifp = ic->ic_ifp; 314138568Ssam 315159915Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s\n", "notify scan done"); 316138568Ssam 317138568Ssam /* dispatch wireless event indicating scan completed */ 318138568Ssam rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0); 319138568Ssam} 320138568Ssam 321138568Ssamvoid 322138568Ssamieee80211_notify_replay_failure(struct ieee80211com *ic, 323138568Ssam const struct ieee80211_frame *wh, const struct ieee80211_key *k, 324138568Ssam u_int64_t rsc) 325138568Ssam{ 326138568Ssam struct ifnet *ifp = ic->ic_ifp; 327138568Ssam 328138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, 329148863Ssam "[%s] %s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>\n", 330148863Ssam ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name, 331148863Ssam (intmax_t) rsc, (intmax_t) k->wk_keyrsc, 332148863Ssam k->wk_keyix, k->wk_rxkeyix); 333138568Ssam 334138568Ssam if (ifp != NULL) { /* NB: for cipher test modules */ 335138568Ssam struct ieee80211_replay_event iev; 336138568Ssam 337138568Ssam IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1); 338138568Ssam IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2); 339138568Ssam iev.iev_cipher = k->wk_cipher->ic_cipher; 340148863Ssam if (k->wk_rxkeyix != IEEE80211_KEYIX_NONE) 341148863Ssam iev.iev_keyix = k->wk_rxkeyix; 342148863Ssam else 343148863Ssam iev.iev_keyix = k->wk_keyix; 344138568Ssam iev.iev_keyrsc = k->wk_keyrsc; 345138568Ssam iev.iev_rsc = rsc; 346138568Ssam rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev)); 347138568Ssam } 348138568Ssam} 349138568Ssam 350138568Ssamvoid 351138568Ssamieee80211_notify_michael_failure(struct ieee80211com *ic, 352138568Ssam const struct ieee80211_frame *wh, u_int keyix) 353138568Ssam{ 354138568Ssam struct ifnet *ifp = ic->ic_ifp; 355138568Ssam 356138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, 357139512Ssam "[%s] michael MIC verification failed <keyix %u>\n", 358138568Ssam ether_sprintf(wh->i_addr2), keyix); 359138568Ssam ic->ic_stats.is_rx_tkipmic++; 360138568Ssam 361138568Ssam if (ifp != NULL) { /* NB: for cipher test modules */ 362138568Ssam struct ieee80211_michael_event iev; 363138568Ssam 364138568Ssam IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1); 365138568Ssam IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2); 366138568Ssam iev.iev_cipher = IEEE80211_CIPHER_TKIP; 367138568Ssam iev.iev_keyix = keyix; 368138568Ssam rt_ieee80211msg(ifp, RTM_IEEE80211_MICHAEL, &iev, sizeof(iev)); 369138568Ssam } 370138568Ssam} 371138568Ssam 372138568Ssamvoid 373138568Ssamieee80211_load_module(const char *modname) 374138568Ssam{ 375159590Sjhb 376138777Ssam#ifdef notyet 377159590Sjhb (void)kern_kldload(curthread, modname, NULL); 378138777Ssam#else 379138777Ssam printf("%s: load the %s module by hand for now.\n", __func__, modname); 380138777Ssam#endif 381138568Ssam} 382138568Ssam 383138568Ssam/* 384138568Ssam * Module glue. 385138568Ssam * 386138568Ssam * NB: the module name is "wlan" for compatibility with NetBSD. 387138568Ssam */ 388138568Ssamstatic int 389138568Ssamwlan_modevent(module_t mod, int type, void *unused) 390138568Ssam{ 391138568Ssam switch (type) { 392138568Ssam case MOD_LOAD: 393138568Ssam if (bootverbose) 394138568Ssam printf("wlan: <802.11 Link Layer>\n"); 395138568Ssam return 0; 396138568Ssam case MOD_UNLOAD: 397138568Ssam return 0; 398138568Ssam } 399138568Ssam return EINVAL; 400138568Ssam} 401138568Ssam 402138568Ssamstatic moduledata_t wlan_mod = { 403138568Ssam "wlan", 404138568Ssam wlan_modevent, 405138568Ssam 0 406138568Ssam}; 407138568SsamDECLARE_MODULE(wlan, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 408138568SsamMODULE_VERSION(wlan, 1); 409138568SsamMODULE_DEPEND(wlan, ether, 1, 1, 1); 410