summaryrefslogtreecommitdiffstats
path: root/sys/net80211/ieee80211.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/net80211/ieee80211.c')
-rw-r--r--sys/net80211/ieee80211.c282
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)
{
OpenPOWER on IntegriCloud