Changeset View
Changeset View
Standalone View
Standalone View
sys/net80211/ieee80211.c
Context not available. | |||||
static int ieee80211_media_setup(struct ieee80211com *ic, | static int ieee80211_media_setup(struct ieee80211com *ic, | ||||
struct ifmedia *media, int caps, int addsta, | struct ifmedia *media, int caps, int addsta, | ||||
ifm_change_cb_t media_change, ifm_stat_cb_t media_stat); | 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, | static int media_status(enum ieee80211_opmode, | ||||
const struct ieee80211_channel *); | const struct ieee80211_channel *); | ||||
static uint64_t ieee80211_get_counter(struct ifnet *, ift_counter); | static uint64_t ieee80211_get_counter(struct ifnet *, ift_counter); | ||||
Context not available. | |||||
* all available channels as active, and pick | * all available channels as active, and pick | ||||
* a default channel if not already specified. | * a default channel if not already specified. | ||||
*/ | */ | ||||
static void | void | ||||
ieee80211_chan_init(struct ieee80211com *ic) | ieee80211_chan_init(struct ieee80211com *ic) | ||||
{ | { | ||||
#define DEFAULTRATES(m, def) do { \ | #define DEFAULTRATES(m, def) do { \ | ||||
Context not available. | |||||
ic_printf(ic, "need promiscuous mode update callback\n"); | 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 | 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) | null_update_chw(struct ieee80211com *ic) | ||||
{ | { | ||||
Context not available. | |||||
ic_printf(ic, "%s: need callback\n", __func__); | ic_printf(ic, "%s: need callback\n", __func__); | ||||
} | } | ||||
static LIST_HEAD(, ieee80211com) ic_head = LIST_HEAD_INITIALIZER(ic_head); | |||||
int | int | ||||
ic_printf(struct ieee80211com *ic, const char * fmt, ...) | ic_printf(struct ieee80211com *ic, const char * fmt, ...) | ||||
{ | { | ||||
Context not available. | |||||
* the driver on attach to prior to creating any vap's. | * the driver on attach to prior to creating any vap's. | ||||
*/ | */ | ||||
void | 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_LOCK_INIT(ic, ic->ic_name); | ||||
IEEE80211_TX_LOCK_INIT(ic, ic->ic_name); | IEEE80211_TX_LOCK_INIT(ic, ic->ic_name); | ||||
TAILQ_INIT(&ic->ic_vaps); | TAILQ_INIT(&ic->ic_vaps); | ||||
Context not available. | |||||
* available channels as active, and pick a default | * available channels as active, and pick a default | ||||
* channel if not already specified. | * channel if not already specified. | ||||
*/ | */ | ||||
ieee80211_media_init(ic); | ieee80211_chan_init(ic); | ||||
ic->ic_update_mcast = null_update_mcast; | ic->ic_update_mcast = null_update_mcast; | ||||
ic->ic_update_promisc = null_update_promisc; | ic->ic_update_promisc = null_update_promisc; | ||||
Context not available. | |||||
ieee80211_sysctl_attach(ic); | ieee80211_sysctl_attach(ic); | ||||
ifp->if_addrlen = IEEE80211_ADDR_LEN; | LIST_INSERT_HEAD(&ic_head, ic, ic_next); | ||||
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(); | |||||
} | } | ||||
/* | /* | ||||
Context not available. | |||||
void | void | ||||
ieee80211_ifdetach(struct ieee80211com *ic) | ieee80211_ifdetach(struct ieee80211com *ic) | ||||
{ | { | ||||
struct ifnet *ifp = ic->ic_ifp; | |||||
struct ieee80211vap *vap; | struct ieee80211vap *vap; | ||||
/* | LIST_REMOVE(ic, ic_next); | ||||
* 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 | * The VAP is responsible for setting and clearing | ||||
Context not available. | |||||
ieee80211_power_detach(ic); | ieee80211_power_detach(ic); | ||||
ieee80211_node_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_ierrors); | ||||
counter_u64_free(ic->ic_oerrors); | counter_u64_free(ic->ic_oerrors); | ||||
Context not available. | |||||
IEEE80211_LOCK_DESTROY(ic); | IEEE80211_LOCK_DESTROY(ic); | ||||
} | } | ||||
struct ieee80211com * | |||||
ieee80211_find_com(const char *name) | |||||
{ | |||||
struct ieee80211com *ic; | |||||
LIST_FOREACH(ic, &ic_head, ic_next) | |||||
if (strcmp(ic->ic_name, name) == 0) | |||||
break; | |||||
return (ic); | |||||
} | |||||
/* | /* | ||||
* Default reset method for use with the ioctl support. This | * Default reset method for use with the ioctl support. This | ||||
* method is invoked after any state change in the 802.11 | * method is invoked after any state change in the 802.11 | ||||
Context not available. | |||||
int | int | ||||
ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, | ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, | ||||
const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, | 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; | struct ifnet *ifp; | ||||
Context not available. | |||||
vap->iv_htextcaps = ic->ic_htextcaps; | vap->iv_htextcaps = ic->ic_htextcaps; | ||||
vap->iv_opmode = opmode; | vap->iv_opmode = opmode; | ||||
vap->iv_caps |= ieee80211_opcap[opmode]; | vap->iv_caps |= ieee80211_opcap[opmode]; | ||||
vap->iv_myaddr = ic->ic_macaddr; | |||||
switch (opmode) { | switch (opmode) { | ||||
case IEEE80211_M_WDS: | case IEEE80211_M_WDS: | ||||
/* | /* | ||||
Context not available. | |||||
*/ | */ | ||||
vap->iv_reset = default_reset; | vap->iv_reset = default_reset; | ||||
IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr); | |||||
ieee80211_sysctl_vattach(vap); | ieee80211_sysctl_vattach(vap); | ||||
ieee80211_crypto_vattach(vap); | ieee80211_crypto_vattach(vap); | ||||
ieee80211_node_vattach(vap); | ieee80211_node_vattach(vap); | ||||
Context not available. | |||||
* from this call the vap is ready for use. | * from this call the vap is ready for use. | ||||
*/ | */ | ||||
int | int | ||||
ieee80211_vap_attach(struct ieee80211vap *vap, | ieee80211_vap_attach(struct ieee80211vap *vap, ifm_change_cb_t media_change, | ||||
ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) | ifm_stat_cb_t media_stat, const uint8_t macaddr[IEEE80211_ADDR_LEN]) | ||||
{ | { | ||||
struct ifnet *ifp = vap->iv_ifp; | struct ifnet *ifp = vap->iv_ifp; | ||||
struct ieee80211com *ic = vap->iv_ic; | struct ieee80211com *ic = vap->iv_ic; | ||||
Context not available. | |||||
if (maxrate) | if (maxrate) | ||||
ifp->if_baudrate = IF_Mbps(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 */ | /* hook output method setup by ether_ifattach */ | ||||
vap->iv_output = ifp->if_output; | vap->iv_output = ifp->if_output; | ||||
ifp->if_output = ieee80211_output; | ifp->if_output = ieee80211_output; | ||||
Context not available. | |||||
ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); | ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); | ||||
ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT); | ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT); | ||||
ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); | ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); | ||||
ieee80211_syncifflag_locked(ic, IFF_PROMISC); | |||||
ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); | |||||
IEEE80211_UNLOCK(ic); | IEEE80211_UNLOCK(ic); | ||||
return 1; | return 1; | ||||
Context not available. | |||||
ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); | ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); | ||||
/* NB: this handles the bpfdetach done below */ | /* NB: this handles the bpfdetach done below */ | ||||
ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_BPF); | ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_BPF); | ||||
ieee80211_syncifflag_locked(ic, IFF_PROMISC); | if (vap->iv_ifflags & IFF_PROMISC) | ||||
ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); | ieee80211_promisc(vap, false); | ||||
if (vap->iv_ifflags & IFF_ALLMULTI) | |||||
ieee80211_allmulti(vap, false); | |||||
IEEE80211_UNLOCK(ic); | IEEE80211_UNLOCK(ic); | ||||
ifmedia_removeall(&vap->iv_media); | ifmedia_removeall(&vap->iv_media); | ||||
Context not available. | |||||
} | } | ||||
/* | /* | ||||
* Synchronize flag bit state in the parent ifnet structure | * Count number of vaps in promisc, and issue promisc on | ||||
* according to the state of all vap ifnet's. This is used, | * parent respectively. | ||||
* for example, to handle IFF_PROMISC and IFF_ALLMULTI. | |||||
*/ | */ | ||||
void | void | ||||
ieee80211_syncifflag_locked(struct ieee80211com *ic, int flag) | ieee80211_promisc(struct ieee80211vap *vap, bool on) | ||||
{ | { | ||||
struct ifnet *ifp = ic->ic_ifp; | struct ieee80211com *ic = vap->iv_ic; | ||||
struct ieee80211vap *vap; | |||||
int bit, oflags; | |||||
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; | IEEE80211_LOCK(ic); | ||||
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) | if (on) { | ||||
if (vap->iv_ifp->if_flags & flag) { | if (++ic->ic_promisc == 1) | ||||
avos: here | |||||
Not Done Inline ActionsYep, good catch. Thanks! glebius: Yep, good catch. Thanks! | |||||
/* | ieee80211_runtask(ic, &ic->ic_promisc_task); | ||||
* XXX the bridge sets PROMISC but we don't want to | } else { | ||||
* enable it on the device, discard here so all the | KASSERT(ic->ic_promisc > 0, ("%s: ic %p not promisc", | ||||
* drivers don't need to special-case it | __func__, ic)); | ||||
*/ | if (--ic->ic_promisc == 0) | ||||
Done Inline Actionshere avos: here | |||||
if (flag == IFF_PROMISC && | ieee80211_runtask(ic, &ic->ic_promisc_task); | ||||
!(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); | |||||
} | } | ||||
/* | /* | ||||
* 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) | |||||
Done Inline Actionshere avos: here | |||||
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) | |||||
Done Inline Actionsand here avos: and here | |||||
ieee80211_runtask(ic, &ic->ic_mcast_task); | |||||
} | |||||
IEEE80211_UNLOCK(ic); | |||||
} | |||||
/* | |||||
* Synchronize flag bit state in the com structure | * Synchronize flag bit state in the com structure | ||||
* according to the state of all vap's. This is used, | * according to the state of all vap's. This is used, | ||||
* for example, to handle state changes via ioctls. | * for example, to handle state changes via ioctls. | ||||
Context not available. | |||||
return maxrate; | 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? */ | /* XXX inline or eliminate? */ | ||||
const struct ieee80211_rateset * | const struct ieee80211_rateset * | ||||
ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) | ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) | ||||
Context not available. | |||||
} | } | ||||
/* | /* | ||||
* 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. | * Handle a media change request on the vap interface. | ||||
*/ | */ | ||||
int | int | ||||
Context not available. | |||||
return status; | 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 | void | ||||
ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) | ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) | ||||
{ | { | ||||
Context not available. |
here