diff options
Diffstat (limited to 'sys/net80211/ieee80211.c')
-rw-r--r-- | sys/net80211/ieee80211.c | 282 |
1 files changed, 173 insertions, 109 deletions
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index 39e03ea..3902227 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -36,7 +36,6 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/kernel.h> #include <sys/socket.h> -#include <sys/sbuf.h> #include <machine/stdarg.h> @@ -92,6 +91,8 @@ 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); @@ -120,7 +121,7 @@ static const struct ieee80211_rateset ieee80211_rateset_11g = * all available channels as active, and pick * a default channel if not already specified. */ -void +static void ieee80211_chan_init(struct ieee80211com *ic) { #define DEFAULTRATES(m, def) do { \ @@ -237,6 +238,29 @@ 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) { @@ -257,43 +281,19 @@ 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) +ieee80211_ifattach(struct ieee80211com *ic, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) { + 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_chan_init(ic); + ieee80211_media_init(ic); ic->ic_update_mcast = null_update_mcast; ic->ic_update_promisc = null_update_promisc; @@ -336,9 +336,28 @@ ieee80211_ifattach(struct ieee80211com *ic) ieee80211_sysctl_attach(ic); - mtx_lock(&ic_list_mtx); - LIST_INSERT_HEAD(&ic_head, ic, ic_next); - mtx_unlock(&ic_list_mtx); + 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(); } /* @@ -350,11 +369,16 @@ ieee80211_ifattach(struct ieee80211com *ic) void ieee80211_ifdetach(struct ieee80211com *ic) { + struct ifnet *ifp = ic->ic_ifp; struct ieee80211vap *vap; - mtx_lock(&ic_list_mtx); - LIST_REMOVE(ic, ic_next); - mtx_unlock(&ic_list_mtx); + /* + * 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(); /* * The VAP is responsible for setting and clearing @@ -378,6 +402,8 @@ 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); @@ -386,20 +412,6 @@ 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 @@ -448,7 +460,8 @@ 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]) + int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t macaddr[IEEE80211_ADDR_LEN]) { struct ifnet *ifp; @@ -477,7 +490,6 @@ 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: /* @@ -544,6 +556,8 @@ 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); @@ -567,8 +581,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, const uint8_t macaddr[IEEE80211_ADDR_LEN]) +ieee80211_vap_attach(struct ieee80211vap *vap, + ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) { struct ifnet *ifp = vap->iv_ifp; struct ieee80211com *ic = vap->iv_ic; @@ -596,8 +610,7 @@ ieee80211_vap_attach(struct ieee80211vap *vap, ifm_change_cb_t media_change, if (maxrate) ifp->if_baudrate = IF_Mbps(maxrate); - ether_ifattach(ifp, macaddr); - vap->iv_myaddr = IF_LLADDR(ifp); + ether_ifattach(ifp, vap->iv_myaddr); /* hook output method setup by ether_ifattach */ vap->iv_output = ifp->if_output; ifp->if_output = ieee80211_output; @@ -613,6 +626,8 @@ ieee80211_vap_attach(struct ieee80211vap *vap, ifm_change_cb_t media_change, 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; @@ -662,10 +677,8 @@ 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); - if (vap->iv_ifflags & IFF_PROMISC) - ieee80211_promisc(vap, false); - if (vap->iv_ifflags & IFF_ALLMULTI) - ieee80211_allmulti(vap, false); + ieee80211_syncifflag_locked(ic, IFF_PROMISC); + ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); IEEE80211_UNLOCK(ic); ifmedia_removeall(&vap->iv_media); @@ -690,57 +703,49 @@ ieee80211_vap_detach(struct ieee80211vap *vap) } /* - * Count number of vaps in promisc, and issue promisc on - * parent respectively. + * 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. */ void -ieee80211_promisc(struct ieee80211vap *vap, bool on) +ieee80211_syncifflag_locked(struct ieee80211com *ic, int flag) { - struct ieee80211com *ic = vap->iv_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; - - 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); -} + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211vap *vap; + int bit, oflags; -/* - * 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_ASSERT(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); + 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_UNLOCK(ic); } /* @@ -1229,6 +1234,39 @@ 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) @@ -1357,6 +1395,15 @@ 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 @@ -1433,6 +1480,23 @@ 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) { |