diff options
Diffstat (limited to 'sys/net80211/ieee80211.c')
-rw-r--r-- | sys/net80211/ieee80211.c | 282 |
1 files changed, 109 insertions, 173 deletions
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index 3902227..39e03ea 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/kernel.h> #include <sys/socket.h> +#include <sys/sbuf.h> #include <machine/stdarg.h> @@ -91,8 +92,6 @@ static void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag); static int ieee80211_media_setup(struct ieee80211com *ic, struct ifmedia *media, int caps, int addsta, ifm_change_cb_t media_change, ifm_stat_cb_t media_stat); -static void ieee80211com_media_status(struct ifnet *, struct ifmediareq *); -static int ieee80211com_media_change(struct ifnet *); static int media_status(enum ieee80211_opmode, const struct ieee80211_channel *); static uint64_t ieee80211_get_counter(struct ifnet *, ift_counter); @@ -121,7 +120,7 @@ static const struct ieee80211_rateset ieee80211_rateset_11g = * all available channels as active, and pick * a default channel if not already specified. */ -static void +void ieee80211_chan_init(struct ieee80211com *ic) { #define DEFAULTRATES(m, def) do { \ @@ -238,29 +237,6 @@ null_update_promisc(struct ieee80211com *ic) ic_printf(ic, "need promiscuous mode update callback\n"); } -static int -null_transmit(struct ifnet *ifp, struct mbuf *m) -{ - m_freem(m); - if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); - return EACCES; /* XXX EIO/EPERM? */ -} - -static int -null_output(struct ifnet *ifp, struct mbuf *m, - const struct sockaddr *dst, struct route *ro) -{ - if_printf(ifp, "discard raw packet\n"); - return null_transmit(ifp, m); -} - -static void -null_input(struct ifnet *ifp, struct mbuf *m) -{ - if_printf(ifp, "if_input should not be called\n"); - m_freem(m); -} - static void null_update_chw(struct ieee80211com *ic) { @@ -281,19 +257,43 @@ ic_printf(struct ieee80211com *ic, const char * fmt, ...) return (retval); } +static LIST_HEAD(, ieee80211com) ic_head = LIST_HEAD_INITIALIZER(ic_head); +static struct mtx ic_list_mtx; +MTX_SYSINIT(ic_list, &ic_list_mtx, "ieee80211com list", MTX_DEF); + +static int +sysctl_ieee80211coms(SYSCTL_HANDLER_ARGS) +{ + struct ieee80211com *ic; + struct sbuf *sb; + char *sp; + int error; + + sb = sbuf_new_auto(); + sp = ""; + mtx_lock(&ic_list_mtx); + LIST_FOREACH(ic, &ic_head, ic_next) { + sbuf_printf(sb, "%s%s", sp, ic->ic_name); + sp = " "; + } + mtx_unlock(&ic_list_mtx); + sbuf_finish(sb); + error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); + sbuf_delete(sb); + return (error); +} + +SYSCTL_PROC(_net_wlan, OID_AUTO, devices, + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, + sysctl_ieee80211coms, "A", "names of available 802.11 devices"); + /* * Attach/setup the common net80211 state. Called by * the driver on attach to prior to creating any vap's. */ void -ieee80211_ifattach(struct ieee80211com *ic, - const uint8_t macaddr[IEEE80211_ADDR_LEN]) +ieee80211_ifattach(struct ieee80211com *ic) { - struct ifnet *ifp = ic->ic_ifp; - struct sockaddr_dl *sdl; - struct ifaddr *ifa; - - KASSERT(ifp->if_type == IFT_IEEE80211, ("if_type %d", ifp->if_type)); IEEE80211_LOCK_INIT(ic, ic->ic_name); IEEE80211_TX_LOCK_INIT(ic, ic->ic_name); @@ -311,7 +311,7 @@ ieee80211_ifattach(struct ieee80211com *ic, * available channels as active, and pick a default * channel if not already specified. */ - ieee80211_media_init(ic); + ieee80211_chan_init(ic); ic->ic_update_mcast = null_update_mcast; ic->ic_update_promisc = null_update_promisc; @@ -336,28 +336,9 @@ ieee80211_ifattach(struct ieee80211com *ic, ieee80211_sysctl_attach(ic); - ifp->if_addrlen = IEEE80211_ADDR_LEN; - ifp->if_hdrlen = 0; - - CURVNET_SET(vnet0); - - if_attach(ifp); - - ifp->if_mtu = IEEE80211_MTU_MAX; - ifp->if_broadcastaddr = ieee80211broadcastaddr; - ifp->if_output = null_output; - ifp->if_input = null_input; /* just in case */ - ifp->if_resolvemulti = NULL; /* NB: callers check */ - - ifa = ifaddr_byindex(ifp->if_index); - KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__)); - sdl = (struct sockaddr_dl *)ifa->ifa_addr; - sdl->sdl_type = IFT_ETHER; /* XXX IFT_IEEE80211? */ - sdl->sdl_alen = IEEE80211_ADDR_LEN; - IEEE80211_ADDR_COPY(LLADDR(sdl), macaddr); - ifa_free(ifa); - - CURVNET_RESTORE(); + mtx_lock(&ic_list_mtx); + LIST_INSERT_HEAD(&ic_head, ic, ic_next); + mtx_unlock(&ic_list_mtx); } /* @@ -369,16 +350,11 @@ ieee80211_ifattach(struct ieee80211com *ic, void ieee80211_ifdetach(struct ieee80211com *ic) { - struct ifnet *ifp = ic->ic_ifp; struct ieee80211vap *vap; - /* - * This detaches the main interface, but not the vaps. - * Each VAP may be in a separate VIMAGE. - */ - CURVNET_SET(ifp->if_vnet); - if_detach(ifp); - CURVNET_RESTORE(); + mtx_lock(&ic_list_mtx); + LIST_REMOVE(ic, ic_next); + mtx_unlock(&ic_list_mtx); /* * The VAP is responsible for setting and clearing @@ -402,8 +378,6 @@ ieee80211_ifdetach(struct ieee80211com *ic) ieee80211_power_detach(ic); ieee80211_node_detach(ic); - /* XXX VNET needed? */ - ifmedia_removeall(&ic->ic_media); counter_u64_free(ic->ic_ierrors); counter_u64_free(ic->ic_oerrors); @@ -412,6 +386,20 @@ ieee80211_ifdetach(struct ieee80211com *ic) IEEE80211_LOCK_DESTROY(ic); } +struct ieee80211com * +ieee80211_find_com(const char *name) +{ + struct ieee80211com *ic; + + mtx_lock(&ic_list_mtx); + LIST_FOREACH(ic, &ic_head, ic_next) + if (strcmp(ic->ic_name, name) == 0) + break; + mtx_unlock(&ic_list_mtx); + + return (ic); +} + /* * Default reset method for use with the ioctl support. This * method is invoked after any state change in the 802.11 @@ -460,8 +448,7 @@ ieee80211_get_counter(struct ifnet *ifp, ift_counter cnt) int ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, - int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], - const uint8_t macaddr[IEEE80211_ADDR_LEN]) + int flags, const uint8_t bssid[IEEE80211_ADDR_LEN]) { struct ifnet *ifp; @@ -490,6 +477,7 @@ ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, vap->iv_htextcaps = ic->ic_htextcaps; vap->iv_opmode = opmode; vap->iv_caps |= ieee80211_opcap[opmode]; + vap->iv_myaddr = ic->ic_macaddr; switch (opmode) { case IEEE80211_M_WDS: /* @@ -556,8 +544,6 @@ ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, */ vap->iv_reset = default_reset; - IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr); - ieee80211_sysctl_vattach(vap); ieee80211_crypto_vattach(vap); ieee80211_node_vattach(vap); @@ -581,8 +567,8 @@ ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, * from this call the vap is ready for use. */ int -ieee80211_vap_attach(struct ieee80211vap *vap, - ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) +ieee80211_vap_attach(struct ieee80211vap *vap, ifm_change_cb_t media_change, + ifm_stat_cb_t media_stat, const uint8_t macaddr[IEEE80211_ADDR_LEN]) { struct ifnet *ifp = vap->iv_ifp; struct ieee80211com *ic = vap->iv_ic; @@ -610,7 +596,8 @@ ieee80211_vap_attach(struct ieee80211vap *vap, if (maxrate) ifp->if_baudrate = IF_Mbps(maxrate); - ether_ifattach(ifp, vap->iv_myaddr); + ether_ifattach(ifp, macaddr); + vap->iv_myaddr = IF_LLADDR(ifp); /* hook output method setup by ether_ifattach */ vap->iv_output = ifp->if_output; ifp->if_output = ieee80211_output; @@ -626,8 +613,6 @@ ieee80211_vap_attach(struct ieee80211vap *vap, ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT); ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); - ieee80211_syncifflag_locked(ic, IFF_PROMISC); - ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); IEEE80211_UNLOCK(ic); return 1; @@ -677,8 +662,10 @@ ieee80211_vap_detach(struct ieee80211vap *vap) ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); /* NB: this handles the bpfdetach done below */ ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_BPF); - ieee80211_syncifflag_locked(ic, IFF_PROMISC); - ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); + if (vap->iv_ifflags & IFF_PROMISC) + ieee80211_promisc(vap, false); + if (vap->iv_ifflags & IFF_ALLMULTI) + ieee80211_allmulti(vap, false); IEEE80211_UNLOCK(ic); ifmedia_removeall(&vap->iv_media); @@ -703,49 +690,57 @@ ieee80211_vap_detach(struct ieee80211vap *vap) } /* - * Synchronize flag bit state in the parent ifnet structure - * according to the state of all vap ifnet's. This is used, - * for example, to handle IFF_PROMISC and IFF_ALLMULTI. + * Count number of vaps in promisc, and issue promisc on + * parent respectively. */ void -ieee80211_syncifflag_locked(struct ieee80211com *ic, int flag) +ieee80211_promisc(struct ieee80211vap *vap, bool on) { - struct ifnet *ifp = ic->ic_ifp; - struct ieee80211vap *vap; - int bit, oflags; + struct ieee80211com *ic = vap->iv_ic; - IEEE80211_LOCK_ASSERT(ic); + /* + * XXX the bridge sets PROMISC but we don't want to + * enable it on the device, discard here so all the + * drivers don't need to special-case it + */ + if (!(vap->iv_opmode == IEEE80211_M_MONITOR || + (vap->iv_opmode == IEEE80211_M_AHDEMO && + (vap->iv_caps & IEEE80211_C_TDMA) == 0))) + return; - bit = 0; - TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) - if (vap->iv_ifp->if_flags & flag) { - /* - * XXX the bridge sets PROMISC but we don't want to - * enable it on the device, discard here so all the - * drivers don't need to special-case it - */ - if (flag == IFF_PROMISC && - !(vap->iv_opmode == IEEE80211_M_MONITOR || - (vap->iv_opmode == IEEE80211_M_AHDEMO && - (vap->iv_caps & IEEE80211_C_TDMA) == 0))) - continue; - bit = 1; - break; - } - oflags = ifp->if_flags; - if (bit) - ifp->if_flags |= flag; - else - ifp->if_flags &= ~flag; - if ((ifp->if_flags ^ oflags) & flag) { - /* XXX should we return 1/0 and let caller do this? */ - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - if (flag == IFF_PROMISC) - ieee80211_runtask(ic, &ic->ic_promisc_task); - else if (flag == IFF_ALLMULTI) - ieee80211_runtask(ic, &ic->ic_mcast_task); - } + IEEE80211_LOCK(ic); + if (on) { + if (++ic->ic_promisc == 1) + ieee80211_runtask(ic, &ic->ic_promisc_task); + } else { + KASSERT(ic->ic_promisc > 0, ("%s: ic %p not promisc", + __func__, ic)); + if (--ic->ic_promisc == 0) + ieee80211_runtask(ic, &ic->ic_promisc_task); } + IEEE80211_UNLOCK(ic); +} + +/* + * Count number of vaps in allmulti, and issue allmulti on + * parent respectively. + */ +void +ieee80211_allmulti(struct ieee80211vap *vap, bool on) +{ + struct ieee80211com *ic = vap->iv_ic; + + IEEE80211_LOCK(ic); + if (on) { + if (++ic->ic_allmulti == 1) + ieee80211_runtask(ic, &ic->ic_mcast_task); + } else { + KASSERT(ic->ic_allmulti > 0, ("%s: ic %p not allmulti", + __func__, ic)); + if (--ic->ic_allmulti == 0) + ieee80211_runtask(ic, &ic->ic_mcast_task); + } + IEEE80211_UNLOCK(ic); } /* @@ -1234,39 +1229,6 @@ ieee80211_media_setup(struct ieee80211com *ic, return maxrate; } -void -ieee80211_media_init(struct ieee80211com *ic) -{ - struct ifnet *ifp = ic->ic_ifp; - int maxrate; - - /* NB: this works because the structure is initialized to zero */ - if (!LIST_EMPTY(&ic->ic_media.ifm_list)) { - /* - * We are re-initializing the channel list; clear - * the existing media state as the media routines - * don't suppress duplicates. - */ - ifmedia_removeall(&ic->ic_media); - } - ieee80211_chan_init(ic); - - /* - * Recalculate media settings in case new channel list changes - * the set of available modes. - */ - maxrate = ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, 1, - ieee80211com_media_change, ieee80211com_media_status); - /* NB: strip explicit mode; we're actually in autoselect */ - ifmedia_set(&ic->ic_media, - media_status(ic->ic_opmode, ic->ic_curchan) &~ - (IFM_MMASK | IFM_IEEE80211_TURBO)); - if (maxrate) - ifp->if_baudrate = IF_Mbps(maxrate); - - /* XXX need to propagate new media settings to vap's */ -} - /* XXX inline or eliminate? */ const struct ieee80211_rateset * ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) @@ -1395,15 +1357,6 @@ media2mode(const struct ifmedia_entry *ime, uint32_t flags, uint16_t *mode) } /* - * Handle a media change request on the underlying interface. - */ -int -ieee80211com_media_change(struct ifnet *ifp) -{ - return EINVAL; -} - -/* * Handle a media change request on the vap interface. */ int @@ -1480,23 +1433,6 @@ media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan) return status; } -static void -ieee80211com_media_status(struct ifnet *ifp, struct ifmediareq *imr) -{ - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap; - - imr->ifm_status = IFM_AVALID; - TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) - if (vap->iv_ifp->if_flags & IFF_UP) { - imr->ifm_status |= IFM_ACTIVE; - break; - } - imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan); - if (imr->ifm_status & IFM_ACTIVE) - imr->ifm_current = imr->ifm_active; -} - void ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) { |