Index: head/sys/net80211/ieee80211.c =================================================================== --- head/sys/net80211/ieee80211.c (revision 117810) +++ head/sys/net80211/ieee80211.c (revision 117811) @@ -1,857 +1,857 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 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 "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #endif #ifdef IEEE80211_DEBUG int ieee80211_debug = 0; SYSCTL_INT(_debug, OID_AUTO, ieee80211, CTLFLAG_RW, &ieee80211_debug, 0, "IEEE 802.11 media debugging printfs"); #endif static void ieee80211_set11gbasicrates(struct ieee80211_rateset *, enum ieee80211_phymode); static const char *ieee80211_phymode_name[] = { "auto", /* IEEE80211_MODE_AUTO */ "11a", /* IEEE80211_MODE_11A */ "11b", /* IEEE80211_MODE_11B */ "11g", /* IEEE80211_MODE_11G */ "turbo", /* IEEE80211_MODE_TURBO */ }; void ieee80211_ifattach(struct ifnet *ifp) { struct ieee80211com *ic = (void *)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(ifp); /* * 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_curmode */ if ((ic->ic_modecaps & (1<ic_curmode)) == 0) ic->ic_curmode = IEEE80211_MODE_AUTO; (void) ieee80211_setmode(ic, ic->ic_curmode); ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ if (ic->ic_lintval == 0) ic->ic_lintval = 100; /* default sleep */ ic->ic_bmisstimeout = 7*ic->ic_lintval; /* default 7 beacons */ ieee80211_node_attach(ifp); ieee80211_proto_attach(ifp); } void ieee80211_ifdetach(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; ieee80211_proto_detach(ifp); ieee80211_crypto_detach(ifp); ieee80211_node_detach(ifp); ifmedia_removeall(&ic->ic_media); 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_if, "invalid channel freq %u flags %x\n", c->ic_freq, c->ic_flags); return 0; /* XXX */ } else { if_printf(&ic->ic_if, "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 ifnet *ifp, 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 ieee80211com *ic = (void *)ifp; struct ifmediareq imr; int i, j, mode, rate, maxrate, mword, mopt, r; struct ieee80211_rateset *rs; struct ieee80211_rateset allrates; /* * 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_MAKEMODE(IFM_IEEE80211_11A), IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_MAKEMODE(IFM_IEEE80211_11A) | 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 (mode == IEEE80211_MODE_AUTO) continue; if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); 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; printf("%s%d%sMbps", (i != 0 ? " " : ""), (rate & IEEE80211_RATE_VAL) / 2, ((rate & 0x1) != 0 ? ".5" : "")); 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); /* * 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; } printf("\n"); } 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); } ieee80211_media_status(ifp, &imr); ifmedia_set(&ic->ic_media, imr.ifm_active); if (maxrate) ifp->if_baudrate = IF_Mbps(maxrate); #undef ADD } 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 } /* * Handle a media change request. */ int ieee80211_media_change(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; struct ifmedia_entry *ime; enum ieee80211_opmode newopmode; enum ieee80211_phymode newphymode; int i, j, newrate, error = 0; 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_AUTO: newphymode = IEEE80211_MODE_AUTO; break; default: return EINVAL; } /* * Turbo mode is an ``option''. Eventually it * needs to be applied to 11g too. */ if (ime->ifm_media & IFM_IEEE80211_TURBO) { if (newphymode != IEEE80211_MODE_11A) return EINVAL; newphymode = IEEE80211_MODE_TURBO; } /* * 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 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: ic->ic_flags &= ~IEEE80211_F_IBSSON; break; case IEEE80211_M_IBSS: ic->ic_flags |= IEEE80211_F_IBSSON; #ifdef notdef if (ic->ic_curmode == IEEE80211_MODE_11G) ieee80211_set11gbasicrates( &ic->ic_suprates[newphymode], IEEE80211_MODE_11B); #endif break; } 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 = (void *)ifp; struct ieee80211_node *ni = NULL; imr->ifm_status = IFM_AVALID; imr->ifm_active = IFM_IEEE80211; if (ic->ic_state == IEEE80211_S_RUN) imr->ifm_status |= IFM_ACTIVE; imr->ifm_active |= IFM_AUTO; switch (ic->ic_opmode) { case IEEE80211_M_STA: ni = ic->ic_bss; /* calculate rate subtype */ imr->ifm_active |= ieee80211_rate2media(ic, ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode); 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; } switch (ic->ic_curmode) { case IEEE80211_MODE_11A: imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A); break; case IEEE80211_MODE_11B: imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11B); break; case IEEE80211_MODE_11G: imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11G); break; case IEEE80211_MODE_TURBO: imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A) | IFM_IEEE80211_TURBO; break; } } void ieee80211_watchdog(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) - ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1); + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); if (ic->ic_inact_timer && --ic->ic_inact_timer == 0) ieee80211_timeout_nodes(ic); if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0) ifp->if_timer = 1; } /* * 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. */ static void ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) { static const struct ieee80211_rateset basic[] = { { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11B */ { 7, { 2, 4, 11, 22, 12, 24, 48 } },/* IEEE80211_MODE_11G */ { 0 }, /* IEEE80211_MODE_TURBO */ }; 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; } } } /* * 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_T, /* IEEE80211_MODE_TURBO */ }; 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\n", mode)); modeflags = chanflags[mode]; for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { c = &ic->ic_channels[i]; 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(("%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 (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; } } /* * Set/reset state flags that influence beacon contents, etc. * * XXX what if we have stations already associated??? * XXX probably not right for autoselect? */ if (mode == IEEE80211_MODE_11G) { if (ic->ic_caps & IEEE80211_C_SHSLOT) ic->ic_flags |= IEEE80211_F_SHSLOT; if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) ic->ic_flags |= IEEE80211_F_SHPREAMBLE; ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], IEEE80211_MODE_11G); } else { ic->ic_flags &= ~(IEEE80211_F_SHSLOT | IEEE80211_F_SHPREAMBLE); } ic->ic_curmode = mode; return 0; #undef N } /* * Return the phy mode for with the specified channel so the * caller can select a rate set. This is problematic and the * work here assumes how things work elsewhere in this code. */ enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan) { /* * NB: this assumes the channel would not be supplied to us * unless it was already compatible with the current mode. */ if (ic->ic_curmode != IEEE80211_MODE_AUTO) return ic->ic_curmode; /* * In autoselect mode; deduce a mode based on the channel * characteristics. We assume that turbo-only channels * are not considered when the channel set is constructed. */ if (IEEE80211_IS_CHAN_5GHZ(chan)) return IEEE80211_MODE_11A; else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) 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_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS1 }, { 4 | IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS2 }, { 11 | IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS5 }, { 22 | IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS11 }, { 44 | IFM_MAKEMODE(IFM_IEEE80211_11B), IFM_IEEE80211_DS22 }, { 12 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM6 }, { 18 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM9 }, { 24 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM12 }, { 36 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM18 }, { 48 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM24 }, { 72 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM36 }, { 96 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM48 }, { 108 | IFM_MAKEMODE(IFM_IEEE80211_11A), IFM_IEEE80211_OFDM54 }, { 2 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_DS1 }, { 4 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_DS2 }, { 11 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_DS5 }, { 22 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_DS11 }, { 12 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM6 }, { 18 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM9 }, { 24 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM12 }, { 36 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM18 }, { 48 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM24 }, { 72 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM36 }, { 96 | IFM_MAKEMODE(IFM_IEEE80211_11G), IFM_IEEE80211_OFDM48 }, { 108 | IFM_MAKEMODE(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: mask |= IFM_MAKEMODE(IFM_IEEE80211_11A); break; case IEEE80211_MODE_11B: mask |= IFM_MAKEMODE(IFM_IEEE80211_11B); break; case IEEE80211_MODE_AUTO: /* NB: ic may be NULL for some drivers */ if (ic && ic->ic_phytype == IEEE80211_T_FH) { /* must handle these specially */ switch (mask) { case 2: return IFM_IEEE80211_FH1; case 4: return IFM_IEEE80211_FH2; } return IFM_AUTO; } /* NB: hack, 11g matches both 11b+11a rates */ /* fall thru... */ case IEEE80211_MODE_11G: mask |= IFM_MAKEMODE(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 } /* * Module glue. * * NB: the module name is "wlan" for compatibility with NetBSD. */ static int ieee80211_modevent(module_t mod, int type, void *unused) { switch (type) { case MOD_LOAD: if (bootverbose) printf("wlan: <802.11 Link Layer>\n"); return 0; case MOD_UNLOAD: return 0; } return EINVAL; } static moduledata_t ieee80211_mod = { "wlan", ieee80211_modevent, 0 }; DECLARE_MODULE(wlan, ieee80211_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); MODULE_VERSION(wlan, 1); MODULE_DEPEND(wlan, rc4, 1, 1, 1); Index: head/sys/net80211/ieee80211_input.c =================================================================== --- head/sys/net80211/ieee80211_input.c (revision 117810) +++ head/sys/net80211/ieee80211_input.c (revision 117811) @@ -1,1085 +1,1089 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 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$"); #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #endif void ieee80211_input(struct ifnet *ifp, struct mbuf *m, int rssi, u_int32_t rstamp, u_int rantenna) { struct ieee80211com *ic = (void *)ifp; struct ieee80211_node *ni = NULL; struct ieee80211_frame *wh; struct ether_header *eh; struct mbuf *m1; int len; u_int8_t dir, subtype; u_int8_t *bssid; u_int16_t rxseq; /* trim CRC here for WEP can find its own CRC at the end of packet. */ if (m->m_flags & M_HASFCS) { m_adj(m, -IEEE80211_CRC_LEN); m->m_flags &= ~M_HASFCS; } wh = mtod(m, struct ieee80211_frame *); if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != IEEE80211_FC0_VERSION_0) { if (ifp->if_flags & IFF_DEBUG) if_printf(ifp, "receive packet with wrong version: %x\n", wh->i_fc[0]); goto err; } dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; if (ic->ic_state != IEEE80211_S_SCAN) { switch (ic->ic_opmode) { case IEEE80211_M_STA: ni = ieee80211_ref_node(ic->ic_bss); if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) { IEEE80211_DPRINTF2(("%s: discard frame from " "bss %s\n", __func__, ether_sprintf(wh->i_addr2))); /* not interested in */ ieee80211_unref_node(&ni); goto out; } break; case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: case IEEE80211_M_HOSTAP: if (dir == IEEE80211_FC1_DIR_NODS) bssid = wh->i_addr3; else bssid = wh->i_addr1; if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) && !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) { /* not interested in */ IEEE80211_DPRINTF2(("%s: other bss %s\n", __func__, ether_sprintf(wh->i_addr3))); goto out; } ni = ieee80211_find_node(ic, wh->i_addr2); if (ni == NULL) { IEEE80211_DPRINTF2(("%s: warning, unknown src " "%s\n", __func__, ether_sprintf(wh->i_addr2))); /* * NB: Node allocation is handled in the * management handling routines. Just fake * up a reference to the hosts's node to do * the stuff below. */ ni = ieee80211_ref_node(ic->ic_bss); } break; default: /* XXX catch bad values */ break; } ni->ni_rssi = rssi; ni->ni_rstamp = rstamp; ni->ni_rantenna = rantenna; rxseq = ni->ni_rxseq; ni->ni_rxseq = le16toh(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; /* TODO: fragment */ if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && rxseq == ni->ni_rxseq) { /* duplicate, silently discarded */ ieee80211_unref_node(&ni); goto out; } ni->ni_inact = 0; ieee80211_unref_node(&ni); } switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { case IEEE80211_FC0_TYPE_DATA: switch (ic->ic_opmode) { case IEEE80211_M_STA: if (dir != IEEE80211_FC1_DIR_FROMDS) goto out; if ((ifp->if_flags & IFF_SIMPLEX) && IEEE80211_IS_MULTICAST(wh->i_addr1) && IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) { /* * In IEEE802.11 network, multicast packet * sent from me is broadcasted from AP. * It should be silently discarded for * SIMPLEX interface. */ goto out; } break; case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: if (dir != IEEE80211_FC1_DIR_NODS) goto out; break; case IEEE80211_M_HOSTAP: if (dir != IEEE80211_FC1_DIR_TODS) goto out; /* check if source STA is associated */ ni = ieee80211_find_node(ic, wh->i_addr2); if (ni == NULL) { IEEE80211_DPRINTF(("%s: data from unknown src " "%s\n", __func__, ether_sprintf(wh->i_addr2))); ni = ieee80211_dup_bss(ic, wh->i_addr2); if (ni != NULL) { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_NOT_AUTHED); ieee80211_free_node(ic, ni); } goto err; } if (ni->ni_associd == 0) { IEEE80211_DPRINTF(("ieee80211_input: " "data from unassoc src %s\n", ether_sprintf(wh->i_addr2))); IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, IEEE80211_REASON_NOT_ASSOCED); ieee80211_unref_node(&ni); goto err; } ieee80211_unref_node(&ni); break; } if (wh->i_fc[1] & IEEE80211_FC1_WEP) { if (ic->ic_flags & IEEE80211_F_WEPON) { m = ieee80211_wep_crypt(ifp, m, 0); if (m == NULL) goto err; wh = mtod(m, struct ieee80211_frame *); } else goto out; } /* copy to listener after decrypt */ if (ic->ic_rawbpf) bpf_mtap(ic->ic_rawbpf, m); m = ieee80211_decap(ifp, m); if (m == NULL) goto err; ifp->if_ipackets++; /* perform as a bridge within the AP */ m1 = NULL; if (ic->ic_opmode == IEEE80211_M_HOSTAP) { eh = mtod(m, struct ether_header *); if (ETHER_IS_MULTICAST(eh->ether_dhost)) { m1 = m_copym(m, 0, M_COPYALL, M_DONTWAIT); if (m1 == NULL) ifp->if_oerrors++; else m1->m_flags |= M_MCAST; } else { ni = ieee80211_find_node(ic, eh->ether_dhost); if (ni != NULL) { if (ni->ni_associd != 0) { m1 = m; m = NULL; } ieee80211_unref_node(&ni); } } if (m1 != NULL) { #ifdef ALTQ if (ALTQ_IS_ENABLED(&ifp->if_snd)) altq_etherclassify(&ifp->if_snd, m1, &pktattr); #endif len = m1->m_pkthdr.len; IF_ENQUEUE(&ifp->if_snd, m1); if (m != NULL) ifp->if_omcasts++; ifp->if_obytes += len; } } if (m != NULL) (*ifp->if_input)(ifp, m); return; case IEEE80211_FC0_TYPE_MGT: if (dir != IEEE80211_FC1_DIR_NODS) goto err; if (ic->ic_opmode == IEEE80211_M_AHDEMO) goto out; subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; /* drop frames without interest */ if (ic->ic_state == IEEE80211_S_SCAN) { if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) goto out; } else { if (ic->ic_opmode != IEEE80211_M_IBSS && subtype == IEEE80211_FC0_SUBTYPE_BEACON) goto out; } if (ifp->if_flags & IFF_DEBUG) { /* avoid to print too many frames */ int doprint = 0; switch (subtype) { case IEEE80211_FC0_SUBTYPE_BEACON: if (ic->ic_state == IEEE80211_S_SCAN) doprint = 1; break; case IEEE80211_FC0_SUBTYPE_PROBE_REQ: if (ic->ic_opmode == IEEE80211_M_IBSS) doprint = 1; break; default: doprint = 1; break; } #ifdef IEEE80211_DEBUG doprint += ieee80211_debug; #endif if (doprint) if_printf(ifp, "received %s from %s rssi %d\n", ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], ether_sprintf(wh->i_addr2), rssi); } if (ic->ic_rawbpf) bpf_mtap(ic->ic_rawbpf, m); (*ic->ic_recv_mgmt)(ic, m, subtype, rssi, rstamp, rantenna); m_freem(m); return; case IEEE80211_FC0_TYPE_CTL: default: IEEE80211_DPRINTF(("%s: bad type %x\n", __func__, wh->i_fc[0])); /* should not come here */ break; } err: ifp->if_ierrors++; out: if (m != NULL) { if (ic->ic_rawbpf) bpf_mtap(ic->ic_rawbpf, m); m_freem(m); } } struct mbuf * ieee80211_decap(struct ifnet *ifp, struct mbuf *m) { struct ether_header *eh; struct ieee80211_frame wh; struct llc *llc; if (m->m_len < sizeof(wh) + sizeof(*llc)) { m = m_pullup(m, sizeof(wh) + sizeof(*llc)); if (m == NULL) return NULL; } memcpy(&wh, mtod(m, caddr_t), sizeof(wh)); llc = (struct llc *)(mtod(m, caddr_t) + sizeof(wh)); if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP && llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 && llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0) { m_adj(m, sizeof(wh) + sizeof(struct llc) - sizeof(*eh)); llc = NULL; } else { m_adj(m, sizeof(wh) - sizeof(*eh)); } eh = mtod(m, struct ether_header *); switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) { case IEEE80211_FC1_DIR_NODS: IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1); IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2); break; case IEEE80211_FC1_DIR_TODS: IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3); IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2); break; case IEEE80211_FC1_DIR_FROMDS: IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1); IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3); break; case IEEE80211_FC1_DIR_DSTODS: /* not yet supported */ IEEE80211_DPRINTF(("%s: DS to DS\n", __func__)); m_freem(m); return NULL; } #ifdef ALIGNED_POINTER if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) { struct mbuf *n, *n0, **np; caddr_t newdata; int off, pktlen; n0 = NULL; np = &n0; off = 0; pktlen = m->m_pkthdr.len; while (pktlen > off) { if (n0 == NULL) { MGETHDR(n, M_DONTWAIT, MT_DATA); if (n == NULL) { m_freem(m); return NULL; } M_MOVE_PKTHDR(n, m); n->m_len = MHLEN; } else { MGET(n, M_DONTWAIT, MT_DATA); if (n == NULL) { m_freem(m); m_freem(n0); return NULL; } n->m_len = MLEN; } if (pktlen - off >= MINCLSIZE) { MCLGET(n, M_DONTWAIT); if (n->m_flags & M_EXT) n->m_len = n->m_ext.ext_size; } if (n0 == NULL) { newdata = (caddr_t)ALIGN(n->m_data + sizeof(*eh)) - sizeof(*eh); n->m_len -= newdata - n->m_data; n->m_data = newdata; } if (n->m_len > pktlen - off) n->m_len = pktlen - off; m_copydata(m, off, n->m_len, mtod(n, caddr_t)); off += n->m_len; *np = n; np = &n->m_next; } m_freem(m); m = n0; } #endif /* ALIGNED_POINTER */ if (llc != NULL) { eh = mtod(m, struct ether_header *); eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh)); } return m; } /* * Install received rate set information in the node's state block. */ static int ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni, u_int8_t *rates, u_int8_t *xrates, int flags) { struct ieee80211_rateset *rs = &ni->ni_rates; memset(rs, 0, sizeof(*rs)); rs->rs_nrates = rates[1]; memcpy(rs->rs_rates, rates + 2, rs->rs_nrates); if (xrates != NULL) { u_int8_t nxrates; /* * Tack on 11g extended supported rate element. */ nxrates = xrates[1]; if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) { nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates; IEEE80211_DPRINTF(("%s: extended rate set too large;" " only using %u of %u rates\n", __func__, nxrates, xrates[1])); } memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates); rs->rs_nrates += nxrates; } return ieee80211_fix_rate(ic, ni, flags); } /* XXX statistics */ /* Verify the existence and length of __elem or get out. */ #define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \ if ((__elem) == NULL) { \ IEEE80211_DPRINTF(("%s: no " #__elem "in %s frame\n", \ __func__, ieee80211_mgt_subtype_name[subtype >> \ IEEE80211_FC0_SUBTYPE_SHIFT])); \ return; \ } \ if ((__elem)[1] > (__maxlen)) { \ IEEE80211_DPRINTF(("%s: bad " #__elem " len %d in %s " \ "frame from %s\n", __func__, (__elem)[1], \ ieee80211_mgt_subtype_name[subtype >> \ IEEE80211_FC0_SUBTYPE_SHIFT], \ ether_sprintf(wh->i_addr2))); \ return; \ } \ } while (0) #define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \ if ((_len) < (_minlen)) { \ IEEE80211_DPRINTF(("%s: %s frame too short from %s\n", \ __func__, \ ieee80211_mgt_subtype_name[subtype >> \ IEEE80211_FC0_SUBTYPE_SHIFT], \ ether_sprintf(wh->i_addr2))); \ return; \ } \ } while (0) void ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype, int rssi, u_int32_t rstamp, u_int rantenna) { #define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) struct ifnet *ifp = &ic->ic_if; struct ieee80211_frame *wh; struct ieee80211_node *ni; u_int8_t *frm, *efrm; u_int8_t *ssid, *rates, *xrates; int reassoc, resp, newassoc, allocbs; wh = mtod(m0, struct ieee80211_frame *); frm = (u_int8_t *)&wh[1]; efrm = mtod(m0, u_int8_t *) + m0->m_len; switch (subtype) { case IEEE80211_FC0_SUBTYPE_PROBE_RESP: case IEEE80211_FC0_SUBTYPE_BEACON: { u_int8_t *tstamp, *bintval, *capinfo, *country; u_int8_t chan, bchan, fhindex, erp; u_int16_t fhdwell; if (ic->ic_opmode != IEEE80211_M_IBSS && ic->ic_state != IEEE80211_S_SCAN) { /* XXX: may be useful for background scan */ return; } /* * beacon/probe response frame format * [8] time stamp * [2] beacon interval * [2] capability information * [tlv] ssid * [tlv] supported rates * [tlv] country information * [tlv] parameter set (FH/DS) * [tlv] erp information * [tlv] extended supported rates */ IEEE80211_VERIFY_LENGTH(efrm - frm, 12); tstamp = frm; frm += 8; bintval = frm; frm += 2; capinfo = frm; frm += 2; ssid = rates = xrates = country = NULL; bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan); chan = bchan; fhdwell = 0; fhindex = 0; erp = 0; while (frm < efrm) { switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; break; case IEEE80211_ELEMID_RATES: rates = frm; break; case IEEE80211_ELEMID_COUNTRY: country = frm; break; case IEEE80211_ELEMID_FHPARMS: if (ic->ic_phytype == IEEE80211_T_FH) { fhdwell = (frm[3] << 8) | frm[2]; chan = IEEE80211_FH_CHAN(frm[4], frm[5]); fhindex = frm[6]; } break; case IEEE80211_ELEMID_DSPARMS: /* * XXX hack this since depending on phytype * is problematic for multi-mode devices. */ if (ic->ic_phytype != IEEE80211_T_FH) chan = frm[2]; break; case IEEE80211_ELEMID_TIM: break; case IEEE80211_ELEMID_XRATES: xrates = frm; break; case IEEE80211_ELEMID_ERP: if (frm[1] != 1) { IEEE80211_DPRINTF(("%s: invalid ERP " "element; length %u, expecting " "1\n", __func__, frm[1])); break; } erp = frm[2]; break; default: IEEE80211_DPRINTF(("%s: element id %u/len %u " "ignored\n", __func__, *frm, frm[1])); break; } frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_SIZE); IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); - if (chan > IEEE80211_CHAN_MAX || isclr(ic->ic_chan_active, chan)) { + if ( +#if IEEE80211_CHAN_MAX < 255 + chan > IEEE80211_CHAN_MAX || +#endif + isclr(ic->ic_chan_active, chan)) { IEEE80211_DPRINTF(("%s: ignore %s with invalid channel " "%u\n", __func__, ISPROBE(subtype) ? "probe response" : "beacon", chan)); return; } if (chan != bchan) { /* * Frame was received on a channel different from the * one indicated in the DS/FH params element id; * silently discard it. * * NB: this can happen due to signal leakage. */ IEEE80211_DPRINTF(("%s: ignore %s on channel %u marked " "for channel %u\n", __func__, ISPROBE(subtype) ? "probe response" : "beacon", bchan, chan)); /* XXX statistic */ return; } /* * Use mac and channel for lookup so we collect all * potential AP's when scanning. Otherwise we may * see the same AP on multiple channels and will only * record the last one. We could filter APs here based * on rssi, etc. but leave that to the end of the scan * so we can keep the selection criteria in one spot. * This may result in a bloat of the scanned AP list but * it shouldn't be too much. */ ni = ieee80211_lookup_node(ic, wh->i_addr2, &ic->ic_channels[chan]); #ifdef IEEE80211_DEBUG if (ieee80211_debug && (ni == NULL || ic->ic_state == IEEE80211_S_SCAN)) { printf("%s: %s%s on chan %u (bss chan %u) ", __func__, (ni == NULL ? "new " : ""), ISPROBE(subtype) ? "probe response" : "beacon", chan, bchan); ieee80211_print_essid(ssid + 2, ssid[1]); printf(" from %s\n", ether_sprintf(wh->i_addr2)); printf("%s: caps 0x%x bintval %u erp 0x%x\n", __func__, le16toh(*(u_int16_t *)capinfo), le16toh(*(u_int16_t *)bintval), erp); if (country) printf("%s: country info %*D\n", __func__, country[1], country+2, " "); } #endif if (ni == NULL) { ni = ieee80211_alloc_node(ic, wh->i_addr2); if (ni == NULL) return; ni->ni_esslen = ssid[1]; memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); memcpy(ni->ni_essid, ssid + 2, ssid[1]); } else if (ssid[1] != 0 && ISPROBE(subtype)) { /* * Update ESSID at probe response to adopt hidden AP by * Lucent/Cisco, which announces null ESSID in beacon. */ ni->ni_esslen = ssid[1]; memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); memcpy(ni->ni_essid, ssid + 2, ssid[1]); } IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); ni->ni_rssi = rssi; ni->ni_rstamp = rstamp; ni->ni_rantenna = rantenna; memcpy(ni->ni_tstamp, tstamp, sizeof(ni->ni_tstamp)); ni->ni_intval = le16toh(*(u_int16_t *)bintval); ni->ni_capinfo = le16toh(*(u_int16_t *)capinfo); /* XXX validate channel # */ ni->ni_chan = &ic->ic_channels[chan]; ni->ni_fhdwell = fhdwell; ni->ni_fhindex = fhindex; ni->ni_erp = erp; /* NB: must be after ni_chan is setup */ ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT); ieee80211_unref_node(&ni); break; } case IEEE80211_FC0_SUBTYPE_PROBE_REQ: { u_int8_t rate; if (ic->ic_opmode == IEEE80211_M_STA) return; if (ic->ic_state != IEEE80211_S_RUN) return; /* * prreq frame format * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates */ ssid = rates = xrates = NULL; while (frm < efrm) { switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; break; case IEEE80211_ELEMID_RATES: rates = frm; break; case IEEE80211_ELEMID_XRATES: xrates = frm; break; } frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_SIZE); IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); if (ssid[1] != 0 && (ssid[1] != ic->ic_bss->ni_esslen || memcmp(ssid + 2, ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen) != 0)) { #ifdef IEEE80211_DEBUG if (ieee80211_debug) { printf("%s: ssid unmatch ", __func__); ieee80211_print_essid(ssid + 2, ssid[1]); printf(" from %s\n", ether_sprintf(wh->i_addr2)); } #endif return; } ni = ieee80211_find_node(ic, wh->i_addr2); if (ni == NULL) { ni = ieee80211_dup_bss(ic, wh->i_addr2); if (ni == NULL) return; IEEE80211_DPRINTF(("%s: new req from %s\n", __func__, ether_sprintf(wh->i_addr2))); allocbs = 1; } else allocbs = 0; ni->ni_rssi = rssi; ni->ni_rstamp = rstamp; ni->ni_rantenna = rantenna; rate = ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); if (rate & IEEE80211_RATE_BASIC) { IEEE80211_DPRINTF(("%s: rate negotiation failed: %s\n", __func__,ether_sprintf(wh->i_addr2))); } else { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0); } if (allocbs && ic->ic_opmode == IEEE80211_M_HOSTAP) ieee80211_free_node(ic, ni); else ieee80211_unref_node(&ni); break; } case IEEE80211_FC0_SUBTYPE_AUTH: { u_int16_t algo, seq, status; /* * auth frame format * [2] algorithm * [2] sequence * [2] status * [tlv*] challenge */ IEEE80211_VERIFY_LENGTH(efrm - frm, 6); algo = le16toh(*(u_int16_t *)frm); seq = le16toh(*(u_int16_t *)(frm + 2)); status = le16toh(*(u_int16_t *)(frm + 4)); if (algo != IEEE80211_AUTH_ALG_OPEN) { /* TODO: shared key auth */ IEEE80211_DPRINTF(("%s: unsupported auth %d from %s\n", __func__, algo, ether_sprintf(wh->i_addr2))); return; } switch (ic->ic_opmode) { case IEEE80211_M_IBSS: if (ic->ic_state != IEEE80211_S_RUN || seq != 1) return; - ieee80211_new_state(&ic->ic_if, IEEE80211_S_AUTH, + ieee80211_new_state(ic, IEEE80211_S_AUTH, wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); break; case IEEE80211_M_AHDEMO: /* should not come here */ break; case IEEE80211_M_HOSTAP: if (ic->ic_state != IEEE80211_S_RUN || seq != 1) return; allocbs = 0; ni = ieee80211_find_node(ic, wh->i_addr2); if (ni == NULL) { ni = ieee80211_alloc_node(ic, wh->i_addr2); if (ni == NULL) return; IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid); ni->ni_rssi = rssi; ni->ni_rstamp = rstamp; ni->ni_rantenna = rantenna; ni->ni_chan = ic->ic_bss->ni_chan; allocbs = 1; } IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 2); if (ifp->if_flags & IFF_DEBUG) if_printf(ifp, "station %s %s authenticated\n", (allocbs ? "newly" : "already"), ether_sprintf(ni->ni_macaddr)); ieee80211_unref_node(&ni); break; case IEEE80211_M_STA: if (ic->ic_state != IEEE80211_S_AUTH || seq != 2) return; if (status != 0) { if_printf(&ic->ic_if, "authentication failed (reason %d) for %s\n", status, ether_sprintf(wh->i_addr3)); ni = ieee80211_find_node(ic, wh->i_addr2); if (ni != NULL) { ni->ni_fails++; ieee80211_unref_node(&ni); } return; } - ieee80211_new_state(&ic->ic_if, IEEE80211_S_ASSOC, + ieee80211_new_state(ic, IEEE80211_S_ASSOC, wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); break; } break; } case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { u_int16_t capinfo, bintval; if (ic->ic_opmode != IEEE80211_M_HOSTAP || (ic->ic_state != IEEE80211_S_RUN)) return; if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { reassoc = 1; resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP; } else { reassoc = 0; resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP; } /* * asreq frame format * [2] capability information * [2] listen interval * [6*] current AP address (reassoc only) * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates */ IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4)); if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) { IEEE80211_DPRINTF(("%s: ignore other bss from %s\n", __func__, ether_sprintf(wh->i_addr2))); return; } capinfo = le16toh(*(u_int16_t *)frm); frm += 2; bintval = le16toh(*(u_int16_t *)frm); frm += 2; if (reassoc) frm += 6; /* ignore current AP info */ ssid = rates = xrates = NULL; while (frm < efrm) { switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; break; case IEEE80211_ELEMID_RATES: rates = frm; break; case IEEE80211_ELEMID_XRATES: xrates = frm; break; } frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_SIZE); IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); if (ssid[1] != ic->ic_bss->ni_esslen || memcmp(ssid + 2, ic->ic_bss->ni_essid, ssid[1]) != 0) { #ifdef IEEE80211_DEBUG if (ieee80211_debug) { printf("%s: ssid unmatch ", __func__); ieee80211_print_essid(ssid + 2, ssid[1]); printf(" from %s\n", ether_sprintf(wh->i_addr2)); } #endif return; } ni = ieee80211_find_node(ic, wh->i_addr2); if (ni == NULL) { IEEE80211_DPRINTF(("%s: not authenticated for %s\n", __func__, ether_sprintf(wh->i_addr2))); ni = ieee80211_dup_bss(ic, wh->i_addr2); if (ni == NULL) return; IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_ASSOC_NOT_AUTHED); ieee80211_free_node(ic, ni); return; } /* XXX per-node cipher suite */ /* XXX some stations use the privacy bit for handling APs that suport both encrypted and unencrypted traffic */ if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 || (capinfo & IEEE80211_CAPINFO_PRIVACY) != ((ic->ic_flags & IEEE80211_F_WEPON) ? IEEE80211_CAPINFO_PRIVACY : 0)) { IEEE80211_DPRINTF(("%s: capability mismatch %x for %s\n", __func__, capinfo, ether_sprintf(wh->i_addr2))); ni->ni_associd = 0; IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO); ieee80211_unref_node(&ni); return; } ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); if (ni->ni_rates.rs_nrates == 0) { IEEE80211_DPRINTF(("%s: rate unmatch for %s\n", __func__, ether_sprintf(wh->i_addr2))); ni->ni_associd = 0; IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE); ieee80211_unref_node(&ni); return; } ni->ni_rssi = rssi; ni->ni_rstamp = rstamp; ni->ni_rantenna = rantenna; ni->ni_intval = bintval; ni->ni_capinfo = capinfo; ni->ni_chan = ic->ic_bss->ni_chan; ni->ni_fhdwell = ic->ic_bss->ni_fhdwell; ni->ni_fhindex = ic->ic_bss->ni_fhindex; if (ni->ni_associd == 0) { /* XXX handle rollover at 2007 */ /* XXX guarantee uniqueness */ ni->ni_associd = 0xc000 | ic->ic_bss->ni_associd++; newassoc = 1; } else newassoc = 0; /* XXX for 11g must turn off short slot time if long slot time sta associates */ IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS); if (ifp->if_flags & IFF_DEBUG) if_printf(ifp, "station %s %s associated\n", (newassoc ? "newly" : "already"), ether_sprintf(ni->ni_macaddr)); /* give driver a chance to setup state like ni_txrate */ if (ic->ic_newassoc) (*ic->ic_newassoc)(ic, ni, newassoc); ieee80211_unref_node(&ni); break; } case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { u_int16_t status; if (ic->ic_opmode != IEEE80211_M_STA || ic->ic_state != IEEE80211_S_ASSOC) return; /* * asresp frame format * [2] capability information * [2] status * [2] association ID * [tlv] supported rates * [tlv] extended supported rates */ IEEE80211_VERIFY_LENGTH(efrm - frm, 6); ni = ic->ic_bss; ni->ni_capinfo = le16toh(*(u_int16_t *)frm); frm += 2; status = le16toh(*(u_int16_t *)frm); frm += 2; if (status != 0) { if_printf(ifp, "association failed (reason %d) for %s\n", status, ether_sprintf(wh->i_addr3)); ni = ieee80211_find_node(ic, wh->i_addr2); if (ni != NULL) { ni->ni_fails++; ieee80211_unref_node(&ni); } return; } ni->ni_associd = le16toh(*(u_int16_t *)frm); frm += 2; rates = xrates = NULL; while (frm < efrm) { switch (*frm) { case IEEE80211_ELEMID_RATES: rates = frm; break; case IEEE80211_ELEMID_XRATES: xrates = frm; break; } frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_SIZE); ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); if (ni->ni_rates.rs_nrates != 0) - ieee80211_new_state(ifp, IEEE80211_S_RUN, + ieee80211_new_state(ic, IEEE80211_S_RUN, wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); break; } case IEEE80211_FC0_SUBTYPE_DEAUTH: { u_int16_t reason; /* * deauth frame format * [2] reason */ IEEE80211_VERIFY_LENGTH(efrm - frm, 2); reason = le16toh(*(u_int16_t *)frm); switch (ic->ic_opmode) { case IEEE80211_M_STA: - ieee80211_new_state(&ic->ic_if, IEEE80211_S_AUTH, + ieee80211_new_state(ic, IEEE80211_S_AUTH, wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); break; case IEEE80211_M_HOSTAP: ni = ieee80211_find_node(ic, wh->i_addr2); if (ni != NULL) { if (ifp->if_flags & IFF_DEBUG) if_printf(ifp, "station %s deauthenticated" " by peer (reason %d)\n", ether_sprintf(ni->ni_macaddr), reason); ieee80211_free_node(ic, ni); } break; default: break; } break; } case IEEE80211_FC0_SUBTYPE_DISASSOC: { u_int16_t reason; /* * disassoc frame format * [2] reason */ IEEE80211_VERIFY_LENGTH(efrm - frm, 2); reason = le16toh(*(u_int16_t *)frm); switch (ic->ic_opmode) { case IEEE80211_M_STA: - ieee80211_new_state(&ic->ic_if, IEEE80211_S_ASSOC, + ieee80211_new_state(ic, IEEE80211_S_ASSOC, wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); break; case IEEE80211_M_HOSTAP: ni = ieee80211_find_node(ic, wh->i_addr2); if (ni != NULL) { if (ifp->if_flags & IFF_DEBUG) if_printf(ifp, "station %s disassociated" " by peer (reason %d)\n", ether_sprintf(ni->ni_macaddr), reason); ni->ni_associd = 0; ieee80211_unref_node(&ni); } break; default: break; } break; } default: IEEE80211_DPRINTF(("%s: mgmt frame with subtype 0x%x not " "handled\n", __func__, subtype)); break; } #undef ISPROBE } #undef IEEE80211_VERIFY_LENGTH #undef IEEE80211_VERIFY_ELEMENT Index: head/sys/net80211/ieee80211_ioctl.c =================================================================== --- head/sys/net80211/ieee80211_ioctl.c (revision 117810) +++ head/sys/net80211/ieee80211_ioctl.c (revision 117811) @@ -1,988 +1,988 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 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 #include #include #include #include #include #include #include #include #include #include #include #include /* * XXX * Wireless LAN specific configuration interface, which is compatible * with wicontrol(8). */ int ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ieee80211com *ic = (void *)ifp; int i, j, error; struct ifreq *ifr = (struct ifreq *)data; struct wi_req wreq; struct wi_ltv_keys *keys; struct wi_apinfo *ap; struct ieee80211_node *ni; struct ieee80211_rateset *rs; struct wi_sigcache wsc; struct wi_scan_p2_hdr *p2; struct wi_scan_res *res; 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_bss->ni_chan)); wreq.wi_len = 1; break; case WI_RID_COMMS_QUALITY: wreq.wi_val[0] = 0; /* quality */ wreq.wi_val[1] = htole16(ic->ic_bss->ni_rssi); /* signal */ 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 == -1) 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(1); /* enabled ... not supported */ 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((ic->ic_caps & IEEE80211_C_WEP) ? 1 : 0); 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_WEPON) ? 1 : 0); wreq.wi_len = 1; break; case WI_RID_TX_CRYPT_KEY: wreq.wi_val[0] = htole16(ic->ic_wep_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_len); memcpy(keys->wi_keys[i].wi_keydat, ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len); } wreq.wi_len = sizeof(*keys) / 2; break; case WI_RID_MAX_DATALEN: wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN); /* TODO: frag */ wreq.wi_len = 1; break; case WI_RID_IFACE_STATS: /* XXX: should be implemented in lower drivers */ break; case WI_RID_READ_APS: if (ic->ic_opmode != IEEE80211_M_HOSTAP) { /* * Don't return results until active scan completes. */ if (ic->ic_state == IEEE80211_S_SCAN && (ic->ic_flags & IEEE80211_F_ASCAN)) { error = EINPROGRESS; break; } } i = 0; ap = (void *)((char *)wreq.wi_val + sizeof(i)); TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1)) break; memset(ap, 0, sizeof(*ap)); 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 = ni->ni_rssi; 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 */ } } i++; ap++; } memcpy(wreq.wi_val, &i, sizeof(i)); wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2; break; case WI_RID_PRISM2: wreq.wi_val[0] = 1; /* XXX lie so SCAN_RES can give rates */ wreq.wi_len = sizeof(u_int16_t) / 2; break; case WI_RID_SCAN_RES: /* compatibility interface */ if (ic->ic_opmode != IEEE80211_M_HOSTAP && ic->ic_state == IEEE80211_S_SCAN) { error = EINPROGRESS; break; } /* NB: we use the Prism2 format so we can return rate info */ p2 = (struct wi_scan_p2_hdr *)wreq.wi_val; res = (void *)&p2[1]; i = 0; TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { if ((caddr_t)(res + 1) > (caddr_t)(&wreq + 1)) break; res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan); res->wi_noise = 0; res->wi_signal = ni->ni_rssi; 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; res++, i++; } p2->wi_rsvd = 0; p2->wi_reason = i; wreq.wi_len = (sizeof(*p2) + sizeof(*res) * i) / 2; break; case WI_RID_READ_CACHE: i = 0; TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { if (i == (WI_MAX_DATALEN/sizeof(struct wi_sigcache))-1) break; IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr); memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc)); wsc.signal = ni->ni_rssi; wsc.noise = 0; wsc.quality = 0; memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i, &wsc, sizeof(wsc)); i++; } wreq.wi_len = sizeof(wsc) * i / 2; break; case WI_RID_SCAN_APS: error = EINVAL; 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 } int ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ieee80211com *ic = (void *)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_flags & IEEE80211_F_SIBSS) 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 = ENETRESET; } 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 = -1; 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 = ENETRESET; 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 = ENETRESET; } } 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 = ENETRESET; } } } 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; if (le16toh(wreq.wi_val[0]) != 1) return EINVAL; /* not supported */ 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 = ENETRESET; } } else { if (ic->ic_flags & IEEE80211_F_PMGTON) { ic->ic_flags &= ~IEEE80211_F_PMGTON; error = ENETRESET; } } 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 = ENETRESET; 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; if (le16toh(wreq.wi_val[0]) != 1) return EINVAL; /* TODO: shared key auth */ 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_WEPON) == 0) { ic->ic_flags |= IEEE80211_F_WEPON; error = ENETRESET; } } else { if (ic->ic_flags & IEEE80211_F_WEPON) { ic->ic_flags &= ~IEEE80211_F_WEPON; 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_wep_txkey = i; 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 > sizeof(ic->ic_nw_keys[i].wk_key)) return EINVAL; } memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys)); for (i = 0; i < IEEE80211_WEP_NKID; i++) { len = le16toh(keys->wi_keys[i].wi_keylen); ic->ic_nw_keys[i].wk_len = len; memcpy(ic->ic_nw_keys[i].wk_key, keys->wi_keys[i].wi_keydat, len); } 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; if (len != IEEE80211_MAX_LEN) return EINVAL; /* TODO: fragment */ ic->ic_fragthreshold = len; error = ENETRESET; break; case WI_RID_IFACE_STATS: error = EPERM; break; case WI_RID_SCAN_REQ: /* XXX wicontrol */ if (ic->ic_opmode == IEEE80211_M_HOSTAP) break; /* NB: ignore channel list and tx rate parameters */ - error = ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1); + 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); } memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); if (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]; break; } } if (isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan))) ic->ic_bss->ni_chan = ic->ic_ibss_chan; if (wreq.wi_type == WI_RID_CHANNEL_LIST) error = ENETRESET; else - error = ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1); + error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); break; default: error = EINVAL; break; } return error; } int ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ieee80211com *ic = (void *)ifp; int error = 0; u_int kid, len; struct ieee80211req *ireq; u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; char tmpssid[IEEE80211_NWID_LEN]; struct ieee80211_channel *chan; switch (cmd) { case SIOCSIFMEDIA: case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, (struct ifreq *) data, &ic->ic_media, cmd); break; case SIOCG80211: ireq = (struct ieee80211req *) data; 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_caps & IEEE80211_C_WEP) == 0) { ireq->i_val = IEEE80211_WEP_NOSUP; } else { if (ic->ic_flags & IEEE80211_F_WEPON) { ireq->i_val = IEEE80211_WEP_MIXED; } else { ireq->i_val = IEEE80211_WEP_OFF; } } break; case IEEE80211_IOC_WEPKEY: if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { error = EINVAL; break; } kid = (u_int) ireq->i_val; if (kid >= IEEE80211_WEP_NKID) { error = EINVAL; break; } len = (u_int) ic->ic_nw_keys[kid].wk_len; /* NB: only root can read WEP keys */ if (suser(curthread)) { 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: if ((ic->ic_caps & IEEE80211_C_WEP) == 0) error = EINVAL; else ireq->i_val = IEEE80211_WEP_NKID; break; case IEEE80211_IOC_WEPTXKEY: if ((ic->ic_caps & IEEE80211_C_WEP) == 0) error = EINVAL; else ireq->i_val = ic->ic_wep_txkey; break; case IEEE80211_IOC_AUTHMODE: ireq->i_val = IEEE80211_AUTH_OPEN; break; case IEEE80211_IOC_CHANNEL: switch (ic->ic_state) { case IEEE80211_S_INIT: case IEEE80211_S_SCAN: if (ic->ic_opmode == IEEE80211_M_STA) chan = ic->ic_des_chan; else chan = ic->ic_ibss_chan; break; default: chan = ic->ic_bss->ni_chan; break; } ireq->i_val = ieee80211_chan2ieee(ic, chan); 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_IOCT_RTSTHRESHOLD: ireq->i_val = ic->ic_rtsthreshold; break; default: error = EINVAL; } break; case SIOCS80211: error = suser(curthread); if (error) break; ireq = (struct ieee80211req *) data; switch (ireq->i_type) { case IEEE80211_IOC_SSID: if (ireq->i_val != 0 || ireq->i_len > IEEE80211_NWID_LEN) { error = EINVAL; break; } 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: /* * These cards only support one mode so * we just turn wep on if what ever is * passed in is not OFF. */ if (ireq->i_val == IEEE80211_WEP_OFF) { ic->ic_flags &= ~IEEE80211_F_WEPON; } else { ic->ic_flags |= IEEE80211_F_WEPON; } error = ENETRESET; break; case IEEE80211_IOC_WEPKEY: if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { error = EINVAL; break; } kid = (u_int) ireq->i_val; if (kid >= IEEE80211_WEP_NKID) { error = EINVAL; break; } if (ireq->i_len > sizeof(tmpkey)) { error = EINVAL; break; } memset(tmpkey, 0, sizeof(tmpkey)); error = copyin(ireq->i_data, tmpkey, ireq->i_len); if (error) break; memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey, sizeof(tmpkey)); ic->ic_nw_keys[kid].wk_len = ireq->i_len; error = ENETRESET; break; case IEEE80211_IOC_WEPTXKEY: kid = (u_int) ireq->i_val; if (kid >= IEEE80211_WEP_NKID) { error = EINVAL; break; } ic->ic_wep_txkey = kid; error = ENETRESET; break; #if 0 case IEEE80211_IOC_AUTHMODE: sc->wi_authmode = ireq->i_val; break; #endif 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)) { error = EINVAL; break; } 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 (ic->ic_opmode == IEEE80211_M_STA) { if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && ic->ic_bss->ni_chan != ic->ic_des_chan) error = ENETRESET; } else { if (ic->ic_bss->ni_chan != ic->ic_ibss_chan) error = ENETRESET; } break; } 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) { error = EINVAL; break; } ic->ic_lintval = ireq->i_val; error = ENETRESET; break; case IEEE80211_IOCT_RTSTHRESHOLD: if (!(IEEE80211_RTS_MIN < ireq->i_val && ireq->i_val < IEEE80211_RTS_MAX)) { error = EINVAL; break; } ic->ic_rtsthreshold = ireq->i_val; error = ENETRESET; break; default: error = EINVAL; break; } break; case SIOCGIFGENERIC: error = ieee80211_cfgget(ifp, cmd, data); break; case SIOCSIFGENERIC: error = suser(curthread); if (error) break; error = ieee80211_cfgset(ifp, cmd, data); break; default: error = ether_ioctl(ifp, cmd, data); break; } return error; } Index: head/sys/net80211/ieee80211_node.c =================================================================== --- head/sys/net80211/ieee80211_node.c (revision 117810) +++ head/sys/net80211/ieee80211_node.c (revision 117811) @@ -1,554 +1,560 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 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$"); #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #endif static struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *); static void ieee80211_node_free(struct ieee80211com *, struct ieee80211_node *); static void ieee80211_node_copy(struct ieee80211com *, struct ieee80211_node *, const struct ieee80211_node *); static void ieee80211_setup_node(struct ieee80211com *ic, struct ieee80211_node *ni, u_int8_t *macaddr); static void _ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *); void ieee80211_node_attach(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; /* XXX need unit */ mtx_init(&ic->ic_nodelock, ifp->if_name, "802.11 node table", MTX_DEF); TAILQ_INIT(&ic->ic_node); ic->ic_node_alloc = ieee80211_node_alloc; ic->ic_node_free = ieee80211_node_free; ic->ic_node_copy = ieee80211_node_copy; ic->ic_bss = (*ic->ic_node_alloc)(ic); KASSERT(ic->ic_bss != NULL, ("unable to setup inital BSS node")); ic->ic_bss->ni_chan = IEEE80211_CHAN_ANYC; } void ieee80211_node_detach(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; if (ic->ic_bss != NULL) (*ic->ic_node_free)(ic, ic->ic_bss); ieee80211_free_allnodes(ic); mtx_destroy(&ic->ic_nodelock); } /* * AP scanning support. */ /* * Initialize the active channel set based on the set * of available channels and the current PHY mode. */ -void +static void ieee80211_reset_scan(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; memcpy(ic->ic_chan_scan, ic->ic_chan_active, sizeof(ic->ic_chan_active)); + /* NB: hack, setup so next_scan starts with the first channel */ + if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC) + ic->ic_bss->ni_chan = &ic->ic_channels[IEEE80211_CHAN_MAX]; } /* * Begin an active scan. */ void -ieee80211_begin_scan(struct ifnet *ifp, struct ieee80211_node *ni) +ieee80211_begin_scan(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; + /* + * In all but hostap mode scanning starts off in + * an active mode before switching to passive. + */ + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + ic->ic_flags |= IEEE80211_F_ASCAN; if (ifp->if_flags & IFF_DEBUG) if_printf(ifp, "begin %s scan\n", - ic->ic_opmode != IEEE80211_M_HOSTAP ? + (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive"); - - ieee80211_reset_scan(ifp); /* - * Flush any previously seen AP's. Note that this - * assumes we don't act as both an AP and a station, - * otherwise we'll potentially flush state of stations - * associated with us. + * Clear scan state and flush any previously seen + * AP's. Note that the latter assumes we don't act + * as both an AP and a station, otherwise we'll + * potentially flush state of stations associated + * with us. */ + ieee80211_reset_scan(ifp); ieee80211_free_allnodes(ic); - clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, ni->ni_chan)); - if (ic->ic_opmode != IEEE80211_M_HOSTAP) { - ic->ic_flags |= IEEE80211_F_ASCAN; - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); - } + /* Scan the next channel. */ + ieee80211_next_scan(ifp); } /* * Switch to the next channel marked for scanning. */ void ieee80211_next_scan(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; struct ieee80211_channel *chan; chan = ic->ic_bss->ni_chan; for (;;) { if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX]) chan = &ic->ic_channels[0]; if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) { /* * Honor channels marked passive-only * during an active scan. */ if ((ic->ic_flags & IEEE80211_F_ASCAN) == 0 || (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) break; } if (chan == ic->ic_bss->ni_chan) { ieee80211_end_scan(ifp); return; } } clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan)); IEEE80211_DPRINTF(("ieee80211_next_scan: chan %d->%d\n", ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan), ieee80211_chan2ieee(ic, chan))); ic->ic_bss->ni_chan = chan; - ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1); + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); } void ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) { struct ieee80211_node *ni; struct ifnet *ifp = &ic->ic_if; ni = ic->ic_bss; if (ifp->if_flags & IFF_DEBUG) if_printf(ifp, "creating ibss\n"); ic->ic_flags |= IEEE80211_F_SIBSS; ni->ni_chan = chan; ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr); IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr); if (ic->ic_opmode == IEEE80211_M_IBSS) ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */ ni->ni_esslen = ic->ic_des_esslen; memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen); ni->ni_rssi = 0; ni->ni_rstamp = 0; ni->ni_rantenna = 0; memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp)); ni->ni_intval = ic->ic_lintval; ni->ni_capinfo = IEEE80211_CAPINFO_IBSS; if (ic->ic_flags & IEEE80211_F_WEPON) ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; if (ic->ic_phytype == IEEE80211_T_FH) { ni->ni_fhdwell = 200; /* XXX */ ni->ni_fhindex = 1; } - ieee80211_new_state(ifp, IEEE80211_S_RUN, -1); + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); } /* * Complete a scan of potential channels. */ void ieee80211_end_scan(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; struct ieee80211_node *ni, *nextbs, *selbs; u_int8_t rate; int i, fail; ic->ic_flags &= ~IEEE80211_F_ASCAN; ni = TAILQ_FIRST(&ic->ic_node); if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX off stack? */ u_char occupied[roundup(IEEE80211_CHAN_MAX, NBBY)]; /* * The passive scan to look for existing AP's completed, * select a channel to camp on. Identify the channels * that already have one or more AP's and try to locate * an unnoccupied one. If that fails, pick a random * channel from the active set. */ for (; ni != NULL; ni = nextbs) { ieee80211_ref_node(ni); nextbs = TAILQ_NEXT(ni, ni_list); setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan)); ieee80211_free_node(ic, ni); } for (i = 0; i < IEEE80211_CHAN_MAX; i++) if (isset(ic->ic_chan_active, i) && isclr(occupied, i)) break; if (i == IEEE80211_CHAN_MAX) { fail = arc4random() & 3; /* random 0-3 */ for (i = 0; i < IEEE80211_CHAN_MAX; i++) if (isset(ic->ic_chan_active, i) && fail-- == 0) break; } ieee80211_create_ibss(ic, &ic->ic_channels[i]); return; } if (ni == NULL) { IEEE80211_DPRINTF(("%s: no scan candidate\n", __func__)); notfound: if (ic->ic_opmode == IEEE80211_M_IBSS && (ic->ic_flags & IEEE80211_F_IBSSON) && ic->ic_des_esslen != 0) { ieee80211_create_ibss(ic, ic->ic_ibss_chan); return; } /* * Reset the list of channels to scan and start again. */ ieee80211_reset_scan(ifp); ieee80211_next_scan(ifp); return; } selbs = NULL; if (ifp->if_flags & IFF_DEBUG) if_printf(ifp, "\tmacaddr bssid chan rssi rate ant flag wep essid\n"); for (; ni != NULL; ni = nextbs) { ieee80211_ref_node(ni); nextbs = TAILQ_NEXT(ni, ni_list); if (ni->ni_fails) { /* * The configuration of the access points may change * during my scan. So delete the entry for the AP * and retry to associate if there is another beacon. */ if (ni->ni_fails++ > 2) ieee80211_free_node(ic, ni); continue; } fail = 0; if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) fail |= 0x01; if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && ni->ni_chan != ic->ic_des_chan) fail |= 0x01; if (ic->ic_opmode == IEEE80211_M_IBSS) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) fail |= 0x02; } else { if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) fail |= 0x02; } if (ic->ic_flags & IEEE80211_F_WEPON) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) fail |= 0x04; } else { if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) fail |= 0x04; } rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO); if (rate & IEEE80211_RATE_BASIC) fail |= 0x08; if (ic->ic_des_esslen != 0 && (ni->ni_esslen != ic->ic_des_esslen || memcmp(ni->ni_essid, ic->ic_des_essid, ic->ic_des_esslen != 0))) fail |= 0x10; if ((ic->ic_flags & IEEE80211_F_DESBSSID) && !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) fail |= 0x20; if (ifp->if_flags & IFF_DEBUG) { printf(" %c %s", fail ? '-' : '+', ether_sprintf(ni->ni_macaddr)); printf(" %s%c", ether_sprintf(ni->ni_bssid), fail & 0x20 ? '!' : ' '); printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan), fail & 0x01 ? '!' : ' '); printf(" %+4d", ni->ni_rssi); printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, fail & 0x08 ? '!' : ' '); printf(" %3d", ni->ni_rantenna); printf(" %4s%c", (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : "????", fail & 0x02 ? '!' : ' '); printf(" %3s%c ", (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? "wep" : "no", fail & 0x04 ? '!' : ' '); ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); printf("%s\n", fail & 0x10 ? "!" : ""); } if (!fail) { if (selbs == NULL) selbs = ni; else if (ni->ni_rssi > selbs->ni_rssi) { ieee80211_unref_node(&selbs); selbs = ni; } else ieee80211_unref_node(&ni); } else { ieee80211_unref_node(&ni); } } if (selbs == NULL) goto notfound; (*ic->ic_node_copy)(ic, ic->ic_bss, selbs); if (ic->ic_opmode == IEEE80211_M_IBSS) { ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); if (ic->ic_bss->ni_rates.rs_nrates == 0) { selbs->ni_fails++; ieee80211_unref_node(&selbs); goto notfound; } ieee80211_unref_node(&selbs); - ieee80211_new_state(ifp, IEEE80211_S_RUN, -1); + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); } else { ieee80211_unref_node(&selbs); - ieee80211_new_state(ifp, IEEE80211_S_AUTH, -1); + ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); } } static struct ieee80211_node * ieee80211_node_alloc(struct ieee80211com *ic) { return malloc(sizeof(struct ieee80211_node), M_DEVBUF, M_NOWAIT | M_ZERO); } static void ieee80211_node_free(struct ieee80211com *ic, struct ieee80211_node *ni) { free(ni, M_DEVBUF); } static void ieee80211_node_copy(struct ieee80211com *ic, struct ieee80211_node *dst, const struct ieee80211_node *src) { *dst = *src; } static void ieee80211_setup_node(struct ieee80211com *ic, struct ieee80211_node *ni, u_int8_t *macaddr) { int hash; IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); hash = IEEE80211_NODE_HASH(macaddr); ni->ni_refcnt = 1; /* mark referenced */ mtx_lock(&ic->ic_nodelock); TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list); LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash); /* * Note we don't enable the inactive timer when acting * as a station. Nodes created in this mode represent * AP's identified while scanning. If we time them out * then several things happen: we can't return the data * to users to show the list of AP's we encountered, and * more importantly, we'll incorrectly deauthenticate * ourself because the inactivity timer will kick us off. */ if (ic->ic_opmode != IEEE80211_M_STA) ic->ic_inact_timer = IEEE80211_INACT_WAIT; mtx_unlock(&ic->ic_nodelock); } struct ieee80211_node * ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr) { struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); if (ni != NULL) ieee80211_setup_node(ic, ni, macaddr); return ni; } struct ieee80211_node * ieee80211_dup_bss(struct ieee80211com *ic, u_int8_t *macaddr) { struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); if (ni != NULL) { memcpy(ni, ic->ic_bss, sizeof(struct ieee80211_node)); ieee80211_setup_node(ic, ni, macaddr); } return ni; } struct ieee80211_node * ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr) { struct ieee80211_node *ni; int hash; hash = IEEE80211_NODE_HASH(macaddr); mtx_lock(&ic->ic_nodelock); LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { atomic_add_int(&ni->ni_refcnt, 1); /* mark referenced */ break; } } mtx_unlock(&ic->ic_nodelock); return ni; } /* * Like find but search based on the channel too. */ struct ieee80211_node * ieee80211_lookup_node(struct ieee80211com *ic, u_int8_t *macaddr, struct ieee80211_channel *chan) { struct ieee80211_node *ni; int hash; hash = IEEE80211_NODE_HASH(macaddr); mtx_lock(&ic->ic_nodelock); LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && ni->ni_chan == chan) { atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */ break; } } mtx_unlock(&ic->ic_nodelock); return ni; } static void _ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) { TAILQ_REMOVE(&ic->ic_node, ni, ni_list); LIST_REMOVE(ni, ni_hash); if (TAILQ_EMPTY(&ic->ic_node)) ic->ic_inact_timer = 0; (*ic->ic_node_free)(ic, ni); } void ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) { /* XXX need equivalent of atomic_dec_and_test */ atomic_subtract_int(&ni->ni_refcnt, 1); if (atomic_cmpset_int(&ni->ni_refcnt, 0, 1)) { mtx_lock(&ic->ic_nodelock); _ieee80211_free_node(ic, ni); mtx_unlock(&ic->ic_nodelock); } } void ieee80211_free_allnodes(struct ieee80211com *ic) { struct ieee80211_node *ni; mtx_lock(&ic->ic_nodelock); while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL) _ieee80211_free_node(ic, ni); mtx_unlock(&ic->ic_nodelock); } void ieee80211_timeout_nodes(struct ieee80211com *ic) { struct ieee80211_node *ni, *nextbs; mtx_lock(&ic->ic_nodelock); for (ni = TAILQ_FIRST(&ic->ic_node); ni != NULL;) { if (++ni->ni_inact <= IEEE80211_INACT_MAX) { ni = TAILQ_NEXT(ni, ni_list); continue; } /* NB: don't honor reference count */ IEEE80211_DPRINTF(("station %s timed out " "due to inactivity (%u secs)\n", ether_sprintf(ni->ni_macaddr), ni->ni_inact)); nextbs = TAILQ_NEXT(ni, ni_list); IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_AUTH_EXPIRE); _ieee80211_free_node(ic, ni); ni = nextbs; } if (!TAILQ_EMPTY(&ic->ic_node)) ic->ic_inact_timer = IEEE80211_INACT_WAIT; mtx_unlock(&ic->ic_nodelock); } void ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *f, void *arg) { struct ieee80211_node *ni; mtx_lock(&ic->ic_nodelock); TAILQ_FOREACH(ni, &ic->ic_node, ni_list) (*f)(arg, ni); mtx_unlock(&ic->ic_nodelock); } Index: head/sys/net80211/ieee80211_node.h =================================================================== --- head/sys/net80211/ieee80211_node.h (revision 117810) +++ head/sys/net80211/ieee80211_node.h (revision 117811) @@ -1,148 +1,147 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 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_NODE_H_ #define _NET80211_IEEE80211_NODE_H_ #define IEEE80211_PSCAN_WAIT 5 /* passive scan wait */ #define IEEE80211_TRANS_WAIT 5 /* transition wait */ #define IEEE80211_INACT_WAIT 5 /* inactivity timer interval */ #define IEEE80211_INACT_MAX (300/IEEE80211_INACT_WAIT) #define IEEE80211_NODE_HASHSIZE 32 /* simple hash is enough for variation of macaddr */ #define IEEE80211_NODE_HASH(addr) \ (((u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % IEEE80211_NODE_HASHSIZE) #define IEEE80211_RATE_SIZE 8 /* 802.11 standard */ #define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */ struct ieee80211_rateset { u_int8_t rs_nrates; u_int8_t rs_rates[IEEE80211_RATE_MAXSIZE]; }; /* * Node specific information. Note that drivers are expected * to derive from this structure to add device-specific per-node * state. This is done by overriding the ic_node_* methods in * the ieee80211com structure. */ struct ieee80211_node { TAILQ_ENTRY(ieee80211_node) ni_list; LIST_ENTRY(ieee80211_node) ni_hash; u_int ni_refcnt; /* hardware */ u_int8_t ni_rssi; /* recv ssi */ u_int32_t ni_rstamp; /* recv timestamp */ u_int8_t ni_rantenna; /* recv antenna */ /* header */ u_int8_t ni_macaddr[IEEE80211_ADDR_LEN]; u_int8_t ni_bssid[IEEE80211_ADDR_LEN]; /* beacon, probe response */ u_int8_t ni_tstamp[8]; /* from last rcv'd beacon */ u_int16_t ni_intval; /* beacon interval */ u_int16_t ni_capinfo; /* capabilities */ u_int8_t ni_esslen; u_int8_t ni_essid[IEEE80211_NWID_LEN]; struct ieee80211_rateset ni_rates; /* negotiated rate set */ u_int8_t *ni_country; /* country information XXX */ struct ieee80211_channel *ni_chan; u_int16_t ni_fhdwell; /* FH only */ u_int8_t ni_fhindex; /* FH only */ u_int8_t ni_erp; /* 11g only */ #ifdef notyet /* DTIM and contention free period (CFP) */ u_int8_t ni_dtimperiod; u_int8_t ni_cfpperiod; /* # of DTIMs between CFPs */ u_int16_t ni_cfpduremain; /* remaining cfp duration */ u_int16_t ni_cfpmaxduration;/* max CFP duration in TU */ u_int16_t ni_nextdtim; /* time to next DTIM */ u_int16_t ni_timoffset; #endif /* others */ u_int16_t ni_associd; /* assoc response */ u_int16_t ni_txseq; /* seq to be transmitted */ u_int16_t ni_rxseq; /* seq previous received */ int ni_fails; /* failure count to associate */ int ni_inact; /* inactivity mark count */ int ni_txrate; /* index to ni_rates[] */ }; static __inline struct ieee80211_node * ieee80211_ref_node(struct ieee80211_node *ni) { atomic_add_int(&ni->ni_refcnt, 1); return ni; } static __inline void ieee80211_unref_node(struct ieee80211_node **ni) { atomic_subtract_int(&(*ni)->ni_refcnt, 1); *ni = NULL; /* guard against use */ } struct ieee80211com; extern void ieee80211_node_attach(struct ifnet *); extern void ieee80211_node_detach(struct ifnet *); -extern void ieee80211_reset_scan(struct ifnet *); -extern void ieee80211_begin_scan(struct ifnet *, struct ieee80211_node *); +extern void ieee80211_begin_scan(struct ifnet *); extern void ieee80211_next_scan(struct ifnet *); extern void ieee80211_end_scan(struct ifnet *); extern struct ieee80211_node *ieee80211_alloc_node(struct ieee80211com *, u_int8_t *); extern struct ieee80211_node *ieee80211_dup_bss(struct ieee80211com *, u_int8_t *); extern struct ieee80211_node *ieee80211_find_node(struct ieee80211com *, u_int8_t *); extern struct ieee80211_node * ieee80211_lookup_node(struct ieee80211com *, u_int8_t *macaddr, struct ieee80211_channel *); extern void ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *); extern void ieee80211_free_allnodes(struct ieee80211com *); typedef void ieee80211_iter_func(void *, struct ieee80211_node *); extern void ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *, void *); extern void ieee80211_timeout_nodes(struct ieee80211com *); extern void ieee80211_create_ibss(struct ieee80211com* , struct ieee80211_channel *); #endif /* _NET80211_IEEE80211_NODE_H_ */ Index: head/sys/net80211/ieee80211_proto.c =================================================================== --- head/sys/net80211/ieee80211_proto.c (revision 117810) +++ head/sys/net80211/ieee80211_proto.c (revision 117811) @@ -1,504 +1,503 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #endif #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_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 */ +}; +static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); + void ieee80211_proto_attach(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; ifp->if_hdrlen = sizeof(struct ieee80211_frame); #ifdef notdef ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; #else ic->ic_rtsthreshold = IEEE80211_RTS_MAX; #endif ic->ic_fragthreshold = 2346; /* XXX not used yet */ ic->ic_fixed_rate = -1; /* no fixed rate */ mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_name, "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 ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; IF_DRAIN(&ic->ic_mgtq); mtx_destroy(&ic->ic_mgtq.ifq_mtx); } void ieee80211_print_essid(u_int8_t *essid, int len) { int i; u_int8_t *p; 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(u_int8_t *buf, int len, int rate, int rssi) { struct ieee80211_frame *wh; int i; wh = (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((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) printf(" WEP"); 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 ieee80211com *ic, struct ieee80211_node *ni, int flags) { #define RV(v) ((v) & IEEE80211_RATE_VAL) int i, j, ignore, error; int okrate, badrate; struct ieee80211_rateset *srs, *nrs; u_int8_t r; error = 0; okrate = badrate = 0; srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; nrs = &ni->ni_rates; for (i = 0; i < ni->ni_rates.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) { /* * Apply fixed rate constraint. Note that we do * not apply the constraint to basic rates as * otherwise we may not be able to associate if * the rate set we submit to the AP is invalid * (e.g. fix rate at 36Mb/s which is not a basic * rate for 11a operation). */ if ((nrs->rs_rates[i] & IEEE80211_RATE_BASIC) == 0 && ic->ic_fixed_rate >= 0 && r != RV(srs->rs_rates[ic->ic_fixed_rate])) ignore++; } if (flags & IEEE80211_F_DONEGO) { /* * Check against supported rates. */ for (j = 0; j < srs->rs_nrates; j++) { if (r == RV(srs->rs_rates[j])) break; } if (j == srs->rs_nrates) { if (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) return badrate | IEEE80211_RATE_BASIC; else return RV(okrate); #undef RV } -int -ieee80211_new_state(struct ifnet *ifp, enum ieee80211_state nstate, int mgt) +static int +ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt) { - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp = &ic->ic_if; struct ieee80211_node *ni; - int error, ostate; -#ifdef IEEE80211_DEBUG - static const char *stname[] = - { "INIT", "SCAN", "AUTH", "ASSOC", "RUN" }; -#endif + enum ieee80211_state ostate; ostate = ic->ic_state; IEEE80211_DPRINTF(("%s: %s -> %s\n", __func__, - stname[ostate], stname[nstate])); - if (ic->ic_newstate) { - error = (*ic->ic_newstate)(ic->ic_softc, nstate); - if (error == EINPROGRESS) - return 0; - if (error != 0) - return error; - } - - /* state transition */ - ic->ic_state = nstate; + 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); break; case IEEE80211_M_HOSTAP: mtx_lock(&ic->ic_nodelock); TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { if (ni->ni_associd == 0) continue; IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, IEEE80211_REASON_ASSOC_LEAVE); } mtx_unlock(&ic->ic_nodelock); break; default: break; } /* FALLTHRU */ 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: mtx_lock(&ic->ic_nodelock); TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_AUTH_LEAVE); } mtx_unlock(&ic->ic_nodelock); break; default: break; } /* FALLTHRU */ case IEEE80211_S_AUTH: case IEEE80211_S_SCAN: ic->ic_mgt_timer = 0; IF_DRAIN(&ic->ic_mgtq); if (ic->ic_wep_ctx != NULL) { free(ic->ic_wep_ctx, M_DEVBUF); ic->ic_wep_ctx = NULL; } ieee80211_free_allnodes(ic); break; } break; case IEEE80211_S_SCAN: ic->ic_flags &= ~IEEE80211_F_SIBSS; /* initialize bss for probe request */ IEEE80211_ADDR_COPY(ni->ni_macaddr, ifp->if_broadcastaddr); IEEE80211_ADDR_COPY(ni->ni_bssid, ifp->if_broadcastaddr); ni->ni_rates = ic->ic_sup_rates[ ieee80211_chan2mode(ic, ni->ni_chan)]; ni->ni_associd = 0; ni->ni_rstamp = 0; switch (ostate) { case IEEE80211_S_INIT: if (ic->ic_opmode == IEEE80211_M_HOSTAP && 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(ifp, ni); + ieee80211_begin_scan(ifp); } break; case IEEE80211_S_SCAN: /* scan next */ if (ic->ic_flags & IEEE80211_F_ASCAN) { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); } break; case IEEE80211_S_RUN: /* beacon miss */ if (ifp->if_flags & IFF_DEBUG) { /* XXX bssid clobbered above */ if_printf(ifp, "no recent beacons from %s;" " rescanning\n", ether_sprintf(ic->ic_bss->ni_bssid)); } ieee80211_free_allnodes(ic); /* FALLTHRU */ case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: /* timeout restart scan */ ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr); if (ni != NULL) { ni->ni_fails++; ieee80211_unref_node(&ni); } - ieee80211_begin_scan(ifp, ic->ic_bss); + ieee80211_begin_scan(ifp); break; } break; case IEEE80211_S_AUTH: switch (ostate) { case IEEE80211_S_INIT: IEEE80211_DPRINTF(("%s: invalid transition\n", __func__)); break; case IEEE80211_S_SCAN: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); break; case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: switch (mgt) { 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 (mgt) { 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: /* 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(("%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_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); break; } break; case IEEE80211_S_RUN: switch (ostate) { case IEEE80211_S_INIT: case IEEE80211_S_AUTH: case IEEE80211_S_RUN: IEEE80211_DPRINTF(("%s: invalid transition\n", __func__)); 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)); if (ifp->if_flags & IFF_DEBUG) { if_printf(ifp, " "); if (ic->ic_opmode == IEEE80211_M_STA) printf("associated "); else printf("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, ni->ni_chan), IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); } ic->ic_mgt_timer = 0; (*ifp->if_start)(ifp); break; } break; } return 0; } Index: head/sys/net80211/ieee80211_proto.h =================================================================== --- head/sys/net80211/ieee80211_proto.h (revision 117810) +++ head/sys/net80211/ieee80211_proto.h (revision 117811) @@ -1,74 +1,78 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 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_PROTO_H_ #define _NET80211_IEEE80211_PROTO_H_ /* * 802.11 protocol implementation definitions. */ enum ieee80211_state { - IEEE80211_S_INIT, /* default state */ - IEEE80211_S_SCAN, /* scanning */ - IEEE80211_S_AUTH, /* try to authenticate */ - IEEE80211_S_ASSOC, /* try to assoc */ - IEEE80211_S_RUN /* associated */ + IEEE80211_S_INIT = 0, /* default state */ + IEEE80211_S_SCAN = 1, /* scanning */ + IEEE80211_S_AUTH = 2, /* try to authenticate */ + IEEE80211_S_ASSOC = 3, /* try to assoc */ + IEEE80211_S_RUN = 4, /* associated */ }; +#define IEEE80211_S_MAX (IEEE80211_S_RUN+1) #define IEEE80211_SEND_MGMT(_ic,_ni,_type,_arg) \ ((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg)) extern const char *ieee80211_mgt_subtype_name[]; extern void ieee80211_proto_attach(struct ifnet *); extern void ieee80211_proto_detach(struct ifnet *); extern void ieee80211_input(struct ifnet *, struct mbuf *, int, u_int32_t, u_int); extern void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *, int, int, u_int32_t, u_int); extern int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *, int, int); extern int ieee80211_mgmt_output(struct ifnet *, struct ieee80211_node *, struct mbuf *, int); extern struct mbuf *ieee80211_encap(struct ifnet *, struct mbuf *); extern struct mbuf *ieee80211_decap(struct ifnet *, struct mbuf *); extern u_int8_t *ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *); +#define ieee80211_new_state(_ic, _nstate, _arg) \ + (((_ic)->ic_newstate)((_ic), (_nstate), (_arg))) extern u_int8_t *ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *); -extern int ieee80211_new_state(struct ifnet *, enum ieee80211_state, int); extern void ieee80211_print_essid(u_int8_t *, int); extern void ieee80211_dump_pkt(u_int8_t *, int, int, int); + +extern const char *ieee80211_state_name[IEEE80211_S_MAX]; #endif /* _NET80211_IEEE80211_PROTO_H_ */ Index: head/sys/net80211/ieee80211_var.h =================================================================== --- head/sys/net80211/ieee80211_var.h (revision 117810) +++ head/sys/net80211/ieee80211_var.h (revision 117811) @@ -1,261 +1,262 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 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. */ #include #include #include #include #define IEEE80211_CHAN_MAX 255 #define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */ #define IEEE80211_CHAN_ANYC \ ((struct ieee80211_channel *) IEEE80211_CHAN_ANY) enum ieee80211_phytype { IEEE80211_T_DS, /* direct sequence spread spectrum */ IEEE80211_T_FH, /* frequency hopping */ IEEE80211_T_OFDM, /* frequency division multiplexing */ IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */ }; #define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */ /* XXX not really a mode; there are really multiple PHY's */ enum ieee80211_phymode { IEEE80211_MODE_AUTO = 0, /* autoselect */ IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */ IEEE80211_MODE_11B = 2, /* 2GHz, CCK */ IEEE80211_MODE_11G = 3, /* 2GHz, OFDM */ IEEE80211_MODE_TURBO = 4, /* 5GHz, OFDM, 2x clock */ }; #define IEEE80211_MODE_MAX (IEEE80211_MODE_TURBO+1) enum ieee80211_opmode { IEEE80211_M_STA = 1, /* infrastructure station */ IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */ IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */ IEEE80211_M_HOSTAP = 6 /* Software Access Point */ }; /* * Channels are specified by frequency and attributes. */ struct ieee80211_channel { u_int16_t ic_freq; /* setting in Mhz */ u_int16_t ic_flags; /* see below */ }; /* bits 0-3 are for private use by drivers */ /* channel attributes */ #define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ #define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ #define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ #define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ #define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ #define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ #define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ /* * Useful combinations of channel characteristics. */ #define IEEE80211_CHAN_A \ (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM) #define IEEE80211_CHAN_B \ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK) #define IEEE80211_CHAN_PUREG \ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM) #define IEEE80211_CHAN_G \ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN) #define IEEE80211_CHAN_T \ (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO) #define IEEE80211_IS_CHAN_A(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) #define IEEE80211_IS_CHAN_B(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) #define IEEE80211_IS_CHAN_PUREG(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG) #define IEEE80211_IS_CHAN_G(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) #define IEEE80211_IS_CHAN_T(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T) #define IEEE80211_IS_CHAN_2GHZ(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0) #define IEEE80211_IS_CHAN_5GHZ(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0) #define IEEE80211_IS_CHAN_OFDM(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0) #define IEEE80211_IS_CHAN_CCK(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0) /* ni_chan encoding for FH phy */ #define IEEE80211_FH_CHANMOD 80 #define IEEE80211_FH_CHAN(set,pat) (((set)-1)*IEEE80211_FH_CHANMOD+(pat)) #define IEEE80211_FH_CHANSET(chan) ((chan)/IEEE80211_FH_CHANMOD+1) #define IEEE80211_FH_CHANPAT(chan) ((chan)%IEEE80211_FH_CHANMOD) struct ieee80211com { struct arpcom ic_ac; void (*ic_recv_mgmt)(struct ieee80211com *, struct mbuf *, int, int, u_int32_t, u_int); int (*ic_send_mgmt)(struct ieee80211com *, struct ieee80211_node *, int, int); - int (*ic_newstate)(void *, enum ieee80211_state); + int (*ic_newstate)(struct ieee80211com *, + enum ieee80211_state, int); void (*ic_newassoc)(struct ieee80211com *, 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_char ic_chan_avail[roundup(IEEE80211_CHAN_MAX,NBBY)]; u_char ic_chan_active[roundup(IEEE80211_CHAN_MAX, NBBY)]; u_char ic_chan_scan[roundup(IEEE80211_CHAN_MAX,NBBY)]; struct ifqueue ic_mgtq; u_int32_t ic_flags; /* 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 */ 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; int ic_fixed_rate; /* index to ic_sup_rates[] */ u_int16_t ic_rtsthreshold; u_int16_t ic_fragthreshold; struct mtx ic_nodelock; /* on node table */ struct ieee80211_node *(*ic_node_alloc)(struct ieee80211com *); void (*ic_node_free)(struct ieee80211com *, struct ieee80211_node *); void (*ic_node_copy)(struct ieee80211com *, struct ieee80211_node *, const struct ieee80211_node *); TAILQ_HEAD(, ieee80211_node) ic_node; /* information of all nodes */ LIST_HEAD(, ieee80211_node) ic_hash[IEEE80211_NODE_HASHSIZE]; u_int16_t ic_lintval; /* listen 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_txpower; /* tx power setting (dbM) */ u_int16_t ic_bmisstimeout;/* beacon miss threshold (ms) */ 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]; struct ieee80211_wepkey ic_nw_keys[IEEE80211_WEP_NKID]; int ic_wep_txkey; /* default tx key index */ void *ic_wep_ctx; /* wep crypt context */ u_int32_t ic_iv; /* initial vector for wep */ }; #define ic_if ic_ac.ac_if #define ic_softc ic_if.if_softc #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 */ #define IEEE80211_F_ASCAN 0x00000001 /* STATUS: active scan */ #define IEEE80211_F_SIBSS 0x00000002 /* STATUS: start IBSS */ #define IEEE80211_F_WEPON 0x00000100 /* CONF: WEP enabled */ #define IEEE80211_F_IBSSON 0x00000200 /* CONF: IBSS creation enable */ #define IEEE80211_F_PMGTON 0x00000400 /* CONF: Power mgmt enable */ #define IEEE80211_F_DESBSSID 0x00000800 /* CONF: des_bssid is set */ #define IEEE80211_F_SCANAP 0x00001000 /* CONF: Scanning AP */ #define IEEE80211_F_ROAMING 0x00002000 /* CONF: roaming enabled */ #define IEEE80211_F_SWRETRY 0x00004000 /* CONF: sw tx retry enabled */ #define IEEE80211_F_TXPMGT 0x00018000 /* STATUS: tx power */ #define IEEE80211_F_TXPOW_OFF 0x00000000 /* TX Power: radio disabled */ #define IEEE80211_F_TXPOW_FIXED 0x00008000 /* TX Power: fixed rate */ #define IEEE80211_F_TXPOW_AUTO 0x00010000 /* TX Power: undefined */ #define IEEE80211_F_SHSLOT 0x00020000 /* CONF: short slot time */ #define IEEE80211_F_SHPREAMBLE 0x00040000 /* CONF: short preamble */ /* ic_capabilities */ #define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */ #define IEEE80211_C_IBSS 0x00000002 /* CAPABILITY: IBSS available */ #define IEEE80211_C_PMGT 0x00000004 /* CAPABILITY: Power mgmt */ #define IEEE80211_C_HOSTAP 0x00000008 /* CAPABILITY: HOSTAP avail */ #define IEEE80211_C_AHDEMO 0x00000010 /* CAPABILITY: Old Adhoc Demo */ #define IEEE80211_C_SWRETRY 0x00000020 /* CAPABILITY: sw tx retry */ #define IEEE80211_C_TXPMGT 0x00000040 /* CAPABILITY: tx power mgmt */ #define IEEE80211_C_SHSLOT 0x00000080 /* CAPABILITY: short slottime */ #define IEEE80211_C_SHPREAMBLE 0x00000100 /* CAPABILITY: short preamble */ /* flags for ieee80211_fix_rate() */ #define IEEE80211_F_DOSORT 0x00000001 /* sort rate list */ #define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */ #define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */ #define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */ void ieee80211_ifattach(struct ifnet *); void ieee80211_ifdetach(struct ifnet *); void ieee80211_media_init(struct ifnet *, ifm_change_cb_t, ifm_stat_cb_t); int ieee80211_media_change(struct ifnet *); void ieee80211_media_status(struct ifnet *, struct ifmediareq *); int ieee80211_ioctl(struct ifnet *, u_long, caddr_t); int ieee80211_cfgget(struct ifnet *, u_long, caddr_t); int ieee80211_cfgset(struct ifnet *, u_long, caddr_t); void ieee80211_watchdog(struct ifnet *); int ieee80211_fix_rate(struct ieee80211com *, struct ieee80211_node *, int); 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 *); #define IEEE80211_DEBUG #ifdef IEEE80211_DEBUG extern int ieee80211_debug; #define IEEE80211_DPRINTF(X) if (ieee80211_debug) printf X #define IEEE80211_DPRINTF2(X) if (ieee80211_debug>1) printf X #else #define IEEE80211_DPRINTF(X) #define IEEE80211_DPRINTF2(X) #endif #endif /* _NET80211_IEEE80211_VAR_H_ */