Index: head/sys/net/if_clone.c =================================================================== --- head/sys/net/if_clone.c (revision 183209) +++ head/sys/net/if_clone.c (revision 183210) @@ -1,565 +1,564 @@ /*- * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if.c 8.5 (Berkeley) 1/9/95 * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #if 0 #include #endif #include #include #include #include static void if_clone_free(struct if_clone *ifc); static int if_clone_createif(struct if_clone *ifc, char *name, size_t len, caddr_t params); -static int if_clone_destroyif(struct if_clone *ifc, struct ifnet *ifp); static struct mtx if_cloners_mtx; static int if_cloners_count; LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners); #define IF_CLONERS_LOCK_INIT() \ mtx_init(&if_cloners_mtx, "if_cloners lock", NULL, MTX_DEF) #define IF_CLONERS_LOCK_ASSERT() mtx_assert(&if_cloners_mtx, MA_OWNED) #define IF_CLONERS_LOCK() mtx_lock(&if_cloners_mtx) #define IF_CLONERS_UNLOCK() mtx_unlock(&if_cloners_mtx) #define IF_CLONE_LOCK_INIT(ifc) \ mtx_init(&(ifc)->ifc_mtx, "if_clone lock", NULL, MTX_DEF) #define IF_CLONE_LOCK_DESTROY(ifc) mtx_destroy(&(ifc)->ifc_mtx) #define IF_CLONE_LOCK_ASSERT(ifc) mtx_assert(&(ifc)->ifc_mtx, MA_OWNED) #define IF_CLONE_LOCK(ifc) mtx_lock(&(ifc)->ifc_mtx) #define IF_CLONE_UNLOCK(ifc) mtx_unlock(&(ifc)->ifc_mtx) #define IF_CLONE_ADDREF(ifc) \ do { \ IF_CLONE_LOCK(ifc); \ IF_CLONE_ADDREF_LOCKED(ifc); \ IF_CLONE_UNLOCK(ifc); \ } while (0) #define IF_CLONE_ADDREF_LOCKED(ifc) \ do { \ IF_CLONE_LOCK_ASSERT(ifc); \ KASSERT((ifc)->ifc_refcnt >= 0, \ ("negative refcnt %ld", (ifc)->ifc_refcnt)); \ (ifc)->ifc_refcnt++; \ } while (0) #define IF_CLONE_REMREF(ifc) \ do { \ IF_CLONE_LOCK(ifc); \ IF_CLONE_REMREF_LOCKED(ifc); \ } while (0) #define IF_CLONE_REMREF_LOCKED(ifc) \ do { \ IF_CLONE_LOCK_ASSERT(ifc); \ KASSERT((ifc)->ifc_refcnt > 0, \ ("bogus refcnt %ld", (ifc)->ifc_refcnt)); \ if (--(ifc)->ifc_refcnt == 0) { \ IF_CLONE_UNLOCK(ifc); \ if_clone_free(ifc); \ } else { \ /* silently free the lock */ \ IF_CLONE_UNLOCK(ifc); \ } \ } while (0) #define IFC_IFLIST_INSERT(_ifc, _ifp) \ LIST_INSERT_HEAD(&_ifc->ifc_iflist, _ifp, if_clones) #define IFC_IFLIST_REMOVE(_ifc, _ifp) \ LIST_REMOVE(_ifp, if_clones) static MALLOC_DEFINE(M_CLONE, "clone", "interface cloning framework"); void if_clone_init(void) { IF_CLONERS_LOCK_INIT(); } /* * Lookup and create a clone network interface. */ int if_clone_create(char *name, size_t len, caddr_t params) { struct if_clone *ifc; /* Try to find an applicable cloner for this request */ IF_CLONERS_LOCK(); LIST_FOREACH(ifc, &if_cloners, ifc_list) { if (ifc->ifc_match(ifc, name)) { break; } } IF_CLONERS_UNLOCK(); if (ifc == NULL) return (EINVAL); return (if_clone_createif(ifc, name, len, params)); } /* * Create a clone network interface. */ static int if_clone_createif(struct if_clone *ifc, char *name, size_t len, caddr_t params) { int err; struct ifnet *ifp; if (ifunit(name) != NULL) return (EEXIST); err = (*ifc->ifc_create)(ifc, name, len, params); if (!err) { ifp = ifunit(name); if (ifp == NULL) panic("%s: lookup failed for %s", __func__, name); if_addgroup(ifp, ifc->ifc_name); IF_CLONE_LOCK(ifc); IFC_IFLIST_INSERT(ifc, ifp); IF_CLONE_UNLOCK(ifc); } return (err); } /* * Lookup and destroy a clone network interface. */ int if_clone_destroy(const char *name) { struct if_clone *ifc; struct ifnet *ifp; ifp = ifunit(name); if (ifp == NULL) return (ENXIO); /* Find the cloner for this interface */ IF_CLONERS_LOCK(); LIST_FOREACH(ifc, &if_cloners, ifc_list) { if (strcmp(ifc->ifc_name, ifp->if_dname) == 0) { break; } } IF_CLONERS_UNLOCK(); if (ifc == NULL) return (EINVAL); return (if_clone_destroyif(ifc, ifp)); } /* * Destroy a clone network interface. */ -static int +int if_clone_destroyif(struct if_clone *ifc, struct ifnet *ifp) { int err; if (ifc->ifc_destroy == NULL) { err = EOPNOTSUPP; goto done; } IF_CLONE_LOCK(ifc); IFC_IFLIST_REMOVE(ifc, ifp); IF_CLONE_UNLOCK(ifc); if_delgroup(ifp, ifc->ifc_name); err = (*ifc->ifc_destroy)(ifc, ifp); if (err != 0) { if_addgroup(ifp, ifc->ifc_name); IF_CLONE_LOCK(ifc); IFC_IFLIST_INSERT(ifc, ifp); IF_CLONE_UNLOCK(ifc); } done: return (err); } /* * Register a network interface cloner. */ void if_clone_attach(struct if_clone *ifc) { int len, maxclone; /* * Compute bitmap size and allocate it. */ maxclone = ifc->ifc_maxunit + 1; len = maxclone >> 3; if ((len << 3) < maxclone) len++; ifc->ifc_units = malloc(len, M_CLONE, M_WAITOK | M_ZERO); ifc->ifc_bmlen = len; IF_CLONE_LOCK_INIT(ifc); IF_CLONE_ADDREF(ifc); IF_CLONERS_LOCK(); LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list); if_cloners_count++; IF_CLONERS_UNLOCK(); LIST_INIT(&ifc->ifc_iflist); if (ifc->ifc_attach != NULL) (*ifc->ifc_attach)(ifc); EVENTHANDLER_INVOKE(if_clone_event, ifc); } /* * Unregister a network interface cloner. */ void if_clone_detach(struct if_clone *ifc) { struct ifc_simple_data *ifcs = ifc->ifc_data; IF_CLONERS_LOCK(); LIST_REMOVE(ifc, ifc_list); if_cloners_count--; IF_CLONERS_UNLOCK(); /* Allow all simples to be destroyed */ if (ifc->ifc_attach == ifc_simple_attach) ifcs->ifcs_minifs = 0; /* destroy all interfaces for this cloner */ while (!LIST_EMPTY(&ifc->ifc_iflist)) if_clone_destroyif(ifc, LIST_FIRST(&ifc->ifc_iflist)); IF_CLONE_REMREF(ifc); } static void if_clone_free(struct if_clone *ifc) { for (int bytoff = 0; bytoff < ifc->ifc_bmlen; bytoff++) { KASSERT(ifc->ifc_units[bytoff] == 0x00, ("ifc_units[%d] is not empty", bytoff)); } KASSERT(LIST_EMPTY(&ifc->ifc_iflist), ("%s: ifc_iflist not empty", __func__)); IF_CLONE_LOCK_DESTROY(ifc); free(ifc->ifc_units, M_CLONE); } /* * Provide list of interface cloners to userspace. */ int if_clone_list(struct if_clonereq *ifcr) { char *buf, *dst, *outbuf = NULL; struct if_clone *ifc; int buf_count, count, err = 0; if (ifcr->ifcr_count < 0) return (EINVAL); IF_CLONERS_LOCK(); /* * Set our internal output buffer size. We could end up not * reporting a cloner that is added between the unlock and lock * below, but that's not a major problem. Not caping our * allocation to the number of cloners actually in the system * could be because that would let arbitrary users cause us to * allocate abritrary amounts of kernel memory. */ buf_count = (if_cloners_count < ifcr->ifcr_count) ? if_cloners_count : ifcr->ifcr_count; IF_CLONERS_UNLOCK(); outbuf = malloc(IFNAMSIZ*buf_count, M_CLONE, M_WAITOK | M_ZERO); IF_CLONERS_LOCK(); ifcr->ifcr_total = if_cloners_count; if ((dst = ifcr->ifcr_buffer) == NULL) { /* Just asking how many there are. */ goto done; } count = (if_cloners_count < buf_count) ? if_cloners_count : buf_count; for (ifc = LIST_FIRST(&if_cloners), buf = outbuf; ifc != NULL && count != 0; ifc = LIST_NEXT(ifc, ifc_list), count--, buf += IFNAMSIZ) { strlcpy(buf, ifc->ifc_name, IFNAMSIZ); } done: IF_CLONERS_UNLOCK(); if (err == 0) err = copyout(outbuf, dst, buf_count*IFNAMSIZ); if (outbuf != NULL) free(outbuf, M_CLONE); return (err); } /* * A utility function to extract unit numbers from interface names of * the form name###. * * Returns 0 on success and an error on failure. */ int ifc_name2unit(const char *name, int *unit) { const char *cp; int cutoff = INT_MAX / 10; int cutlim = INT_MAX % 10; for (cp = name; *cp != '\0' && (*cp < '0' || *cp > '9'); cp++); if (*cp == '\0') { *unit = -1; } else if (cp[0] == '0' && cp[1] != '\0') { /* Disallow leading zeroes. */ return (EINVAL); } else { for (*unit = 0; *cp != '\0'; cp++) { if (*cp < '0' || *cp > '9') { /* Bogus unit number. */ return (EINVAL); } if (*unit > cutoff || (*unit == cutoff && *cp - '0' > cutlim)) return (EINVAL); *unit = (*unit * 10) + (*cp - '0'); } } return (0); } int ifc_alloc_unit(struct if_clone *ifc, int *unit) { int wildcard, bytoff, bitoff; int err = 0; IF_CLONE_LOCK(ifc); bytoff = bitoff = 0; wildcard = (*unit < 0); /* * Find a free unit if none was given. */ if (wildcard) { while ((bytoff < ifc->ifc_bmlen) && (ifc->ifc_units[bytoff] == 0xff)) bytoff++; if (bytoff >= ifc->ifc_bmlen) { err = ENOSPC; goto done; } while ((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0) bitoff++; *unit = (bytoff << 3) + bitoff; } if (*unit > ifc->ifc_maxunit) { err = ENOSPC; goto done; } if (!wildcard) { bytoff = *unit >> 3; bitoff = *unit - (bytoff << 3); } if((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0) { err = EEXIST; goto done; } /* * Allocate the unit in the bitmap. */ KASSERT((ifc->ifc_units[bytoff] & (1 << bitoff)) == 0, ("%s: bit is already set", __func__)); ifc->ifc_units[bytoff] |= (1 << bitoff); IF_CLONE_ADDREF_LOCKED(ifc); done: IF_CLONE_UNLOCK(ifc); return (err); } void ifc_free_unit(struct if_clone *ifc, int unit) { int bytoff, bitoff; /* * Compute offset in the bitmap and deallocate the unit. */ bytoff = unit >> 3; bitoff = unit - (bytoff << 3); IF_CLONE_LOCK(ifc); KASSERT((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0, ("%s: bit is already cleared", __func__)); ifc->ifc_units[bytoff] &= ~(1 << bitoff); IF_CLONE_REMREF_LOCKED(ifc); /* releases lock */ } void ifc_simple_attach(struct if_clone *ifc) { int err; int unit; char name[IFNAMSIZ]; struct ifc_simple_data *ifcs = ifc->ifc_data; KASSERT(ifcs->ifcs_minifs - 1 <= ifc->ifc_maxunit, ("%s: %s requested more units than allowed (%d > %d)", __func__, ifc->ifc_name, ifcs->ifcs_minifs, ifc->ifc_maxunit + 1)); for (unit = 0; unit < ifcs->ifcs_minifs; unit++) { snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, unit); err = if_clone_createif(ifc, name, IFNAMSIZ, NULL); KASSERT(err == 0, ("%s: failed to create required interface %s", __func__, name)); } } int ifc_simple_match(struct if_clone *ifc, const char *name) { const char *cp; int i; /* Match the name */ for (cp = name, i = 0; i < strlen(ifc->ifc_name); i++, cp++) { if (ifc->ifc_name[i] != *cp) return (0); } /* Make sure there's a unit number or nothing after the name */ for (; *cp != '\0'; cp++) { if (*cp < '0' || *cp > '9') return (0); } return (1); } int ifc_simple_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) { char *dp; int wildcard; int unit; int err; struct ifc_simple_data *ifcs = ifc->ifc_data; err = ifc_name2unit(name, &unit); if (err != 0) return (err); wildcard = (unit < 0); err = ifc_alloc_unit(ifc, &unit); if (err != 0) return (err); err = ifcs->ifcs_create(ifc, unit, params); if (err != 0) { ifc_free_unit(ifc, unit); return (err); } /* In the wildcard case, we need to update the name. */ if (wildcard) { for (dp = name; *dp != '\0'; dp++); if (snprintf(dp, len - (dp-name), "%d", unit) > len - (dp-name) - 1) { /* * This can only be a programmer error and * there's no straightforward way to recover if * it happens. */ panic("if_clone_create(): interface name too long"); } } return (0); } int ifc_simple_destroy(struct if_clone *ifc, struct ifnet *ifp) { int unit; struct ifc_simple_data *ifcs = ifc->ifc_data; unit = ifp->if_dunit; if (unit < ifcs->ifcs_minifs) return (EINVAL); ifcs->ifcs_destroy(ifp); ifc_free_unit(ifc, unit); return (0); } Index: head/sys/net/if_clone.h =================================================================== --- head/sys/net/if_clone.h (revision 183209) +++ head/sys/net/if_clone.h (revision 183210) @@ -1,114 +1,115 @@ /*- * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * From: @(#)if.h 8.1 (Berkeley) 6/10/93 * $FreeBSD$ */ #ifndef _NET_IF_CLONE_H_ #define _NET_IF_CLONE_H_ #ifdef _KERNEL #define IFC_CLONE_INITIALIZER(name, data, maxunit, \ attach, match, create, destroy) \ { { 0 }, name, maxunit, NULL, 0, data, attach, match, create, destroy } /* * Structure describing a `cloning' interface. * * List of locks * (c) const until freeing * (d) driver specific data, may need external protection. * (e) locked by if_cloners_mtx * (i) locked by ifc_mtx mtx */ struct if_clone { LIST_ENTRY(if_clone) ifc_list; /* (e) On list of cloners */ const char *ifc_name; /* (c) Name of device, e.g. `gif' */ int ifc_maxunit; /* (c) Maximum unit number */ unsigned char *ifc_units; /* (i) Bitmap to handle units. */ /* Considered private, access */ /* via ifc_(alloc|free)_unit(). */ int ifc_bmlen; /* (c) Bitmap length. */ void *ifc_data; /* (*) Data for ifc_* functions. */ /* (c) Driver specific cloning functions. Called with no locks held. */ void (*ifc_attach)(struct if_clone *); int (*ifc_match)(struct if_clone *, const char *); int (*ifc_create)(struct if_clone *, char *, size_t, caddr_t); int (*ifc_destroy)(struct if_clone *, struct ifnet *); long ifc_refcnt; /* (i) Refrence count. */ struct mtx ifc_mtx; /* Muted to protect members. */ LIST_HEAD(, ifnet) ifc_iflist; /* (i) List of cloned interfaces */ }; void if_clone_init(void); void if_clone_attach(struct if_clone *); void if_clone_detach(struct if_clone *); int if_clone_create(char *, size_t, caddr_t); int if_clone_destroy(const char *); +int if_clone_destroyif(struct if_clone *, struct ifnet *); int if_clone_list(struct if_clonereq *); int ifc_name2unit(const char *name, int *unit); int ifc_alloc_unit(struct if_clone *, int *); void ifc_free_unit(struct if_clone *, int); /* * The ifc_simple functions, structures, and macros implement basic * cloning as in 5.[012]. */ struct ifc_simple_data { int ifcs_minifs; /* minimum number of interfaces */ int (*ifcs_create)(struct if_clone *, int, caddr_t); void (*ifcs_destroy)(struct ifnet *); }; /* interface clone event */ typedef void (*if_clone_event_handler_t)(void *, struct if_clone *); EVENTHANDLER_DECLARE(if_clone_event, if_clone_event_handler_t); #define IFC_SIMPLE_DECLARE(name, minifs) \ struct ifc_simple_data name##_cloner_data = \ {minifs, name##_clone_create, name##_clone_destroy}; \ struct if_clone name##_cloner = \ IFC_CLONE_INITIALIZER(#name, &name##_cloner_data, IF_MAXUNIT, \ ifc_simple_attach, ifc_simple_match, ifc_simple_create, ifc_simple_destroy) void ifc_simple_attach(struct if_clone *); int ifc_simple_match(struct if_clone *, const char *); int ifc_simple_create(struct if_clone *, char *, size_t, caddr_t); int ifc_simple_destroy(struct if_clone *, struct ifnet *); #endif /* _KERNEL */ #endif /* !_NET_IF_CLONE_H_ */ Index: head/sys/net80211/ieee80211_freebsd.c =================================================================== --- head/sys/net80211/ieee80211_freebsd.c (revision 183209) +++ head/sys/net80211/ieee80211_freebsd.c (revision 183210) @@ -1,726 +1,726 @@ /*- * Copyright (c) 2003-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * IEEE 802.11 support (FreeBSD-specific code) */ #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SYSCTL_NODE(_net, OID_AUTO, wlan, CTLFLAG_RD, 0, "IEEE 80211 parameters"); #ifdef IEEE80211_DEBUG int ieee80211_debug = 0; SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug, 0, "debugging printfs"); #endif extern int ieee80211_recv_bar_ena; SYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena, 0, "BAR frame processing (ena/dis)"); extern int ieee80211_nol_timeout; SYSCTL_INT(_net_wlan, OID_AUTO, nol_timeout, CTLFLAG_RW, &ieee80211_nol_timeout, 0, "NOL timeout (secs)"); extern int ieee80211_cac_timeout; SYSCTL_INT(_net_wlan, OID_AUTO, cac_timeout, CTLFLAG_RW, &ieee80211_cac_timeout, 0, "CAC timeout (secs)"); MALLOC_DEFINE(M_80211_COM, "80211com", "802.11 com state"); /* * Allocate/free com structure in conjunction with ifnet; * these routines are registered with if_register_com_alloc * below and are called automatically by the ifnet code * when the ifnet of the parent device is created. */ static void * wlan_alloc(u_char type, struct ifnet *ifp) { struct ieee80211com *ic; ic = malloc(sizeof(struct ieee80211com), M_80211_COM, M_WAITOK|M_ZERO); ic->ic_ifp = ifp; return (ic); } static void wlan_free(void *ic, u_char type) { free(ic, M_80211_COM); } static int wlan_clone_create(struct if_clone *ifc, int unit, caddr_t params) { struct ieee80211_clone_params cp; struct ieee80211vap *vap; struct ieee80211com *ic; struct ifnet *ifp; int error; error = copyin(params, &cp, sizeof(cp)); if (error) return error; ifp = ifunit(cp.icp_parent); if (ifp == NULL) return ENXIO; /* XXX move printfs to DIAGNOSTIC before release */ if (ifp->if_type != IFT_IEEE80211) { if_printf(ifp, "%s: reject, not an 802.11 device\n", __func__); return ENXIO; } if (cp.icp_opmode >= IEEE80211_OPMODE_MAX) { if_printf(ifp, "%s: invalid opmode %d\n", __func__, cp.icp_opmode); return EINVAL; } ic = ifp->if_l2com; if ((ic->ic_caps & ieee80211_opcap[cp.icp_opmode]) == 0) { if_printf(ifp, "%s mode not supported\n", ieee80211_opmode_name[cp.icp_opmode]); return EOPNOTSUPP; } vap = ic->ic_vap_create(ic, ifc->ifc_name, unit, cp.icp_opmode, cp.icp_flags, cp.icp_bssid, cp.icp_flags & IEEE80211_CLONE_MACADDR ? cp.icp_macaddr : ic->ic_myaddr); return (vap == NULL ? EIO : 0); } static void wlan_clone_destroy(struct ifnet *ifp) { struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; ic->ic_vap_delete(vap); } IFC_SIMPLE_DECLARE(wlan, 0); void ieee80211_vap_destroy(struct ieee80211vap *vap) { - ifc_simple_destroy(&wlan_cloner, vap->iv_ifp); + if_clone_destroyif(&wlan_cloner, vap->iv_ifp); } static int ieee80211_sysctl_msecs_ticks(SYSCTL_HANDLER_ARGS) { int msecs = ticks_to_msecs(*(int *)arg1); int error, t; error = sysctl_handle_int(oidp, &msecs, 0, req); if (error || !req->newptr) return error; t = msecs_to_ticks(msecs); *(int *)arg1 = (t < 1) ? 1 : t; return 0; } #ifdef IEEE80211_AMPDU_AGE extern int ieee80211_ampdu_age; SYSCTL_PROC(_net_wlan, OID_AUTO, ampdu_age, CTLFLAG_RW, &ieee80211_ampdu_age, 0, ieee80211_sysctl_msecs_ticks, "I", "AMPDU max reorder age (ms)"); #endif extern int ieee80211_addba_timeout; SYSCTL_PROC(_net_wlan, OID_AUTO, addba_timeout, CTLFLAG_RW, &ieee80211_addba_timeout, 0, ieee80211_sysctl_msecs_ticks, "I", "ADDBA request timeout (ms)"); extern int ieee80211_addba_backoff; SYSCTL_PROC(_net_wlan, OID_AUTO, addba_backoff, CTLFLAG_RW, &ieee80211_addba_backoff, 0, ieee80211_sysctl_msecs_ticks, "I", "ADDBA request backoff (ms)"); extern int ieee80211_addba_maxtries; SYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLFLAG_RW, &ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff"); static int ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS) { int inact = (*(int *)arg1) * IEEE80211_INACT_WAIT; int error; error = sysctl_handle_int(oidp, &inact, 0, req); if (error || !req->newptr) return error; *(int *)arg1 = inact / IEEE80211_INACT_WAIT; return 0; } static int ieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS) { struct ieee80211com *ic = arg1; const char *name = ic->ic_ifp->if_xname; return SYSCTL_OUT(req, name, strlen(name)); } static int ieee80211_sysctl_radar(SYSCTL_HANDLER_ARGS) { struct ieee80211com *ic = arg1; int t = 0, error; error = sysctl_handle_int(oidp, &t, 0, req); if (error || !req->newptr) return error; IEEE80211_LOCK(ic); ieee80211_dfs_notify_radar(ic, ic->ic_curchan); IEEE80211_UNLOCK(ic); return 0; } void ieee80211_sysctl_attach(struct ieee80211com *ic) { } void ieee80211_sysctl_detach(struct ieee80211com *ic) { } void ieee80211_sysctl_vattach(struct ieee80211vap *vap) { struct ifnet *ifp = vap->iv_ifp; struct sysctl_ctx_list *ctx; struct sysctl_oid *oid; char num[14]; /* sufficient for 32 bits */ MALLOC(ctx, struct sysctl_ctx_list *, sizeof(struct sysctl_ctx_list), M_DEVBUF, M_NOWAIT | M_ZERO); if (ctx == NULL) { if_printf(ifp, "%s: cannot allocate sysctl context!\n", __func__); return; } sysctl_ctx_init(ctx); snprintf(num, sizeof(num), "%u", ifp->if_dunit); oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan), OID_AUTO, num, CTLFLAG_RD, NULL, ""); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "%parent", CTLFLAG_RD, vap->iv_ic, 0, ieee80211_sysctl_parent, "A", "parent device"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "driver_caps", CTLFLAG_RW, &vap->iv_caps, 0, "driver capabilities"); #ifdef IEEE80211_DEBUG vap->iv_debug = ieee80211_debug; SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "debug", CTLFLAG_RW, &vap->iv_debug, 0, "control debugging printfs"); #endif SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "bmiss_max", CTLFLAG_RW, &vap->iv_bmiss_max, 0, "consecutive beacon misses before scanning"); /* XXX inherit from tunables */ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "inact_run", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_run, 0, ieee80211_sysctl_inact, "I", "station inactivity timeout (sec)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_probe, 0, ieee80211_sysctl_inact, "I", "station inactivity probe timeout (sec)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_auth, 0, ieee80211_sysctl_inact, "I", "station authentication timeout (sec)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "inact_init", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_init, 0, ieee80211_sysctl_inact, "I", "station initial state timeout (sec)"); if (vap->iv_htcaps & IEEE80211_HTC_HT) { SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "ampdu_mintraffic_bk", CTLFLAG_RW, &vap->iv_ampdu_mintraffic[WME_AC_BK], 0, "BK traffic tx aggr threshold (pps)"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "ampdu_mintraffic_be", CTLFLAG_RW, &vap->iv_ampdu_mintraffic[WME_AC_BE], 0, "BE traffic tx aggr threshold (pps)"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "ampdu_mintraffic_vo", CTLFLAG_RW, &vap->iv_ampdu_mintraffic[WME_AC_VO], 0, "VO traffic tx aggr threshold (pps)"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "ampdu_mintraffic_vi", CTLFLAG_RW, &vap->iv_ampdu_mintraffic[WME_AC_VI], 0, "VI traffic tx aggr threshold (pps)"); } if (vap->iv_caps & IEEE80211_C_DFS) { SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "radar", CTLTYPE_INT | CTLFLAG_RW, vap->iv_ic, 0, ieee80211_sysctl_radar, "I", "simulare radar event"); } vap->iv_sysctl = ctx; vap->iv_oid = oid; } void ieee80211_sysctl_vdetach(struct ieee80211vap *vap) { if (vap->iv_sysctl != NULL) { sysctl_ctx_free(vap->iv_sysctl); FREE(vap->iv_sysctl, M_DEVBUF); vap->iv_sysctl = NULL; } } int ieee80211_node_dectestref(struct ieee80211_node *ni) { /* XXX need equivalent of atomic_dec_and_test */ atomic_subtract_int(&ni->ni_refcnt, 1); return atomic_cmpset_int(&ni->ni_refcnt, 0, 1); } void ieee80211_drain_ifq(struct ifqueue *ifq) { struct ieee80211_node *ni; struct mbuf *m; for (;;) { IF_DEQUEUE(ifq, m); if (m == NULL) break; ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; KASSERT(ni != NULL, ("frame w/o node")); ieee80211_free_node(ni); m->m_pkthdr.rcvif = NULL; m_freem(m); } } void ieee80211_flush_ifq(struct ifqueue *ifq, struct ieee80211vap *vap) { struct ieee80211_node *ni; struct mbuf *m, **mprev; IF_LOCK(ifq); mprev = &ifq->ifq_head; while ((m = *mprev) != NULL) { ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; if (ni != NULL && ni->ni_vap == vap) { *mprev = m->m_nextpkt; /* remove from list */ ifq->ifq_len--; m_freem(m); ieee80211_free_node(ni); /* reclaim ref */ } else mprev = &m->m_nextpkt; } /* recalculate tail ptr */ m = ifq->ifq_head; for (; m != NULL && m->m_nextpkt != NULL; m = m->m_nextpkt) ; ifq->ifq_tail = m; IF_UNLOCK(ifq); } /* * As above, for mbufs allocated with m_gethdr/MGETHDR * or initialized by M_COPY_PKTHDR. */ #define MC_ALIGN(m, len) \ do { \ (m)->m_data += (MCLBYTES - (len)) &~ (sizeof(long) - 1); \ } while (/* CONSTCOND */ 0) /* * Allocate and setup a management frame of the specified * size. We return the mbuf and a pointer to the start * of the contiguous data area that's been reserved based * on the packet length. The data area is forced to 32-bit * alignment and the buffer length to a multiple of 4 bytes. * This is done mainly so beacon frames (that require this) * can use this interface too. */ struct mbuf * ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen) { struct mbuf *m; u_int len; /* * NB: we know the mbuf routines will align the data area * so we don't need to do anything special. */ len = roundup2(headroom + pktlen, 4); KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len)); if (len < MINCLSIZE) { m = m_gethdr(M_NOWAIT, MT_DATA); /* * Align the data in case additional headers are added. * This should only happen when a WEP header is added * which only happens for shared key authentication mgt * frames which all fit in MHLEN. */ if (m != NULL) MH_ALIGN(m, len); } else { m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m != NULL) MC_ALIGN(m, len); } if (m != NULL) { m->m_data += headroom; *frm = m->m_data; } return m; } int ieee80211_add_callback(struct mbuf *m, void (*func)(struct ieee80211_node *, void *, int), void *arg) { struct m_tag *mtag; struct ieee80211_cb *cb; mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_CALLBACK, sizeof(struct ieee80211_cb), M_NOWAIT); if (mtag == NULL) return 0; cb = (struct ieee80211_cb *)(mtag+1); cb->func = func; cb->arg = arg; m_tag_prepend(m, mtag); m->m_flags |= M_TXCB; return 1; } void ieee80211_process_callback(struct ieee80211_node *ni, struct mbuf *m, int status) { struct m_tag *mtag; mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_CALLBACK, NULL); if (mtag != NULL) { struct ieee80211_cb *cb = (struct ieee80211_cb *)(mtag+1); cb->func(ni, cb->arg, status); } } #include void get_random_bytes(void *p, size_t n) { uint8_t *dp = p; while (n > 0) { uint32_t v = arc4random(); size_t nb = n > sizeof(uint32_t) ? sizeof(uint32_t) : n; bcopy(&v, dp, n > sizeof(uint32_t) ? sizeof(uint32_t) : n); dp += sizeof(uint32_t), n -= nb; } } /* * Helper function for events that pass just a single mac address. */ static void notify_macaddr(struct ifnet *ifp, int op, const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ieee80211_join_event iev; memset(&iev, 0, sizeof(iev)); IEEE80211_ADDR_COPY(iev.iev_addr, mac); rt_ieee80211msg(ifp, op, &iev, sizeof(iev)); } void ieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc) { struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = vap->iv_ifp; IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode join", (ni == vap->iv_bss) ? "bss " : ""); if (ni == vap->iv_bss) { notify_macaddr(ifp, newassoc ? RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC, ni->ni_bssid); if_link_state_change(ifp, LINK_STATE_UP); } else { notify_macaddr(ifp, newassoc ? RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN, ni->ni_macaddr); } } void ieee80211_notify_node_leave(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = vap->iv_ifp; IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode leave", (ni == vap->iv_bss) ? "bss " : ""); if (ni == vap->iv_bss) { rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0); if_link_state_change(ifp, LINK_STATE_DOWN); } else { /* fire off wireless event station leaving */ notify_macaddr(ifp, RTM_IEEE80211_LEAVE, ni->ni_macaddr); } } void ieee80211_notify_scan_done(struct ieee80211vap *vap) { struct ifnet *ifp = vap->iv_ifp; IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", "notify scan done"); /* dispatch wireless event indicating scan completed */ rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0); } void ieee80211_notify_replay_failure(struct ieee80211vap *vap, const struct ieee80211_frame *wh, const struct ieee80211_key *k, u_int64_t rsc) { struct ifnet *ifp = vap->iv_ifp; IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, "%s replay detected ", k->wk_cipher->ic_name, (intmax_t) rsc, (intmax_t) k->wk_keyrsc[IEEE80211_NONQOS_TID], k->wk_keyix, k->wk_rxkeyix); if (ifp != NULL) { /* NB: for cipher test modules */ struct ieee80211_replay_event iev; IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1); IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2); iev.iev_cipher = k->wk_cipher->ic_cipher; if (k->wk_rxkeyix != IEEE80211_KEYIX_NONE) iev.iev_keyix = k->wk_rxkeyix; else iev.iev_keyix = k->wk_keyix; iev.iev_keyrsc = k->wk_keyrsc[0]; /* XXX need tid */ iev.iev_rsc = rsc; rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev)); } } void ieee80211_notify_michael_failure(struct ieee80211vap *vap, const struct ieee80211_frame *wh, u_int keyix) { struct ifnet *ifp = vap->iv_ifp; IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, "michael MIC verification failed ", keyix); vap->iv_stats.is_rx_tkipmic++; if (ifp != NULL) { /* NB: for cipher test modules */ struct ieee80211_michael_event iev; IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1); IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2); iev.iev_cipher = IEEE80211_CIPHER_TKIP; iev.iev_keyix = keyix; rt_ieee80211msg(ifp, RTM_IEEE80211_MICHAEL, &iev, sizeof(iev)); } } void ieee80211_notify_wds_discover(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = vap->iv_ifp; notify_macaddr(ifp, RTM_IEEE80211_WDS, ni->ni_macaddr); } void ieee80211_notify_csa(struct ieee80211com *ic, const struct ieee80211_channel *c, int mode, int count) { struct ifnet *ifp = ic->ic_ifp; struct ieee80211_csa_event iev; memset(&iev, 0, sizeof(iev)); iev.iev_flags = c->ic_flags; iev.iev_freq = c->ic_freq; iev.iev_ieee = c->ic_ieee; iev.iev_mode = mode; iev.iev_count = count; rt_ieee80211msg(ifp, RTM_IEEE80211_CSA, &iev, sizeof(iev)); } void ieee80211_notify_radar(struct ieee80211com *ic, const struct ieee80211_channel *c) { struct ifnet *ifp = ic->ic_ifp; struct ieee80211_radar_event iev; memset(&iev, 0, sizeof(iev)); iev.iev_flags = c->ic_flags; iev.iev_freq = c->ic_freq; iev.iev_ieee = c->ic_ieee; rt_ieee80211msg(ifp, RTM_IEEE80211_RADAR, &iev, sizeof(iev)); } void ieee80211_notify_cac(struct ieee80211com *ic, const struct ieee80211_channel *c, enum ieee80211_notify_cac_event type) { struct ifnet *ifp = ic->ic_ifp; struct ieee80211_cac_event iev; memset(&iev, 0, sizeof(iev)); iev.iev_flags = c->ic_flags; iev.iev_freq = c->ic_freq; iev.iev_ieee = c->ic_ieee; iev.iev_type = type; rt_ieee80211msg(ifp, RTM_IEEE80211_CAC, &iev, sizeof(iev)); } void ieee80211_notify_node_deauth(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = vap->iv_ifp; IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node deauth"); notify_macaddr(ifp, RTM_IEEE80211_DEAUTH, ni->ni_macaddr); } void ieee80211_notify_node_auth(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = vap->iv_ifp; IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node auth"); notify_macaddr(ifp, RTM_IEEE80211_AUTH, ni->ni_macaddr); } void ieee80211_notify_country(struct ieee80211vap *vap, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t cc[2]) { struct ifnet *ifp = vap->iv_ifp; struct ieee80211_country_event iev; memset(&iev, 0, sizeof(iev)); IEEE80211_ADDR_COPY(iev.iev_addr, bssid); iev.iev_cc[0] = cc[0]; iev.iev_cc[1] = cc[1]; rt_ieee80211msg(ifp, RTM_IEEE80211_COUNTRY, &iev, sizeof(iev)); } void ieee80211_notify_radio(struct ieee80211com *ic, int state) { struct ifnet *ifp = ic->ic_ifp; struct ieee80211_radio_event iev; memset(&iev, 0, sizeof(iev)); iev.iev_state = state; rt_ieee80211msg(ifp, RTM_IEEE80211_RADIO, &iev, sizeof(iev)); } void ieee80211_load_module(const char *modname) { #ifdef notyet (void)kern_kldload(curthread, modname, NULL); #else printf("%s: load the %s module by hand for now.\n", __func__, modname); #endif } /* * Module glue. * * NB: the module name is "wlan" for compatibility with NetBSD. */ static int wlan_modevent(module_t mod, int type, void *unused) { switch (type) { case MOD_LOAD: if (bootverbose) printf("wlan: <802.11 Link Layer>\n"); if_clone_attach(&wlan_cloner); if_register_com_alloc(IFT_IEEE80211, wlan_alloc, wlan_free); return 0; case MOD_UNLOAD: if_deregister_com_alloc(IFT_IEEE80211); if_clone_detach(&wlan_cloner); return 0; } return EINVAL; } static moduledata_t wlan_mod = { "wlan", wlan_modevent, 0 }; DECLARE_MODULE(wlan, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); MODULE_VERSION(wlan, 1); MODULE_DEPEND(wlan, ether, 1, 1, 1);