Index: stable/6/sys/net80211/ieee80211.c =================================================================== --- stable/6/sys/net80211/ieee80211.c (revision 153658) +++ stable/6/sys/net80211/ieee80211.c (revision 153659) @@ -1,1020 +1,1022 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2005 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * 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 generic handler */ #include #include #include #include #include #include #include #include #include const char *ieee80211_phymode_name[] = { "auto", /* IEEE80211_MODE_AUTO */ "11a", /* IEEE80211_MODE_11A */ "11b", /* IEEE80211_MODE_11B */ "11g", /* IEEE80211_MODE_11G */ "FH", /* IEEE80211_MODE_FH */ "turboA", /* IEEE80211_MODE_TURBO_A */ "turboG", /* IEEE80211_MODE_TURBO_G */ }; /* list of all instances */ SLIST_HEAD(ieee80211_list, ieee80211com); static struct ieee80211_list ieee80211_list = SLIST_HEAD_INITIALIZER(ieee80211_list); static u_int8_t ieee80211_vapmap[32]; /* enough for 256 */ static struct mtx ieee80211_vap_mtx; MTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF); static void ieee80211_add_vap(struct ieee80211com *ic) { #define N(a) (sizeof(a)/sizeof(a[0])) int i; u_int8_t b; mtx_lock(&ieee80211_vap_mtx); ic->ic_vap = 0; for (i = 0; i < N(ieee80211_vapmap) && ieee80211_vapmap[i] == 0xff; i++) ic->ic_vap += NBBY; if (i == N(ieee80211_vapmap)) panic("vap table full"); for (b = ieee80211_vapmap[i]; b & 1; b >>= 1) ic->ic_vap++; setbit(ieee80211_vapmap, ic->ic_vap); SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next); mtx_unlock(&ieee80211_vap_mtx); #undef N } static void ieee80211_remove_vap(struct ieee80211com *ic) { mtx_lock(&ieee80211_vap_mtx); SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next); KASSERT(ic->ic_vap < sizeof(ieee80211_vapmap)*NBBY, ("invalid vap id %d", ic->ic_vap)); KASSERT(isset(ieee80211_vapmap, ic->ic_vap), ("vap id %d not allocated", ic->ic_vap)); clrbit(ieee80211_vapmap, ic->ic_vap); mtx_unlock(&ieee80211_vap_mtx); } /* * Default reset method for use with the ioctl support. This * method is invoked after any state change in the 802.11 * layer that should be propagated to the hardware but not * require re-initialization of the 802.11 state machine (e.g * rescanning for an ap). We always return ENETRESET which * should cause the driver to re-initialize the device. Drivers * can override this method to implement more optimized support. */ static int ieee80211_default_reset(struct ifnet *ifp) { return ENETRESET; } void ieee80211_ifattach(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct ieee80211_channel *c; int i; ether_ifattach(ifp, ic->ic_myaddr); bpfattach2(ifp, DLT_IEEE802_11, sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); ieee80211_crypto_attach(ic); /* * Fill in 802.11 available channel set, mark * all available channels as active, and pick * a default channel if not already specified. */ memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); ic->ic_modecaps |= 1<ic_channels[i]; if (c->ic_flags) { /* * Verify driver passed us valid data. */ if (i != ieee80211_chan2ieee(ic, c)) { if_printf(ifp, "bad channel ignored; " "freq %u flags %x number %u\n", c->ic_freq, c->ic_flags, i); c->ic_flags = 0; /* NB: remove */ continue; } setbit(ic->ic_chan_avail, i); /* * Identify mode capabilities. */ if (IEEE80211_IS_CHAN_A(c)) ic->ic_modecaps |= 1<ic_modecaps |= 1<ic_modecaps |= 1<ic_modecaps |= 1<ic_modecaps |= 1<ic_modecaps |= 1<ic_curchan == NULL) { /* arbitrarily pick the first channel */ ic->ic_curchan = &ic->ic_channels[i]; } } } /* validate ic->ic_curmode */ if ((ic->ic_modecaps & (1<ic_curmode)) == 0) ic->ic_curmode = IEEE80211_MODE_AUTO; ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ #if 0 /* * Enable WME by default if we're capable. */ if (ic->ic_caps & IEEE80211_C_WME) ic->ic_flags |= IEEE80211_F_WME; #endif + if (ic->ic_caps & IEEE80211_C_BURST) + ic->ic_flags |= IEEE80211_F_BURST; (void) ieee80211_setmode(ic, ic->ic_curmode); if (ic->ic_bintval == 0) ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; ic->ic_bmisstimeout = 7*ic->ic_bintval; /* default 7 beacons */ ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT; IEEE80211_BEACON_LOCK_INIT(ic, "beacon"); if (ic->ic_lintval == 0) ic->ic_lintval = ic->ic_bintval; ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; ieee80211_node_attach(ic); ieee80211_proto_attach(ic); ieee80211_add_vap(ic); ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */ /* * Install a default reset method for the ioctl support. * The driver is expected to fill this in before calling us. */ if (ic->ic_reset == NULL) ic->ic_reset = ieee80211_default_reset; } void ieee80211_ifdetach(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; ieee80211_remove_vap(ic); ieee80211_sysctl_detach(ic); ieee80211_proto_detach(ic); ieee80211_crypto_detach(ic); ieee80211_node_detach(ic); ifmedia_removeall(&ic->ic_media); IEEE80211_BEACON_LOCK_DESTROY(ic); bpfdetach(ifp); ether_ifdetach(ifp); } /* * Convert MHz frequency to IEEE channel number. */ u_int ieee80211_mhz2ieee(u_int freq, u_int flags) { if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ if (freq == 2484) return 14; if (freq < 2484) return (freq - 2407) / 5; else return 15 + ((freq - 2512) / 20); } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ return (freq - 5000) / 5; } else { /* either, guess */ if (freq == 2484) return 14; if (freq < 2484) return (freq - 2407) / 5; if (freq < 5000) return 15 + ((freq - 2512) / 20); return (freq - 5000) / 5; } } /* * Convert channel to IEEE channel number. */ u_int ieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c) { if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX]) return c - ic->ic_channels; else if (c == IEEE80211_CHAN_ANYC) return IEEE80211_CHAN_ANY; else if (c != NULL) { if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n", c->ic_freq, c->ic_flags); return 0; /* XXX */ } else { if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); return 0; /* XXX */ } } /* * Convert IEEE channel number to MHz frequency. */ u_int ieee80211_ieee2mhz(u_int chan, u_int flags) { if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ if (chan == 14) return 2484; if (chan < 14) return 2407 + chan*5; else return 2512 + ((chan-15)*20); } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ return 5000 + (chan*5); } else { /* either, guess */ if (chan == 14) return 2484; if (chan < 14) /* 0-13 */ return 2407 + chan*5; if (chan < 27) /* 15-26 */ return 2512 + ((chan-15)*20); return 5000 + (chan*5); } } /* * Setup the media data structures according to the channel and * rate tables. This must be called by the driver after * ieee80211_attach and before most anything else. */ void ieee80211_media_init(struct ieee80211com *ic, ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) { #define ADD(_ic, _s, _o) \ ifmedia_add(&(_ic)->ic_media, \ IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) struct ifnet *ifp = ic->ic_ifp; struct ifmediareq imr; int i, j, mode, rate, maxrate, mword, mopt, r; struct ieee80211_rateset *rs; struct ieee80211_rateset allrates; /* * Do late attach work that must wait for any subclass * (i.e. driver) work such as overriding methods. */ ieee80211_node_lateattach(ic); /* * Fill in media characteristics. */ ifmedia_init(&ic->ic_media, 0, media_change, media_stat); maxrate = 0; memset(&allrates, 0, sizeof(allrates)); for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { static const u_int mopts[] = { IFM_AUTO, IFM_IEEE80211_11A, IFM_IEEE80211_11B, IFM_IEEE80211_11G, IFM_IEEE80211_FH, IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, }; if ((ic->ic_modecaps & (1<ic_caps & IEEE80211_C_IBSS) ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC); if (ic->ic_caps & IEEE80211_C_HOSTAP) ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP); if (ic->ic_caps & IEEE80211_C_AHDEMO) ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); if (ic->ic_caps & IEEE80211_C_MONITOR) ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR); if (mode == IEEE80211_MODE_AUTO) continue; rs = &ic->ic_sup_rates[mode]; for (i = 0; i < rs->rs_nrates; i++) { rate = rs->rs_rates[i]; mword = ieee80211_rate2media(ic, rate, mode); if (mword == 0) continue; ADD(ic, mword, mopt); if (ic->ic_caps & IEEE80211_C_IBSS) ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); if (ic->ic_caps & IEEE80211_C_HOSTAP) ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); if (ic->ic_caps & IEEE80211_C_AHDEMO) ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); if (ic->ic_caps & IEEE80211_C_MONITOR) ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); /* * Add rate to the collection of all rates. */ r = rate & IEEE80211_RATE_VAL; for (j = 0; j < allrates.rs_nrates; j++) if (allrates.rs_rates[j] == r) break; if (j == allrates.rs_nrates) { /* unique, add to the set */ allrates.rs_rates[j] = r; allrates.rs_nrates++; } rate = (rate & IEEE80211_RATE_VAL) / 2; if (rate > maxrate) maxrate = rate; } } for (i = 0; i < allrates.rs_nrates; i++) { mword = ieee80211_rate2media(ic, allrates.rs_rates[i], IEEE80211_MODE_AUTO); if (mword == 0) continue; mword = IFM_SUBTYPE(mword); /* remove media options */ ADD(ic, mword, 0); if (ic->ic_caps & IEEE80211_C_IBSS) ADD(ic, mword, IFM_IEEE80211_ADHOC); if (ic->ic_caps & IEEE80211_C_HOSTAP) ADD(ic, mword, IFM_IEEE80211_HOSTAP); if (ic->ic_caps & IEEE80211_C_AHDEMO) ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); if (ic->ic_caps & IEEE80211_C_MONITOR) ADD(ic, mword, IFM_IEEE80211_MONITOR); } ieee80211_media_status(ifp, &imr); ifmedia_set(&ic->ic_media, imr.ifm_active); if (maxrate) ifp->if_baudrate = IF_Mbps(maxrate); #undef ADD } void ieee80211_announce(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; int i, mode, rate, mword; struct ieee80211_rateset *rs; for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) { if ((ic->ic_modecaps & (1<ic_sup_rates[mode]; for (i = 0; i < rs->rs_nrates; i++) { rate = rs->rs_rates[i]; mword = ieee80211_rate2media(ic, rate, mode); if (mword == 0) continue; printf("%s%d%sMbps", (i != 0 ? " " : ""), (rate & IEEE80211_RATE_VAL) / 2, ((rate & 0x1) != 0 ? ".5" : "")); } printf("\n"); } } static int findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) { #define IEEERATE(_ic,_m,_i) \ ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) int i, nrates = ic->ic_sup_rates[mode].rs_nrates; for (i = 0; i < nrates; i++) if (IEEERATE(ic, mode, i) == rate) return i; return -1; #undef IEEERATE } /* * Find an instance by it's mac address. */ struct ieee80211com * ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]) { struct ieee80211com *ic; /* XXX lock */ SLIST_FOREACH(ic, &ieee80211_list, ic_next) if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr)) return ic; return NULL; } static struct ieee80211com * ieee80211_find_instance(struct ifnet *ifp) { struct ieee80211com *ic; /* XXX lock */ /* XXX not right for multiple instances but works for now */ SLIST_FOREACH(ic, &ieee80211_list, ic_next) if (ic->ic_ifp == ifp) return ic; return NULL; } /* * Handle a media change request. */ int ieee80211_media_change(struct ifnet *ifp) { struct ieee80211com *ic; struct ifmedia_entry *ime; enum ieee80211_opmode newopmode; enum ieee80211_phymode newphymode; int i, j, newrate, error = 0; ic = ieee80211_find_instance(ifp); if (!ic) { if_printf(ifp, "%s: no 802.11 instance!\n", __func__); return EINVAL; } ime = ic->ic_media.ifm_cur; /* * First, identify the phy mode. */ switch (IFM_MODE(ime->ifm_media)) { case IFM_IEEE80211_11A: newphymode = IEEE80211_MODE_11A; break; case IFM_IEEE80211_11B: newphymode = IEEE80211_MODE_11B; break; case IFM_IEEE80211_11G: newphymode = IEEE80211_MODE_11G; break; case IFM_IEEE80211_FH: newphymode = IEEE80211_MODE_FH; break; case IFM_AUTO: newphymode = IEEE80211_MODE_AUTO; break; default: return EINVAL; } /* * Turbo mode is an ``option''. * XXX does not apply to AUTO */ if (ime->ifm_media & IFM_IEEE80211_TURBO) { if (newphymode == IEEE80211_MODE_11A) newphymode = IEEE80211_MODE_TURBO_A; else if (newphymode == IEEE80211_MODE_11G) newphymode = IEEE80211_MODE_TURBO_G; else return EINVAL; } /* * Validate requested mode is available. */ if ((ic->ic_modecaps & (1<ifm_media) != IFM_AUTO) { /* * Convert media subtype to rate. */ newrate = ieee80211_media2rate(ime->ifm_media); if (newrate == 0) return EINVAL; /* * Check the rate table for the specified/current phy. */ if (newphymode == IEEE80211_MODE_AUTO) { /* * In autoselect mode search for the rate. */ for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) { if ((ic->ic_modecaps & (1<ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) == (IFM_IEEE80211_ADHOC|IFM_FLAG0)) newopmode = IEEE80211_M_AHDEMO; else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) newopmode = IEEE80211_M_HOSTAP; else if (ime->ifm_media & IFM_IEEE80211_ADHOC) newopmode = IEEE80211_M_IBSS; else if (ime->ifm_media & IFM_IEEE80211_MONITOR) newopmode = IEEE80211_M_MONITOR; else newopmode = IEEE80211_M_STA; /* * Autoselect doesn't make sense when operating as an AP. * If no phy mode has been selected, pick one and lock it * down so rate tables can be used in forming beacon frames * and the like. */ if (newopmode == IEEE80211_M_HOSTAP && newphymode == IEEE80211_MODE_AUTO) { for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) if (ic->ic_modecaps & (1<ic_curmode != newphymode) { /* change phy mode */ error = ieee80211_setmode(ic, newphymode); if (error != 0) return error; error = ENETRESET; } /* * Committed to changes, install the rate setting. */ if (ic->ic_fixed_rate != i) { ic->ic_fixed_rate = i; /* set fixed tx rate */ error = ENETRESET; } /* * Handle operating mode change. */ if (ic->ic_opmode != newopmode) { ic->ic_opmode = newopmode; switch (newopmode) { case IEEE80211_M_AHDEMO: case IEEE80211_M_HOSTAP: case IEEE80211_M_STA: case IEEE80211_M_MONITOR: ic->ic_flags &= ~IEEE80211_F_IBSSON; break; case IEEE80211_M_IBSS: ic->ic_flags |= IEEE80211_F_IBSSON; break; } /* * Yech, slot time may change depending on the * operating mode so reset it to be sure everything * is setup appropriately. */ ieee80211_reset_erp(ic); ieee80211_wme_initparams(ic); /* after opmode change */ error = ENETRESET; } #ifdef notdef if (error == 0) ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); #endif return error; } void ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) { struct ieee80211com *ic; struct ieee80211_rateset *rs; ic = ieee80211_find_instance(ifp); if (!ic) { if_printf(ifp, "%s: no 802.11 instance!\n", __func__); return; } imr->ifm_status = IFM_AVALID; imr->ifm_active = IFM_IEEE80211; if (ic->ic_state == IEEE80211_S_RUN) imr->ifm_status |= IFM_ACTIVE; /* * Calculate a current rate if possible. */ if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { /* * A fixed rate is set, report that. */ rs = &ic->ic_sup_rates[ic->ic_curmode]; imr->ifm_active |= ieee80211_rate2media(ic, rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode); } else if (ic->ic_opmode == IEEE80211_M_STA) { /* * In station mode report the current transmit rate. */ rs = &ic->ic_bss->ni_rates; imr->ifm_active |= ieee80211_rate2media(ic, rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode); } else imr->ifm_active |= IFM_AUTO; switch (ic->ic_opmode) { case IEEE80211_M_STA: break; case IEEE80211_M_IBSS: imr->ifm_active |= IFM_IEEE80211_ADHOC; break; case IEEE80211_M_AHDEMO: /* should not come here */ break; case IEEE80211_M_HOSTAP: imr->ifm_active |= IFM_IEEE80211_HOSTAP; break; case IEEE80211_M_MONITOR: imr->ifm_active |= IFM_IEEE80211_MONITOR; break; } switch (ic->ic_curmode) { case IEEE80211_MODE_11A: imr->ifm_active |= IFM_IEEE80211_11A; break; case IEEE80211_MODE_11B: imr->ifm_active |= IFM_IEEE80211_11B; break; case IEEE80211_MODE_11G: imr->ifm_active |= IFM_IEEE80211_11G; break; case IEEE80211_MODE_FH: imr->ifm_active |= IFM_IEEE80211_FH; break; case IEEE80211_MODE_TURBO_A: imr->ifm_active |= IFM_IEEE80211_11A | IFM_IEEE80211_TURBO; break; case IEEE80211_MODE_TURBO_G: imr->ifm_active |= IFM_IEEE80211_11G | IFM_IEEE80211_TURBO; break; } } void ieee80211_watchdog(struct ieee80211com *ic) { struct ieee80211_node_table *nt; int need_inact_timer = 0; if (ic->ic_state != IEEE80211_S_INIT) { if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); nt = &ic->ic_scan; if (nt->nt_inact_timer) { if (--nt->nt_inact_timer == 0) nt->nt_timeout(nt); need_inact_timer += nt->nt_inact_timer; } nt = &ic->ic_sta; if (nt->nt_inact_timer) { if (--nt->nt_inact_timer == 0) nt->nt_timeout(nt); need_inact_timer += nt->nt_inact_timer; } } if (ic->ic_mgt_timer != 0 || need_inact_timer) ic->ic_ifp->if_timer = 1; } /* * Set the current phy mode and recalculate the active channel * set based on the available channels for this mode. Also * select a new default/current channel if the current one is * inappropriate for this mode. */ int ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) { #define N(a) (sizeof(a) / sizeof(a[0])) static const u_int chanflags[] = { 0, /* IEEE80211_MODE_AUTO */ IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */ IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ }; struct ieee80211_channel *c; u_int modeflags; int i; /* validate new mode */ if ((ic->ic_modecaps & (1<ic_modecaps); return EINVAL; } /* * Verify at least one channel is present in the available * channel list before committing to the new mode. */ KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode)); modeflags = chanflags[mode]; for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { c = &ic->ic_channels[i]; if (c->ic_flags == 0) continue; if (mode == IEEE80211_MODE_AUTO) { /* ignore turbo channels for autoselect */ if ((c->ic_flags & IEEE80211_CHAN_TURBO) == 0) break; } else { if ((c->ic_flags & modeflags) == modeflags) break; } } if (i > IEEE80211_CHAN_MAX) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, "%s: no channels found for mode %u\n", __func__, mode); return EINVAL; } /* * Calculate the active channel set. */ memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active)); for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { c = &ic->ic_channels[i]; if (c->ic_flags == 0) continue; if (mode == IEEE80211_MODE_AUTO) { /* take anything but pure turbo channels */ if ((c->ic_flags & IEEE80211_CHAN_TURBO) == 0) setbit(ic->ic_chan_active, i); } else { if ((c->ic_flags & modeflags) == modeflags) setbit(ic->ic_chan_active, i); } } /* * If no current/default channel is setup or the current * channel is wrong for the mode then pick the first * available channel from the active list. This is likely * not the right one. */ if (ic->ic_ibss_chan == NULL || isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { for (i = 0; i <= IEEE80211_CHAN_MAX; i++) if (isset(ic->ic_chan_active, i)) { ic->ic_ibss_chan = &ic->ic_channels[i]; break; } KASSERT(ic->ic_ibss_chan != NULL && isset(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan)), ("Bad IBSS channel %u", ieee80211_chan2ieee(ic, ic->ic_ibss_chan))); } /* * If the desired channel is set but no longer valid then reset it. */ if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan))) ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* * Do mode-specific rate setup. */ if (mode == IEEE80211_MODE_11G) { /* * Use a mixed 11b/11g rate set. */ ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], IEEE80211_MODE_11G); } else if (mode == IEEE80211_MODE_11B) { /* * Force pure 11b rate set. */ ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], IEEE80211_MODE_11B); } /* * Setup an initial rate set according to the * current/default channel selected above. This * will be changed when scanning but must exist * now so driver have a consistent state of ic_ibss_chan. */ if (ic->ic_bss) /* NB: can be called before lateattach */ ic->ic_bss->ni_rates = ic->ic_sup_rates[mode]; ic->ic_curmode = mode; ieee80211_reset_erp(ic); /* reset ERP state */ ieee80211_wme_initparams(ic); /* reset WME stat */ return 0; #undef N } /* * Return the phy mode for with the specified channel so the * caller can select a rate set. This is problematic for channels * where multiple operating modes are possible (e.g. 11g+11b). * In those cases we defer to the current operating mode when set. */ enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan) { if (IEEE80211_IS_CHAN_T(chan)) { return IEEE80211_MODE_TURBO_A; } else if (IEEE80211_IS_CHAN_5GHZ(chan)) { return IEEE80211_MODE_11A; } else if (IEEE80211_IS_CHAN_FHSS(chan)) return IEEE80211_MODE_FH; else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) { /* * This assumes all 11g channels are also usable * for 11b, which is currently true. */ if (ic->ic_curmode == IEEE80211_MODE_TURBO_G) return IEEE80211_MODE_TURBO_G; if (ic->ic_curmode == IEEE80211_MODE_11B) return IEEE80211_MODE_11B; return IEEE80211_MODE_11G; } else return IEEE80211_MODE_11B; } /* * convert IEEE80211 rate value to ifmedia subtype. * ieee80211 rate is in unit of 0.5Mbps. */ int ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) { #define N(a) (sizeof(a) / sizeof(a[0])) static const struct { u_int m; /* rate + mode */ u_int r; /* if_media rate */ } rates[] = { { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, /* NB: OFDM72 doesn't realy exist so we don't handle it */ }; u_int mask, i; mask = rate & IEEE80211_RATE_VAL; switch (mode) { case IEEE80211_MODE_11A: case IEEE80211_MODE_TURBO_A: mask |= IFM_IEEE80211_11A; break; case IEEE80211_MODE_11B: mask |= IFM_IEEE80211_11B; break; case IEEE80211_MODE_FH: mask |= IFM_IEEE80211_FH; break; case IEEE80211_MODE_AUTO: /* NB: ic may be NULL for some drivers */ if (ic && ic->ic_phytype == IEEE80211_T_FH) { mask |= IFM_IEEE80211_FH; break; } /* NB: hack, 11g matches both 11b+11a rates */ /* fall thru... */ case IEEE80211_MODE_11G: case IEEE80211_MODE_TURBO_G: mask |= IFM_IEEE80211_11G; break; } for (i = 0; i < N(rates); i++) if (rates[i].m == mask) return rates[i].r; return IFM_AUTO; #undef N } int ieee80211_media2rate(int mword) { #define N(a) (sizeof(a) / sizeof(a[0])) static const int ieeerates[] = { -1, /* IFM_AUTO */ 0, /* IFM_MANUAL */ 0, /* IFM_NONE */ 2, /* IFM_IEEE80211_FH1 */ 4, /* IFM_IEEE80211_FH2 */ 2, /* IFM_IEEE80211_DS1 */ 4, /* IFM_IEEE80211_DS2 */ 11, /* IFM_IEEE80211_DS5 */ 22, /* IFM_IEEE80211_DS11 */ 44, /* IFM_IEEE80211_DS22 */ 12, /* IFM_IEEE80211_OFDM6 */ 18, /* IFM_IEEE80211_OFDM9 */ 24, /* IFM_IEEE80211_OFDM12 */ 36, /* IFM_IEEE80211_OFDM18 */ 48, /* IFM_IEEE80211_OFDM24 */ 72, /* IFM_IEEE80211_OFDM36 */ 96, /* IFM_IEEE80211_OFDM48 */ 108, /* IFM_IEEE80211_OFDM54 */ 144, /* IFM_IEEE80211_OFDM72 */ }; return IFM_SUBTYPE(mword) < N(ieeerates) ? ieeerates[IFM_SUBTYPE(mword)] : 0; #undef N } Index: stable/6/sys/net80211/ieee80211_ioctl.c =================================================================== --- stable/6/sys/net80211/ieee80211_ioctl.c (revision 153658) +++ stable/6/sys/net80211/ieee80211_ioctl.c (revision 153659) @@ -1,2489 +1,2501 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2005 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * 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 ioctl support (FreeBSD-specific) */ #include "opt_inet.h" #include "opt_ipx.h" #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #endif #ifdef IPX #include #include #endif #include #include #include #define IS_UP(_ic) \ (((_ic)->ic_ifp->if_flags & IFF_UP) && \ ((_ic)->ic_ifp->if_drv_flags & IFF_DRV_RUNNING)) #define IS_UP_AUTO(_ic) \ (IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO) /* * XXX * Wireless LAN specific configuration interface, which is compatible * with wicontrol(8). */ struct wi_read_ap_args { int i; /* result count */ struct wi_apinfo *ap; /* current entry in result buffer */ caddr_t max; /* result buffer bound */ }; static void wi_read_ap_result(void *arg, struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct wi_read_ap_args *sa = arg; struct wi_apinfo *ap = sa->ap; struct ieee80211_rateset *rs; int j; if ((caddr_t)(ap + 1) > sa->max) return; memset(ap, 0, sizeof(struct wi_apinfo)); if (ic->ic_opmode == IEEE80211_M_HOSTAP) { IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr); ap->namelen = ic->ic_des_esslen; if (ic->ic_des_esslen) memcpy(ap->name, ic->ic_des_essid, ic->ic_des_esslen); } else { IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid); ap->namelen = ni->ni_esslen; if (ni->ni_esslen) memcpy(ap->name, ni->ni_essid, ni->ni_esslen); } ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan); ap->signal = ic->ic_node_getrssi(ni); ap->capinfo = ni->ni_capinfo; ap->interval = ni->ni_intval; rs = &ni->ni_rates; for (j = 0; j < rs->rs_nrates; j++) { if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) { ap->rate = (rs->rs_rates[j] & IEEE80211_RATE_VAL) * 5; /* XXX */ } } sa->i++; sa->ap++; } struct wi_read_prism2_args { int i; /* result count */ struct wi_scan_res *res;/* current entry in result buffer */ caddr_t max; /* result buffer bound */ }; static void wi_read_prism2_result(void *arg, struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct wi_read_prism2_args *sa = arg; struct wi_scan_res *res = sa->res; if ((caddr_t)(res + 1) > sa->max) return; res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan); res->wi_noise = 0; res->wi_signal = ic->ic_node_getrssi(ni); IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid); res->wi_interval = ni->ni_intval; res->wi_capinfo = ni->ni_capinfo; res->wi_ssid_len = ni->ni_esslen; memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN); /* NB: assumes wi_srates holds <= ni->ni_rates */ memcpy(res->wi_srates, ni->ni_rates.rs_rates, sizeof(res->wi_srates)); if (ni->ni_rates.rs_nrates < 10) res->wi_srates[ni->ni_rates.rs_nrates] = 0; res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate]; res->wi_rsvd = 0; sa->i++; sa->res++; } struct wi_read_sigcache_args { int i; /* result count */ struct wi_sigcache *wsc;/* current entry in result buffer */ caddr_t max; /* result buffer bound */ }; static void wi_read_sigcache(void *arg, struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct wi_read_sigcache_args *sa = arg; struct wi_sigcache *wsc = sa->wsc; if ((caddr_t)(wsc + 1) > sa->max) return; memset(wsc, 0, sizeof(struct wi_sigcache)); IEEE80211_ADDR_COPY(wsc->macsrc, ni->ni_macaddr); wsc->signal = ic->ic_node_getrssi(ni); sa->wsc++; sa->i++; } int ieee80211_cfgget(struct ieee80211com *ic, u_long cmd, caddr_t data) { struct ifnet *ifp = ic->ic_ifp; int i, j, error; struct ifreq *ifr = (struct ifreq *)data; struct wi_req wreq; struct wi_ltv_keys *keys; error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); if (error) return error; wreq.wi_len = 0; switch (wreq.wi_type) { case WI_RID_SERIALNO: /* nothing appropriate */ break; case WI_RID_NODENAME: strcpy((char *)&wreq.wi_val[1], hostname); wreq.wi_val[0] = htole16(strlen(hostname)); wreq.wi_len = (1 + strlen(hostname) + 1) / 2; break; case WI_RID_CURRENT_SSID: if (ic->ic_state != IEEE80211_S_RUN) { wreq.wi_val[0] = 0; wreq.wi_len = 1; break; } wreq.wi_val[0] = htole16(ic->ic_bss->ni_esslen); memcpy(&wreq.wi_val[1], ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen); wreq.wi_len = (1 + ic->ic_bss->ni_esslen + 1) / 2; break; case WI_RID_OWN_SSID: case WI_RID_DESIRED_SSID: wreq.wi_val[0] = htole16(ic->ic_des_esslen); memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen); wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2; break; case WI_RID_CURRENT_BSSID: if (ic->ic_state == IEEE80211_S_RUN) IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss->ni_bssid); else memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN); wreq.wi_len = IEEE80211_ADDR_LEN / 2; break; case WI_RID_CHANNEL_LIST: memset(wreq.wi_val, 0, sizeof(wreq.wi_val)); /* * Since channel 0 is not available for DS, channel 1 * is assigned to LSB on WaveLAN. */ if (ic->ic_phytype == IEEE80211_T_DS) i = 1; else i = 0; for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) if (isset(ic->ic_chan_active, i)) { setbit((u_int8_t *)wreq.wi_val, j); wreq.wi_len = j / 16 + 1; } break; case WI_RID_OWN_CHNL: wreq.wi_val[0] = htole16( ieee80211_chan2ieee(ic, ic->ic_ibss_chan)); wreq.wi_len = 1; break; case WI_RID_CURRENT_CHAN: wreq.wi_val[0] = htole16( ieee80211_chan2ieee(ic, ic->ic_curchan)); wreq.wi_len = 1; break; case WI_RID_COMMS_QUALITY: wreq.wi_val[0] = 0; /* quality */ wreq.wi_val[1] = htole16(ic->ic_node_getrssi(ic->ic_bss)); wreq.wi_val[2] = 0; /* noise */ wreq.wi_len = 3; break; case WI_RID_PROMISC: wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0); wreq.wi_len = 1; break; case WI_RID_PORTTYPE: wreq.wi_val[0] = htole16(ic->ic_opmode); wreq.wi_len = 1; break; case WI_RID_MAC_NODE: IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr); wreq.wi_len = IEEE80211_ADDR_LEN / 2; break; case WI_RID_TX_RATE: if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) wreq.wi_val[0] = 0; /* auto */ else wreq.wi_val[0] = htole16( (ic->ic_sup_rates[ic->ic_curmode].rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL) / 2); wreq.wi_len = 1; break; case WI_RID_CUR_TX_RATE: wreq.wi_val[0] = htole16( (ic->ic_bss->ni_rates.rs_rates[ic->ic_bss->ni_txrate] & IEEE80211_RATE_VAL) / 2); wreq.wi_len = 1; break; case WI_RID_RTS_THRESH: wreq.wi_val[0] = htole16(ic->ic_rtsthreshold); wreq.wi_len = 1; break; case WI_RID_CREATE_IBSS: wreq.wi_val[0] = htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0); wreq.wi_len = 1; break; case WI_RID_MICROWAVE_OVEN: wreq.wi_val[0] = 0; /* no ... not supported */ wreq.wi_len = 1; break; case WI_RID_ROAMING_MODE: wreq.wi_val[0] = htole16(ic->ic_roaming); /* XXX map */ wreq.wi_len = 1; break; case WI_RID_SYSTEM_SCALE: wreq.wi_val[0] = htole16(1); /* low density ... not supp */ wreq.wi_len = 1; break; case WI_RID_PM_ENABLED: wreq.wi_val[0] = htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0); wreq.wi_len = 1; break; case WI_RID_MAX_SLEEP: wreq.wi_val[0] = htole16(ic->ic_lintval); wreq.wi_len = 1; break; case WI_RID_CUR_BEACON_INT: wreq.wi_val[0] = htole16(ic->ic_bss->ni_intval); wreq.wi_len = 1; break; case WI_RID_WEP_AVAIL: wreq.wi_val[0] = htole16(1); /* always available */ wreq.wi_len = 1; break; case WI_RID_CNFAUTHMODE: wreq.wi_val[0] = htole16(1); /* TODO: open system only */ wreq.wi_len = 1; break; case WI_RID_ENCRYPTION: wreq.wi_val[0] = htole16((ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0); wreq.wi_len = 1; break; case WI_RID_TX_CRYPT_KEY: wreq.wi_val[0] = htole16(ic->ic_def_txkey); wreq.wi_len = 1; break; case WI_RID_DEFLT_CRYPT_KEYS: keys = (struct wi_ltv_keys *)&wreq; /* do not show keys to non-root user */ error = suser(curthread); if (error) { memset(keys, 0, sizeof(*keys)); error = 0; break; } for (i = 0; i < IEEE80211_WEP_NKID; i++) { keys->wi_keys[i].wi_keylen = htole16(ic->ic_nw_keys[i].wk_keylen); memcpy(keys->wi_keys[i].wi_keydat, ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_keylen); } wreq.wi_len = sizeof(*keys) / 2; break; case WI_RID_MAX_DATALEN: wreq.wi_val[0] = htole16(ic->ic_fragthreshold); wreq.wi_len = 1; break; case WI_RID_IFACE_STATS: /* XXX: should be implemented in lower drivers */ break; case WI_RID_READ_APS: /* * Don't return results until active scan completes. */ if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) { struct wi_read_ap_args args; args.i = 0; args.ap = (void *)((char *)wreq.wi_val + sizeof(i)); args.max = (void *)(&wreq + 1); ieee80211_iterate_nodes(&ic->ic_scan, wi_read_ap_result, &args); memcpy(wreq.wi_val, &args.i, sizeof(args.i)); wreq.wi_len = (sizeof(int) + sizeof(struct wi_apinfo) * args.i) / 2; } else error = EINPROGRESS; break; case WI_RID_PRISM2: /* NB: we lie so WI_RID_SCAN_RES can include rates */ wreq.wi_val[0] = 1; wreq.wi_len = sizeof(u_int16_t) / 2; break; case WI_RID_SCAN_RES: /* compatibility interface */ if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) { struct wi_read_prism2_args args; struct wi_scan_p2_hdr *p2; /* NB: use Prism2 format so we can include rate info */ p2 = (struct wi_scan_p2_hdr *)wreq.wi_val; args.i = 0; args.res = (void *)&p2[1]; args.max = (void *)(&wreq + 1); ieee80211_iterate_nodes(&ic->ic_scan, wi_read_prism2_result, &args); p2->wi_rsvd = 0; p2->wi_reason = args.i; wreq.wi_len = (sizeof(*p2) + sizeof(struct wi_scan_res) * args.i) / 2; } else error = EINPROGRESS; break; case WI_RID_READ_CACHE: { struct wi_read_sigcache_args args; args.i = 0; args.wsc = (struct wi_sigcache *) wreq.wi_val; args.max = (void *)(&wreq + 1); ieee80211_iterate_nodes(&ic->ic_scan, wi_read_sigcache, &args); wreq.wi_len = sizeof(struct wi_sigcache) * args.i / 2; break; } default: error = EINVAL; break; } if (error == 0) { wreq.wi_len++; error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); } return error; } static int findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) { #define IEEERATE(_ic,_m,_i) \ ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) int i, nrates = ic->ic_sup_rates[mode].rs_nrates; for (i = 0; i < nrates; i++) if (IEEERATE(ic, mode, i) == rate) return i; return -1; #undef IEEERATE } /* * Prepare to do a user-initiated scan for AP's. If no * current/default channel is setup or the current channel * is invalid then pick the first available channel from * the active list as the place to start the scan. */ static int ieee80211_setupscan(struct ieee80211com *ic, const u_int8_t chanlist[]) { /* * XXX don't permit a scan to be started unless we * know the device is ready. For the moment this means * the device is marked up as this is the required to * initialize the hardware. It would be better to permit * scanning prior to being up but that'll require some * changes to the infrastructure. */ if (!IS_UP(ic)) return EINVAL; memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); /* * We force the state to INIT before calling ieee80211_new_state * to get ieee80211_begin_scan called. We really want to scan w/o * altering the current state but that's not possible right now. */ /* XXX handle proberequest case */ ic->ic_state = IEEE80211_S_INIT; /* XXX bypass state machine */ return 0; } int ieee80211_cfgset(struct ieee80211com *ic, u_long cmd, caddr_t data) { struct ifnet *ifp = ic->ic_ifp; int i, j, len, error, rate; struct ifreq *ifr = (struct ifreq *)data; struct wi_ltv_keys *keys; struct wi_req wreq; u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)]; error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); if (error) return error; len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0; switch (wreq.wi_type) { case WI_RID_SERIALNO: case WI_RID_NODENAME: return EPERM; case WI_RID_CURRENT_SSID: return EPERM; case WI_RID_OWN_SSID: case WI_RID_DESIRED_SSID: if (le16toh(wreq.wi_val[0]) * 2 > len || le16toh(wreq.wi_val[0]) > IEEE80211_NWID_LEN) { error = ENOSPC; break; } memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid)); ic->ic_des_esslen = le16toh(wreq.wi_val[0]) * 2; memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen); error = ENETRESET; break; case WI_RID_CURRENT_BSSID: return EPERM; case WI_RID_OWN_CHNL: if (len != 2) return EINVAL; i = le16toh(wreq.wi_val[0]); if (i < 0 || i > IEEE80211_CHAN_MAX || isclr(ic->ic_chan_active, i)) return EINVAL; ic->ic_ibss_chan = &ic->ic_channels[i]; if (ic->ic_opmode == IEEE80211_M_MONITOR) error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; else error = ENETRESET; break; case WI_RID_CURRENT_CHAN: return EPERM; case WI_RID_COMMS_QUALITY: return EPERM; case WI_RID_PROMISC: if (len != 2) return EINVAL; if (ifp->if_flags & IFF_PROMISC) { if (wreq.wi_val[0] == 0) { ifp->if_flags &= ~IFF_PROMISC; error = ENETRESET; } } else { if (wreq.wi_val[0] != 0) { ifp->if_flags |= IFF_PROMISC; error = ENETRESET; } } break; case WI_RID_PORTTYPE: if (len != 2) return EINVAL; switch (le16toh(wreq.wi_val[0])) { case IEEE80211_M_STA: break; case IEEE80211_M_IBSS: if (!(ic->ic_caps & IEEE80211_C_IBSS)) return EINVAL; break; case IEEE80211_M_AHDEMO: if (ic->ic_phytype != IEEE80211_T_DS || !(ic->ic_caps & IEEE80211_C_AHDEMO)) return EINVAL; break; case IEEE80211_M_HOSTAP: if (!(ic->ic_caps & IEEE80211_C_HOSTAP)) return EINVAL; break; default: return EINVAL; } if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) { ic->ic_opmode = le16toh(wreq.wi_val[0]); error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; } break; #if 0 case WI_RID_MAC_NODE: if (len != IEEE80211_ADDR_LEN) return EINVAL; IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val); /* if_init will copy lladdr into ic_myaddr */ error = ENETRESET; break; #endif case WI_RID_TX_RATE: if (len != 2) return EINVAL; if (wreq.wi_val[0] == 0) { /* auto */ ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; break; } rate = 2 * le16toh(wreq.wi_val[0]); if (ic->ic_curmode == IEEE80211_MODE_AUTO) { /* * In autoselect mode search for the rate. We take * the first instance which may not be right, but we * are limited by the interface. Note that we also * lock the mode to insure the rate is meaningful * when it is used. */ for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) { if ((ic->ic_modecaps & (1<ic_curmode = j; goto setrate; } } } else { i = findrate(ic, ic->ic_curmode, rate); if (i != -1) goto setrate; } return EINVAL; setrate: ic->ic_fixed_rate = i; error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case WI_RID_CUR_TX_RATE: return EPERM; case WI_RID_RTS_THRESH: if (len != 2) return EINVAL; if (le16toh(wreq.wi_val[0]) != IEEE80211_MAX_LEN) return EINVAL; /* TODO: RTS */ break; case WI_RID_CREATE_IBSS: if (len != 2) return EINVAL; if (wreq.wi_val[0] != 0) { if ((ic->ic_caps & IEEE80211_C_IBSS) == 0) return EINVAL; if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) { ic->ic_flags |= IEEE80211_F_IBSSON; if (ic->ic_opmode == IEEE80211_M_IBSS && ic->ic_state == IEEE80211_S_SCAN) error = IS_UP_AUTO(ic) ? ENETRESET : 0; } } else { if (ic->ic_flags & IEEE80211_F_IBSSON) { ic->ic_flags &= ~IEEE80211_F_IBSSON; if (ic->ic_flags & IEEE80211_F_SIBSS) { ic->ic_flags &= ~IEEE80211_F_SIBSS; error = IS_UP_AUTO(ic) ? ENETRESET : 0; } } } break; case WI_RID_MICROWAVE_OVEN: if (len != 2) return EINVAL; if (wreq.wi_val[0] != 0) return EINVAL; /* not supported */ break; case WI_RID_ROAMING_MODE: if (len != 2) return EINVAL; i = le16toh(wreq.wi_val[0]); if (i > IEEE80211_ROAMING_MANUAL) return EINVAL; /* not supported */ ic->ic_roaming = i; break; case WI_RID_SYSTEM_SCALE: if (len != 2) return EINVAL; if (le16toh(wreq.wi_val[0]) != 1) return EINVAL; /* not supported */ break; case WI_RID_PM_ENABLED: if (len != 2) return EINVAL; if (wreq.wi_val[0] != 0) { if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) return EINVAL; if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { ic->ic_flags |= IEEE80211_F_PMGTON; error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; } } else { if (ic->ic_flags & IEEE80211_F_PMGTON) { ic->ic_flags &= ~IEEE80211_F_PMGTON; error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; } } break; case WI_RID_MAX_SLEEP: if (len != 2) return EINVAL; ic->ic_lintval = le16toh(wreq.wi_val[0]); if (ic->ic_flags & IEEE80211_F_PMGTON) error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case WI_RID_CUR_BEACON_INT: return EPERM; case WI_RID_WEP_AVAIL: return EPERM; case WI_RID_CNFAUTHMODE: if (len != 2) return EINVAL; i = le16toh(wreq.wi_val[0]); if (i > IEEE80211_AUTH_WPA) return EINVAL; ic->ic_bss->ni_authmode = i; /* XXX ENETRESET? */ error = ENETRESET; break; case WI_RID_ENCRYPTION: if (len != 2) return EINVAL; if (wreq.wi_val[0] != 0) { if ((ic->ic_caps & IEEE80211_C_WEP) == 0) return EINVAL; if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { ic->ic_flags |= IEEE80211_F_PRIVACY; error = ENETRESET; } } else { if (ic->ic_flags & IEEE80211_F_PRIVACY) { ic->ic_flags &= ~IEEE80211_F_PRIVACY; error = ENETRESET; } } break; case WI_RID_TX_CRYPT_KEY: if (len != 2) return EINVAL; i = le16toh(wreq.wi_val[0]); if (i >= IEEE80211_WEP_NKID) return EINVAL; ic->ic_def_txkey = i; error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case WI_RID_DEFLT_CRYPT_KEYS: if (len != sizeof(struct wi_ltv_keys)) return EINVAL; keys = (struct wi_ltv_keys *)&wreq; for (i = 0; i < IEEE80211_WEP_NKID; i++) { len = le16toh(keys->wi_keys[i].wi_keylen); if (len != 0 && len < IEEE80211_WEP_KEYLEN) return EINVAL; if (len > IEEE80211_KEYBUF_SIZE) return EINVAL; } for (i = 0; i < IEEE80211_WEP_NKID; i++) { struct ieee80211_key *k = &ic->ic_nw_keys[i]; len = le16toh(keys->wi_keys[i].wi_keylen); k->wk_keylen = len; k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; memset(k->wk_key, 0, sizeof(k->wk_key)); memcpy(k->wk_key, keys->wi_keys[i].wi_keydat, len); #if 0 k->wk_type = IEEE80211_CIPHER_WEP; #endif } error = ENETRESET; break; case WI_RID_MAX_DATALEN: if (len != 2) return EINVAL; len = le16toh(wreq.wi_val[0]); if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN) return EINVAL; ic->ic_fragthreshold = len; error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case WI_RID_IFACE_STATS: error = EPERM; break; case WI_RID_SCAN_REQ: /* XXX wicontrol */ if (ic->ic_opmode == IEEE80211_M_HOSTAP) break; error = ieee80211_setupscan(ic, ic->ic_chan_avail); if (error == 0) error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); break; case WI_RID_SCAN_APS: if (ic->ic_opmode == IEEE80211_M_HOSTAP) break; len--; /* XXX: tx rate? */ /* FALLTHRU */ case WI_RID_CHANNEL_LIST: memset(chanlist, 0, sizeof(chanlist)); /* * Since channel 0 is not available for DS, channel 1 * is assigned to LSB on WaveLAN. */ if (ic->ic_phytype == IEEE80211_T_DS) i = 1; else i = 0; for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { if ((j / 8) >= len) break; if (isclr((u_int8_t *)wreq.wi_val, j)) continue; if (isclr(ic->ic_chan_active, i)) { if (wreq.wi_type != WI_RID_CHANNEL_LIST) continue; if (isclr(ic->ic_chan_avail, i)) return EPERM; } setbit(chanlist, i); } error = ieee80211_setupscan(ic, chanlist); if (wreq.wi_type == WI_RID_CHANNEL_LIST) { /* NB: ignore error from ieee80211_setupscan */ error = ENETRESET; } else if (error == 0) error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); break; default: error = EINVAL; break; } if (error == ENETRESET && !IS_UP_AUTO(ic)) error = 0; return error; } static int cap2cipher(int flag) { switch (flag) { case IEEE80211_C_WEP: return IEEE80211_CIPHER_WEP; case IEEE80211_C_AES: return IEEE80211_CIPHER_AES_OCB; case IEEE80211_C_AES_CCM: return IEEE80211_CIPHER_AES_CCM; case IEEE80211_C_CKIP: return IEEE80211_CIPHER_CKIP; case IEEE80211_C_TKIP: return IEEE80211_CIPHER_TKIP; } return -1; } static int ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211_node *ni; struct ieee80211req_key ik; struct ieee80211_key *wk; const struct ieee80211_cipher *cip; u_int kid; int error; if (ireq->i_len != sizeof(ik)) return EINVAL; error = copyin(ireq->i_data, &ik, sizeof(ik)); if (error) return error; kid = ik.ik_keyix; if (kid == IEEE80211_KEYIX_NONE) { ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr); if (ni == NULL) return EINVAL; /* XXX */ wk = &ni->ni_ucastkey; } else { if (kid >= IEEE80211_WEP_NKID) return EINVAL; wk = &ic->ic_nw_keys[kid]; IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr); ni = NULL; } cip = wk->wk_cipher; ik.ik_type = cip->ic_cipher; ik.ik_keylen = wk->wk_keylen; ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV); if (wk->wk_keyix == ic->ic_def_txkey) ik.ik_flags |= IEEE80211_KEY_DEFAULT; if (suser(curthread) == 0) { /* NB: only root can read key data */ ik.ik_keyrsc = wk->wk_keyrsc; ik.ik_keytsc = wk->wk_keytsc; memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen); if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) { memcpy(ik.ik_keydata+wk->wk_keylen, wk->wk_key + IEEE80211_KEYBUF_SIZE, IEEE80211_MICBUF_SIZE); ik.ik_keylen += IEEE80211_MICBUF_SIZE; } } else { ik.ik_keyrsc = 0; ik.ik_keytsc = 0; memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata)); } if (ni != NULL) ieee80211_free_node(ni); return copyout(&ik, ireq->i_data, sizeof(ik)); } static int ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) { if (sizeof(ic->ic_chan_active) < ireq->i_len) ireq->i_len = sizeof(ic->ic_chan_active); return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len); } static int ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211req_chaninfo chans; /* XXX off stack? */ int i, space; /* * Since channel 0 is not available for DS, channel 1 * is assigned to LSB on WaveLAN. */ if (ic->ic_phytype == IEEE80211_T_DS) i = 1; else i = 0; memset(&chans, 0, sizeof(chans)); for (; i <= IEEE80211_CHAN_MAX; i++) if (isset(ic->ic_chan_avail, i)) { struct ieee80211_channel *c = &ic->ic_channels[i]; chans.ic_chans[chans.ic_nchans].ic_freq = c->ic_freq; chans.ic_chans[chans.ic_nchans].ic_flags = c->ic_flags; chans.ic_nchans++; } space = __offsetof(struct ieee80211req_chaninfo, ic_chans[chans.ic_nchans]); if (space > ireq->i_len) space = ireq->i_len; return copyout(&chans, ireq->i_data, space); } static int ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211_node *ni; struct ieee80211req_wpaie wpaie; int error; if (ireq->i_len < IEEE80211_ADDR_LEN) return EINVAL; error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN); if (error != 0) return error; ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr); if (ni == NULL) return EINVAL; /* XXX */ memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie)); if (ni->ni_wpa_ie != NULL) { int ielen = ni->ni_wpa_ie[1] + 2; if (ielen > sizeof(wpaie.wpa_ie)) ielen = sizeof(wpaie.wpa_ie); memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen); } ieee80211_free_node(ni); if (ireq->i_len > sizeof(wpaie)) ireq->i_len = sizeof(wpaie); return copyout(&wpaie, ireq->i_data, ireq->i_len); } static int ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211_node *ni; u_int8_t macaddr[IEEE80211_ADDR_LEN]; const int off = __offsetof(struct ieee80211req_sta_stats, is_stats); int error; if (ireq->i_len < off) return EINVAL; error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); if (error != 0) return error; ni = ieee80211_find_node(&ic->ic_sta, macaddr); if (ni == NULL) return EINVAL; /* XXX */ if (ireq->i_len > sizeof(struct ieee80211req_sta_stats)) ireq->i_len = sizeof(struct ieee80211req_sta_stats); /* NB: copy out only the statistics */ error = copyout(&ni->ni_stats, (u_int8_t *) ireq->i_data + off, ireq->i_len - off); ieee80211_free_node(ni); return error; } static void get_scan_result(struct ieee80211req_scan_result *sr, const struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; memset(sr, 0, sizeof(*sr)); sr->isr_ssid_len = ni->ni_esslen; if (ni->ni_wpa_ie != NULL) sr->isr_ie_len += 2+ni->ni_wpa_ie[1]; if (ni->ni_wme_ie != NULL) sr->isr_ie_len += 2+ni->ni_wme_ie[1]; sr->isr_len = sizeof(*sr) + sr->isr_ssid_len + sr->isr_ie_len; sr->isr_len = roundup(sr->isr_len, sizeof(u_int32_t)); if (ni->ni_chan != IEEE80211_CHAN_ANYC) { sr->isr_freq = ni->ni_chan->ic_freq; sr->isr_flags = ni->ni_chan->ic_flags; } sr->isr_rssi = ic->ic_node_getrssi(ni); sr->isr_intval = ni->ni_intval; sr->isr_capinfo = ni->ni_capinfo; sr->isr_erp = ni->ni_erp; IEEE80211_ADDR_COPY(sr->isr_bssid, ni->ni_bssid); sr->isr_nrates = ni->ni_rates.rs_nrates; if (sr->isr_nrates > 15) sr->isr_nrates = 15; memcpy(sr->isr_rates, ni->ni_rates.rs_rates, sr->isr_nrates); } static int ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) { union { struct ieee80211req_scan_result res; char data[512]; /* XXX shrink? */ } u; struct ieee80211req_scan_result *sr = &u.res; struct ieee80211_node_table *nt; struct ieee80211_node *ni; int error, space; u_int8_t *p, *cp; p = ireq->i_data; space = ireq->i_len; error = 0; /* XXX locking */ nt = &ic->ic_scan; TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { /* NB: skip pre-scan node state */ if (ni->ni_chan == IEEE80211_CHAN_ANYC) continue; get_scan_result(sr, ni); if (sr->isr_len > sizeof(u)) continue; /* XXX */ if (space < sr->isr_len) break; cp = (u_int8_t *)(sr+1); memcpy(cp, ni->ni_essid, ni->ni_esslen); cp += ni->ni_esslen; if (ni->ni_wpa_ie != NULL) { memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); cp += 2+ni->ni_wpa_ie[1]; } if (ni->ni_wme_ie != NULL) { memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); cp += 2+ni->ni_wme_ie[1]; } error = copyout(sr, p, sr->isr_len); if (error) break; p += sr->isr_len; space -= sr->isr_len; } ireq->i_len -= space; return error; } struct stainforeq { struct ieee80211com *ic; struct ieee80211req_sta_info *si; size_t space; }; static size_t sta_space(const struct ieee80211_node *ni, size_t *ielen) { *ielen = 0; if (ni->ni_wpa_ie != NULL) *ielen += 2+ni->ni_wpa_ie[1]; if (ni->ni_wme_ie != NULL) *ielen += 2+ni->ni_wme_ie[1]; return roundup(sizeof(struct ieee80211req_sta_info) + *ielen, sizeof(u_int32_t)); } static void get_sta_space(void *arg, struct ieee80211_node *ni) { struct stainforeq *req = arg; struct ieee80211com *ic = ni->ni_ic; size_t ielen; if (ic->ic_opmode == IEEE80211_M_HOSTAP && ni->ni_associd == 0) /* only associated stations */ return; req->space += sta_space(ni, &ielen); } static void get_sta_info(void *arg, struct ieee80211_node *ni) { struct stainforeq *req = arg; struct ieee80211com *ic = ni->ni_ic; struct ieee80211req_sta_info *si; size_t ielen, len; u_int8_t *cp; if (ic->ic_opmode == IEEE80211_M_HOSTAP && ni->ni_associd == 0) /* only associated stations */ return; if (ni->ni_chan == IEEE80211_CHAN_ANYC) /* XXX bogus entry */ return; len = sta_space(ni, &ielen); if (len > req->space) return; si = req->si; si->isi_len = len; si->isi_ie_len = ielen; si->isi_freq = ni->ni_chan->ic_freq; si->isi_flags = ni->ni_chan->ic_flags; si->isi_state = ni->ni_flags; si->isi_authmode = ni->ni_authmode; si->isi_rssi = ic->ic_node_getrssi(ni); si->isi_capinfo = ni->ni_capinfo; si->isi_erp = ni->ni_erp; IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr); si->isi_nrates = ni->ni_rates.rs_nrates; if (si->isi_nrates > 15) si->isi_nrates = 15; memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates); si->isi_txrate = ni->ni_txrate; si->isi_associd = ni->ni_associd; si->isi_txpower = ni->ni_txpower; si->isi_vlan = ni->ni_vlan; if (ni->ni_flags & IEEE80211_NODE_QOS) { memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs)); memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs)); } else { si->isi_txseqs[0] = ni->ni_txseqs[0]; si->isi_rxseqs[0] = ni->ni_rxseqs[0]; } /* NB: leave all cases in case we relax ni_associd == 0 check */ if (ieee80211_node_is_authorized(ni)) si->isi_inact = ic->ic_inact_run; else if (ni->ni_associd != 0) si->isi_inact = ic->ic_inact_auth; else si->isi_inact = ic->ic_inact_init; si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT; cp = (u_int8_t *)(si+1); if (ni->ni_wpa_ie != NULL) { memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); cp += 2+ni->ni_wpa_ie[1]; } if (ni->ni_wme_ie != NULL) { memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); cp += 2+ni->ni_wme_ie[1]; } req->si = (struct ieee80211req_sta_info *)(((u_int8_t *)si) + len); req->space -= len; } static int ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) { struct stainforeq req; int error; if (ireq->i_len < sizeof(struct stainforeq)) return EFAULT; error = 0; req.space = 0; ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req); if (req.space > ireq->i_len) req.space = ireq->i_len; if (req.space > 0) { size_t space; void *p; space = req.space; /* XXX M_WAITOK after driver lock released */ MALLOC(p, void *, space, M_TEMP, M_NOWAIT); if (p == NULL) return ENOMEM; req.si = p; ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req); ireq->i_len = space - req.space; error = copyout(p, ireq->i_data, ireq->i_len); FREE(p, M_TEMP); } else ireq->i_len = 0; return error; } static int ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211_node *ni; struct ieee80211req_sta_txpow txpow; int error; if (ireq->i_len != sizeof(txpow)) return EINVAL; error = copyin(ireq->i_data, &txpow, sizeof(txpow)); if (error != 0) return error; ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr); if (ni == NULL) return EINVAL; /* XXX */ txpow.it_txpow = ni->ni_txpower; error = copyout(&txpow, ireq->i_data, sizeof(txpow)); ieee80211_free_node(ni); return error; } static int ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211_wme_state *wme = &ic->ic_wme; struct wmeParams *wmep; int ac; if ((ic->ic_caps & IEEE80211_C_WME) == 0) return EINVAL; ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); if (ac >= WME_NUM_AC) ac = WME_AC_BE; if (ireq->i_len & IEEE80211_WMEPARAM_BSS) wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; else wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; switch (ireq->i_type) { case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ ireq->i_val = wmep->wmep_logcwmin; break; case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ ireq->i_val = wmep->wmep_logcwmax; break; case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ ireq->i_val = wmep->wmep_aifsn; break; case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ ireq->i_val = wmep->wmep_txopLimit; break; case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; ireq->i_val = wmep->wmep_acm; break; case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; ireq->i_val = !wmep->wmep_noackPolicy; break; } return 0; } static int ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq) { const struct ieee80211_aclator *acl = ic->ic_acl; return (acl == NULL ? EINVAL : acl->iac_getioctl(ic, ireq)); } /* * When building the kernel with -O2 on the i386 architecture, gcc * seems to want to inline this function into ieee80211_ioctl() * (which is the only routine that calls it). When this happens, * ieee80211_ioctl() ends up consuming an additional 2K of stack * space. (Exactly why it needs so much is unclear.) The problem * is that it's possible for ieee80211_ioctl() to invoke other * routines (including driver init functions) which could then find * themselves perilously close to exhausting the stack. * * To avoid this, we deliberately prevent gcc from inlining this * routine. Another way to avoid this is to use less agressive * optimization when compiling this file (i.e. -O instead of -O2) * but special-casing the compilation of this one module in the * build system would be awkward. */ #ifdef __GNUC__ __attribute__ ((noinline)) #endif static int ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) { const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; int error = 0; u_int kid, len, m; u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; char tmpssid[IEEE80211_NWID_LEN]; switch (ireq->i_type) { case IEEE80211_IOC_SSID: switch (ic->ic_state) { case IEEE80211_S_INIT: case IEEE80211_S_SCAN: ireq->i_len = ic->ic_des_esslen; memcpy(tmpssid, ic->ic_des_essid, ireq->i_len); break; default: ireq->i_len = ic->ic_bss->ni_esslen; memcpy(tmpssid, ic->ic_bss->ni_essid, ireq->i_len); break; } error = copyout(tmpssid, ireq->i_data, ireq->i_len); break; case IEEE80211_IOC_NUMSSIDS: ireq->i_val = 1; break; case IEEE80211_IOC_WEP: if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) ireq->i_val = IEEE80211_WEP_OFF; else if (ic->ic_flags & IEEE80211_F_DROPUNENC) ireq->i_val = IEEE80211_WEP_ON; else ireq->i_val = IEEE80211_WEP_MIXED; break; case IEEE80211_IOC_WEPKEY: kid = (u_int) ireq->i_val; if (kid >= IEEE80211_WEP_NKID) return EINVAL; len = (u_int) ic->ic_nw_keys[kid].wk_keylen; /* NB: only root can read WEP keys */ if (suser(curthread) == 0) { bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len); } else { bzero(tmpkey, len); } ireq->i_len = len; error = copyout(tmpkey, ireq->i_data, len); break; case IEEE80211_IOC_NUMWEPKEYS: ireq->i_val = IEEE80211_WEP_NKID; break; case IEEE80211_IOC_WEPTXKEY: ireq->i_val = ic->ic_def_txkey; break; case IEEE80211_IOC_AUTHMODE: if (ic->ic_flags & IEEE80211_F_WPA) ireq->i_val = IEEE80211_AUTH_WPA; else ireq->i_val = ic->ic_bss->ni_authmode; break; case IEEE80211_IOC_CHANNEL: ireq->i_val = ieee80211_chan2ieee(ic, ic->ic_curchan); break; case IEEE80211_IOC_POWERSAVE: if (ic->ic_flags & IEEE80211_F_PMGTON) ireq->i_val = IEEE80211_POWERSAVE_ON; else ireq->i_val = IEEE80211_POWERSAVE_OFF; break; case IEEE80211_IOC_POWERSAVESLEEP: ireq->i_val = ic->ic_lintval; break; case IEEE80211_IOC_RTSTHRESHOLD: ireq->i_val = ic->ic_rtsthreshold; break; case IEEE80211_IOC_PROTMODE: ireq->i_val = ic->ic_protmode; break; case IEEE80211_IOC_TXPOWER: if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) return EINVAL; ireq->i_val = ic->ic_txpowlimit; break; case IEEE80211_IOC_MCASTCIPHER: ireq->i_val = rsn->rsn_mcastcipher; break; case IEEE80211_IOC_MCASTKEYLEN: ireq->i_val = rsn->rsn_mcastkeylen; break; case IEEE80211_IOC_UCASTCIPHERS: ireq->i_val = 0; for (m = 0x1; m != 0; m <<= 1) if (rsn->rsn_ucastcipherset & m) ireq->i_val |= 1<i_val = rsn->rsn_ucastcipher; break; case IEEE80211_IOC_UCASTKEYLEN: ireq->i_val = rsn->rsn_ucastkeylen; break; case IEEE80211_IOC_KEYMGTALGS: ireq->i_val = rsn->rsn_keymgmtset; break; case IEEE80211_IOC_RSNCAPS: ireq->i_val = rsn->rsn_caps; break; case IEEE80211_IOC_WPA: switch (ic->ic_flags & IEEE80211_F_WPA) { case IEEE80211_F_WPA1: ireq->i_val = 1; break; case IEEE80211_F_WPA2: ireq->i_val = 2; break; case IEEE80211_F_WPA1 | IEEE80211_F_WPA2: ireq->i_val = 3; break; default: ireq->i_val = 0; break; } break; case IEEE80211_IOC_CHANLIST: error = ieee80211_ioctl_getchanlist(ic, ireq); break; case IEEE80211_IOC_ROAMING: ireq->i_val = ic->ic_roaming; break; case IEEE80211_IOC_PRIVACY: ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0; break; case IEEE80211_IOC_DROPUNENCRYPTED: ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0; break; case IEEE80211_IOC_COUNTERMEASURES: ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0; break; case IEEE80211_IOC_DRIVER_CAPS: ireq->i_val = ic->ic_caps>>16; ireq->i_len = ic->ic_caps&0xffff; break; case IEEE80211_IOC_WME: ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0; break; case IEEE80211_IOC_HIDESSID: ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0; break; case IEEE80211_IOC_APBRIDGE: ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0; break; case IEEE80211_IOC_OPTIE: if (ic->ic_opt_ie == NULL) return EINVAL; /* NB: truncate, caller can check length */ if (ireq->i_len > ic->ic_opt_ie_len) ireq->i_len = ic->ic_opt_ie_len; error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len); break; case IEEE80211_IOC_WPAKEY: error = ieee80211_ioctl_getkey(ic, ireq); break; case IEEE80211_IOC_CHANINFO: error = ieee80211_ioctl_getchaninfo(ic, ireq); break; case IEEE80211_IOC_BSSID: if (ireq->i_len != IEEE80211_ADDR_LEN) return EINVAL; error = copyout(ic->ic_state == IEEE80211_S_RUN ? ic->ic_bss->ni_bssid : ic->ic_des_bssid, ireq->i_data, ireq->i_len); break; case IEEE80211_IOC_WPAIE: error = ieee80211_ioctl_getwpaie(ic, ireq); break; case IEEE80211_IOC_SCAN_RESULTS: error = ieee80211_ioctl_getscanresults(ic, ireq); break; case IEEE80211_IOC_STA_STATS: error = ieee80211_ioctl_getstastats(ic, ireq); break; case IEEE80211_IOC_TXPOWMAX: ireq->i_val = ic->ic_bss->ni_txpower; break; case IEEE80211_IOC_STA_TXPOW: error = ieee80211_ioctl_getstatxpow(ic, ireq); break; case IEEE80211_IOC_STA_INFO: error = ieee80211_ioctl_getstainfo(ic, ireq); break; case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ error = ieee80211_ioctl_getwmeparam(ic, ireq); break; case IEEE80211_IOC_DTIM_PERIOD: ireq->i_val = ic->ic_dtim_period; break; case IEEE80211_IOC_BEACON_INTERVAL: /* NB: get from ic_bss for station mode */ ireq->i_val = ic->ic_bss->ni_intval; break; case IEEE80211_IOC_PUREG: ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0; break; case IEEE80211_IOC_MCAST_RATE: ireq->i_val = ic->ic_mcast_rate; break; case IEEE80211_IOC_FRAGTHRESHOLD: ireq->i_val = ic->ic_fragthreshold; break; case IEEE80211_IOC_MACCMD: error = ieee80211_ioctl_getmaccmd(ic, ireq); break; + case IEEE80211_IOC_BURST: + ireq->i_val = (ic->ic_flags & IEEE80211_F_BURST) != 0; + break; default: error = EINVAL; break; } return error; } static int ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq) { int error; void *ie; /* * NB: Doing this for ap operation could be useful (e.g. for * WPA and/or WME) except that it typically is worthless * without being able to intervene when processing * association response frames--so disallow it for now. */ if (ic->ic_opmode != IEEE80211_M_STA) return EINVAL; if (ireq->i_len > IEEE80211_MAX_OPT_IE) return EINVAL; MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_NOWAIT); if (ie == NULL) return ENOMEM; error = copyin(ireq->i_data, ie, ireq->i_len); /* XXX sanity check data? */ if (ic->ic_opt_ie != NULL) FREE(ic->ic_opt_ie, M_DEVBUF); ic->ic_opt_ie = ie; ic->ic_opt_ie_len = ireq->i_len; return 0; } static int ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211req_key ik; struct ieee80211_node *ni; struct ieee80211_key *wk; u_int16_t kid; int error; if (ireq->i_len != sizeof(ik)) return EINVAL; error = copyin(ireq->i_data, &ik, sizeof(ik)); if (error) return error; /* NB: cipher support is verified by ieee80211_crypt_newkey */ /* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */ if (ik.ik_keylen > sizeof(ik.ik_keydata)) return E2BIG; kid = ik.ik_keyix; if (kid == IEEE80211_KEYIX_NONE) { /* XXX unicast keys currently must be tx/rx */ if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)) return EINVAL; if (ic->ic_opmode == IEEE80211_M_STA) { ni = ieee80211_ref_node(ic->ic_bss); if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) { ieee80211_free_node(ni); return EADDRNOTAVAIL; } } else { ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr); if (ni == NULL) return ENOENT; } wk = &ni->ni_ucastkey; } else { if (kid >= IEEE80211_WEP_NKID) return EINVAL; wk = &ic->ic_nw_keys[kid]; ni = NULL; } error = 0; ieee80211_key_update_begin(ic); if (ieee80211_crypto_newkey(ic, ik.ik_type, ik.ik_flags, wk)) { wk->wk_keylen = ik.ik_keylen; /* NB: MIC presence is implied by cipher type */ if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE) wk->wk_keylen = IEEE80211_KEYBUF_SIZE; wk->wk_keyrsc = ik.ik_keyrsc; wk->wk_keytsc = 0; /* new key, reset */ memset(wk->wk_key, 0, sizeof(wk->wk_key)); memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen); if (!ieee80211_crypto_setkey(ic, wk, ni != NULL ? ni->ni_macaddr : ik.ik_macaddr)) error = EIO; else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT)) ic->ic_def_txkey = kid; } else error = ENXIO; ieee80211_key_update_end(ic); if (ni != NULL) ieee80211_free_node(ni); return error; } static int ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211req_del_key dk; int kid, error; if (ireq->i_len != sizeof(dk)) return EINVAL; error = copyin(ireq->i_data, &dk, sizeof(dk)); if (error) return error; kid = dk.idk_keyix; /* XXX u_int8_t -> u_int16_t */ if (dk.idk_keyix == (u_int8_t) IEEE80211_KEYIX_NONE) { struct ieee80211_node *ni; if (ic->ic_opmode == IEEE80211_M_STA) { ni = ieee80211_ref_node(ic->ic_bss); if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) { ieee80211_free_node(ni); return EADDRNOTAVAIL; } } else { ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr); if (ni == NULL) return ENOENT; } /* XXX error return */ ieee80211_node_delucastkey(ni); ieee80211_free_node(ni); } else { if (kid >= IEEE80211_WEP_NKID) return EINVAL; /* XXX error return */ ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]); } return 0; } static void domlme(void *arg, struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211req_mlme *mlme = arg; if (ni->ni_associd != 0) { IEEE80211_SEND_MGMT(ic, ni, mlme->im_op == IEEE80211_MLME_DEAUTH ? IEEE80211_FC0_SUBTYPE_DEAUTH : IEEE80211_FC0_SUBTYPE_DISASSOC, mlme->im_reason); } ieee80211_node_leave(ic, ni); } static int ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211req_mlme mlme; struct ieee80211_node *ni; int error; if (ireq->i_len != sizeof(mlme)) return EINVAL; error = copyin(ireq->i_data, &mlme, sizeof(mlme)); if (error) return error; switch (mlme.im_op) { case IEEE80211_MLME_ASSOC: if (ic->ic_opmode != IEEE80211_M_STA) return EINVAL; /* XXX must be in S_SCAN state? */ if (mlme.im_ssid_len != 0) { /* * Desired ssid specified; must match both bssid and * ssid to distinguish ap advertising multiple ssid's. */ ni = ieee80211_find_node_with_ssid(&ic->ic_scan, mlme.im_macaddr, mlme.im_ssid_len, mlme.im_ssid); } else { /* * Normal case; just match bssid. */ ni = ieee80211_find_node(&ic->ic_scan, mlme.im_macaddr); } if (ni == NULL) return EINVAL; if (!ieee80211_sta_join(ic, ni)) { ieee80211_free_node(ni); return EINVAL; } break; case IEEE80211_MLME_DISASSOC: case IEEE80211_MLME_DEAUTH: switch (ic->ic_opmode) { case IEEE80211_M_STA: /* XXX not quite right */ ieee80211_new_state(ic, IEEE80211_S_INIT, mlme.im_reason); break; case IEEE80211_M_HOSTAP: /* NB: the broadcast address means do 'em all */ if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) { if ((ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr)) == NULL) return EINVAL; domlme(&mlme, ni); ieee80211_free_node(ni); } else { ieee80211_iterate_nodes(&ic->ic_sta, domlme, &mlme); } break; default: return EINVAL; } break; case IEEE80211_MLME_AUTHORIZE: case IEEE80211_MLME_UNAUTHORIZE: if (ic->ic_opmode != IEEE80211_M_HOSTAP) return EINVAL; ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr); if (ni == NULL) return EINVAL; if (mlme.im_op == IEEE80211_MLME_AUTHORIZE) ieee80211_node_authorize(ni); else ieee80211_node_unauthorize(ni); ieee80211_free_node(ni); break; default: return EINVAL; } return 0; } static int ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq) { u_int8_t mac[IEEE80211_ADDR_LEN]; const struct ieee80211_aclator *acl = ic->ic_acl; int error; if (ireq->i_len != sizeof(mac)) return EINVAL; error = copyin(ireq->i_data, mac, ireq->i_len); if (error) return error; if (acl == NULL) { acl = ieee80211_aclator_get("mac"); if (acl == NULL || !acl->iac_attach(ic)) return EINVAL; ic->ic_acl = acl; } if (ireq->i_type == IEEE80211_IOC_ADDMAC) acl->iac_add(ic, mac); else acl->iac_remove(ic, mac); return 0; } static int ieee80211_ioctl_setmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq) { const struct ieee80211_aclator *acl = ic->ic_acl; switch (ireq->i_val) { case IEEE80211_MACCMD_POLICY_OPEN: case IEEE80211_MACCMD_POLICY_ALLOW: case IEEE80211_MACCMD_POLICY_DENY: if (acl == NULL) { acl = ieee80211_aclator_get("mac"); if (acl == NULL || !acl->iac_attach(ic)) return EINVAL; ic->ic_acl = acl; } acl->iac_setpolicy(ic, ireq->i_val); break; case IEEE80211_MACCMD_FLUSH: if (acl != NULL) acl->iac_flush(ic); /* NB: silently ignore when not in use */ break; case IEEE80211_MACCMD_DETACH: if (acl != NULL) { ic->ic_acl = NULL; acl->iac_detach(ic); } break; default: if (acl == NULL) return EINVAL; else return acl->iac_setioctl(ic, ireq); } return 0; } static int ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211req_chanlist list; u_char chanlist[IEEE80211_CHAN_BYTES]; int i, j, error; if (ireq->i_len != sizeof(list)) return EINVAL; error = copyin(ireq->i_data, &list, sizeof(list)); if (error) return error; memset(chanlist, 0, sizeof(chanlist)); /* * Since channel 0 is not available for DS, channel 1 * is assigned to LSB on WaveLAN. */ if (ic->ic_phytype == IEEE80211_T_DS) i = 1; else i = 0; for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { /* * NB: silently discard unavailable channels so users * can specify 1-255 to get all available channels. */ if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) setbit(chanlist, i); } if (ic->ic_ibss_chan == NULL || isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { for (i = 0; i <= IEEE80211_CHAN_MAX; i++) if (isset(chanlist, i)) { ic->ic_ibss_chan = &ic->ic_channels[i]; goto found; } return EINVAL; /* no active channels */ found: ; } memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); return IS_UP_AUTO(ic) ? ENETRESET : 0; } static int ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211_node *ni; struct ieee80211req_sta_txpow txpow; int error; if (ireq->i_len != sizeof(txpow)) return EINVAL; error = copyin(ireq->i_data, &txpow, sizeof(txpow)); if (error != 0) return error; ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr); if (ni == NULL) return EINVAL; /* XXX */ ni->ni_txpower = txpow.it_txpow; ieee80211_free_node(ni); return error; } static int ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211_wme_state *wme = &ic->ic_wme; struct wmeParams *wmep, *chanp; int isbss, ac; if ((ic->ic_caps & IEEE80211_C_WME) == 0) return EINVAL; isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS); ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); if (ac >= WME_NUM_AC) ac = WME_AC_BE; if (isbss) { chanp = &wme->wme_bssChanParams.cap_wmeParams[ac]; wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; } else { chanp = &wme->wme_chanParams.cap_wmeParams[ac]; wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; } switch (ireq->i_type) { case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ if (isbss) { wmep->wmep_logcwmin = ireq->i_val; if ((wme->wme_flags & WME_F_AGGRMODE) == 0) chanp->wmep_logcwmin = ireq->i_val; } else { wmep->wmep_logcwmin = chanp->wmep_logcwmin = ireq->i_val; } break; case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ if (isbss) { wmep->wmep_logcwmax = ireq->i_val; if ((wme->wme_flags & WME_F_AGGRMODE) == 0) chanp->wmep_logcwmax = ireq->i_val; } else { wmep->wmep_logcwmax = chanp->wmep_logcwmax = ireq->i_val; } break; case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ if (isbss) { wmep->wmep_aifsn = ireq->i_val; if ((wme->wme_flags & WME_F_AGGRMODE) == 0) chanp->wmep_aifsn = ireq->i_val; } else { wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val; } break; case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ if (isbss) { wmep->wmep_txopLimit = ireq->i_val; if ((wme->wme_flags & WME_F_AGGRMODE) == 0) chanp->wmep_txopLimit = ireq->i_val; } else { wmep->wmep_txopLimit = chanp->wmep_txopLimit = ireq->i_val; } break; case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ wmep->wmep_acm = ireq->i_val; if ((wme->wme_flags & WME_F_AGGRMODE) == 0) chanp->wmep_acm = ireq->i_val; break; case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ wmep->wmep_noackPolicy = chanp->wmep_noackPolicy = (ireq->i_val) == 0; break; } ieee80211_wme_updateparams(ic); return 0; } static int cipher2cap(int cipher) { switch (cipher) { case IEEE80211_CIPHER_WEP: return IEEE80211_C_WEP; case IEEE80211_CIPHER_AES_OCB: return IEEE80211_C_AES; case IEEE80211_CIPHER_AES_CCM: return IEEE80211_C_AES_CCM; case IEEE80211_CIPHER_CKIP: return IEEE80211_C_CKIP; case IEEE80211_CIPHER_TKIP: return IEEE80211_C_TKIP; } return 0; } static int ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) { static const u_int8_t zerobssid[IEEE80211_ADDR_LEN]; struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; int error; const struct ieee80211_authenticator *auth; u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; char tmpssid[IEEE80211_NWID_LEN]; u_int8_t tmpbssid[IEEE80211_ADDR_LEN]; struct ieee80211_key *k; int j, caps; u_int kid; error = 0; switch (ireq->i_type) { case IEEE80211_IOC_SSID: if (ireq->i_val != 0 || ireq->i_len > IEEE80211_NWID_LEN) return EINVAL; error = copyin(ireq->i_data, tmpssid, ireq->i_len); if (error) break; memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); ic->ic_des_esslen = ireq->i_len; memcpy(ic->ic_des_essid, tmpssid, ireq->i_len); error = ENETRESET; break; case IEEE80211_IOC_WEP: switch (ireq->i_val) { case IEEE80211_WEP_OFF: ic->ic_flags &= ~IEEE80211_F_PRIVACY; ic->ic_flags &= ~IEEE80211_F_DROPUNENC; break; case IEEE80211_WEP_ON: ic->ic_flags |= IEEE80211_F_PRIVACY; ic->ic_flags |= IEEE80211_F_DROPUNENC; break; case IEEE80211_WEP_MIXED: ic->ic_flags |= IEEE80211_F_PRIVACY; ic->ic_flags &= ~IEEE80211_F_DROPUNENC; break; } error = ENETRESET; break; case IEEE80211_IOC_WEPKEY: kid = (u_int) ireq->i_val; if (kid >= IEEE80211_WEP_NKID) return EINVAL; k = &ic->ic_nw_keys[kid]; if (ireq->i_len == 0) { /* zero-len =>'s delete any existing key */ (void) ieee80211_crypto_delkey(ic, k); break; } if (ireq->i_len > sizeof(tmpkey)) return EINVAL; memset(tmpkey, 0, sizeof(tmpkey)); error = copyin(ireq->i_data, tmpkey, ireq->i_len); if (error) break; ieee80211_key_update_begin(ic); k->wk_keyix = kid; /* NB: force fixed key id */ if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP, IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) { k->wk_keylen = ireq->i_len; memcpy(k->wk_key, tmpkey, sizeof(tmpkey)); if (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr)) error = EINVAL; } else error = EINVAL; ieee80211_key_update_end(ic); if (!error) /* NB: for compatibility */ error = ENETRESET; break; case IEEE80211_IOC_WEPTXKEY: kid = (u_int) ireq->i_val; if (kid >= IEEE80211_WEP_NKID && (u_int16_t) kid != IEEE80211_KEYIX_NONE) return EINVAL; ic->ic_def_txkey = kid; error = ENETRESET; /* push to hardware */ break; case IEEE80211_IOC_AUTHMODE: switch (ireq->i_val) { case IEEE80211_AUTH_WPA: case IEEE80211_AUTH_8021X: /* 802.1x */ case IEEE80211_AUTH_OPEN: /* open */ case IEEE80211_AUTH_SHARED: /* shared-key */ case IEEE80211_AUTH_AUTO: /* auto */ auth = ieee80211_authenticator_get(ireq->i_val); if (auth == NULL) return EINVAL; break; default: return EINVAL; } switch (ireq->i_val) { case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */ ic->ic_flags |= IEEE80211_F_PRIVACY; ireq->i_val = IEEE80211_AUTH_8021X; break; case IEEE80211_AUTH_OPEN: /* open */ ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY); break; case IEEE80211_AUTH_SHARED: /* shared-key */ case IEEE80211_AUTH_8021X: /* 802.1x */ ic->ic_flags &= ~IEEE80211_F_WPA; /* both require a key so mark the PRIVACY capability */ ic->ic_flags |= IEEE80211_F_PRIVACY; break; case IEEE80211_AUTH_AUTO: /* auto */ ic->ic_flags &= ~IEEE80211_F_WPA; /* XXX PRIVACY handling? */ /* XXX what's the right way to do this? */ break; } /* NB: authenticator attach/detach happens on state change */ ic->ic_bss->ni_authmode = ireq->i_val; /* XXX mixed/mode/usage? */ ic->ic_auth = auth; error = ENETRESET; break; case IEEE80211_IOC_CHANNEL: /* XXX 0xffff overflows 16-bit signed */ if (ireq->i_val == 0 || ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) ic->ic_des_chan = IEEE80211_CHAN_ANYC; else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX || isclr(ic->ic_chan_active, ireq->i_val)) { return EINVAL; } else ic->ic_ibss_chan = ic->ic_des_chan = &ic->ic_channels[ireq->i_val]; switch (ic->ic_state) { case IEEE80211_S_INIT: case IEEE80211_S_SCAN: error = ENETRESET; break; default: /* * If the desired channel has changed (to something * other than any) and we're not already scanning, * then kick the state machine. */ if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && ic->ic_bss->ni_chan != ic->ic_des_chan && (ic->ic_flags & IEEE80211_F_SCAN) == 0) error = ENETRESET; break; } if (error == ENETRESET && ic->ic_opmode == IEEE80211_M_MONITOR) { if (IS_UP(ic)) { /* * Monitor mode can switch directly. */ if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) ic->ic_curchan = ic->ic_des_chan; error = ic->ic_reset(ic->ic_ifp); } else error = 0; } break; case IEEE80211_IOC_POWERSAVE: switch (ireq->i_val) { case IEEE80211_POWERSAVE_OFF: if (ic->ic_flags & IEEE80211_F_PMGTON) { ic->ic_flags &= ~IEEE80211_F_PMGTON; error = ENETRESET; } break; case IEEE80211_POWERSAVE_ON: if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) error = EINVAL; else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { ic->ic_flags |= IEEE80211_F_PMGTON; error = ENETRESET; } break; default: error = EINVAL; break; } break; case IEEE80211_IOC_POWERSAVESLEEP: if (ireq->i_val < 0) return EINVAL; ic->ic_lintval = ireq->i_val; error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case IEEE80211_IOC_RTSTHRESHOLD: if (!(IEEE80211_RTS_MIN <= ireq->i_val && ireq->i_val <= IEEE80211_RTS_MAX)) return EINVAL; ic->ic_rtsthreshold = ireq->i_val; error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case IEEE80211_IOC_PROTMODE: if (ireq->i_val > IEEE80211_PROT_RTSCTS) return EINVAL; ic->ic_protmode = ireq->i_val; /* NB: if not operating in 11g this can wait */ if (ic->ic_curmode == IEEE80211_MODE_11G) error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case IEEE80211_IOC_TXPOWER: if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) return EINVAL; if (!(IEEE80211_TXPOWER_MIN < ireq->i_val && ireq->i_val < IEEE80211_TXPOWER_MAX)) return EINVAL; ic->ic_txpowlimit = ireq->i_val; error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case IEEE80211_IOC_ROAMING: if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val && ireq->i_val <= IEEE80211_ROAMING_MANUAL)) return EINVAL; ic->ic_roaming = ireq->i_val; /* XXXX reset? */ break; case IEEE80211_IOC_PRIVACY: if (ireq->i_val) { /* XXX check for key state? */ ic->ic_flags |= IEEE80211_F_PRIVACY; } else ic->ic_flags &= ~IEEE80211_F_PRIVACY; break; case IEEE80211_IOC_DROPUNENCRYPTED: if (ireq->i_val) ic->ic_flags |= IEEE80211_F_DROPUNENC; else ic->ic_flags &= ~IEEE80211_F_DROPUNENC; break; case IEEE80211_IOC_WPAKEY: error = ieee80211_ioctl_setkey(ic, ireq); break; case IEEE80211_IOC_DELKEY: error = ieee80211_ioctl_delkey(ic, ireq); break; case IEEE80211_IOC_MLME: error = ieee80211_ioctl_setmlme(ic, ireq); break; case IEEE80211_IOC_OPTIE: error = ieee80211_ioctl_setoptie(ic, ireq); break; case IEEE80211_IOC_COUNTERMEASURES: if (ireq->i_val) { if ((ic->ic_flags & IEEE80211_F_WPA) == 0) return EINVAL; ic->ic_flags |= IEEE80211_F_COUNTERM; } else ic->ic_flags &= ~IEEE80211_F_COUNTERM; break; case IEEE80211_IOC_WPA: if (ireq->i_val > 3) return EINVAL; /* XXX verify ciphers available */ ic->ic_flags &= ~IEEE80211_F_WPA; switch (ireq->i_val) { case 1: ic->ic_flags |= IEEE80211_F_WPA1; break; case 2: ic->ic_flags |= IEEE80211_F_WPA2; break; case 3: ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2; break; } error = ENETRESET; /* XXX? */ break; case IEEE80211_IOC_WME: if (ireq->i_val) { if ((ic->ic_caps & IEEE80211_C_WME) == 0) return EINVAL; ic->ic_flags |= IEEE80211_F_WME; } else ic->ic_flags &= ~IEEE80211_F_WME; error = ENETRESET; /* XXX maybe not for station? */ break; case IEEE80211_IOC_HIDESSID: if (ireq->i_val) ic->ic_flags |= IEEE80211_F_HIDESSID; else ic->ic_flags &= ~IEEE80211_F_HIDESSID; error = ENETRESET; break; case IEEE80211_IOC_APBRIDGE: if (ireq->i_val == 0) ic->ic_flags |= IEEE80211_F_NOBRIDGE; else ic->ic_flags &= ~IEEE80211_F_NOBRIDGE; break; case IEEE80211_IOC_MCASTCIPHER: if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 && !ieee80211_crypto_available(ireq->i_val)) return EINVAL; rsn->rsn_mcastcipher = ireq->i_val; error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; break; case IEEE80211_IOC_MCASTKEYLEN: if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE)) return EINVAL; /* XXX no way to verify driver capability */ rsn->rsn_mcastkeylen = ireq->i_val; error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; break; case IEEE80211_IOC_UCASTCIPHERS: /* * Convert user-specified cipher set to the set * we can support (via hardware or software). * NB: this logic intentionally ignores unknown and * unsupported ciphers so folks can specify 0xff or * similar and get all available ciphers. */ caps = 0; for (j = 1; j < 32; j++) /* NB: skip WEP */ if ((ireq->i_val & (1<ic_caps & cipher2cap(j)) || ieee80211_crypto_available(j))) caps |= 1<rsn_ucastcipherset = caps; error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; break; case IEEE80211_IOC_UCASTCIPHER: if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0) return EINVAL; rsn->rsn_ucastcipher = ireq->i_val; break; case IEEE80211_IOC_UCASTKEYLEN: if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE)) return EINVAL; /* XXX no way to verify driver capability */ rsn->rsn_ucastkeylen = ireq->i_val; break; case IEEE80211_IOC_DRIVER_CAPS: /* NB: for testing */ ic->ic_caps = (((u_int16_t) ireq->i_val) << 16) | ((u_int16_t) ireq->i_len); break; case IEEE80211_IOC_KEYMGTALGS: /* XXX check */ rsn->rsn_keymgmtset = ireq->i_val; error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; break; case IEEE80211_IOC_RSNCAPS: /* XXX check */ rsn->rsn_caps = ireq->i_val; error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; break; case IEEE80211_IOC_BSSID: if (ireq->i_len != sizeof(tmpbssid)) return EINVAL; error = copyin(ireq->i_data, tmpbssid, ireq->i_len); if (error) break; IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid); if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid)) ic->ic_flags &= ~IEEE80211_F_DESBSSID; else ic->ic_flags |= IEEE80211_F_DESBSSID; error = ENETRESET; break; case IEEE80211_IOC_CHANLIST: error = ieee80211_ioctl_setchanlist(ic, ireq); break; case IEEE80211_IOC_SCAN_REQ: if (ic->ic_opmode == IEEE80211_M_HOSTAP) /* XXX ignore */ break; error = ieee80211_setupscan(ic, ic->ic_chan_avail); if (error == 0) /* XXX background scan */ error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); break; case IEEE80211_IOC_ADDMAC: case IEEE80211_IOC_DELMAC: error = ieee80211_ioctl_macmac(ic, ireq); break; case IEEE80211_IOC_MACCMD: error = ieee80211_ioctl_setmaccmd(ic, ireq); break; case IEEE80211_IOC_STA_TXPOW: error = ieee80211_ioctl_setstatxpow(ic, ireq); break; case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ error = ieee80211_ioctl_setwmeparam(ic, ireq); break; case IEEE80211_IOC_DTIM_PERIOD: if (ic->ic_opmode != IEEE80211_M_HOSTAP && ic->ic_opmode != IEEE80211_M_IBSS) return EINVAL; if (IEEE80211_DTIM_MIN <= ireq->i_val && ireq->i_val <= IEEE80211_DTIM_MAX) { ic->ic_dtim_period = ireq->i_val; error = ENETRESET; /* requires restart */ } else error = EINVAL; break; case IEEE80211_IOC_BEACON_INTERVAL: if (ic->ic_opmode != IEEE80211_M_HOSTAP && ic->ic_opmode != IEEE80211_M_IBSS) return EINVAL; if (IEEE80211_BINTVAL_MIN <= ireq->i_val && ireq->i_val <= IEEE80211_BINTVAL_MAX) { ic->ic_bintval = ireq->i_val; error = ENETRESET; /* requires restart */ } else error = EINVAL; break; case IEEE80211_IOC_PUREG: if (ireq->i_val) ic->ic_flags |= IEEE80211_F_PUREG; else ic->ic_flags &= ~IEEE80211_F_PUREG; /* NB: reset only if we're operating on an 11g channel */ if (ic->ic_curmode == IEEE80211_MODE_11G) error = ENETRESET; break; case IEEE80211_IOC_MCAST_RATE: ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL; break; case IEEE80211_IOC_FRAGTHRESHOLD: if ((ic->ic_caps & IEEE80211_C_TXFRAG) == 0 && ireq->i_val != IEEE80211_FRAG_MAX) return EINVAL; if (!(IEEE80211_FRAG_MIN <= ireq->i_val && ireq->i_val <= IEEE80211_FRAG_MAX)) return EINVAL; ic->ic_fragthreshold = ireq->i_val; error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_BURST: + if (ireq->i_val) { + if ((ic->ic_caps & IEEE80211_C_BURST) == 0) + return EINVAL; + ic->ic_flags |= IEEE80211_F_BURST; + } else + ic->ic_flags &= ~IEEE80211_F_BURST; + error = ENETRESET; /* XXX maybe not for station? */ break; default: error = EINVAL; break; } if (error == ENETRESET && !IS_UP_AUTO(ic)) error = 0; return error; } int ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data) { struct ifnet *ifp = ic->ic_ifp; int error = 0; struct ifreq *ifr; struct ifaddr *ifa; /* XXX */ switch (cmd) { case SIOCSIFMEDIA: case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, (struct ifreq *) data, &ic->ic_media, cmd); break; case SIOCG80211: error = ieee80211_ioctl_get80211(ic, cmd, (struct ieee80211req *) data); break; case SIOCS80211: error = suser(curthread); if (error == 0) error = ieee80211_ioctl_set80211(ic, cmd, (struct ieee80211req *) data); break; case SIOCGIFGENERIC: error = ieee80211_cfgget(ic, cmd, data); break; case SIOCSIFGENERIC: error = suser(curthread); if (error) break; error = ieee80211_cfgset(ic, cmd, data); break; case SIOCG80211STATS: ifr = (struct ifreq *)data; copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats)); break; case SIOCSIFMTU: ifr = (struct ifreq *)data; if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu && ifr->ifr_mtu <= IEEE80211_MTU_MAX)) error = EINVAL; else ifp->if_mtu = ifr->ifr_mtu; break; case SIOCSIFADDR: /* * XXX Handle this directly so we can supress if_init calls. * XXX This should be done in ether_ioctl but for the moment * XXX there are too many other parts of the system that * XXX set IFF_UP and so supress if_init being called when * XXX it should be. */ ifa = (struct ifaddr *) data; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: if ((ifp->if_flags & IFF_UP) == 0) { ifp->if_flags |= IFF_UP; ifp->if_init(ifp->if_softc); } arp_ifinit(ifp, ifa); break; #endif #ifdef IPX /* * XXX - This code is probably wrong, * but has been copied many times. */ case AF_IPX: { struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); if (ipx_nullhost(*ina)) ina->x_host = *(union ipx_host *) IFP2ENADDR(ifp); else bcopy((caddr_t) ina->x_host.c_host, (caddr_t) IFP2ENADDR(ifp), ETHER_ADDR_LEN); /* fall thru... */ } #endif default: if ((ifp->if_flags & IFF_UP) == 0) { ifp->if_flags |= IFF_UP; ifp->if_init(ifp->if_softc); } break; } break; default: error = ether_ioctl(ifp, cmd, data); break; } return error; } Index: stable/6/sys/net80211/ieee80211_ioctl.h =================================================================== --- stable/6/sys/net80211/ieee80211_ioctl.h (revision 153658) +++ stable/6/sys/net80211/ieee80211_ioctl.h (revision 153659) @@ -1,472 +1,473 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2005 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * 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. * * $FreeBSD$ */ #ifndef _NET80211_IEEE80211_IOCTL_H_ #define _NET80211_IEEE80211_IOCTL_H_ /* * IEEE 802.11 ioctls. */ #include #include #include /* * Per/node (station) statistics available when operating as an AP. */ struct ieee80211_nodestats { u_int32_t ns_rx_data; /* rx data frames */ u_int32_t ns_rx_mgmt; /* rx management frames */ u_int32_t ns_rx_ctrl; /* rx control frames */ u_int32_t ns_rx_ucast; /* rx unicast frames */ u_int32_t ns_rx_mcast; /* rx multi/broadcast frames */ u_int64_t ns_rx_bytes; /* rx data count (bytes) */ u_int64_t ns_rx_beacons; /* rx beacon frames */ u_int32_t ns_rx_proberesp; /* rx probe response frames */ u_int32_t ns_rx_dup; /* rx discard 'cuz dup */ u_int32_t ns_rx_noprivacy; /* rx w/ wep but privacy off */ u_int32_t ns_rx_wepfail; /* rx wep processing failed */ u_int32_t ns_rx_demicfail; /* rx demic failed */ u_int32_t ns_rx_decap; /* rx decapsulation failed */ u_int32_t ns_rx_defrag; /* rx defragmentation failed */ u_int32_t ns_rx_disassoc; /* rx disassociation */ u_int32_t ns_rx_deauth; /* rx deauthentication */ u_int32_t ns_rx_decryptcrc; /* rx decrypt failed on crc */ u_int32_t ns_rx_unauth; /* rx on unauthorized port */ u_int32_t ns_rx_unencrypted; /* rx unecrypted w/ privacy */ u_int32_t ns_tx_data; /* tx data frames */ u_int32_t ns_tx_mgmt; /* tx management frames */ u_int32_t ns_tx_ucast; /* tx unicast frames */ u_int32_t ns_tx_mcast; /* tx multi/broadcast frames */ u_int64_t ns_tx_bytes; /* tx data count (bytes) */ u_int32_t ns_tx_probereq; /* tx probe request frames */ u_int32_t ns_tx_novlantag; /* tx discard 'cuz no tag */ u_int32_t ns_tx_vlanmismatch; /* tx discard 'cuz bad tag */ u_int32_t ns_ps_discard; /* ps discard 'cuz of age */ /* MIB-related state */ u_int32_t ns_tx_assoc; /* [re]associations */ u_int32_t ns_tx_assoc_fail; /* [re]association failures */ u_int32_t ns_tx_auth; /* [re]authentications */ u_int32_t ns_tx_auth_fail; /* [re]authentication failures*/ u_int32_t ns_tx_deauth; /* deauthentications */ u_int32_t ns_tx_deauth_code; /* last deauth reason */ u_int32_t ns_tx_disassoc; /* disassociations */ u_int32_t ns_tx_disassoc_code; /* last disassociation reason */ }; /* * Summary statistics. */ struct ieee80211_stats { u_int32_t is_rx_badversion; /* rx frame with bad version */ u_int32_t is_rx_tooshort; /* rx frame too short */ u_int32_t is_rx_wrongbss; /* rx from wrong bssid */ u_int32_t is_rx_dup; /* rx discard 'cuz dup */ u_int32_t is_rx_wrongdir; /* rx w/ wrong direction */ u_int32_t is_rx_mcastecho; /* rx discard 'cuz mcast echo */ u_int32_t is_rx_notassoc; /* rx discard 'cuz sta !assoc */ u_int32_t is_rx_noprivacy; /* rx w/ wep but privacy off */ u_int32_t is_rx_unencrypted; /* rx w/o wep and privacy on */ u_int32_t is_rx_wepfail; /* rx wep processing failed */ u_int32_t is_rx_decap; /* rx decapsulation failed */ u_int32_t is_rx_mgtdiscard; /* rx discard mgt frames */ u_int32_t is_rx_ctl; /* rx discard ctrl frames */ u_int32_t is_rx_beacon; /* rx beacon frames */ u_int32_t is_rx_rstoobig; /* rx rate set truncated */ u_int32_t is_rx_elem_missing; /* rx required element missing*/ u_int32_t is_rx_elem_toobig; /* rx element too big */ u_int32_t is_rx_elem_toosmall; /* rx element too small */ u_int32_t is_rx_elem_unknown; /* rx element unknown */ u_int32_t is_rx_badchan; /* rx frame w/ invalid chan */ u_int32_t is_rx_chanmismatch; /* rx frame chan mismatch */ u_int32_t is_rx_nodealloc; /* rx frame dropped */ u_int32_t is_rx_ssidmismatch; /* rx frame ssid mismatch */ u_int32_t is_rx_auth_unsupported; /* rx w/ unsupported auth alg */ u_int32_t is_rx_auth_fail; /* rx sta auth failure */ u_int32_t is_rx_auth_countermeasures;/* rx auth discard 'cuz CM */ u_int32_t is_rx_assoc_bss; /* rx assoc from wrong bssid */ u_int32_t is_rx_assoc_notauth; /* rx assoc w/o auth */ u_int32_t is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */ u_int32_t is_rx_assoc_norate; /* rx assoc w/ no rate match */ u_int32_t is_rx_assoc_badwpaie; /* rx assoc w/ bad WPA IE */ u_int32_t is_rx_deauth; /* rx deauthentication */ u_int32_t is_rx_disassoc; /* rx disassociation */ u_int32_t is_rx_badsubtype; /* rx frame w/ unknown subtype*/ u_int32_t is_rx_nobuf; /* rx failed for lack of buf */ u_int32_t is_rx_decryptcrc; /* rx decrypt failed on crc */ u_int32_t is_rx_ahdemo_mgt; /* rx discard ahdemo mgt frame*/ u_int32_t is_rx_bad_auth; /* rx bad auth request */ u_int32_t is_rx_unauth; /* rx on unauthorized port */ u_int32_t is_rx_badkeyid; /* rx w/ incorrect keyid */ u_int32_t is_rx_ccmpreplay; /* rx seq# violation (CCMP) */ u_int32_t is_rx_ccmpformat; /* rx format bad (CCMP) */ u_int32_t is_rx_ccmpmic; /* rx MIC check failed (CCMP) */ u_int32_t is_rx_tkipreplay; /* rx seq# violation (TKIP) */ u_int32_t is_rx_tkipformat; /* rx format bad (TKIP) */ u_int32_t is_rx_tkipmic; /* rx MIC check failed (TKIP) */ u_int32_t is_rx_tkipicv; /* rx ICV check failed (TKIP) */ u_int32_t is_rx_badcipher; /* rx failed 'cuz key type */ u_int32_t is_rx_nocipherctx; /* rx failed 'cuz key !setup */ u_int32_t is_rx_acl; /* rx discard 'cuz acl policy */ u_int32_t is_tx_nobuf; /* tx failed for lack of buf */ u_int32_t is_tx_nonode; /* tx failed for no node */ u_int32_t is_tx_unknownmgt; /* tx of unknown mgt frame */ u_int32_t is_tx_badcipher; /* tx failed 'cuz key type */ u_int32_t is_tx_nodefkey; /* tx failed 'cuz no defkey */ u_int32_t is_tx_noheadroom; /* tx failed 'cuz no space */ u_int32_t is_tx_fragframes; /* tx frames fragmented */ u_int32_t is_tx_frags; /* tx fragments created */ u_int32_t is_scan_active; /* active scans started */ u_int32_t is_scan_passive; /* passive scans started */ u_int32_t is_node_timeout; /* nodes timed out inactivity */ u_int32_t is_crypto_nomem; /* no memory for crypto ctx */ u_int32_t is_crypto_tkip; /* tkip crypto done in s/w */ u_int32_t is_crypto_tkipenmic; /* tkip en-MIC done in s/w */ u_int32_t is_crypto_tkipdemic; /* tkip de-MIC done in s/w */ u_int32_t is_crypto_tkipcm; /* tkip counter measures */ u_int32_t is_crypto_ccmp; /* ccmp crypto done in s/w */ u_int32_t is_crypto_wep; /* wep crypto done in s/w */ u_int32_t is_crypto_setkey_cipher;/* cipher rejected key */ u_int32_t is_crypto_setkey_nokey; /* no key index for setkey */ u_int32_t is_crypto_delkey; /* driver key delete failed */ u_int32_t is_crypto_badcipher; /* unknown cipher */ u_int32_t is_crypto_nocipher; /* cipher not available */ u_int32_t is_crypto_attachfail; /* cipher attach failed */ u_int32_t is_crypto_swfallback; /* cipher fallback to s/w */ u_int32_t is_crypto_keyfail; /* driver key alloc failed */ u_int32_t is_crypto_enmicfail; /* en-MIC failed */ u_int32_t is_ibss_capmismatch; /* merge failed-cap mismatch */ u_int32_t is_ibss_norate; /* merge failed-rate mismatch */ u_int32_t is_ps_unassoc; /* ps-poll for unassoc. sta */ u_int32_t is_ps_badaid; /* ps-poll w/ incorrect aid */ u_int32_t is_ps_qempty; /* ps-poll w/ nothing to send */ u_int32_t is_ff_badhdr; /* fast frame rx'd w/ bad hdr */ u_int32_t is_ff_tooshort; /* fast frame rx decap error */ u_int32_t is_ff_split; /* fast frame rx split error */ u_int32_t is_ff_decap; /* fast frames decap'd */ u_int32_t is_ff_encap; /* fast frames encap'd for tx */ u_int32_t is_rx_badbintval; /* rx frame w/ bogus bintval */ u_int32_t is_spare[9]; }; /* * Max size of optional information elements. We artificially * constrain this; it's limited only by the max frame size (and * the max parameter size of the wireless extensions). */ #define IEEE80211_MAX_OPT_IE 256 /* * WPA/RSN get/set key request. Specify the key/cipher * type and whether the key is to be used for sending and/or * receiving. The key index should be set only when working * with global keys (use IEEE80211_KEYIX_NONE for ``no index''). * Otherwise a unicast/pairwise key is specified by the bssid * (on a station) or mac address (on an ap). They key length * must include any MIC key data; otherwise it should be no more than IEEE80211_KEYBUF_SIZE. */ struct ieee80211req_key { u_int8_t ik_type; /* key/cipher type */ u_int8_t ik_pad; u_int16_t ik_keyix; /* key index */ u_int8_t ik_keylen; /* key length in bytes */ u_int8_t ik_flags; /* NB: IEEE80211_KEY_XMIT and IEEE80211_KEY_RECV defined elsewhere */ #define IEEE80211_KEY_DEFAULT 0x80 /* default xmit key */ u_int8_t ik_macaddr[IEEE80211_ADDR_LEN]; u_int64_t ik_keyrsc; /* key receive sequence counter */ u_int64_t ik_keytsc; /* key transmit sequence counter */ u_int8_t ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE]; }; /* * Delete a key either by index or address. Set the index * to IEEE80211_KEYIX_NONE when deleting a unicast key. */ struct ieee80211req_del_key { u_int8_t idk_keyix; /* key index */ u_int8_t idk_macaddr[IEEE80211_ADDR_LEN]; }; /* * MLME state manipulation request. IEEE80211_MLME_ASSOC * only makes sense when operating as a station. The other * requests can be used when operating as a station or an * ap (to effect a station). */ struct ieee80211req_mlme { u_int8_t im_op; /* operation to perform */ #define IEEE80211_MLME_ASSOC 1 /* associate station */ #define IEEE80211_MLME_DISASSOC 2 /* disassociate station */ #define IEEE80211_MLME_DEAUTH 3 /* deauthenticate station */ #define IEEE80211_MLME_AUTHORIZE 4 /* authorize station */ #define IEEE80211_MLME_UNAUTHORIZE 5 /* unauthorize station */ u_int8_t im_ssid_len; /* length of optional ssid */ u_int16_t im_reason; /* 802.11 reason code */ u_int8_t im_macaddr[IEEE80211_ADDR_LEN]; u_int8_t im_ssid[IEEE80211_NWID_LEN]; }; /* * MAC ACL operations. */ enum { IEEE80211_MACCMD_POLICY_OPEN = 0, /* set policy: no ACL's */ IEEE80211_MACCMD_POLICY_ALLOW = 1, /* set policy: allow traffic */ IEEE80211_MACCMD_POLICY_DENY = 2, /* set policy: deny traffic */ IEEE80211_MACCMD_FLUSH = 3, /* flush ACL database */ IEEE80211_MACCMD_DETACH = 4, /* detach ACL policy */ IEEE80211_MACCMD_POLICY = 5, /* get ACL policy */ IEEE80211_MACCMD_LIST = 6, /* get ACL database */ }; struct ieee80211req_maclist { u_int8_t ml_macaddr[IEEE80211_ADDR_LEN]; }; /* * Set the active channel list. Note this list is * intersected with the available channel list in * calculating the set of channels actually used in * scanning. */ struct ieee80211req_chanlist { u_int8_t ic_channels[IEEE80211_CHAN_BYTES]; }; /* * Get the active channel list info. */ struct ieee80211req_chaninfo { u_int ic_nchans; struct ieee80211_channel ic_chans[IEEE80211_CHAN_MAX]; }; /* * Retrieve the WPA/RSN information element for an associated station. */ struct ieee80211req_wpaie { u_int8_t wpa_macaddr[IEEE80211_ADDR_LEN]; u_int8_t wpa_ie[IEEE80211_MAX_OPT_IE]; }; /* * Retrieve per-node statistics. */ struct ieee80211req_sta_stats { union { /* NB: explicitly force 64-bit alignment */ u_int8_t macaddr[IEEE80211_ADDR_LEN]; u_int64_t pad; } is_u; struct ieee80211_nodestats is_stats; }; /* * Station information block; the mac address is used * to retrieve other data like stats, unicast key, etc. */ struct ieee80211req_sta_info { u_int16_t isi_len; /* length (mult of 4) */ u_int16_t isi_freq; /* MHz */ u_int16_t isi_flags; /* channel flags */ u_int16_t isi_state; /* state flags */ u_int8_t isi_authmode; /* authentication algorithm */ u_int8_t isi_rssi; u_int8_t isi_capinfo; /* capabilities */ u_int8_t isi_erp; /* ERP element */ u_int8_t isi_macaddr[IEEE80211_ADDR_LEN]; u_int8_t isi_nrates; /* negotiated rates */ u_int8_t isi_rates[IEEE80211_RATE_MAXSIZE]; u_int8_t isi_txrate; /* index to isi_rates[] */ u_int16_t isi_ie_len; /* IE length */ u_int16_t isi_associd; /* assoc response */ u_int16_t isi_txpower; /* current tx power */ u_int16_t isi_vlan; /* vlan tag */ u_int16_t isi_txseqs[17]; /* seq to be transmitted */ u_int16_t isi_rxseqs[17]; /* seq previous for qos frames*/ u_int16_t isi_inact; /* inactivity timer */ /* XXX frag state? */ /* variable length IE data */ }; /* * Retrieve per-station information; to retrieve all * specify a mac address of ff:ff:ff:ff:ff:ff. */ struct ieee80211req_sta_req { union { /* NB: explicitly force 64-bit alignment */ u_int8_t macaddr[IEEE80211_ADDR_LEN]; u_int64_t pad; } is_u; struct ieee80211req_sta_info info[1]; /* variable length */ }; /* * Get/set per-station tx power cap. */ struct ieee80211req_sta_txpow { u_int8_t it_macaddr[IEEE80211_ADDR_LEN]; u_int8_t it_txpow; }; /* * WME parameters are set and return using i_val and i_len. * i_val holds the value itself. i_len specifies the AC * and, as appropriate, then high bit specifies whether the * operation is to be applied to the BSS or ourself. */ #define IEEE80211_WMEPARAM_SELF 0x0000 /* parameter applies to self */ #define IEEE80211_WMEPARAM_BSS 0x8000 /* parameter applies to BSS */ #define IEEE80211_WMEPARAM_VAL 0x7fff /* parameter value */ #ifdef __FreeBSD__ /* * FreeBSD-style ioctls. */ /* the first member must be matched with struct ifreq */ struct ieee80211req { char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */ u_int16_t i_type; /* req type */ int16_t i_val; /* Index or simple value */ int16_t i_len; /* Index or simple value */ void *i_data; /* Extra data */ }; #define SIOCS80211 _IOW('i', 234, struct ieee80211req) #define SIOCG80211 _IOWR('i', 235, struct ieee80211req) #define IEEE80211_IOC_SSID 1 #define IEEE80211_IOC_NUMSSIDS 2 #define IEEE80211_IOC_WEP 3 #define IEEE80211_WEP_NOSUP -1 #define IEEE80211_WEP_OFF 0 #define IEEE80211_WEP_ON 1 #define IEEE80211_WEP_MIXED 2 #define IEEE80211_IOC_WEPKEY 4 #define IEEE80211_IOC_NUMWEPKEYS 5 #define IEEE80211_IOC_WEPTXKEY 6 #define IEEE80211_IOC_AUTHMODE 7 #define IEEE80211_IOC_STATIONNAME 8 #define IEEE80211_IOC_CHANNEL 9 #define IEEE80211_IOC_POWERSAVE 10 #define IEEE80211_POWERSAVE_NOSUP -1 #define IEEE80211_POWERSAVE_OFF 0 #define IEEE80211_POWERSAVE_CAM 1 #define IEEE80211_POWERSAVE_PSP 2 #define IEEE80211_POWERSAVE_PSP_CAM 3 #define IEEE80211_POWERSAVE_ON IEEE80211_POWERSAVE_CAM #define IEEE80211_IOC_POWERSAVESLEEP 11 #define IEEE80211_IOC_RTSTHRESHOLD 12 #define IEEE80211_IOC_PROTMODE 13 #define IEEE80211_PROTMODE_OFF 0 #define IEEE80211_PROTMODE_CTS 1 #define IEEE80211_PROTMODE_RTSCTS 2 #define IEEE80211_IOC_TXPOWER 14 /* global tx power limit */ #define IEEE80211_IOC_BSSID 15 #define IEEE80211_IOC_ROAMING 16 /* roaming mode */ #define IEEE80211_IOC_PRIVACY 17 /* privacy invoked */ #define IEEE80211_IOC_DROPUNENCRYPTED 18 /* discard unencrypted frames */ #define IEEE80211_IOC_WPAKEY 19 #define IEEE80211_IOC_DELKEY 20 #define IEEE80211_IOC_MLME 21 #define IEEE80211_IOC_OPTIE 22 /* optional info. element */ #define IEEE80211_IOC_SCAN_REQ 23 #define IEEE80211_IOC_SCAN_RESULTS 24 #define IEEE80211_IOC_COUNTERMEASURES 25 /* WPA/TKIP countermeasures */ #define IEEE80211_IOC_WPA 26 /* WPA mode (0,1,2) */ #define IEEE80211_IOC_CHANLIST 27 /* channel list */ #define IEEE80211_IOC_WME 28 /* WME mode (on, off) */ #define IEEE80211_IOC_HIDESSID 29 /* hide SSID mode (on, off) */ #define IEEE80211_IOC_APBRIDGE 30 /* AP inter-sta bridging */ #define IEEE80211_IOC_MCASTCIPHER 31 /* multicast/default cipher */ #define IEEE80211_IOC_MCASTKEYLEN 32 /* multicast key length */ #define IEEE80211_IOC_UCASTCIPHERS 33 /* unicast cipher suites */ #define IEEE80211_IOC_UCASTCIPHER 34 /* unicast cipher */ #define IEEE80211_IOC_UCASTKEYLEN 35 /* unicast key length */ #define IEEE80211_IOC_DRIVER_CAPS 36 /* driver capabilities */ #define IEEE80211_IOC_KEYMGTALGS 37 /* key management algorithms */ #define IEEE80211_IOC_RSNCAPS 38 /* RSN capabilities */ #define IEEE80211_IOC_WPAIE 39 /* WPA information element */ #define IEEE80211_IOC_STA_STATS 40 /* per-station statistics */ #define IEEE80211_IOC_MACCMD 41 /* MAC ACL operation */ #define IEEE80211_IOC_CHANINFO 42 /* channel info list */ #define IEEE80211_IOC_TXPOWMAX 43 /* max tx power for channel */ #define IEEE80211_IOC_STA_TXPOW 44 /* per-station tx power limit */ #define IEEE80211_IOC_STA_INFO 45 /* station/neighbor info */ #define IEEE80211_IOC_WME_CWMIN 46 /* WME: ECWmin */ #define IEEE80211_IOC_WME_CWMAX 47 /* WME: ECWmax */ #define IEEE80211_IOC_WME_AIFS 48 /* WME: AIFSN */ #define IEEE80211_IOC_WME_TXOPLIMIT 49 /* WME: txops limit */ #define IEEE80211_IOC_WME_ACM 50 /* WME: ACM (bss only) */ #define IEEE80211_IOC_WME_ACKPOLICY 51 /* WME: ACK policy (!bss only)*/ #define IEEE80211_IOC_DTIM_PERIOD 52 /* DTIM period (beacons) */ #define IEEE80211_IOC_BEACON_INTERVAL 53 /* beacon interval (ms) */ #define IEEE80211_IOC_ADDMAC 54 /* add sta to MAC ACL table */ #define IEEE80211_IOC_DELMAC 55 /* del sta from MAC ACL table */ #define IEEE80211_IOC_PUREG 56 /* pure 11g (no 11b stations) */ #define IEEE80211_IOC_MCAST_RATE 72 /* tx rate for mcast frames */ #define IEEE80211_IOC_FRAGTHRESHOLD 73 /* tx fragmentation threshold */ +#define IEEE80211_IOC_BURST 75 /* packet bursting */ /* * Scan result data returned for IEEE80211_IOC_SCAN_RESULTS. */ struct ieee80211req_scan_result { u_int16_t isr_len; /* length (mult of 4) */ u_int16_t isr_freq; /* MHz */ u_int16_t isr_flags; /* channel flags */ u_int8_t isr_noise; u_int8_t isr_rssi; u_int8_t isr_intval; /* beacon interval */ u_int8_t isr_capinfo; /* capabilities */ u_int8_t isr_erp; /* ERP element */ u_int8_t isr_bssid[IEEE80211_ADDR_LEN]; u_int8_t isr_nrates; u_int8_t isr_rates[IEEE80211_RATE_MAXSIZE]; u_int8_t isr_ssid_len; /* SSID length */ u_int8_t isr_ie_len; /* IE length */ u_int8_t isr_pad[5]; /* variable length SSID followed by IE data */ }; #define SIOCG80211STATS _IOWR('i', 236, struct ifreq) #endif /* __FreeBSD__ */ #endif /* _NET80211_IEEE80211_IOCTL_H_ */ Index: stable/6/sys/net80211/ieee80211_proto.c =================================================================== --- stable/6/sys/net80211/ieee80211_proto.c (revision 153658) +++ stable/6/sys/net80211/ieee80211_proto.c (revision 153659) @@ -1,1085 +1,1085 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2005 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * 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 protocol support. */ #include "opt_inet.h" #include #include #include #include #include #include #include /* XXX for ether_sprintf */ #include /* XXX tunables */ #define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */ #define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */ #define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) const char *ieee80211_mgt_subtype_name[] = { "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", "probe_req", "probe_resp", "reserved#6", "reserved#7", "beacon", "atim", "disassoc", "auth", "deauth", "reserved#13", "reserved#14", "reserved#15" }; const char *ieee80211_ctl_subtype_name[] = { "reserved#0", "reserved#1", "reserved#2", "reserved#3", "reserved#3", "reserved#5", "reserved#6", "reserved#7", "reserved#8", "reserved#9", "ps_poll", "rts", "cts", "ack", "cf_end", "cf_end_ack" }; const char *ieee80211_state_name[IEEE80211_S_MAX] = { "INIT", /* IEEE80211_S_INIT */ "SCAN", /* IEEE80211_S_SCAN */ "AUTH", /* IEEE80211_S_AUTH */ "ASSOC", /* IEEE80211_S_ASSOC */ "RUN" /* IEEE80211_S_RUN */ }; const char *ieee80211_wme_acnames[] = { "WME_AC_BE", "WME_AC_BK", "WME_AC_VI", "WME_AC_VO", "WME_UPSD", }; static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); void ieee80211_proto_attach(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; /* XXX room for crypto */ ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4); ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT; ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT; ic->ic_protmode = IEEE80211_PROT_CTSONLY; ic->ic_roaming = IEEE80211_ROAMING_AUTO; ic->ic_wme.wme_hipri_switch_hysteresis = AGGRESSIVE_MODE_SWITCH_HYSTERESIS; mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF); /* protocol state change handler */ ic->ic_newstate = ieee80211_newstate; /* initialize management frame handlers */ ic->ic_recv_mgmt = ieee80211_recv_mgmt; ic->ic_send_mgmt = ieee80211_send_mgmt; } void ieee80211_proto_detach(struct ieee80211com *ic) { /* * This should not be needed as we detach when reseting * the state but be conservative here since the * authenticator may do things like spawn kernel threads. */ if (ic->ic_auth->ia_detach) ic->ic_auth->ia_detach(ic); IF_DRAIN(&ic->ic_mgtq); mtx_destroy(&ic->ic_mgtq.ifq_mtx); /* * Detach any ACL'ator. */ if (ic->ic_acl != NULL) ic->ic_acl->iac_detach(ic); } /* * Simple-minded authenticator module support. */ #define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) /* XXX well-known names */ static const char *auth_modnames[IEEE80211_AUTH_MAX] = { "wlan_internal", /* IEEE80211_AUTH_NONE */ "wlan_internal", /* IEEE80211_AUTH_OPEN */ "wlan_internal", /* IEEE80211_AUTH_SHARED */ "wlan_xauth", /* IEEE80211_AUTH_8021X */ "wlan_internal", /* IEEE80211_AUTH_AUTO */ "wlan_xauth", /* IEEE80211_AUTH_WPA */ }; static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; static const struct ieee80211_authenticator auth_internal = { .ia_name = "wlan_internal", .ia_attach = NULL, .ia_detach = NULL, .ia_node_join = NULL, .ia_node_leave = NULL, }; /* * Setup internal authenticators once; they are never unregistered. */ static void ieee80211_auth_setup(void) { ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); } SYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); const struct ieee80211_authenticator * ieee80211_authenticator_get(int auth) { if (auth >= IEEE80211_AUTH_MAX) return NULL; if (authenticators[auth] == NULL) ieee80211_load_module(auth_modnames[auth]); return authenticators[auth]; } void ieee80211_authenticator_register(int type, const struct ieee80211_authenticator *auth) { if (type >= IEEE80211_AUTH_MAX) return; authenticators[type] = auth; } void ieee80211_authenticator_unregister(int type) { if (type >= IEEE80211_AUTH_MAX) return; authenticators[type] = NULL; } /* * Very simple-minded ACL module support. */ /* XXX just one for now */ static const struct ieee80211_aclator *acl = NULL; void ieee80211_aclator_register(const struct ieee80211_aclator *iac) { printf("wlan: %s acl policy registered\n", iac->iac_name); acl = iac; } void ieee80211_aclator_unregister(const struct ieee80211_aclator *iac) { if (acl == iac) acl = NULL; printf("wlan: %s acl policy unregistered\n", iac->iac_name); } const struct ieee80211_aclator * ieee80211_aclator_get(const char *name) { if (acl == NULL) ieee80211_load_module("wlan_acl"); return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; } void ieee80211_print_essid(const u_int8_t *essid, int len) { const u_int8_t *p; int i; if (len > IEEE80211_NWID_LEN) len = IEEE80211_NWID_LEN; /* determine printable or not */ for (i = 0, p = essid; i < len; i++, p++) { if (*p < ' ' || *p > 0x7e) break; } if (i == len) { printf("\""); for (i = 0, p = essid; i < len; i++, p++) printf("%c", *p); printf("\""); } else { printf("0x"); for (i = 0, p = essid; i < len; i++, p++) printf("%02x", *p); } } void ieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi) { const struct ieee80211_frame *wh; int i; wh = (const struct ieee80211_frame *)buf; switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { case IEEE80211_FC1_DIR_NODS: printf("NODS %s", ether_sprintf(wh->i_addr2)); printf("->%s", ether_sprintf(wh->i_addr1)); printf("(%s)", ether_sprintf(wh->i_addr3)); break; case IEEE80211_FC1_DIR_TODS: printf("TODS %s", ether_sprintf(wh->i_addr2)); printf("->%s", ether_sprintf(wh->i_addr3)); printf("(%s)", ether_sprintf(wh->i_addr1)); break; case IEEE80211_FC1_DIR_FROMDS: printf("FRDS %s", ether_sprintf(wh->i_addr3)); printf("->%s", ether_sprintf(wh->i_addr1)); printf("(%s)", ether_sprintf(wh->i_addr2)); break; case IEEE80211_FC1_DIR_DSTODS: printf("DSDS %s", ether_sprintf((const u_int8_t *)&wh[1])); printf("->%s", ether_sprintf(wh->i_addr3)); printf("(%s", ether_sprintf(wh->i_addr2)); printf("->%s)", ether_sprintf(wh->i_addr1)); break; } switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { case IEEE80211_FC0_TYPE_DATA: printf(" data"); break; case IEEE80211_FC0_TYPE_MGT: printf(" %s", ieee80211_mgt_subtype_name[ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) >> IEEE80211_FC0_SUBTYPE_SHIFT]); break; default: printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); break; } if (wh->i_fc[1] & IEEE80211_FC1_WEP) { int i; printf(" WEP [IV"); for (i = 0; i < IEEE80211_WEP_IVLEN; i++) printf(" %.02x", buf[sizeof(*wh)+i]); printf(" KID %u]", buf[sizeof(*wh)+i] >> 6); } if (rate >= 0) printf(" %dM", rate / 2); if (rssi >= 0) printf(" +%d", rssi); printf("\n"); if (len > 0) { for (i = 0; i < len; i++) { if ((i & 1) == 0) printf(" "); printf("%02x", buf[i]); } printf("\n"); } } int ieee80211_fix_rate(struct ieee80211_node *ni, int flags) { #define RV(v) ((v) & IEEE80211_RATE_VAL) struct ieee80211com *ic = ni->ni_ic; int i, j, ignore, error; int okrate, badrate, fixedrate; struct ieee80211_rateset *srs, *nrs; u_int8_t r; /* * If the fixed rate check was requested but no * fixed has been defined then just remove it. */ if ((flags & IEEE80211_F_DOFRATE) && ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) flags &= ~IEEE80211_F_DOFRATE; error = 0; okrate = badrate = fixedrate = 0; srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; nrs = &ni->ni_rates; for (i = 0; i < nrs->rs_nrates; ) { ignore = 0; if (flags & IEEE80211_F_DOSORT) { /* * Sort rates. */ for (j = i + 1; j < nrs->rs_nrates; j++) { if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { r = nrs->rs_rates[i]; nrs->rs_rates[i] = nrs->rs_rates[j]; nrs->rs_rates[j] = r; } } } r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; badrate = r; if (flags & IEEE80211_F_DOFRATE) { /* * Check any fixed rate is included. */ if (r == RV(srs->rs_rates[ic->ic_fixed_rate])) fixedrate = r; } if (flags & IEEE80211_F_DONEGO) { /* * Check against supported rates. */ for (j = 0; j < srs->rs_nrates; j++) { if (r == RV(srs->rs_rates[j])) { /* * Overwrite with the supported rate * value so any basic rate bit is set. * This insures that response we send * to stations have the necessary basic * rate bit set. */ nrs->rs_rates[i] = srs->rs_rates[j]; break; } } if (j == srs->rs_nrates) { /* * A rate in the node's rate set is not * supported. If this is a basic rate and we * are operating as an AP then this is an error. * Otherwise we just discard/ignore the rate. * Note that this is important for 11b stations * when they want to associate with an 11g AP. */ if (ic->ic_opmode == IEEE80211_M_HOSTAP && (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) error++; ignore++; } } if (flags & IEEE80211_F_DODEL) { /* * Delete unacceptable rates. */ if (ignore) { nrs->rs_nrates--; for (j = i; j < nrs->rs_nrates; j++) nrs->rs_rates[j] = nrs->rs_rates[j + 1]; nrs->rs_rates[j] = 0; continue; } } if (!ignore) okrate = nrs->rs_rates[i]; i++; } if (okrate == 0 || error != 0 || ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0)) return badrate | IEEE80211_RATE_BASIC; else return RV(okrate); #undef RV } /* * Reset 11g-related state. */ void ieee80211_reset_erp(struct ieee80211com *ic) { ic->ic_flags &= ~IEEE80211_F_USEPROT; ic->ic_nonerpsta = 0; ic->ic_longslotsta = 0; /* * Short slot time is enabled only when operating in 11g * and not in an IBSS. We must also honor whether or not * the driver is capable of doing it. */ ieee80211_set_shortslottime(ic, ic->ic_curmode == IEEE80211_MODE_11A || (ic->ic_curmode == IEEE80211_MODE_11G && ic->ic_opmode == IEEE80211_M_HOSTAP && (ic->ic_caps & IEEE80211_C_SHSLOT))); /* * Set short preamble and ERP barker-preamble flags. */ if (ic->ic_curmode == IEEE80211_MODE_11A || (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { ic->ic_flags |= IEEE80211_F_SHPREAMBLE; ic->ic_flags &= ~IEEE80211_F_USEBARKER; } else { ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; ic->ic_flags |= IEEE80211_F_USEBARKER; } } /* * Set the short slot time state and notify the driver. */ void ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) { if (onoff) ic->ic_flags |= IEEE80211_F_SHSLOT; else ic->ic_flags &= ~IEEE80211_F_SHSLOT; /* notify driver */ if (ic->ic_updateslot != NULL) ic->ic_updateslot(ic->ic_ifp); } /* * Check if the specified rate set supports ERP. * NB: the rate set is assumed to be sorted. */ int ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) { #define N(a) (sizeof(a) / sizeof(a[0])) static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; int i, j; if (rs->rs_nrates < N(rates)) return 0; for (i = 0; i < N(rates); i++) { for (j = 0; j < rs->rs_nrates; j++) { int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; if (rates[i] == r) goto next; if (r > rates[i]) return 0; } return 0; next: ; } return 1; #undef N } /* * Mark the basic rates for the 11g rate table based on the * operating mode. For real 11g we mark all the 11b rates * and 6, 12, and 24 OFDM. For 11b compatibility we mark only * 11b rates. There's also a pseudo 11a-mode used to mark only * the basic OFDM rates. */ void ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) { static const struct ieee80211_rateset basic[] = { { 0 }, /* IEEE80211_MODE_AUTO */ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ { 0 }, /* IEEE80211_MODE_FH */ /* IEEE80211_MODE_PUREG (not yet) */ { 7, { 2, 4, 11, 22, 12, 24, 48 } }, }; int i, j; for (i = 0; i < rs->rs_nrates; i++) { rs->rs_rates[i] &= IEEE80211_RATE_VAL; for (j = 0; j < basic[mode].rs_nrates; j++) if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { rs->rs_rates[i] |= IEEE80211_RATE_BASIC; break; } } } /* * WME protocol support. The following parameters come from the spec. */ typedef struct phyParamType { u_int8_t aifsn; u_int8_t logcwmin; u_int8_t logcwmax; u_int16_t txopLimit; u_int8_t acm; } paramType; static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { { 3, 4, 6 }, /* IEEE80211_MODE_AUTO */ { 3, 4, 6 }, /* IEEE80211_MODE_11A */ { 3, 5, 7 }, /* IEEE80211_MODE_11B */ { 3, 4, 6 }, /* IEEE80211_MODE_11G */ { 3, 5, 7 }, /* IEEE80211_MODE_FH */ { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_A */ { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_G */ }; static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { { 7, 4, 10 }, /* IEEE80211_MODE_AUTO */ { 7, 4, 10 }, /* IEEE80211_MODE_11A */ { 7, 5, 10 }, /* IEEE80211_MODE_11B */ { 7, 4, 10 }, /* IEEE80211_MODE_11G */ { 7, 5, 10 }, /* IEEE80211_MODE_FH */ { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ }; static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { { 1, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ { 1, 3, 4, 94 }, /* IEEE80211_MODE_11A */ { 1, 4, 5, 188 }, /* IEEE80211_MODE_11B */ { 1, 3, 4, 94 }, /* IEEE80211_MODE_11G */ { 1, 4, 5, 188 }, /* IEEE80211_MODE_FH */ { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ }; static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { { 1, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ { 1, 2, 3, 47 }, /* IEEE80211_MODE_11A */ { 1, 3, 4, 102 }, /* IEEE80211_MODE_11B */ { 1, 2, 3, 47 }, /* IEEE80211_MODE_11G */ { 1, 3, 4, 102 }, /* IEEE80211_MODE_FH */ { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ }; static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { { 3, 4, 10 }, /* IEEE80211_MODE_AUTO */ { 3, 4, 10 }, /* IEEE80211_MODE_11A */ { 3, 5, 10 }, /* IEEE80211_MODE_11B */ { 3, 4, 10 }, /* IEEE80211_MODE_11G */ { 3, 5, 10 }, /* IEEE80211_MODE_FH */ { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ }; static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { { 2, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ { 2, 3, 4, 94 }, /* IEEE80211_MODE_11A */ { 2, 4, 5, 188 }, /* IEEE80211_MODE_11B */ { 2, 3, 4, 94 }, /* IEEE80211_MODE_11G */ { 2, 4, 5, 188 }, /* IEEE80211_MODE_FH */ { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ }; static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { { 2, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ { 2, 2, 3, 47 }, /* IEEE80211_MODE_11A */ { 2, 3, 4, 102 }, /* IEEE80211_MODE_11B */ { 2, 2, 3, 47 }, /* IEEE80211_MODE_11G */ { 2, 3, 4, 102 }, /* IEEE80211_MODE_FH */ { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ }; void ieee80211_wme_initparams(struct ieee80211com *ic) { struct ieee80211_wme_state *wme = &ic->ic_wme; const paramType *pPhyParam, *pBssPhyParam; struct wmeParams *wmep; int i; if ((ic->ic_caps & IEEE80211_C_WME) == 0) return; for (i = 0; i < WME_NUM_AC; i++) { switch (i) { case WME_AC_BK: pPhyParam = &phyParamForAC_BK[ic->ic_curmode]; pBssPhyParam = &phyParamForAC_BK[ic->ic_curmode]; break; case WME_AC_VI: pPhyParam = &phyParamForAC_VI[ic->ic_curmode]; pBssPhyParam = &bssPhyParamForAC_VI[ic->ic_curmode]; break; case WME_AC_VO: pPhyParam = &phyParamForAC_VO[ic->ic_curmode]; pBssPhyParam = &bssPhyParamForAC_VO[ic->ic_curmode]; break; case WME_AC_BE: default: pPhyParam = &phyParamForAC_BE[ic->ic_curmode]; pBssPhyParam = &bssPhyParamForAC_BE[ic->ic_curmode]; break; } wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; if (ic->ic_opmode == IEEE80211_M_HOSTAP) { wmep->wmep_acm = pPhyParam->acm; wmep->wmep_aifsn = pPhyParam->aifsn; wmep->wmep_logcwmin = pPhyParam->logcwmin; wmep->wmep_logcwmax = pPhyParam->logcwmax; wmep->wmep_txopLimit = pPhyParam->txopLimit; } else { wmep->wmep_acm = pBssPhyParam->acm; wmep->wmep_aifsn = pBssPhyParam->aifsn; wmep->wmep_logcwmin = pBssPhyParam->logcwmin; wmep->wmep_logcwmax = pBssPhyParam->logcwmax; wmep->wmep_txopLimit = pBssPhyParam->txopLimit; } IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " "log2(cwmax) %u txpoLimit %u]\n", __func__ , ieee80211_wme_acnames[i] , wmep->wmep_acm , wmep->wmep_aifsn , wmep->wmep_logcwmin , wmep->wmep_logcwmax , wmep->wmep_txopLimit ); wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; wmep->wmep_acm = pBssPhyParam->acm; wmep->wmep_aifsn = pBssPhyParam->aifsn; wmep->wmep_logcwmin = pBssPhyParam->logcwmin; wmep->wmep_logcwmax = pBssPhyParam->logcwmax; wmep->wmep_txopLimit = pBssPhyParam->txopLimit; IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " "log2(cwmax) %u txpoLimit %u]\n", __func__ , ieee80211_wme_acnames[i] , wmep->wmep_acm , wmep->wmep_aifsn , wmep->wmep_logcwmin , wmep->wmep_logcwmax , wmep->wmep_txopLimit ); } /* NB: check ic_bss to avoid NULL deref on initial attach */ if (ic->ic_bss != NULL) { /* * Calculate agressive mode switching threshold based * on beacon interval. This doesn't need locking since * we're only called before entering the RUN state at * which point we start sending beacon frames. */ wme->wme_hipri_switch_thresh = (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100; ieee80211_wme_updateparams(ic); } } /* * Update WME parameters for ourself and the BSS. */ void ieee80211_wme_updateparams_locked(struct ieee80211com *ic) { static const paramType phyParam[IEEE80211_MODE_MAX] = { { 2, 4, 10, 64 }, /* IEEE80211_MODE_AUTO */ { 2, 4, 10, 64 }, /* IEEE80211_MODE_11A */ { 2, 5, 10, 64 }, /* IEEE80211_MODE_11B */ { 2, 4, 10, 64 }, /* IEEE80211_MODE_11G */ { 2, 5, 10, 64 }, /* IEEE80211_MODE_FH */ { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_A */ { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_G */ }; struct ieee80211_wme_state *wme = &ic->ic_wme; const struct wmeParams *wmep; struct wmeParams *chanp, *bssp; int i; /* set up the channel access parameters for the physical device */ for (i = 0; i < WME_NUM_AC; i++) { chanp = &wme->wme_chanParams.cap_wmeParams[i]; wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; chanp->wmep_aifsn = wmep->wmep_aifsn; chanp->wmep_logcwmin = wmep->wmep_logcwmin; chanp->wmep_logcwmax = wmep->wmep_logcwmax; chanp->wmep_txopLimit = wmep->wmep_txopLimit; chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; chanp->wmep_aifsn = wmep->wmep_aifsn; chanp->wmep_logcwmin = wmep->wmep_logcwmin; chanp->wmep_logcwmax = wmep->wmep_logcwmax; chanp->wmep_txopLimit = wmep->wmep_txopLimit; } /* * This implements agressive mode as found in certain * vendors' AP's. When there is significant high * priority (VI/VO) traffic in the BSS throttle back BE * traffic by using conservative parameters. Otherwise * BE uses agressive params to optimize performance of * legacy/non-QoS traffic. */ if ((ic->ic_opmode == IEEE80211_M_HOSTAP && (wme->wme_flags & WME_F_AGGRMODE) == 0) || (ic->ic_opmode != IEEE80211_M_HOSTAP && (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || (ic->ic_flags & IEEE80211_F_WME) == 0) { chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; chanp->wmep_aifsn = bssp->wmep_aifsn = phyParam[ic->ic_curmode].aifsn; chanp->wmep_logcwmin = bssp->wmep_logcwmin = phyParam[ic->ic_curmode].logcwmin; chanp->wmep_logcwmax = bssp->wmep_logcwmax = phyParam[ic->ic_curmode].logcwmax; chanp->wmep_txopLimit = bssp->wmep_txopLimit = - (ic->ic_caps & IEEE80211_C_BURST) ? + (ic->ic_flags & IEEE80211_F_BURST) ? phyParam[ic->ic_curmode].txopLimit : 0; IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, "%s: %s [acm %u aifsn %u log2(cwmin) %u " "log2(cwmax) %u txpoLimit %u]\n", __func__ , ieee80211_wme_acnames[WME_AC_BE] , chanp->wmep_acm , chanp->wmep_aifsn , chanp->wmep_logcwmin , chanp->wmep_logcwmax , chanp->wmep_txopLimit ); } if (ic->ic_opmode == IEEE80211_M_HOSTAP && ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) == 0) { static const u_int8_t logCwMin[IEEE80211_MODE_MAX] = { 3, /* IEEE80211_MODE_AUTO */ 3, /* IEEE80211_MODE_11A */ 4, /* IEEE80211_MODE_11B */ 3, /* IEEE80211_MODE_11G */ 4, /* IEEE80211_MODE_FH */ 3, /* IEEE80211_MODE_TURBO_A */ 3, /* IEEE80211_MODE_TURBO_G */ }; chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[ic->ic_curmode]; IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, "%s: %s log2(cwmin) %u\n", __func__ , ieee80211_wme_acnames[WME_AC_BE] , chanp->wmep_logcwmin ); } if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ /* * Arrange for a beacon update and bump the parameter * set number so associated stations load the new values. */ wme->wme_bssChanParams.cap_info = (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; ic->ic_flags |= IEEE80211_F_WMEUPDATE; } wme->wme_update(ic); IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, "%s: WME params updated, cap_info 0x%x\n", __func__, ic->ic_opmode == IEEE80211_M_STA ? wme->wme_wmeChanParams.cap_info : wme->wme_bssChanParams.cap_info); } void ieee80211_wme_updateparams(struct ieee80211com *ic) { if (ic->ic_caps & IEEE80211_C_WME) { IEEE80211_BEACON_LOCK(ic); ieee80211_wme_updateparams_locked(ic); IEEE80211_BEACON_UNLOCK(ic); } } static void sta_disassoc(void *arg, struct ieee80211_node *ni) { struct ieee80211com *ic = arg; if (ni->ni_associd != 0) { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, IEEE80211_REASON_ASSOC_LEAVE); ieee80211_node_leave(ic, ni); } } static void sta_deauth(void *arg, struct ieee80211_node *ni) { struct ieee80211com *ic = arg; IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_ASSOC_LEAVE); } static int ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { struct ifnet *ifp = ic->ic_ifp; struct ieee80211_node *ni; enum ieee80211_state ostate; ostate = ic->ic_state; IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate]); ic->ic_state = nstate; /* state transition */ ni = ic->ic_bss; /* NB: no reference held */ switch (nstate) { case IEEE80211_S_INIT: switch (ostate) { case IEEE80211_S_INIT: break; case IEEE80211_S_RUN: switch (ic->ic_opmode) { case IEEE80211_M_STA: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, IEEE80211_REASON_ASSOC_LEAVE); ieee80211_sta_leave(ic, ni); break; case IEEE80211_M_HOSTAP: ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, ic); break; default: break; } goto reset; case IEEE80211_S_ASSOC: switch (ic->ic_opmode) { case IEEE80211_M_STA: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_AUTH_LEAVE); break; case IEEE80211_M_HOSTAP: ieee80211_iterate_nodes(&ic->ic_sta, sta_deauth, ic); break; default: break; } goto reset; case IEEE80211_S_SCAN: ieee80211_cancel_scan(ic); goto reset; case IEEE80211_S_AUTH: reset: ic->ic_mgt_timer = 0; IF_DRAIN(&ic->ic_mgtq); ieee80211_reset_bss(ic); break; } if (ic->ic_auth->ia_detach != NULL) ic->ic_auth->ia_detach(ic); break; case IEEE80211_S_SCAN: switch (ostate) { case IEEE80211_S_INIT: if ((ic->ic_opmode == IEEE80211_M_HOSTAP || ic->ic_opmode == IEEE80211_M_IBSS || ic->ic_opmode == IEEE80211_M_AHDEMO) && ic->ic_des_chan != IEEE80211_CHAN_ANYC) { /* * AP operation and we already have a channel; * bypass the scan and startup immediately. */ ieee80211_create_ibss(ic, ic->ic_des_chan); } else { ieee80211_begin_scan(ic, arg); } break; case IEEE80211_S_SCAN: /* * Scan next. If doing an active scan and the * channel is not marked passive-only then send * a probe request. Otherwise just listen for * beacons on the channel. */ if ((ic->ic_flags & IEEE80211_F_ASCAN) && (ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) { ieee80211_send_probereq(ni, ic->ic_myaddr, ifp->if_broadcastaddr, ifp->if_broadcastaddr, ic->ic_des_essid, ic->ic_des_esslen, ic->ic_opt_ie, ic->ic_opt_ie_len); } break; case IEEE80211_S_RUN: /* beacon miss */ IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "no recent beacons from %s; rescanning\n", ether_sprintf(ic->ic_bss->ni_bssid)); ieee80211_sta_leave(ic, ni); ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */ /* FALLTHRU */ case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: /* timeout restart scan */ ni = ieee80211_find_node(&ic->ic_scan, ic->ic_bss->ni_macaddr); if (ni != NULL) { ni->ni_fails++; ieee80211_unref_node(&ni); } if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) ieee80211_begin_scan(ic, arg); break; } break; case IEEE80211_S_AUTH: switch (ostate) { case IEEE80211_S_INIT: case IEEE80211_S_SCAN: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); break; case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: switch (arg) { case IEEE80211_FC0_SUBTYPE_AUTH: /* ??? */ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 2); break; case IEEE80211_FC0_SUBTYPE_DEAUTH: /* ignore and retry scan on timeout */ break; } break; case IEEE80211_S_RUN: switch (arg) { case IEEE80211_FC0_SUBTYPE_AUTH: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 2); ic->ic_state = ostate; /* stay RUN */ break; case IEEE80211_FC0_SUBTYPE_DEAUTH: ieee80211_sta_leave(ic, ni); if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { /* try to reauth */ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); } break; } break; } break; case IEEE80211_S_ASSOC: switch (ostate) { case IEEE80211_S_INIT: case IEEE80211_S_SCAN: case IEEE80211_S_ASSOC: IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, "%s: invalid transition\n", __func__); break; case IEEE80211_S_AUTH: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); break; case IEEE80211_S_RUN: ieee80211_sta_leave(ic, ni); if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); } break; } break; case IEEE80211_S_RUN: if (ic->ic_flags & IEEE80211_F_WPA) { /* XXX validate prerequisites */ } switch (ostate) { case IEEE80211_S_INIT: if (ic->ic_opmode == IEEE80211_M_MONITOR) break; /* fall thru... */ case IEEE80211_S_AUTH: IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, "%s: invalid transition\n", __func__); /* fall thru... */ case IEEE80211_S_RUN: break; case IEEE80211_S_SCAN: /* adhoc/hostap mode */ case IEEE80211_S_ASSOC: /* infra mode */ KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates, ("%s: bogus xmit rate %u setup\n", __func__, ni->ni_txrate)); #ifdef IEEE80211_DEBUG if (ieee80211_msg_debug(ic)) { if (ic->ic_opmode == IEEE80211_M_STA) if_printf(ifp, "associated "); else if_printf(ifp, "synchronized "); printf("with %s ssid ", ether_sprintf(ni->ni_bssid)); ieee80211_print_essid(ic->ic_bss->ni_essid, ni->ni_esslen); printf(" channel %d start %uMb\n", ieee80211_chan2ieee(ic, ic->ic_curchan), IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); } #endif ic->ic_mgt_timer = 0; if (ic->ic_opmode == IEEE80211_M_STA) ieee80211_notify_node_join(ic, ni, arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); if_start(ifp); /* XXX not authorized yet */ break; } /* * Start/stop the authenticator when operating as an * AP. We delay until here to allow configuration to * happen out of order. */ if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */ ic->ic_auth->ia_attach != NULL) { /* XXX check failure */ ic->ic_auth->ia_attach(ic); } else if (ic->ic_auth->ia_detach != NULL) { ic->ic_auth->ia_detach(ic); } /* * When 802.1x is not in use mark the port authorized * at this point so traffic can flow. */ if (ni->ni_authmode != IEEE80211_AUTH_8021X) ieee80211_node_authorize(ni); /* * Enable inactivity processing. * XXX */ ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT; ic->ic_sta.nt_inact_timer = IEEE80211_INACT_WAIT; break; } return 0; } Index: stable/6/sys/net80211/ieee80211_var.h =================================================================== --- stable/6/sys/net80211/ieee80211_var.h (revision 153658) +++ stable/6/sys/net80211/ieee80211_var.h (revision 153659) @@ -1,417 +1,418 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2005 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * 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. * * $FreeBSD$ */ #ifndef _NET80211_IEEE80211_VAR_H_ #define _NET80211_IEEE80211_VAR_H_ /* * Definitions for IEEE 802.11 drivers. */ #define IEEE80211_DEBUG #undef IEEE80211_DEBUG_REFCNT /* node refcnt stuff */ /* NB: portability glue must go first */ #ifdef __NetBSD__ #include #elif __FreeBSD__ #include #elif __linux__ #include #else #error "No support for your operating system!" #endif #include #include #include #include /* for ieee80211_stats */ #include #include #define IEEE80211_TXPOWER_MAX 100 /* .5 dbM (XXX units?) */ #define IEEE80211_TXPOWER_MIN 0 /* kill radio */ #define IEEE80211_DTIM_MAX 15 /* max DTIM period */ #define IEEE80211_DTIM_MIN 1 /* min DTIM period */ #define IEEE80211_DTIM_DEFAULT 1 /* default DTIM period */ /* NB: min+max come from WiFi requirements */ #define IEEE80211_BINTVAL_MAX 1000 /* max beacon interval (TU's) */ #define IEEE80211_BINTVAL_MIN 25 /* min beacon interval (TU's) */ #define IEEE80211_BINTVAL_DEFAULT 100 /* default beacon interval (TU's) */ #define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */ #define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */ #define IEEE80211_FIXED_RATE_NONE -1 #define IEEE80211_MCAST_RATE_DEFAULT (2*1) /* default mcast rate (1M) */ #define IEEE80211_RTS_DEFAULT IEEE80211_RTS_MAX #define IEEE80211_FRAG_DEFAULT IEEE80211_FRAG_MAX #define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) #define IEEE80211_TU_TO_MS(x) (((x) * 1024) / 1000) struct ieee80211_aclator; struct sysctl_ctx_list; struct ieee80211com { SLIST_ENTRY(ieee80211com) ic_next; struct ifnet *ic_ifp; /* associated device */ struct ieee80211_stats ic_stats; /* statistics */ struct sysctl_ctx_list *ic_sysctl; /* dynamic sysctl context */ u_int32_t ic_debug; /* debug msg flags */ int ic_vap; /* virtual AP index */ ieee80211_beacon_lock_t ic_beaconlock; /* beacon update lock */ int (*ic_reset)(struct ifnet *); void (*ic_recv_mgmt)(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, int, int, u_int32_t); int (*ic_send_mgmt)(struct ieee80211com *, struct ieee80211_node *, int, int); int (*ic_newstate)(struct ieee80211com *, enum ieee80211_state, int); void (*ic_newassoc)(struct ieee80211_node *, int); void (*ic_updateslot)(struct ifnet *); void (*ic_set_tim)(struct ieee80211_node *, int); u_int8_t ic_myaddr[IEEE80211_ADDR_LEN]; struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1]; u_int8_t ic_chan_avail[IEEE80211_CHAN_BYTES]; u_int8_t ic_chan_active[IEEE80211_CHAN_BYTES]; u_int8_t ic_chan_scan[IEEE80211_CHAN_BYTES]; struct ieee80211_node_table ic_scan; /* scan candidates */ struct ifqueue ic_mgtq; u_int32_t ic_flags; /* state flags */ u_int32_t ic_flags_ext; /* extended state flags */ u_int32_t ic_caps; /* capabilities */ u_int16_t ic_modecaps; /* set of mode capabilities */ u_int16_t ic_curmode; /* current mode */ enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */ enum ieee80211_opmode ic_opmode; /* operation mode */ enum ieee80211_state ic_state; /* 802.11 state */ enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */ enum ieee80211_roamingmode ic_roaming; /* roaming mode */ struct ieee80211_node_table ic_sta; /* stations/neighbors */ u_int32_t *ic_aid_bitmap; /* association id map */ u_int16_t ic_max_aid; u_int16_t ic_sta_assoc; /* stations associated */ u_int16_t ic_ps_sta; /* stations in power save */ u_int16_t ic_ps_pending; /* ps sta's w/ pending frames */ u_int8_t *ic_tim_bitmap; /* power-save stations w/ data*/ u_int16_t ic_tim_len; /* ic_tim_bitmap size (bytes) */ u_int8_t ic_dtim_period; /* DTIM period */ u_int8_t ic_dtim_count; /* DTIM count for last bcn */ struct ifmedia ic_media; /* interface media config */ struct bpf_if *ic_rawbpf; /* packet filter structure */ struct ieee80211_node *ic_bss; /* information for this node */ struct ieee80211_channel *ic_ibss_chan; struct ieee80211_channel *ic_curchan; /* current channel */ int ic_fixed_rate; /* index to ic_sup_rates[] */ int ic_mcast_rate; /* rate for mcast frames */ u_int16_t ic_rtsthreshold; u_int16_t ic_fragthreshold; struct ieee80211_node *(*ic_node_alloc)(struct ieee80211_node_table*); void (*ic_node_free)(struct ieee80211_node *); void (*ic_node_cleanup)(struct ieee80211_node *); u_int8_t (*ic_node_getrssi)(const struct ieee80211_node*); u_int16_t ic_lintval; /* listen interval */ u_int16_t ic_bintval; /* beacon interval */ u_int16_t ic_holdover; /* PM hold over duration */ u_int16_t ic_txmin; /* min tx retry count */ u_int16_t ic_txmax; /* max tx retry count */ u_int16_t ic_txlifetime; /* tx lifetime */ u_int16_t ic_txpowlimit; /* global tx power limit */ u_int16_t ic_bmisstimeout;/* beacon miss threshold (ms) */ u_int16_t ic_nonerpsta; /* # non-ERP stations */ u_int16_t ic_longslotsta; /* # long slot time stations */ int ic_mgt_timer; /* mgmt timeout */ int ic_inact_timer; /* inactivity timer wait */ int ic_des_esslen; u_int8_t ic_des_essid[IEEE80211_NWID_LEN]; struct ieee80211_channel *ic_des_chan; /* desired channel */ u_int8_t ic_des_bssid[IEEE80211_ADDR_LEN]; void *ic_opt_ie; /* user-specified IE's */ u_int16_t ic_opt_ie_len; /* length of ni_opt_ie */ /* * Inactivity timer settings for nodes. */ int ic_inact_init; /* initial setting */ int ic_inact_auth; /* auth but not assoc setting */ int ic_inact_run; /* authorized setting */ int ic_inact_probe; /* inactive probe time */ /* * WME/WMM state. */ struct ieee80211_wme_state ic_wme; /* * Cipher state/configuration. */ struct ieee80211_crypto_state ic_crypto; #define ic_nw_keys ic_crypto.cs_nw_keys /* XXX compatibility */ #define ic_def_txkey ic_crypto.cs_def_txkey /* XXX compatibility */ /* * 802.1x glue. When an authenticator attaches it * fills in this section. We assume that when ic_ec * is setup that the methods are safe to call. */ const struct ieee80211_authenticator *ic_auth; struct eapolcom *ic_ec; /* * Access control glue. When a control agent attaches * it fills in this section. We assume that when ic_ac * is setup that the methods are safe to call. */ const struct ieee80211_aclator *ic_acl; void *ic_as; }; #define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0) #define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN) /* ic_flags */ /* NB: bits 0x4c available */ #define IEEE80211_F_FF 0x00000001 /* CONF: ATH FF enabled */ #define IEEE80211_F_TURBOP 0x00000002 /* CONF: ATH Turbo enabled*/ +#define IEEE80211_F_BURST 0x00000004 /* CONF: bursting enabled */ /* NB: this is intentionally setup to be IEEE80211_CAPINFO_PRIVACY */ #define IEEE80211_F_PRIVACY 0x00000010 /* CONF: privacy enabled */ #define IEEE80211_F_PUREG 0x00000020 /* CONF: 11g w/o 11b sta's */ #define IEEE80211_F_SCAN 0x00000080 /* STATUS: scanning */ #define IEEE80211_F_ASCAN 0x00000100 /* STATUS: active scan */ #define IEEE80211_F_SIBSS 0x00000200 /* STATUS: start IBSS */ /* NB: this is intentionally setup to be IEEE80211_CAPINFO_SHORT_SLOTTIME */ #define IEEE80211_F_SHSLOT 0x00000400 /* STATUS: use short slot time*/ #define IEEE80211_F_PMGTON 0x00000800 /* CONF: Power mgmt enable */ #define IEEE80211_F_DESBSSID 0x00001000 /* CONF: des_bssid is set */ #define IEEE80211_F_WME 0x00002000 /* CONF: enable WME use */ #define IEEE80211_F_BGSCAN 0x00004000 /* CONF: bg scan enabled (???)*/ #define IEEE80211_F_SWRETRY 0x00008000 /* CONF: sw tx retry enabled */ #define IEEE80211_F_TXPOW_FIXED 0x00010000 /* TX Power: fixed rate */ #define IEEE80211_F_IBSSON 0x00020000 /* CONF: IBSS creation enable */ #define IEEE80211_F_SHPREAMBLE 0x00040000 /* STATUS: use short preamble */ #define IEEE80211_F_DATAPAD 0x00080000 /* CONF: do alignment pad */ #define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */ #define IEEE80211_F_USEBARKER 0x00200000 /* STATUS: use barker preamble*/ #define IEEE80211_F_TIMUPDATE 0x00400000 /* STATUS: update beacon tim */ #define IEEE80211_F_WPA1 0x00800000 /* CONF: WPA enabled */ #define IEEE80211_F_WPA2 0x01000000 /* CONF: WPA2 enabled */ #define IEEE80211_F_WPA 0x01800000 /* CONF: WPA/WPA2 enabled */ #define IEEE80211_F_DROPUNENC 0x02000000 /* CONF: drop unencrypted */ #define IEEE80211_F_COUNTERM 0x04000000 /* CONF: TKIP countermeasures */ #define IEEE80211_F_HIDESSID 0x08000000 /* CONF: hide SSID in beacon */ #define IEEE80211_F_NOBRIDGE 0x10000000 /* CONF: dis. internal bridge */ #define IEEE80211_F_WMEUPDATE 0x20000000 /* STATUS: update beacon wme */ /* ic_flags_ext */ #define IEEE80211_FEXT_WDS 0x00000001 /* CONF: 4 addr allowed */ /* 0x00000006 reserved */ #define IEEE80211_FEXT_BGSCAN 0x00000008 /* STATUS: enable full bgscan completion */ /* ic_caps */ #define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */ #define IEEE80211_C_TKIP 0x00000002 /* CAPABILITY: TKIP available */ #define IEEE80211_C_AES 0x00000004 /* CAPABILITY: AES OCB avail */ #define IEEE80211_C_AES_CCM 0x00000008 /* CAPABILITY: AES CCM avail */ #define IEEE80211_C_CKIP 0x00000020 /* CAPABILITY: CKIP available */ #define IEEE80211_C_FF 0x00000040 /* CAPABILITY: ATH FF avail */ #define IEEE80211_C_TURBOP 0x00000080 /* CAPABILITY: ATH Turbo avail*/ #define IEEE80211_C_IBSS 0x00000100 /* CAPABILITY: IBSS available */ #define IEEE80211_C_PMGT 0x00000200 /* CAPABILITY: Power mgmt */ #define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */ #define IEEE80211_C_AHDEMO 0x00000800 /* CAPABILITY: Old Adhoc Demo */ #define IEEE80211_C_SWRETRY 0x00001000 /* CAPABILITY: sw tx retry */ #define IEEE80211_C_TXPMGT 0x00002000 /* CAPABILITY: tx power mgmt */ #define IEEE80211_C_SHSLOT 0x00004000 /* CAPABILITY: short slottime */ #define IEEE80211_C_SHPREAMBLE 0x00008000 /* CAPABILITY: short preamble */ #define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */ #define IEEE80211_C_TKIPMIC 0x00020000 /* CAPABILITY: TKIP MIC avail */ #define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */ #define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */ #define IEEE80211_C_WPA 0x01800000 /* CAPABILITY: WPA1+WPA2 avail*/ #define IEEE80211_C_BURST 0x02000000 /* CAPABILITY: frame bursting */ #define IEEE80211_C_WME 0x04000000 /* CAPABILITY: WME avail */ #define IEEE80211_C_WDS 0x08000000 /* CAPABILITY: 4-addr support */ /* 0x10000000 reserved */ #define IEEE80211_C_BGSCAN 0x20000000 /* CAPABILITY: bg scanning */ #define IEEE80211_C_TXFRAG 0x40000000 /* CAPABILITY: tx fragments */ /* XXX protection/barker? */ #define IEEE80211_C_CRYPTO 0x0000002f /* CAPABILITY: crypto alg's */ void ieee80211_ifattach(struct ieee80211com *); void ieee80211_ifdetach(struct ieee80211com *); void ieee80211_announce(struct ieee80211com *); void ieee80211_media_init(struct ieee80211com *, ifm_change_cb_t, ifm_stat_cb_t); struct ieee80211com *ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]); int ieee80211_media_change(struct ifnet *); void ieee80211_media_status(struct ifnet *, struct ifmediareq *); int ieee80211_ioctl(struct ieee80211com *, u_long, caddr_t); int ieee80211_cfgget(struct ieee80211com *, u_long, caddr_t); int ieee80211_cfgset(struct ieee80211com *, u_long, caddr_t); void ieee80211_watchdog(struct ieee80211com *); int ieee80211_rate2media(struct ieee80211com *, int, enum ieee80211_phymode); int ieee80211_media2rate(int); u_int ieee80211_mhz2ieee(u_int, u_int); u_int ieee80211_chan2ieee(struct ieee80211com *, struct ieee80211_channel *); u_int ieee80211_ieee2mhz(u_int, u_int); int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode); enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *, struct ieee80211_channel *); /* * Key update synchronization methods. XXX should not be visible. */ static __inline void ieee80211_key_update_begin(struct ieee80211com *ic) { ic->ic_crypto.cs_key_update_begin(ic); } static __inline void ieee80211_key_update_end(struct ieee80211com *ic) { ic->ic_crypto.cs_key_update_end(ic); } /* * XXX these need to be here for IEEE80211_F_DATAPAD */ /* * Return the space occupied by the 802.11 header and any * padding required by the driver. This works for a * management or data frame. */ static __inline int ieee80211_hdrspace(struct ieee80211com *ic, const void *data) { int size = ieee80211_hdrsize(data); if (ic->ic_flags & IEEE80211_F_DATAPAD) size = roundup(size, sizeof(u_int32_t)); return size; } /* * Like ieee80211_hdrspace, but handles any type of frame. */ static __inline int ieee80211_anyhdrspace(struct ieee80211com *ic, const void *data) { int size = ieee80211_anyhdrsize(data); if (ic->ic_flags & IEEE80211_F_DATAPAD) size = roundup(size, sizeof(u_int32_t)); return size; } #define IEEE80211_MSG_DEBUG 0x40000000 /* IFF_DEBUG equivalent */ #define IEEE80211_MSG_DUMPPKTS 0x20000000 /* IFF_LINK2 equivalant */ #define IEEE80211_MSG_CRYPTO 0x10000000 /* crypto work */ #define IEEE80211_MSG_INPUT 0x08000000 /* input handling */ #define IEEE80211_MSG_XRATE 0x04000000 /* rate set handling */ #define IEEE80211_MSG_ELEMID 0x02000000 /* element id parsing */ #define IEEE80211_MSG_NODE 0x01000000 /* node handling */ #define IEEE80211_MSG_ASSOC 0x00800000 /* association handling */ #define IEEE80211_MSG_AUTH 0x00400000 /* authentication handling */ #define IEEE80211_MSG_SCAN 0x00200000 /* scanning */ #define IEEE80211_MSG_OUTPUT 0x00100000 /* output handling */ #define IEEE80211_MSG_STATE 0x00080000 /* state machine */ #define IEEE80211_MSG_POWER 0x00040000 /* power save handling */ #define IEEE80211_MSG_DOT1X 0x00020000 /* 802.1x authenticator */ #define IEEE80211_MSG_DOT1XSM 0x00010000 /* 802.1x state machine */ #define IEEE80211_MSG_RADIUS 0x00008000 /* 802.1x radius client */ #define IEEE80211_MSG_RADDUMP 0x00004000 /* dump 802.1x radius packets */ #define IEEE80211_MSG_RADKEYS 0x00002000 /* dump 802.1x keys */ #define IEEE80211_MSG_WPA 0x00001000 /* WPA/RSN protocol */ #define IEEE80211_MSG_ACL 0x00000800 /* ACL handling */ #define IEEE80211_MSG_WME 0x00000400 /* WME protocol */ #define IEEE80211_MSG_SUPERG 0x00000200 /* Atheros SuperG protocol */ #define IEEE80211_MSG_DOTH 0x00000100 /* 802.11h support */ #define IEEE80211_MSG_INACT 0x00000080 /* inactivity handling */ #define IEEE80211_MSG_ROAM 0x00000040 /* sta-mode roaming */ #define IEEE80211_MSG_ANY 0xffffffff /* anything */ #ifdef IEEE80211_DEBUG #define ieee80211_msg(_ic, _m) ((_ic)->ic_debug & (_m)) #define IEEE80211_DPRINTF(_ic, _m, _fmt, ...) do { \ if (ieee80211_msg(_ic, _m)) \ ieee80211_note(_ic, _fmt, __VA_ARGS__); \ } while (0) #define IEEE80211_NOTE(_ic, _m, _ni, _fmt, ...) do { \ if (ieee80211_msg(_ic, _m)) \ ieee80211_note_mac(_ic, (_ni)->ni_macaddr, _fmt, __VA_ARGS__);\ } while (0) #define IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...) do { \ if (ieee80211_msg(_ic, _m)) \ ieee80211_note_mac(_ic, _mac, _fmt, __VA_ARGS__); \ } while (0) #define IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...) do { \ if (ieee80211_msg(_ic, _m)) \ ieee80211_note_frame(_ic, _wh, _fmt, __VA_ARGS__); \ } while (0) void ieee80211_note(struct ieee80211com *ic, const char *fmt, ...); void ieee80211_note_mac(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...); void ieee80211_note_frame(struct ieee80211com *ic, const struct ieee80211_frame *wh, const char *fmt, ...); #define ieee80211_msg_debug(_ic) \ ((_ic)->ic_debug & IEEE80211_MSG_DEBUG) #define ieee80211_msg_dumppkts(_ic) \ ((_ic)->ic_debug & IEEE80211_MSG_DUMPPKTS) #define ieee80211_msg_input(_ic) \ ((_ic)->ic_debug & IEEE80211_MSG_INPUT) #define ieee80211_msg_radius(_ic) \ ((_ic)->ic_debug & IEEE80211_MSG_RADIUS) #define ieee80211_msg_dumpradius(_ic) \ ((_ic)->ic_debug & IEEE80211_MSG_RADDUMP) #define ieee80211_msg_dumpradkeys(_ic) \ ((_ic)->ic_debug & IEEE80211_MSG_RADKEYS) #define ieee80211_msg_scan(_ic) \ ((_ic)->ic_debug & IEEE80211_MSG_SCAN) #define ieee80211_msg_assoc(_ic) \ ((_ic)->ic_debug & IEEE80211_MSG_ASSOC) #else #define IEEE80211_DPRINTF(_ic, _m, _fmt, ...) #define IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...) #define IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...) #define ieee80211_msg_dumppkts(_ic) 0 #define ieee80211_msg(_ic, _m) 0 #endif #endif /* _NET80211_IEEE80211_VAR_H_ */