ieee80211_freebsd.c revision 299171
1138568Ssam/*- 2186904Ssam * Copyright (c) 2003-2009 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 299171 2016-05-06 11:41:49Z avos $"); 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/systm.h> 36293470Smelifaro#include <sys/eventhandler.h> 37295126Sglebius#include <sys/kernel.h> 38138568Ssam#include <sys/linker.h> 39295126Sglebius#include <sys/malloc.h> 40138568Ssam#include <sys/mbuf.h> 41138568Ssam#include <sys/module.h> 42138568Ssam#include <sys/proc.h> 43138568Ssam#include <sys/sysctl.h> 44138568Ssam 45138568Ssam#include <sys/socket.h> 46138568Ssam 47192468Ssam#include <net/bpf.h> 48138568Ssam#include <net/if.h> 49257176Sglebius#include <net/if_var.h> 50190526Ssam#include <net/if_dl.h> 51178354Ssam#include <net/if_clone.h> 52138568Ssam#include <net/if_media.h> 53178354Ssam#include <net/if_types.h> 54138568Ssam#include <net/ethernet.h> 55138568Ssam#include <net/route.h> 56196019Srwatson#include <net/vnet.h> 57138568Ssam 58138568Ssam#include <net80211/ieee80211_var.h> 59195757Ssam#include <net80211/ieee80211_input.h> 60138568Ssam 61138568SsamSYSCTL_NODE(_net, OID_AUTO, wlan, CTLFLAG_RD, 0, "IEEE 80211 parameters"); 62138568Ssam 63138568Ssam#ifdef IEEE80211_DEBUG 64138568Ssamint ieee80211_debug = 0; 65138568SsamSYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug, 66138568Ssam 0, "debugging printfs"); 67138568Ssam#endif 68138568Ssam 69227293Sedstatic MALLOC_DEFINE(M_80211_COM, "80211com", "802.11 com state"); 70178354Ssam 71241610Sglebiusstatic const char wlanname[] = "wlan"; 72241610Sglebiusstatic struct if_clone *wlan_cloner; 73241610Sglebius 74138568Ssamstatic int 75178354Ssamwlan_clone_create(struct if_clone *ifc, int unit, caddr_t params) 76173273Ssam{ 77178354Ssam struct ieee80211_clone_params cp; 78178354Ssam struct ieee80211vap *vap; 79178354Ssam struct ieee80211com *ic; 80173273Ssam int error; 81173273Ssam 82178354Ssam error = copyin(params, &cp, sizeof(cp)); 83178354Ssam if (error) 84178354Ssam return error; 85287197Sglebius ic = ieee80211_find_com(cp.icp_parent); 86287197Sglebius if (ic == NULL) 87178354Ssam return ENXIO; 88178957Ssam if (cp.icp_opmode >= IEEE80211_OPMODE_MAX) { 89287197Sglebius ic_printf(ic, "%s: invalid opmode %d\n", __func__, 90287197Sglebius cp.icp_opmode); 91178354Ssam return EINVAL; 92178354Ssam } 93178957Ssam if ((ic->ic_caps & ieee80211_opcap[cp.icp_opmode]) == 0) { 94287197Sglebius ic_printf(ic, "%s mode not supported\n", 95178957Ssam ieee80211_opmode_name[cp.icp_opmode]); 96178957Ssam return EOPNOTSUPP; 97178957Ssam } 98186904Ssam if ((cp.icp_flags & IEEE80211_CLONE_TDMA) && 99186904Ssam#ifdef IEEE80211_SUPPORT_TDMA 100186904Ssam (ic->ic_caps & IEEE80211_C_TDMA) == 0 101186904Ssam#else 102186904Ssam (1) 103186904Ssam#endif 104186904Ssam ) { 105287197Sglebius ic_printf(ic, "TDMA not supported\n"); 106186904Ssam return EOPNOTSUPP; 107186904Ssam } 108241610Sglebius vap = ic->ic_vap_create(ic, wlanname, unit, 109178354Ssam cp.icp_opmode, cp.icp_flags, cp.icp_bssid, 110178354Ssam cp.icp_flags & IEEE80211_CLONE_MACADDR ? 111287197Sglebius cp.icp_macaddr : ic->ic_macaddr); 112242154Sadrian 113178354Ssam return (vap == NULL ? EIO : 0); 114178354Ssam} 115178354Ssam 116178354Ssamstatic void 117178354Ssamwlan_clone_destroy(struct ifnet *ifp) 118178354Ssam{ 119178354Ssam struct ieee80211vap *vap = ifp->if_softc; 120178354Ssam struct ieee80211com *ic = vap->iv_ic; 121178354Ssam 122178354Ssam ic->ic_vap_delete(vap); 123178354Ssam} 124178354Ssam 125178354Ssamvoid 126178354Ssamieee80211_vap_destroy(struct ieee80211vap *vap) 127178354Ssam{ 128242149Sadrian CURVNET_SET(vap->iv_ifp->if_vnet); 129241610Sglebius if_clone_destroyif(wlan_cloner, vap->iv_ifp); 130242149Sadrian CURVNET_RESTORE(); 131178354Ssam} 132178354Ssam 133192473Ssamint 134178354Ssamieee80211_sysctl_msecs_ticks(SYSCTL_HANDLER_ARGS) 135178354Ssam{ 136178354Ssam int msecs = ticks_to_msecs(*(int *)arg1); 137178354Ssam int error, t; 138178354Ssam 139178354Ssam error = sysctl_handle_int(oidp, &msecs, 0, req); 140173273Ssam if (error || !req->newptr) 141173273Ssam return error; 142178354Ssam t = msecs_to_ticks(msecs); 143178354Ssam *(int *)arg1 = (t < 1) ? 1 : t; 144173273Ssam return 0; 145173273Ssam} 146178354Ssam 147173273Ssamstatic int 148138568Ssamieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS) 149138568Ssam{ 150138568Ssam int inact = (*(int *)arg1) * IEEE80211_INACT_WAIT; 151138568Ssam int error; 152138568Ssam 153138568Ssam error = sysctl_handle_int(oidp, &inact, 0, req); 154138568Ssam if (error || !req->newptr) 155138568Ssam return error; 156138568Ssam *(int *)arg1 = inact / IEEE80211_INACT_WAIT; 157138568Ssam return 0; 158138568Ssam} 159138568Ssam 160138568Ssamstatic int 161138568Ssamieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS) 162138568Ssam{ 163138568Ssam struct ieee80211com *ic = arg1; 164138568Ssam 165283529Sglebius return SYSCTL_OUT_STR(req, ic->ic_name); 166138568Ssam} 167138568Ssam 168181194Ssamstatic int 169181194Ssamieee80211_sysctl_radar(SYSCTL_HANDLER_ARGS) 170181194Ssam{ 171181194Ssam struct ieee80211com *ic = arg1; 172181194Ssam int t = 0, error; 173181194Ssam 174181194Ssam error = sysctl_handle_int(oidp, &t, 0, req); 175181194Ssam if (error || !req->newptr) 176181194Ssam return error; 177181194Ssam IEEE80211_LOCK(ic); 178181194Ssam ieee80211_dfs_notify_radar(ic, ic->ic_curchan); 179181194Ssam IEEE80211_UNLOCK(ic); 180181194Ssam return 0; 181181194Ssam} 182181194Ssam 183138568Ssamvoid 184138568Ssamieee80211_sysctl_attach(struct ieee80211com *ic) 185138568Ssam{ 186178354Ssam} 187178354Ssam 188178354Ssamvoid 189178354Ssamieee80211_sysctl_detach(struct ieee80211com *ic) 190178354Ssam{ 191178354Ssam} 192178354Ssam 193178354Ssamvoid 194178354Ssamieee80211_sysctl_vattach(struct ieee80211vap *vap) 195178354Ssam{ 196178354Ssam struct ifnet *ifp = vap->iv_ifp; 197138568Ssam struct sysctl_ctx_list *ctx; 198138568Ssam struct sysctl_oid *oid; 199138568Ssam char num[14]; /* sufficient for 32 bits */ 200138568Ssam 201283538Sadrian ctx = (struct sysctl_ctx_list *) IEEE80211_MALLOC(sizeof(struct sysctl_ctx_list), 202283538Sadrian M_DEVBUF, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); 203138568Ssam if (ctx == NULL) { 204178354Ssam if_printf(ifp, "%s: cannot allocate sysctl context!\n", 205138568Ssam __func__); 206138568Ssam return; 207138568Ssam } 208138568Ssam sysctl_ctx_init(ctx); 209178354Ssam snprintf(num, sizeof(num), "%u", ifp->if_dunit); 210138568Ssam oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan), 211138568Ssam OID_AUTO, num, CTLFLAG_RD, NULL, ""); 212138568Ssam SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 213217554Smdf "%parent", CTLTYPE_STRING | CTLFLAG_RD, vap->iv_ic, 0, 214178354Ssam ieee80211_sysctl_parent, "A", "parent device"); 215217322Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 216178354Ssam "driver_caps", CTLFLAG_RW, &vap->iv_caps, 0, 217178354Ssam "driver capabilities"); 218138568Ssam#ifdef IEEE80211_DEBUG 219178354Ssam vap->iv_debug = ieee80211_debug; 220217322Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 221178354Ssam "debug", CTLFLAG_RW, &vap->iv_debug, 0, 222138568Ssam "control debugging printfs"); 223138568Ssam#endif 224178354Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 225178354Ssam "bmiss_max", CTLFLAG_RW, &vap->iv_bmiss_max, 0, 226178354Ssam "consecutive beacon misses before scanning"); 227138568Ssam /* XXX inherit from tunables */ 228138568Ssam SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 229178354Ssam "inact_run", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_run, 0, 230138568Ssam ieee80211_sysctl_inact, "I", 231138568Ssam "station inactivity timeout (sec)"); 232138568Ssam SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 233178354Ssam "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_probe, 0, 234138568Ssam ieee80211_sysctl_inact, "I", 235138568Ssam "station inactivity probe timeout (sec)"); 236138568Ssam SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 237178354Ssam "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_auth, 0, 238138568Ssam ieee80211_sysctl_inact, "I", 239138568Ssam "station authentication timeout (sec)"); 240138568Ssam SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 241178354Ssam "inact_init", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_init, 0, 242138568Ssam ieee80211_sysctl_inact, "I", 243138568Ssam "station initial state timeout (sec)"); 244178354Ssam if (vap->iv_htcaps & IEEE80211_HTC_HT) { 245217322Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 246178354Ssam "ampdu_mintraffic_bk", CTLFLAG_RW, 247178354Ssam &vap->iv_ampdu_mintraffic[WME_AC_BK], 0, 248178354Ssam "BK traffic tx aggr threshold (pps)"); 249217322Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 250178354Ssam "ampdu_mintraffic_be", CTLFLAG_RW, 251178354Ssam &vap->iv_ampdu_mintraffic[WME_AC_BE], 0, 252178354Ssam "BE traffic tx aggr threshold (pps)"); 253217322Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 254178354Ssam "ampdu_mintraffic_vo", CTLFLAG_RW, 255178354Ssam &vap->iv_ampdu_mintraffic[WME_AC_VO], 0, 256178354Ssam "VO traffic tx aggr threshold (pps)"); 257217322Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 258178354Ssam "ampdu_mintraffic_vi", CTLFLAG_RW, 259178354Ssam &vap->iv_ampdu_mintraffic[WME_AC_VI], 0, 260178354Ssam "VI traffic tx aggr threshold (pps)"); 261178354Ssam } 262181194Ssam if (vap->iv_caps & IEEE80211_C_DFS) { 263181194Ssam SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 264181194Ssam "radar", CTLTYPE_INT | CTLFLAG_RW, vap->iv_ic, 0, 265193115Ssam ieee80211_sysctl_radar, "I", "simulate radar event"); 266181194Ssam } 267178354Ssam vap->iv_sysctl = ctx; 268178354Ssam vap->iv_oid = oid; 269138568Ssam} 270138568Ssam 271138568Ssamvoid 272178354Ssamieee80211_sysctl_vdetach(struct ieee80211vap *vap) 273138568Ssam{ 274138568Ssam 275178354Ssam if (vap->iv_sysctl != NULL) { 276178354Ssam sysctl_ctx_free(vap->iv_sysctl); 277283538Sadrian IEEE80211_FREE(vap->iv_sysctl, M_DEVBUF); 278178354Ssam vap->iv_sysctl = NULL; 279138568Ssam } 280138568Ssam} 281138568Ssam 282138568Ssamint 283138568Ssamieee80211_node_dectestref(struct ieee80211_node *ni) 284138568Ssam{ 285138568Ssam /* XXX need equivalent of atomic_dec_and_test */ 286138568Ssam atomic_subtract_int(&ni->ni_refcnt, 1); 287138568Ssam return atomic_cmpset_int(&ni->ni_refcnt, 0, 1); 288138568Ssam} 289138568Ssam 290165894Ssamvoid 291165894Ssamieee80211_drain_ifq(struct ifqueue *ifq) 292165894Ssam{ 293165894Ssam struct ieee80211_node *ni; 294165894Ssam struct mbuf *m; 295165894Ssam 296165894Ssam for (;;) { 297165894Ssam IF_DEQUEUE(ifq, m); 298165894Ssam if (m == NULL) 299165894Ssam break; 300165894Ssam 301165894Ssam ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; 302165894Ssam KASSERT(ni != NULL, ("frame w/o node")); 303165894Ssam ieee80211_free_node(ni); 304165894Ssam m->m_pkthdr.rcvif = NULL; 305165894Ssam 306165894Ssam m_freem(m); 307165894Ssam } 308165894Ssam} 309165894Ssam 310178354Ssamvoid 311178354Ssamieee80211_flush_ifq(struct ifqueue *ifq, struct ieee80211vap *vap) 312178354Ssam{ 313178354Ssam struct ieee80211_node *ni; 314178354Ssam struct mbuf *m, **mprev; 315178354Ssam 316178354Ssam IF_LOCK(ifq); 317178354Ssam mprev = &ifq->ifq_head; 318178354Ssam while ((m = *mprev) != NULL) { 319178354Ssam ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; 320178354Ssam if (ni != NULL && ni->ni_vap == vap) { 321178354Ssam *mprev = m->m_nextpkt; /* remove from list */ 322178354Ssam ifq->ifq_len--; 323178354Ssam 324178354Ssam m_freem(m); 325178354Ssam ieee80211_free_node(ni); /* reclaim ref */ 326178354Ssam } else 327178354Ssam mprev = &m->m_nextpkt; 328178354Ssam } 329178354Ssam /* recalculate tail ptr */ 330178354Ssam m = ifq->ifq_head; 331178354Ssam for (; m != NULL && m->m_nextpkt != NULL; m = m->m_nextpkt) 332178354Ssam ; 333178354Ssam ifq->ifq_tail = m; 334178354Ssam IF_UNLOCK(ifq); 335178354Ssam} 336178354Ssam 337138568Ssam/* 338170530Ssam * As above, for mbufs allocated with m_gethdr/MGETHDR 339170530Ssam * or initialized by M_COPY_PKTHDR. 340170530Ssam */ 341170530Ssam#define MC_ALIGN(m, len) \ 342170530Ssamdo { \ 343298433Spfg (m)->m_data += rounddown2(MCLBYTES - (len), sizeof(long)); \ 344170530Ssam} while (/* CONSTCOND */ 0) 345170530Ssam 346170530Ssam/* 347138568Ssam * Allocate and setup a management frame of the specified 348138568Ssam * size. We return the mbuf and a pointer to the start 349138568Ssam * of the contiguous data area that's been reserved based 350138568Ssam * on the packet length. The data area is forced to 32-bit 351138568Ssam * alignment and the buffer length to a multiple of 4 bytes. 352138568Ssam * This is done mainly so beacon frames (that require this) 353138568Ssam * can use this interface too. 354138568Ssam */ 355138568Ssamstruct mbuf * 356170530Ssamieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen) 357138568Ssam{ 358138568Ssam struct mbuf *m; 359138568Ssam u_int len; 360138568Ssam 361138568Ssam /* 362138568Ssam * NB: we know the mbuf routines will align the data area 363138568Ssam * so we don't need to do anything special. 364138568Ssam */ 365170530Ssam len = roundup2(headroom + pktlen, 4); 366138568Ssam KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len)); 367138568Ssam if (len < MINCLSIZE) { 368151967Sandre m = m_gethdr(M_NOWAIT, MT_DATA); 369138568Ssam /* 370138568Ssam * Align the data in case additional headers are added. 371138568Ssam * This should only happen when a WEP header is added 372138568Ssam * which only happens for shared key authentication mgt 373138568Ssam * frames which all fit in MHLEN. 374138568Ssam */ 375138568Ssam if (m != NULL) 376276692Srwatson M_ALIGN(m, len); 377170530Ssam } else { 378151967Sandre m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 379170530Ssam if (m != NULL) 380170530Ssam MC_ALIGN(m, len); 381170530Ssam } 382138568Ssam if (m != NULL) { 383171984Ssephe m->m_data += headroom; 384138568Ssam *frm = m->m_data; 385138568Ssam } 386138568Ssam return m; 387138568Ssam} 388138568Ssam 389246710Sglebius#ifndef __NO_STRICT_ALIGNMENT 390195757Ssam/* 391195757Ssam * Re-align the payload in the mbuf. This is mainly used (right now) 392195757Ssam * to handle IP header alignment requirements on certain architectures. 393195757Ssam */ 394195757Ssamstruct mbuf * 395195757Ssamieee80211_realign(struct ieee80211vap *vap, struct mbuf *m, size_t align) 396195757Ssam{ 397195757Ssam int pktlen, space; 398195757Ssam struct mbuf *n; 399195757Ssam 400195757Ssam pktlen = m->m_pkthdr.len; 401195757Ssam space = pktlen + align; 402195757Ssam if (space < MINCLSIZE) 403243882Sglebius n = m_gethdr(M_NOWAIT, MT_DATA); 404195757Ssam else { 405243882Sglebius n = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, 406195757Ssam space <= MCLBYTES ? MCLBYTES : 407195757Ssam#if MJUMPAGESIZE != MCLBYTES 408195757Ssam space <= MJUMPAGESIZE ? MJUMPAGESIZE : 409195757Ssam#endif 410195757Ssam space <= MJUM9BYTES ? MJUM9BYTES : MJUM16BYTES); 411195757Ssam } 412195757Ssam if (__predict_true(n != NULL)) { 413195757Ssam m_move_pkthdr(n, m); 414195757Ssam n->m_data = (caddr_t)(ALIGN(n->m_data + align) - align); 415195757Ssam m_copydata(m, 0, pktlen, mtod(n, caddr_t)); 416195757Ssam n->m_len = pktlen; 417195757Ssam } else { 418195757Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 419195757Ssam mtod(m, const struct ieee80211_frame *), NULL, 420195757Ssam "%s", "no mbuf to realign"); 421195757Ssam vap->iv_stats.is_rx_badalign++; 422195757Ssam } 423195757Ssam m_freem(m); 424195757Ssam return n; 425195757Ssam} 426246710Sglebius#endif /* !__NO_STRICT_ALIGNMENT */ 427195757Ssam 428170530Ssamint 429170530Ssamieee80211_add_callback(struct mbuf *m, 430170530Ssam void (*func)(struct ieee80211_node *, void *, int), void *arg) 431170530Ssam{ 432170530Ssam struct m_tag *mtag; 433170530Ssam struct ieee80211_cb *cb; 434170530Ssam 435170530Ssam mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_CALLBACK, 436170530Ssam sizeof(struct ieee80211_cb), M_NOWAIT); 437170530Ssam if (mtag == NULL) 438170530Ssam return 0; 439170530Ssam 440170530Ssam cb = (struct ieee80211_cb *)(mtag+1); 441170530Ssam cb->func = func; 442170530Ssam cb->arg = arg; 443170530Ssam m_tag_prepend(m, mtag); 444170530Ssam m->m_flags |= M_TXCB; 445170530Ssam return 1; 446170530Ssam} 447170530Ssam 448283980Sadrianint 449283980Sadrianieee80211_add_xmit_params(struct mbuf *m, 450283980Sadrian const struct ieee80211_bpf_params *params) 451283980Sadrian{ 452283980Sadrian struct m_tag *mtag; 453283980Sadrian struct ieee80211_tx_params *tx; 454283980Sadrian 455283980Sadrian mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_XMIT_PARAMS, 456283980Sadrian sizeof(struct ieee80211_tx_params), M_NOWAIT); 457283980Sadrian if (mtag == NULL) 458283980Sadrian return (0); 459283980Sadrian 460283980Sadrian tx = (struct ieee80211_tx_params *)(mtag+1); 461283980Sadrian memcpy(&tx->params, params, sizeof(struct ieee80211_bpf_params)); 462283980Sadrian m_tag_prepend(m, mtag); 463283980Sadrian return (1); 464283980Sadrian} 465283980Sadrian 466283980Sadrianint 467283980Sadrianieee80211_get_xmit_params(struct mbuf *m, 468283980Sadrian struct ieee80211_bpf_params *params) 469283980Sadrian{ 470283980Sadrian struct m_tag *mtag; 471283980Sadrian struct ieee80211_tx_params *tx; 472283980Sadrian 473283980Sadrian mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_XMIT_PARAMS, 474283980Sadrian NULL); 475283980Sadrian if (mtag == NULL) 476283980Sadrian return (-1); 477283980Sadrian tx = (struct ieee80211_tx_params *)(mtag + 1); 478283980Sadrian memcpy(params, &tx->params, sizeof(struct ieee80211_bpf_params)); 479283980Sadrian return (0); 480283980Sadrian} 481283980Sadrian 482170530Ssamvoid 483170530Ssamieee80211_process_callback(struct ieee80211_node *ni, 484170530Ssam struct mbuf *m, int status) 485170530Ssam{ 486170530Ssam struct m_tag *mtag; 487170530Ssam 488170530Ssam mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_CALLBACK, NULL); 489170530Ssam if (mtag != NULL) { 490170530Ssam struct ieee80211_cb *cb = (struct ieee80211_cb *)(mtag+1); 491170530Ssam cb->func(ni, cb->arg, status); 492170530Ssam } 493170530Ssam} 494170530Ssam 495248069Sadrian/* 496288245Sadrian * Add RX parameters to the given mbuf. 497288245Sadrian * 498288245Sadrian * Returns 1 if OK, 0 on error. 499288245Sadrian */ 500288245Sadrianint 501288245Sadrianieee80211_add_rx_params(struct mbuf *m, const struct ieee80211_rx_stats *rxs) 502288245Sadrian{ 503288245Sadrian struct m_tag *mtag; 504288245Sadrian struct ieee80211_rx_params *rx; 505288245Sadrian 506288245Sadrian mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_RECV_PARAMS, 507288245Sadrian sizeof(struct ieee80211_rx_stats), M_NOWAIT); 508288245Sadrian if (mtag == NULL) 509288245Sadrian return (0); 510288245Sadrian 511288245Sadrian rx = (struct ieee80211_rx_params *)(mtag + 1); 512288245Sadrian memcpy(&rx->params, rxs, sizeof(*rxs)); 513288245Sadrian m_tag_prepend(m, mtag); 514288245Sadrian return (1); 515288245Sadrian} 516288245Sadrian 517288245Sadrianint 518288245Sadrianieee80211_get_rx_params(struct mbuf *m, struct ieee80211_rx_stats *rxs) 519288245Sadrian{ 520288245Sadrian struct m_tag *mtag; 521288245Sadrian struct ieee80211_rx_params *rx; 522288245Sadrian 523288245Sadrian mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_RECV_PARAMS, 524288245Sadrian NULL); 525288245Sadrian if (mtag == NULL) 526288245Sadrian return (-1); 527288245Sadrian rx = (struct ieee80211_rx_params *)(mtag + 1); 528288245Sadrian memcpy(rxs, &rx->params, sizeof(*rxs)); 529288245Sadrian return (0); 530288245Sadrian} 531288245Sadrian 532288245Sadrian/* 533248069Sadrian * Transmit a frame to the parent interface. 534248069Sadrian */ 535248069Sadrianint 536287197Sglebiusieee80211_parent_xmitpkt(struct ieee80211com *ic, struct mbuf *m) 537248069Sadrian{ 538287197Sglebius int error; 539287197Sglebius 540248069Sadrian /* 541248069Sadrian * Assert the IC TX lock is held - this enforces the 542248069Sadrian * processing -> queuing order is maintained 543248069Sadrian */ 544248069Sadrian IEEE80211_TX_LOCK_ASSERT(ic); 545287197Sglebius error = ic->ic_transmit(ic, m); 546289164Sadrian if (error) { 547289164Sadrian struct ieee80211_node *ni; 548289164Sadrian 549289164Sadrian ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; 550289164Sadrian 551289164Sadrian /* XXX number of fragments */ 552289164Sadrian if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); 553289164Sadrian ieee80211_free_node(ni); 554289162Sadrian ieee80211_free_mbuf(m); 555289164Sadrian } 556287197Sglebius return (error); 557248069Sadrian} 558248069Sadrian 559248069Sadrian/* 560248069Sadrian * Transmit a frame to the VAP interface. 561248069Sadrian */ 562248069Sadrianint 563254082Sadrianieee80211_vap_xmitpkt(struct ieee80211vap *vap, struct mbuf *m) 564248069Sadrian{ 565248069Sadrian struct ifnet *ifp = vap->iv_ifp; 566248069Sadrian 567248069Sadrian /* 568248069Sadrian * When transmitting via the VAP, we shouldn't hold 569248069Sadrian * any IC TX lock as the VAP TX path will acquire it. 570248069Sadrian */ 571248069Sadrian IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic); 572248069Sadrian 573248069Sadrian return (ifp->if_transmit(ifp, m)); 574248069Sadrian 575248069Sadrian} 576248069Sadrian 577138568Ssam#include <sys/libkern.h> 578138568Ssam 579138568Ssamvoid 580138568Ssamget_random_bytes(void *p, size_t n) 581138568Ssam{ 582170530Ssam uint8_t *dp = p; 583138568Ssam 584138568Ssam while (n > 0) { 585170530Ssam uint32_t v = arc4random(); 586170530Ssam size_t nb = n > sizeof(uint32_t) ? sizeof(uint32_t) : n; 587170530Ssam bcopy(&v, dp, n > sizeof(uint32_t) ? sizeof(uint32_t) : n); 588170530Ssam dp += sizeof(uint32_t), n -= nb; 589138568Ssam } 590138568Ssam} 591138568Ssam 592178354Ssam/* 593178354Ssam * Helper function for events that pass just a single mac address. 594178354Ssam */ 595178354Ssamstatic void 596178354Ssamnotify_macaddr(struct ifnet *ifp, int op, const uint8_t mac[IEEE80211_ADDR_LEN]) 597138568Ssam{ 598138568Ssam struct ieee80211_join_event iev; 599138568Ssam 600191816Szec CURVNET_SET(ifp->if_vnet); 601144302Ssam memset(&iev, 0, sizeof(iev)); 602178354Ssam IEEE80211_ADDR_COPY(iev.iev_addr, mac); 603178354Ssam rt_ieee80211msg(ifp, op, &iev, sizeof(iev)); 604191816Szec CURVNET_RESTORE(); 605178354Ssam} 606178354Ssam 607178354Ssamvoid 608178354Ssamieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc) 609178354Ssam{ 610178354Ssam struct ieee80211vap *vap = ni->ni_vap; 611178354Ssam struct ifnet *ifp = vap->iv_ifp; 612178354Ssam 613191816Szec CURVNET_SET_QUIET(ifp->if_vnet); 614178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode join", 615178354Ssam (ni == vap->iv_bss) ? "bss " : ""); 616178354Ssam 617178354Ssam if (ni == vap->iv_bss) { 618178354Ssam notify_macaddr(ifp, newassoc ? 619178354Ssam RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC, ni->ni_bssid); 620138568Ssam if_link_state_change(ifp, LINK_STATE_UP); 621144302Ssam } else { 622178354Ssam notify_macaddr(ifp, newassoc ? 623178354Ssam RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN, ni->ni_macaddr); 624138568Ssam } 625191816Szec CURVNET_RESTORE(); 626138568Ssam} 627138568Ssam 628138568Ssamvoid 629178354Ssamieee80211_notify_node_leave(struct ieee80211_node *ni) 630138568Ssam{ 631178354Ssam struct ieee80211vap *vap = ni->ni_vap; 632178354Ssam struct ifnet *ifp = vap->iv_ifp; 633138568Ssam 634191816Szec CURVNET_SET_QUIET(ifp->if_vnet); 635178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode leave", 636178354Ssam (ni == vap->iv_bss) ? "bss " : ""); 637178354Ssam 638178354Ssam if (ni == vap->iv_bss) { 639138568Ssam rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0); 640138568Ssam if_link_state_change(ifp, LINK_STATE_DOWN); 641138568Ssam } else { 642138568Ssam /* fire off wireless event station leaving */ 643178354Ssam notify_macaddr(ifp, RTM_IEEE80211_LEAVE, ni->ni_macaddr); 644138568Ssam } 645191816Szec CURVNET_RESTORE(); 646138568Ssam} 647138568Ssam 648138568Ssamvoid 649178354Ssamieee80211_notify_scan_done(struct ieee80211vap *vap) 650138568Ssam{ 651178354Ssam struct ifnet *ifp = vap->iv_ifp; 652138568Ssam 653178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", "notify scan done"); 654138568Ssam 655138568Ssam /* dispatch wireless event indicating scan completed */ 656191816Szec CURVNET_SET(ifp->if_vnet); 657138568Ssam rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0); 658191816Szec CURVNET_RESTORE(); 659138568Ssam} 660138568Ssam 661138568Ssamvoid 662178354Ssamieee80211_notify_replay_failure(struct ieee80211vap *vap, 663138568Ssam const struct ieee80211_frame *wh, const struct ieee80211_key *k, 664193541Ssam u_int64_t rsc, int tid) 665138568Ssam{ 666178354Ssam struct ifnet *ifp = vap->iv_ifp; 667138568Ssam 668178354Ssam IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, 669226885Sadrian "%s replay detected tid %d <rsc %ju, csc %ju, keyix %u rxkeyix %u>", 670233531Sadrian k->wk_cipher->ic_name, tid, (intmax_t) rsc, 671193541Ssam (intmax_t) k->wk_keyrsc[tid], 672148863Ssam k->wk_keyix, k->wk_rxkeyix); 673138568Ssam 674138568Ssam if (ifp != NULL) { /* NB: for cipher test modules */ 675138568Ssam struct ieee80211_replay_event iev; 676138568Ssam 677138568Ssam IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1); 678138568Ssam IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2); 679138568Ssam iev.iev_cipher = k->wk_cipher->ic_cipher; 680148863Ssam if (k->wk_rxkeyix != IEEE80211_KEYIX_NONE) 681148863Ssam iev.iev_keyix = k->wk_rxkeyix; 682148863Ssam else 683148863Ssam iev.iev_keyix = k->wk_keyix; 684193541Ssam iev.iev_keyrsc = k->wk_keyrsc[tid]; 685138568Ssam iev.iev_rsc = rsc; 686191816Szec CURVNET_SET(ifp->if_vnet); 687138568Ssam rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev)); 688191816Szec CURVNET_RESTORE(); 689138568Ssam } 690138568Ssam} 691138568Ssam 692138568Ssamvoid 693178354Ssamieee80211_notify_michael_failure(struct ieee80211vap *vap, 694138568Ssam const struct ieee80211_frame *wh, u_int keyix) 695138568Ssam{ 696178354Ssam struct ifnet *ifp = vap->iv_ifp; 697138568Ssam 698178354Ssam IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, 699178354Ssam "michael MIC verification failed <keyix %u>", keyix); 700178354Ssam vap->iv_stats.is_rx_tkipmic++; 701138568Ssam 702138568Ssam if (ifp != NULL) { /* NB: for cipher test modules */ 703138568Ssam struct ieee80211_michael_event iev; 704138568Ssam 705138568Ssam IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1); 706138568Ssam IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2); 707138568Ssam iev.iev_cipher = IEEE80211_CIPHER_TKIP; 708138568Ssam iev.iev_keyix = keyix; 709191816Szec CURVNET_SET(ifp->if_vnet); 710138568Ssam rt_ieee80211msg(ifp, RTM_IEEE80211_MICHAEL, &iev, sizeof(iev)); 711191816Szec CURVNET_RESTORE(); 712138568Ssam } 713138568Ssam} 714138568Ssam 715138568Ssamvoid 716178354Ssamieee80211_notify_wds_discover(struct ieee80211_node *ni) 717178354Ssam{ 718178354Ssam struct ieee80211vap *vap = ni->ni_vap; 719178354Ssam struct ifnet *ifp = vap->iv_ifp; 720178354Ssam 721178354Ssam notify_macaddr(ifp, RTM_IEEE80211_WDS, ni->ni_macaddr); 722178354Ssam} 723178354Ssam 724178354Ssamvoid 725178354Ssamieee80211_notify_csa(struct ieee80211com *ic, 726178354Ssam const struct ieee80211_channel *c, int mode, int count) 727178354Ssam{ 728178354Ssam struct ieee80211_csa_event iev; 729283539Sglebius struct ieee80211vap *vap; 730283539Sglebius struct ifnet *ifp; 731178354Ssam 732178354Ssam memset(&iev, 0, sizeof(iev)); 733178354Ssam iev.iev_flags = c->ic_flags; 734178354Ssam iev.iev_freq = c->ic_freq; 735178354Ssam iev.iev_ieee = c->ic_ieee; 736178354Ssam iev.iev_mode = mode; 737178354Ssam iev.iev_count = count; 738283539Sglebius TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 739283539Sglebius ifp = vap->iv_ifp; 740283539Sglebius CURVNET_SET(ifp->if_vnet); 741283539Sglebius rt_ieee80211msg(ifp, RTM_IEEE80211_CSA, &iev, sizeof(iev)); 742283539Sglebius CURVNET_RESTORE(); 743283539Sglebius } 744178354Ssam} 745178354Ssam 746178354Ssamvoid 747178354Ssamieee80211_notify_radar(struct ieee80211com *ic, 748178354Ssam const struct ieee80211_channel *c) 749178354Ssam{ 750178354Ssam struct ieee80211_radar_event iev; 751283539Sglebius struct ieee80211vap *vap; 752283539Sglebius struct ifnet *ifp; 753178354Ssam 754178354Ssam memset(&iev, 0, sizeof(iev)); 755178354Ssam iev.iev_flags = c->ic_flags; 756178354Ssam iev.iev_freq = c->ic_freq; 757178354Ssam iev.iev_ieee = c->ic_ieee; 758283539Sglebius TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 759283539Sglebius ifp = vap->iv_ifp; 760283539Sglebius CURVNET_SET(ifp->if_vnet); 761283539Sglebius rt_ieee80211msg(ifp, RTM_IEEE80211_RADAR, &iev, sizeof(iev)); 762283539Sglebius CURVNET_RESTORE(); 763283539Sglebius } 764178354Ssam} 765178354Ssam 766178354Ssamvoid 767178354Ssamieee80211_notify_cac(struct ieee80211com *ic, 768178354Ssam const struct ieee80211_channel *c, enum ieee80211_notify_cac_event type) 769178354Ssam{ 770178354Ssam struct ieee80211_cac_event iev; 771283539Sglebius struct ieee80211vap *vap; 772283539Sglebius struct ifnet *ifp; 773178354Ssam 774178354Ssam memset(&iev, 0, sizeof(iev)); 775178354Ssam iev.iev_flags = c->ic_flags; 776178354Ssam iev.iev_freq = c->ic_freq; 777178354Ssam iev.iev_ieee = c->ic_ieee; 778178354Ssam iev.iev_type = type; 779283539Sglebius TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 780283539Sglebius ifp = vap->iv_ifp; 781283539Sglebius CURVNET_SET(ifp->if_vnet); 782283539Sglebius rt_ieee80211msg(ifp, RTM_IEEE80211_CAC, &iev, sizeof(iev)); 783283539Sglebius CURVNET_RESTORE(); 784283539Sglebius } 785178354Ssam} 786178354Ssam 787178354Ssamvoid 788178354Ssamieee80211_notify_node_deauth(struct ieee80211_node *ni) 789178354Ssam{ 790178354Ssam struct ieee80211vap *vap = ni->ni_vap; 791178354Ssam struct ifnet *ifp = vap->iv_ifp; 792178354Ssam 793178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node deauth"); 794178354Ssam 795178354Ssam notify_macaddr(ifp, RTM_IEEE80211_DEAUTH, ni->ni_macaddr); 796178354Ssam} 797178354Ssam 798178354Ssamvoid 799178354Ssamieee80211_notify_node_auth(struct ieee80211_node *ni) 800178354Ssam{ 801178354Ssam struct ieee80211vap *vap = ni->ni_vap; 802178354Ssam struct ifnet *ifp = vap->iv_ifp; 803178354Ssam 804178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node auth"); 805178354Ssam 806178354Ssam notify_macaddr(ifp, RTM_IEEE80211_AUTH, ni->ni_macaddr); 807178354Ssam} 808178354Ssam 809178354Ssamvoid 810178354Ssamieee80211_notify_country(struct ieee80211vap *vap, 811178354Ssam const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t cc[2]) 812178354Ssam{ 813178354Ssam struct ifnet *ifp = vap->iv_ifp; 814178354Ssam struct ieee80211_country_event iev; 815178354Ssam 816178354Ssam memset(&iev, 0, sizeof(iev)); 817178354Ssam IEEE80211_ADDR_COPY(iev.iev_addr, bssid); 818178354Ssam iev.iev_cc[0] = cc[0]; 819178354Ssam iev.iev_cc[1] = cc[1]; 820248539Sadrian CURVNET_SET(ifp->if_vnet); 821178354Ssam rt_ieee80211msg(ifp, RTM_IEEE80211_COUNTRY, &iev, sizeof(iev)); 822248539Sadrian CURVNET_RESTORE(); 823178354Ssam} 824178354Ssam 825178354Ssamvoid 826178354Ssamieee80211_notify_radio(struct ieee80211com *ic, int state) 827178354Ssam{ 828178354Ssam struct ieee80211_radio_event iev; 829283539Sglebius struct ieee80211vap *vap; 830283539Sglebius struct ifnet *ifp; 831178354Ssam 832178354Ssam memset(&iev, 0, sizeof(iev)); 833178354Ssam iev.iev_state = state; 834283539Sglebius TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 835283539Sglebius ifp = vap->iv_ifp; 836283539Sglebius CURVNET_SET(ifp->if_vnet); 837283539Sglebius rt_ieee80211msg(ifp, RTM_IEEE80211_RADIO, &iev, sizeof(iev)); 838283539Sglebius CURVNET_RESTORE(); 839283539Sglebius } 840178354Ssam} 841178354Ssam 842178354Ssamvoid 843138568Ssamieee80211_load_module(const char *modname) 844138568Ssam{ 845159590Sjhb 846138777Ssam#ifdef notyet 847159590Sjhb (void)kern_kldload(curthread, modname, NULL); 848138777Ssam#else 849138777Ssam printf("%s: load the %s module by hand for now.\n", __func__, modname); 850138777Ssam#endif 851138568Ssam} 852138568Ssam 853192468Ssamstatic eventhandler_tag wlan_bpfevent; 854299171Savosstatic eventhandler_tag wlan_ifllevent; 855192468Ssam 856192468Ssamstatic void 857192764Ssambpf_track(void *arg, struct ifnet *ifp, int dlt, int attach) 858192468Ssam{ 859256294Sadrian /* NB: identify vap's by if_init */ 860254082Sadrian if (dlt == DLT_IEEE802_11_RADIO && 861256294Sadrian ifp->if_init == ieee80211_init) { 862192468Ssam struct ieee80211vap *vap = ifp->if_softc; 863192468Ssam /* 864192468Ssam * Track bpf radiotap listener state. We mark the vap 865192468Ssam * to indicate if any listener is present and the com 866192468Ssam * to indicate if any listener exists on any associated 867192468Ssam * vap. This flag is used by drivers to prepare radiotap 868192468Ssam * state only when needed. 869192468Ssam */ 870193292Ssam if (attach) { 871192468Ssam ieee80211_syncflag_ext(vap, IEEE80211_FEXT_BPF); 872193292Ssam if (vap->iv_opmode == IEEE80211_M_MONITOR) 873193292Ssam atomic_add_int(&vap->iv_ic->ic_montaps, 1); 874193312Ssam } else if (!bpf_peers_present(vap->iv_rawbpf)) { 875192468Ssam ieee80211_syncflag_ext(vap, -IEEE80211_FEXT_BPF); 876193292Ssam if (vap->iv_opmode == IEEE80211_M_MONITOR) 877193292Ssam atomic_subtract_int(&vap->iv_ic->ic_montaps, 1); 878193292Ssam } 879192468Ssam } 880192468Ssam} 881192468Ssam 882138568Ssam/* 883299171Savos * Change MAC address on the vap (if was not started). 884299171Savos */ 885299171Savosstatic void 886299171Savoswlan_iflladdr(void *arg __unused, struct ifnet *ifp) 887299171Savos{ 888299171Savos /* NB: identify vap's by if_init */ 889299171Savos if (ifp->if_init == ieee80211_init && 890299171Savos (ifp->if_flags & IFF_UP) == 0) { 891299171Savos struct ieee80211vap *vap = ifp->if_softc; 892299171Savos 893299171Savos IEEE80211_ADDR_COPY(vap->iv_myaddr, IF_LLADDR(ifp)); 894299171Savos } 895299171Savos} 896299171Savos 897299171Savos/* 898138568Ssam * Module glue. 899138568Ssam * 900138568Ssam * NB: the module name is "wlan" for compatibility with NetBSD. 901138568Ssam */ 902138568Ssamstatic int 903138568Ssamwlan_modevent(module_t mod, int type, void *unused) 904138568Ssam{ 905138568Ssam switch (type) { 906138568Ssam case MOD_LOAD: 907138568Ssam if (bootverbose) 908138568Ssam printf("wlan: <802.11 Link Layer>\n"); 909192468Ssam wlan_bpfevent = EVENTHANDLER_REGISTER(bpf_track, 910192468Ssam bpf_track, 0, EVENTHANDLER_PRI_ANY); 911299171Savos wlan_ifllevent = EVENTHANDLER_REGISTER(iflladdr_event, 912299171Savos wlan_iflladdr, NULL, EVENTHANDLER_PRI_ANY); 913241610Sglebius wlan_cloner = if_clone_simple(wlanname, wlan_clone_create, 914241610Sglebius wlan_clone_destroy, 0); 915138568Ssam return 0; 916138568Ssam case MOD_UNLOAD: 917241610Sglebius if_clone_detach(wlan_cloner); 918192468Ssam EVENTHANDLER_DEREGISTER(bpf_track, wlan_bpfevent); 919299171Savos EVENTHANDLER_DEREGISTER(iflladdr_event, wlan_ifllevent); 920138568Ssam return 0; 921138568Ssam } 922138568Ssam return EINVAL; 923138568Ssam} 924138568Ssam 925138568Ssamstatic moduledata_t wlan_mod = { 926241610Sglebius wlanname, 927138568Ssam wlan_modevent, 928241394Skevlo 0 929138568Ssam}; 930138568SsamDECLARE_MODULE(wlan, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 931138568SsamMODULE_VERSION(wlan, 1); 932138568SsamMODULE_DEPEND(wlan, ether, 1, 1, 1); 933233050Sadrian#ifdef IEEE80211_ALQ 934233050SadrianMODULE_DEPEND(wlan, alq, 1, 1, 1); 935233050Sadrian#endif /* IEEE80211_ALQ */ 936233050Sadrian 937