summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/net80211/ieee80211.c858
-rw-r--r--sys/net80211/ieee80211.h326
-rw-r--r--sys/net80211/ieee80211_crypto.c313
-rw-r--r--sys/net80211/ieee80211_crypto.h44
-rw-r--r--sys/net80211/ieee80211_input.c1093
-rw-r--r--sys/net80211/ieee80211_ioctl.c992
-rw-r--r--sys/net80211/ieee80211_ioctl.h78
-rw-r--r--sys/net80211/ieee80211_node.c557
-rw-r--r--sys/net80211/ieee80211_node.h142
-rw-r--r--sys/net80211/ieee80211_output.c485
-rw-r--r--sys/net80211/ieee80211_proto.c508
-rw-r--r--sys/net80211/ieee80211_proto.h68
-rw-r--r--sys/net80211/ieee80211_var.h255
13 files changed, 5719 insertions, 0 deletions
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c
new file mode 100644
index 0000000..4cec8d1
--- /dev/null
+++ b/sys/net80211/ieee80211.c
@@ -0,0 +1,858 @@
+/*-
+ * 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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may 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.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * IEEE 802.11 generic handler
+ */
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <machine/atomic.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#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<<IEEE80211_MODE_AUTO;
+ for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
+ c = &ic->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<<IEEE80211_MODE_11A;
+ if (IEEE80211_IS_CHAN_B(c))
+ ic->ic_modecaps |= 1<<IEEE80211_MODE_11B;
+ if (IEEE80211_IS_CHAN_PUREG(c))
+ ic->ic_modecaps |= 1<<IEEE80211_MODE_11G;
+ if (IEEE80211_IS_CHAN_T(c))
+ ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO;
+ }
+ }
+ /* validate ic->ic_curmode */
+ if ((ic->ic_modecaps & (1<<ic->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 (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_printf(&ic->ic_if, "invalid channel freq %u flags %x\n",
+ c->ic_freq, c->ic_flags);
+ 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<<mode)) == 0)
+ continue;
+ mopt = mopts[mode];
+ ADD(ic, IFM_AUTO, mopt); /* e.g. 11a auto */
+ if (ic->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<<newphymode)) == 0)
+ return EINVAL;
+
+ /*
+ * Next, the fixed/variable rate.
+ */
+ i = -1;
+ if (IFM_SUBTYPE(ime->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<<j)) == 0)
+ continue;
+ i = findrate(ic, j, newrate);
+ if (i != -1) {
+ /* lock mode too */
+ newphymode = j;
+ break;
+ }
+ }
+ } else {
+ i = findrate(ic, newphymode, newrate);
+ }
+ if (i == -1) /* mode/rate mismatch */
+ return EINVAL;
+ }
+ /* NB: defer rate setting to later */
+
+ /*
+ * Deduce new operating mode but don't install it just yet.
+ */
+ if ((ime->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<<j)) {
+ newphymode = j;
+ break;
+ }
+ }
+
+ /*
+ * Handle phy mode change.
+ */
+ if (ic->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);
+ 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<<mode)) == 0) {
+ IEEE80211_DPRINTF(("%s: mode %u not supported (caps 0x%x)\n",
+ __func__, mode, ic->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);
diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h
new file mode 100644
index 0000000..324a7a0
--- /dev/null
+++ b/sys/net80211/ieee80211.h
@@ -0,0 +1,326 @@
+/*-
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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_H_
+#define _NET80211_IEEE80211_H_
+
+/*
+ * 802.11 protocol definitions.
+ */
+
+#define IEEE80211_ADDR_LEN 6 /* size of 802.11 address */
+/* is 802.11 address multicast/broadcast? */
+#define IEEE80211_IS_MULTICAST(_a) (*(_a) & 0x01)
+
+/* IEEE 802.11 PLCP header */
+struct ieee80211_plcp_hdr {
+ u_int16_t i_sfd;
+ u_int8_t i_signal;
+ u_int8_t i_service;
+ u_int16_t i_length;
+ u_int16_t i_crc;
+} __attribute__((__packed__));
+
+/*
+ * generic definitions for IEEE 802.11 frames
+ */
+struct ieee80211_frame {
+ u_int8_t i_fc[2];
+ u_int8_t i_dur[2];
+ u_int8_t i_addr1[IEEE80211_ADDR_LEN];
+ u_int8_t i_addr2[IEEE80211_ADDR_LEN];
+ u_int8_t i_addr3[IEEE80211_ADDR_LEN];
+ u_int8_t i_seq[2];
+ /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
+ /* see below */
+} __attribute__((__packed__));
+
+struct ieee80211_frame_addr4 {
+ u_int8_t i_fc[2];
+ u_int8_t i_dur[2];
+ u_int8_t i_addr1[IEEE80211_ADDR_LEN];
+ u_int8_t i_addr2[IEEE80211_ADDR_LEN];
+ u_int8_t i_addr3[IEEE80211_ADDR_LEN];
+ u_int8_t i_seq[2];
+ u_int8_t i_addr4[IEEE80211_ADDR_LEN];
+} __attribute__((__packed__));
+
+#define IEEE80211_FC0_VERSION_MASK 0x03
+#define IEEE80211_FC0_VERSION_SHIFT 0
+#define IEEE80211_FC0_VERSION_0 0x00
+#define IEEE80211_FC0_TYPE_MASK 0x0c
+#define IEEE80211_FC0_TYPE_SHIFT 2
+#define IEEE80211_FC0_TYPE_MGT 0x00
+#define IEEE80211_FC0_TYPE_CTL 0x04
+#define IEEE80211_FC0_TYPE_DATA 0x08
+
+#define IEEE80211_FC0_SUBTYPE_MASK 0xf0
+#define IEEE80211_FC0_SUBTYPE_SHIFT 4
+/* for TYPE_MGT */
+#define IEEE80211_FC0_SUBTYPE_ASSOC_REQ 0x00
+#define IEEE80211_FC0_SUBTYPE_ASSOC_RESP 0x10
+#define IEEE80211_FC0_SUBTYPE_REASSOC_REQ 0x20
+#define IEEE80211_FC0_SUBTYPE_REASSOC_RESP 0x30
+#define IEEE80211_FC0_SUBTYPE_PROBE_REQ 0x40
+#define IEEE80211_FC0_SUBTYPE_PROBE_RESP 0x50
+#define IEEE80211_FC0_SUBTYPE_BEACON 0x80
+#define IEEE80211_FC0_SUBTYPE_ATIM 0x90
+#define IEEE80211_FC0_SUBTYPE_DISASSOC 0xa0
+#define IEEE80211_FC0_SUBTYPE_AUTH 0xb0
+#define IEEE80211_FC0_SUBTYPE_DEAUTH 0xc0
+/* for TYPE_CTL */
+#define IEEE80211_FC0_SUBTYPE_PS_POLL 0xa0
+#define IEEE80211_FC0_SUBTYPE_RTS 0xb0
+#define IEEE80211_FC0_SUBTYPE_CTS 0xc0
+#define IEEE80211_FC0_SUBTYPE_ACK 0xd0
+#define IEEE80211_FC0_SUBTYPE_CF_END 0xe0
+#define IEEE80211_FC0_SUBTYPE_CF_END_ACK 0xf0
+/* for TYPE_DATA (bit combination) */
+#define IEEE80211_FC0_SUBTYPE_DATA 0x00
+#define IEEE80211_FC0_SUBTYPE_CF_ACK 0x10
+#define IEEE80211_FC0_SUBTYPE_CF_POLL 0x20
+#define IEEE80211_FC0_SUBTYPE_CF_ACPL 0x30
+#define IEEE80211_FC0_SUBTYPE_NODATA 0x40
+#define IEEE80211_FC0_SUBTYPE_CFACK 0x50
+#define IEEE80211_FC0_SUBTYPE_CFPOLL 0x60
+#define IEEE80211_FC0_SUBTYPE_CF_ACK_CF_ACK 0x70
+
+#define IEEE80211_FC1_DIR_MASK 0x03
+#define IEEE80211_FC1_DIR_NODS 0x00 /* STA->STA */
+#define IEEE80211_FC1_DIR_TODS 0x01 /* STA->AP */
+#define IEEE80211_FC1_DIR_FROMDS 0x02 /* AP ->STA */
+#define IEEE80211_FC1_DIR_DSTODS 0x03 /* AP ->AP */
+
+#define IEEE80211_FC1_MORE_FRAG 0x04
+#define IEEE80211_FC1_RETRY 0x08
+#define IEEE80211_FC1_PWR_MGT 0x10
+#define IEEE80211_FC1_MORE_DATA 0x20
+#define IEEE80211_FC1_WEP 0x40
+#define IEEE80211_FC1_ORDER 0x80
+
+#define IEEE80211_SEQ_FRAG_MASK 0x000f
+#define IEEE80211_SEQ_FRAG_SHIFT 0
+#define IEEE80211_SEQ_SEQ_MASK 0xfff0
+#define IEEE80211_SEQ_SEQ_SHIFT 4
+
+#define IEEE80211_NWID_LEN 32
+
+/*
+ * BEACON management packets
+ *
+ * octet timestamp[8]
+ * octet beacon interval[2]
+ * octet capability information[2]
+ * information element
+ * octet elemid
+ * octet length
+ * octet information[length]
+ */
+
+typedef uint8_t *ieee80211_mgt_beacon_t;
+
+#define IEEE80211_BEACON_INTERVAL(beacon) \
+ ((beacon)[8] | ((beacon)[9] << 8))
+#define IEEE80211_BEACON_CAPABILITY(beacon) \
+ ((beacon)[10] | ((beacon)[11] << 8))
+
+#define IEEE80211_CAPINFO_ESS 0x0001
+#define IEEE80211_CAPINFO_IBSS 0x0002
+#define IEEE80211_CAPINFO_CF_POLLABLE 0x0004
+#define IEEE80211_CAPINFO_CF_POLLREQ 0x0008
+#define IEEE80211_CAPINFO_PRIVACY 0x0010
+#define IEEE80211_CAPINFO_SHORT_PREAMBLE 0x0020
+#define IEEE80211_CAPINFO_PBCC 0x0040
+#define IEEE80211_CAPINFO_CHNL_AGILITY 0x0080
+/* bits 8-9 are reserved */
+#define IEEE80211_CAPINFO_SHORT_SLOTTIME 0x0400
+/* bits 11-12 are reserved */
+#define IEEE80211_CAPINFO_DSSSOFDM 0x2000
+/* bits 14-15 are reserved */
+
+/*
+ * Management information elements
+ */
+struct ieee80211_information {
+ char ssid[IEEE80211_NWID_LEN+1];
+ struct rates {
+ u_int8_t *p;
+ } rates;
+ struct fh {
+ u_int16_t dwell;
+ u_int8_t set;
+ u_int8_t pattern;
+ u_int8_t index;
+ } fh;
+ struct ds {
+ u_int8_t channel;
+ } ds;
+ struct cf {
+ u_int8_t count;
+ u_int8_t period;
+ u_int8_t maxdur[2];
+ u_int8_t dur[2];
+ } cf;
+ struct tim {
+ u_int8_t count;
+ u_int8_t period;
+ u_int8_t bitctl;
+ /* u_int8_t pvt[251]; The driver needs to use this. */
+ } tim;
+ struct ibss {
+ u_int16_t atim;
+ } ibss;
+ struct challenge {
+ u_int8_t *p;
+ u_int8_t len;
+ } challenge;
+ struct erp {
+ u_int8_t flags;
+ } erp;
+};
+
+enum {
+ IEEE80211_ELEMID_SSID = 0,
+ IEEE80211_ELEMID_RATES = 1,
+ IEEE80211_ELEMID_FHPARMS = 2,
+ IEEE80211_ELEMID_DSPARMS = 3,
+ IEEE80211_ELEMID_CFPARMS = 4,
+ IEEE80211_ELEMID_TIM = 5,
+ IEEE80211_ELEMID_IBSSPARMS = 6,
+ IEEE80211_ELEMID_COUNTRY = 7,
+ IEEE80211_ELEMID_CHALLENGE = 16,
+ IEEE80211_ELEMID_ERP = 42,
+ IEEE80211_ELEMID_XRATES = 50,
+};
+
+#define IEEE80211_RATE_BASIC 0x80
+#define IEEE80211_RATE_VAL 0x7f
+
+/* EPR information element flags */
+#define IEEE80211_ERP_NON_ERP_PRESENT 0x01
+#define IEEE80211_ERP_USE_PROTECTION 0x02
+#define IEEE80211_ERP_BARKER_MODE 0x04
+
+/*
+ * AUTH management packets
+ *
+ * octet algo[2]
+ * octet seq[2]
+ * octet status[2]
+ * octet chal.id
+ * octet chal.length
+ * octet chal.text[253]
+ */
+
+typedef u_int8_t *ieee80211_mgt_auth_t;
+
+#define IEEE80211_AUTH_ALGORITHM(auth) \
+ ((auth)[0] | ((auth)[1] << 8))
+#define IEEE80211_AUTH_TRANSACTION(auth) \
+ ((auth)[2] | ((auth)[3] << 8))
+#define IEEE80211_AUTH_STATUS(auth) \
+ ((auth)[4] | ((auth)[5] << 8))
+
+#define IEEE80211_AUTH_ALG_OPEN 0x0000
+#define IEEE80211_AUTH_ALG_SHARED 0x0001
+
+enum {
+ IEEE80211_AUTH_OPEN_REQUEST = 1,
+ IEEE80211_AUTH_OPEN_RESPONSE = 2,
+};
+
+enum {
+ IEEE80211_AUTH_SHARED_REQUEST = 1,
+ IEEE80211_AUTH_SHARED_CHALLENGE = 2,
+ IEEE80211_AUTH_SHARED_RESPONSE = 3,
+ IEEE80211_AUTH_SHARED_PASS = 4,
+};
+
+/*
+ * Reason codes
+ *
+ * Unlisted codes are reserved
+ */
+
+enum {
+ IEEE80211_REASON_UNSPECIFIED = 1,
+ IEEE80211_REASON_AUTH_EXPIRE = 2,
+ IEEE80211_REASON_AUTH_LEAVE = 3,
+ IEEE80211_REASON_ASSOC_EXPIRE = 4,
+ IEEE80211_REASON_ASSOC_TOOMANY = 5,
+ IEEE80211_REASON_NOT_AUTHED = 6,
+ IEEE80211_REASON_NOT_ASSOCED = 7,
+ IEEE80211_REASON_ASSOC_LEAVE = 8,
+ IEEE80211_REASON_ASSOC_NOT_AUTHED = 9,
+
+ IEEE80211_STATUS_SUCCESS = 0,
+ IEEE80211_STATUS_UNSPECIFIED = 1,
+ IEEE80211_STATUS_CAPINFO = 10,
+ IEEE80211_STATUS_NOT_ASSOCED = 11,
+ IEEE80211_STATUS_OTHER = 12,
+ IEEE80211_STATUS_ALG = 13,
+ IEEE80211_STATUS_SEQUENCE = 14,
+ IEEE80211_STATUS_CHALLENGE = 15,
+ IEEE80211_STATUS_TIMEOUT = 16,
+ IEEE80211_STATUS_TOOMANY = 17,
+ IEEE80211_STATUS_BASIC_RATE = 18,
+ IEEE80211_STATUS_SP_REQUIRED = 19,
+ IEEE80211_STATUS_PBCC_REQUIRED = 20,
+ IEEE80211_STATUS_CA_REQUIRED = 21,
+ IEEE80211_STATUS_TOO_MANY_STATIONS = 22,
+ IEEE80211_STATUS_RATES = 23,
+ IEEE80211_STATUS_SHORTSLOT_REQUIRED = 25,
+ IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26,
+};
+
+#define IEEE80211_WEP_KEYLEN 5 /* 40bit */
+#define IEEE80211_WEP_IVLEN 3 /* 24bit */
+#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */
+#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */
+#define IEEE80211_WEP_NKID 4 /* number of key ids */
+
+#define IEEE80211_CRC_LEN 4
+
+#define IEEE80211_MTU 1500
+#define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \
+ (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN))
+
+/*
+ * RTS frame length parameters. The default is specified in
+ * the 802.11 spec. The max may be wrong for jumbo frames.
+ */
+#define IEEE80211_RTS_DEFAULT 512
+#define IEEE80211_RTS_MIN 1
+#define IEEE80211_RTS_MAX IEEE80211_MAX_LEN
+
+enum {
+ IEEE80211_AUTH_NONE = 0,
+ IEEE80211_AUTH_OPEN = 1,
+ IEEE80211_AUTH_SHARED = 2,
+};
+
+#endif /* _NET80211_IEEE80211_H_ */
diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c
new file mode 100644
index 0000000..cdbf203
--- /dev/null
+++ b/sys/net80211/ieee80211_crypto.c
@@ -0,0 +1,313 @@
+/*-
+ * 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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may 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.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: ieee80211_crypto.c,v 1.2 2003/06/22 06:16:32 sam Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <machine/atomic.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <crypto/rc4/rc4.h>
+#define arc4_ctxlen() sizeof (struct rc4_state)
+#define arc4_setkey(_c,_k,_l) rc4_init(_c,_k,_l)
+#define arc4_encrypt(_c,_d,_s,_l) rc4_crypt(_c,_s,_d,_l)
+
+static void ieee80211_crc_init(void);
+static u_int32_t ieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len);
+
+void
+ieee80211_crypto_attach(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+ /*
+ * Setup crypto support.
+ */
+ ieee80211_crc_init();
+ ic->ic_iv = arc4random();
+}
+
+void
+ieee80211_crypto_detach(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+ if (ic->ic_wep_ctx != NULL) {
+ free(ic->ic_wep_ctx, M_DEVBUF);
+ ic->ic_wep_ctx = NULL;
+ }
+}
+
+struct mbuf *
+ieee80211_wep_crypt(struct ifnet *ifp, struct mbuf *m0, int txflag)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct mbuf *m, *n, *n0;
+ struct ieee80211_frame *wh;
+ int i, left, len, moff, noff, kid;
+ u_int32_t iv, crc;
+ u_int8_t *ivp;
+ void *ctx;
+ u_int8_t keybuf[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
+ u_int8_t crcbuf[IEEE80211_WEP_CRCLEN];
+
+ n0 = NULL;
+ if ((ctx = ic->ic_wep_ctx) == NULL) {
+ ctx = malloc(arc4_ctxlen(), M_DEVBUF, M_NOWAIT);
+ if (ctx == NULL)
+ goto fail;
+ ic->ic_wep_ctx = ctx;
+ }
+ m = m0;
+ left = m->m_pkthdr.len;
+ MGET(n, M_DONTWAIT, m->m_type);
+ n0 = n;
+ if (n == NULL)
+ goto fail;
+ M_MOVE_PKTHDR(n, m);
+ len = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN;
+ if (txflag) {
+ n->m_pkthdr.len += len;
+ } else {
+ n->m_pkthdr.len -= len;
+ left -= len;
+ }
+ n->m_len = MHLEN;
+ if (n->m_pkthdr.len >= MINCLSIZE) {
+ MCLGET(n, M_DONTWAIT);
+ if (n->m_flags & M_EXT)
+ n->m_len = n->m_ext.ext_size;
+ }
+ len = sizeof(struct ieee80211_frame);
+ memcpy(mtod(n, caddr_t), mtod(m, caddr_t), len);
+ wh = mtod(n, struct ieee80211_frame *);
+ left -= len;
+ moff = len;
+ noff = len;
+ if (txflag) {
+ kid = ic->ic_wep_txkey;
+ wh->i_fc[1] |= IEEE80211_FC1_WEP;
+ iv = ic->ic_iv;
+ /*
+ * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
+ * (B, 255, N) with 3 <= B < 8
+ */
+ if (iv >= 0x03ff00 &&
+ (iv & 0xf8ff00) == 0x00ff00)
+ iv += 0x000100;
+ ic->ic_iv = iv + 1;
+ /* put iv in little endian to prepare 802.11i */
+ ivp = mtod(n, u_int8_t *) + noff;
+ for (i = 0; i < IEEE80211_WEP_IVLEN; i++) {
+ ivp[i] = iv & 0xff;
+ iv >>= 8;
+ }
+ ivp[IEEE80211_WEP_IVLEN] = kid << 6; /* pad and keyid */
+ noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
+ } else {
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ ivp = mtod(m, u_int8_t *) + moff;
+ kid = ivp[IEEE80211_WEP_IVLEN] >> 6;
+ moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
+ }
+ memcpy(keybuf, ivp, IEEE80211_WEP_IVLEN);
+ memcpy(keybuf + IEEE80211_WEP_IVLEN, ic->ic_nw_keys[kid].wk_key,
+ ic->ic_nw_keys[kid].wk_len);
+ arc4_setkey(ctx, keybuf,
+ IEEE80211_WEP_IVLEN + ic->ic_nw_keys[kid].wk_len);
+
+ /* encrypt with calculating CRC */
+ crc = ~0;
+ while (left > 0) {
+ len = m->m_len - moff;
+ if (len == 0) {
+ m = m->m_next;
+ moff = 0;
+ continue;
+ }
+ if (len > n->m_len - noff) {
+ len = n->m_len - noff;
+ if (len == 0) {
+ MGET(n->m_next, M_DONTWAIT, n->m_type);
+ if (n->m_next == NULL)
+ goto fail;
+ n = n->m_next;
+ n->m_len = MLEN;
+ if (left >= MINCLSIZE) {
+ MCLGET(n, M_DONTWAIT);
+ if (n->m_flags & M_EXT)
+ n->m_len = n->m_ext.ext_size;
+ }
+ noff = 0;
+ continue;
+ }
+ }
+ if (len > left)
+ len = left;
+ arc4_encrypt(ctx, mtod(n, caddr_t) + noff,
+ mtod(m, caddr_t) + moff, len);
+ if (txflag)
+ crc = ieee80211_crc_update(crc,
+ mtod(m, u_int8_t *) + moff, len);
+ else
+ crc = ieee80211_crc_update(crc,
+ mtod(n, u_int8_t *) + noff, len);
+ left -= len;
+ moff += len;
+ noff += len;
+ }
+ crc = ~crc;
+ if (txflag) {
+ *(u_int32_t *)crcbuf = htole32(crc);
+ if (n->m_len >= noff + sizeof(crcbuf))
+ n->m_len = noff + sizeof(crcbuf);
+ else {
+ n->m_len = noff;
+ MGET(n->m_next, M_DONTWAIT, n->m_type);
+ if (n->m_next == NULL)
+ goto fail;
+ n = n->m_next;
+ n->m_len = sizeof(crcbuf);
+ noff = 0;
+ }
+ arc4_encrypt(ctx, mtod(n, caddr_t) + noff, crcbuf,
+ sizeof(crcbuf));
+ } else {
+ n->m_len = noff;
+ for (noff = 0; noff < sizeof(crcbuf); noff += len) {
+ len = sizeof(crcbuf) - noff;
+ if (len > m->m_len - moff)
+ len = m->m_len - moff;
+ if (len > 0)
+ arc4_encrypt(ctx, crcbuf + noff,
+ mtod(m, caddr_t) + moff, len);
+ m = m->m_next;
+ moff = 0;
+ }
+ if (crc != le32toh(*(u_int32_t *)crcbuf)) {
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_debug) {
+ if_printf(ifp, "decrypt CRC error\n");
+ if (ieee80211_debug > 1)
+ ieee80211_dump_pkt(n0->m_data,
+ n0->m_len, -1, -1);
+ }
+#endif
+ goto fail;
+ }
+ }
+ m_freem(m0);
+ return n0;
+
+ fail:
+ m_freem(m0);
+ m_freem(n0);
+ return NULL;
+}
+
+/*
+ * CRC 32 -- routine from RFC 2083
+ */
+
+/* Table of CRCs of all 8-bit messages */
+static u_int32_t ieee80211_crc_table[256];
+
+/* Make the table for a fast CRC. */
+static void
+ieee80211_crc_init(void)
+{
+ u_int32_t c;
+ int n, k;
+
+ for (n = 0; n < 256; n++) {
+ c = (u_int32_t)n;
+ for (k = 0; k < 8; k++) {
+ if (c & 1)
+ c = 0xedb88320UL ^ (c >> 1);
+ else
+ c = c >> 1;
+ }
+ ieee80211_crc_table[n] = c;
+ }
+}
+
+/*
+ * Update a running CRC with the bytes buf[0..len-1]--the CRC
+ * should be initialized to all 1's, and the transmitted value
+ * is the 1's complement of the final running CRC
+ */
+
+static u_int32_t
+ieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len)
+{
+ u_int8_t *endbuf;
+
+ for (endbuf = buf + len; buf < endbuf; buf++)
+ crc = ieee80211_crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
+ return crc;
+}
diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h
new file mode 100644
index 0000000..90737a1
--- /dev/null
+++ b/sys/net80211/ieee80211_crypto.h
@@ -0,0 +1,44 @@
+/*-
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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_CRYPTO_H_
+#define _NET80211_IEEE80211_CRYPTO_H_
+
+/*
+ * 802.11 protocol crypto-related definitions.
+ */
+#define IEEE80211_KEYBUF_SIZE 16
+
+struct ieee80211_wepkey {
+ int wk_len;
+ u_int8_t wk_key[IEEE80211_KEYBUF_SIZE];
+};
+
+extern void ieee80211_crypto_attach(struct ifnet *);
+extern void ieee80211_crypto_detach(struct ifnet *);
+extern struct mbuf *ieee80211_wep_crypt(struct ifnet *, struct mbuf *, int);
+#endif /* _NET80211_IEEE80211_CRYPTO_H_ */
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
new file mode 100644
index 0000000..d232c94
--- /dev/null
+++ b/sys/net80211/ieee80211_input.c
@@ -0,0 +1,1093 @@
+/*-
+ * 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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may 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.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <machine/atomic.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#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 NBPFILTER > 0
+ if (ic->ic_rawbpf)
+ bpf_mtap(ic->ic_rawbpf, m);
+#endif
+ 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 NBPFILTER > 0
+ if (ic->ic_rawbpf)
+ bpf_mtap(ic->ic_rawbpf, m);
+#endif
+ (*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 NBPFILTER > 0
+ if (ic->ic_rawbpf)
+ bpf_mtap(ic->ic_rawbpf, m);
+#endif
+ 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)) {
+ 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,
+ 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,
+ 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) {
+ 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,
+ 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,
+ 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,
+ 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
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c
new file mode 100644
index 0000000..c52686d
--- /dev/null
+++ b/sys/net80211/ieee80211_ioctl.c
@@ -0,0 +1,992 @@
+/*-
+ * 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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may 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.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * IEEE 802.11 ioctl support (FreeBSD-specific)
+ */
+
+#include <sys/endian.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_ioctl.h>
+
+#include <dev/wi/if_wavelan_ieee.h>
+
+/*
+ * 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], len);
+ 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<<j)) == 0)
+ continue;
+ i = findrate(ic, j, rate);
+ if (i != -1) {
+ /* lock mode too */
+ ic->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);
+ 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);
+ 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;
+}
diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h
new file mode 100644
index 0000000..767095d
--- /dev/null
+++ b/sys/net80211/ieee80211_ioctl.h
@@ -0,0 +1,78 @@
+/*-
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_IOCTL_H_
+#define _NET80211_IEEE80211_IOCTL_H_
+
+/*
+ * IEEE 802.11 ioctls.
+ */
+
+#ifdef __FreeBSD__
+/*
+ * FreeBSD-style ioctls.
+ */
+/* the first member must be matched with struct ifreq */
+struct ieee80211req {
+ char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */
+ u_int16_t i_type; /* req type */
+ int16_t i_val; /* Index or simple value */
+ int16_t i_len; /* Index or simple value */
+ void *i_data; /* Extra data */
+};
+#define SIOCS80211 _IOW('i', 234, struct ieee80211req)
+#define SIOCG80211 _IOWR('i', 235, struct ieee80211req)
+
+#define IEEE80211_IOC_SSID 1
+#define IEEE80211_IOC_NUMSSIDS 2
+#define IEEE80211_IOC_WEP 3
+#define IEEE80211_WEP_NOSUP -1
+#define IEEE80211_WEP_OFF 0
+#define IEEE80211_WEP_ON 1
+#define IEEE80211_WEP_MIXED 2
+#define IEEE80211_IOC_WEPKEY 4
+#define IEEE80211_IOC_NUMWEPKEYS 5
+#define IEEE80211_IOC_WEPTXKEY 6
+#define IEEE80211_IOC_AUTHMODE 7
+#define IEEE80211_IOC_STATIONNAME 8
+#define IEEE80211_IOC_CHANNEL 9
+#define IEEE80211_IOC_POWERSAVE 10
+#define IEEE80211_POWERSAVE_NOSUP -1
+#define IEEE80211_POWERSAVE_OFF 0
+#define IEEE80211_POWERSAVE_CAM 1
+#define IEEE80211_POWERSAVE_PSP 2
+#define IEEE80211_POWERSAVE_PSP_CAM 3
+#define IEEE80211_POWERSAVE_ON IEEE80211_POWERSAVE_CAM
+#define IEEE80211_IOC_POWERSAVESLEEP 11
+#define IEEE80211_IOCT_RTSTHRESHOLD 12
+
+#ifndef IEEE80211_CHAN_ANY
+#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */
+#endif
+#endif /* __FreeBSD__ */
+
+#endif /* _NET80211_IEEE80211_IOCTL_H_ */
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
new file mode 100644
index 0000000..5ed7fd1
--- /dev/null
+++ b/sys/net80211/ieee80211_node.c
@@ -0,0 +1,557 @@
+/*-
+ * 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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may 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.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <machine/atomic.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#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);
+ /* XXX KASSERT != NULL? */
+}
+
+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
+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));
+}
+
+/*
+ * Begin an active scan.
+ */
+void
+ieee80211_begin_scan(struct ifnet *ifp, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "begin %s scan\n",
+ ic->ic_opmode != IEEE80211_M_HOSTAP ?
+ "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.
+ */
+ 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);
+ }
+}
+
+/*
+ * 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);
+}
+
+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);
+}
+
+/*
+ * 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);
+ } else {
+ ieee80211_unref_node(&selbs);
+ ieee80211_new_state(ifp, 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);
+}
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
new file mode 100644
index 0000000..a1ec779
--- /dev/null
+++ b/sys/net80211/ieee80211_node.h
@@ -0,0 +1,142 @@
+/*-
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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_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_ */
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c
new file mode 100644
index 0000000..60b4cf0
--- /dev/null
+++ b/sys/net80211/ieee80211_output.c
@@ -0,0 +1,485 @@
+/*-
+ * 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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may 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.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <machine/atomic.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+int
+ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni,
+ struct mbuf *m, int type)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_frame *wh;
+
+ /* XXX this probably shouldn't be permitted */
+ KASSERT(ni != NULL, ("%s: null node", __func__));
+ ni->ni_inact = 0;
+
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ if (m == NULL)
+ return ENOMEM;
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type;
+ wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+ *(u_int16_t *)wh->i_dur = 0;
+ *(u_int16_t *)wh->i_seq =
+ htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
+ ni->ni_txseq++;
+ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+
+ if (ifp->if_flags & IFF_DEBUG) {
+ /* avoid to print too many frames */
+ if (ic->ic_opmode == IEEE80211_M_IBSS ||
+#ifdef IEEE80211_DEBUG
+ ieee80211_debug > 1 ||
+#endif
+ (type & IEEE80211_FC0_SUBTYPE_MASK) !=
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+ if_printf(ifp, "sending %s to %s on channel %u\n",
+ ieee80211_mgt_subtype_name[
+ (type & IEEE80211_FC0_SUBTYPE_MASK)
+ >> IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(ni->ni_macaddr),
+ ieee80211_chan2ieee(ic, ni->ni_chan));
+ }
+ IF_ENQUEUE(&ic->ic_mgtq, m);
+ ifp->if_timer = 1;
+ (*ifp->if_start)(ifp);
+ return 0;
+}
+
+struct mbuf *
+ieee80211_encap(struct ifnet *ifp, struct mbuf *m)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ether_header eh;
+ struct ieee80211_frame *wh;
+ struct llc *llc;
+ struct ieee80211_node *ni;
+
+ if (m->m_len < sizeof(struct ether_header)) {
+ m = m_pullup(m, sizeof(struct ether_header));
+ if (m == NULL)
+ return NULL;
+ }
+ memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
+
+ ni = ieee80211_find_node(ic, eh.ether_dhost);
+ if (ni == NULL) /*ic_opmode?? XXX*/
+ ni = ieee80211_ref_node(ic->ic_bss);
+ ni->ni_inact = 0;
+
+ m_adj(m, sizeof(struct ether_header) - sizeof(struct llc));
+ llc = mtod(m, struct llc *);
+ llc->llc_dsap = 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;
+ llc->llc_snap.ether_type = eh.ether_type;
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ if (m == NULL) {
+ ieee80211_unref_node(&ni);
+ return NULL;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
+ *(u_int16_t *)wh->i_dur = 0;
+ *(u_int16_t *)wh->i_seq =
+ htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
+ ni->ni_txseq++;
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid);
+ IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
+ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+ wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
+ IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+ break;
+ case IEEE80211_M_HOSTAP:
+ wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid);
+ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
+ break;
+ }
+ ieee80211_unref_node(&ni);
+ return m;
+}
+
+/*
+ * Add a supported rates element id to a frame.
+ */
+u_int8_t *
+ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs)
+{
+ int nrates;
+
+ *frm++ = IEEE80211_ELEMID_RATES;
+ nrates = rs->rs_nrates;
+ if (nrates > IEEE80211_RATE_SIZE)
+ nrates = IEEE80211_RATE_SIZE;
+ *frm++ = nrates;
+ memcpy(frm, rs->rs_rates, nrates);
+ return frm + nrates;
+}
+
+/*
+ * Add an extended supported rates element id to a frame.
+ */
+u_int8_t *
+ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs)
+{
+ /*
+ * Add an extended supported rates element if operating in 11g mode.
+ */
+ if (rs->rs_nrates > IEEE80211_RATE_SIZE) {
+ int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE;
+ *frm++ = IEEE80211_ELEMID_XRATES;
+ *frm++ = nrates;
+ memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates);
+ frm += nrates;
+ }
+ return frm;
+}
+
+/*
+ * Add an ssid elemet to a frame.
+ */
+static u_int8_t *
+ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len)
+{
+ *frm++ = IEEE80211_ELEMID_SSID;
+ *frm++ = len;
+ memcpy(frm, ssid, len);
+ return frm + len;
+}
+
+static struct mbuf *
+ieee80211_getmbuf(int flags, int type, u_int pktlen)
+{
+ struct mbuf *m;
+
+ if (pktlen > MHLEN)
+ MGETHDR(m, flags, type);
+ else
+ m = m_getcl(flags, type, M_PKTHDR);
+ return m;
+}
+
+int
+ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
+ int type, int arg)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct mbuf *m;
+ u_int8_t *frm;
+ enum ieee80211_phymode mode;
+ u_int16_t capinfo;
+ int ret, timer;
+
+ timer = 0;
+ switch (type) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ /*
+ * prreq frame format
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
+ 2 + ic->ic_des_esslen
+ + 2 + IEEE80211_RATE_SIZE
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+ if (m == NULL)
+ return ENOMEM;
+ m->m_data += sizeof(struct ieee80211_frame);
+ frm = mtod(m, u_int8_t *);
+ frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen);
+ mode = ieee80211_chan2mode(ic, ni->ni_chan);
+ frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
+ frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+
+ timer = IEEE80211_TRANS_WAIT;
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ /*
+ * probe response frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] cabability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] parameter set (IBSS)
+ * [tlv] extended supported rates
+ */
+ m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
+ 8 + 2 + 2 + 2
+ + 2 + ni->ni_esslen
+ + 2 + IEEE80211_RATE_SIZE
+ + 6
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+ if (m == NULL)
+ return ENOMEM;
+ m->m_data += sizeof(struct ieee80211_frame);
+ frm = mtod(m, u_int8_t *);
+
+ memset(frm, 0, 8); /* timestamp should be filled later */
+ frm += 8;
+ *(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval);
+ frm += 2;
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
+ capinfo = IEEE80211_CAPINFO_IBSS;
+ else
+ capinfo = IEEE80211_CAPINFO_ESS;
+ if (ic->ic_flags & IEEE80211_F_WEPON)
+ capinfo |= IEEE80211_CAPINFO_PRIVACY;
+ *(u_int16_t *)frm = htole16(capinfo);
+ frm += 2;
+
+ frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid,
+ ic->ic_bss->ni_esslen);
+ frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates);
+
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ *frm++ = IEEE80211_ELEMID_IBSSPARMS;
+ *frm++ = 2;
+ *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
+ } else { /* IEEE80211_M_HOSTAP */
+ /* TODO: TIM */
+ *frm++ = IEEE80211_ELEMID_TIM;
+ *frm++ = 4; /* length */
+ *frm++ = 0; /* DTIM count */
+ *frm++ = 1; /* DTIM period */
+ *frm++ = 0; /* bitmap control */
+ *frm++ = 0; /* Partial Virtual Bitmap (variable length) */
+ }
+ frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return ENOMEM;
+ MH_ALIGN(m, 2 * 3);
+ m->m_pkthdr.len = m->m_len = 6;
+ frm = mtod(m, u_int8_t *);
+ /* TODO: shared key auth */
+ ((u_int16_t *)frm)[0] = htole16(IEEE80211_AUTH_ALG_OPEN);
+ ((u_int16_t *)frm)[1] = htole16(arg); /* sequence number */
+ ((u_int16_t *)frm)[2] = 0; /* status */
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ timer = IEEE80211_TRANS_WAIT;
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s deauthenticate (reason %d)\n",
+ ether_sprintf(ni->ni_macaddr), arg);
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return ENOMEM;
+ MH_ALIGN(m, 2);
+ m->m_pkthdr.len = m->m_len = 2;
+ *mtod(m, u_int16_t *) = htole16(arg); /* reason */
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+ /*
+ * asreq frame format
+ * [2] capability information
+ * [2] listen interval
+ * [6*] current AP address (reassoc only)
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
+ sizeof(capinfo)
+ + sizeof(u_int16_t)
+ + IEEE80211_ADDR_LEN
+ + 2 + ni->ni_esslen
+ + 2 + IEEE80211_RATE_SIZE
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+ if (m == NULL)
+ return ENOMEM;
+ m->m_data += sizeof(struct ieee80211_frame);
+ frm = mtod(m, u_int8_t *);
+
+ capinfo = 0;
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
+ capinfo |= IEEE80211_CAPINFO_IBSS;
+ else /* IEEE80211_M_STA */
+ capinfo |= IEEE80211_CAPINFO_ESS;
+ if (ic->ic_flags & IEEE80211_F_WEPON)
+ capinfo |= IEEE80211_CAPINFO_PRIVACY;
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+ if (ic->ic_flags & IEEE80211_F_SHSLOT)
+ capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+ *(u_int16_t *)frm = htole16(capinfo);
+ frm += 2;
+
+ *(u_int16_t *)frm = htole16(ic->ic_lintval);
+ frm += 2;
+
+ if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
+ IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid);
+ frm += IEEE80211_ADDR_LEN;
+ }
+
+ frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
+ frm = ieee80211_add_rates(frm, &ni->ni_rates);
+ frm = ieee80211_add_xrates(frm, &ni->ni_rates);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+
+ timer = IEEE80211_TRANS_WAIT;
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ /*
+ * asreq frame format
+ * [2] capability information
+ * [2] status
+ * [2] association ID
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
+ sizeof(capinfo)
+ + sizeof(u_int16_t)
+ + sizeof(u_int16_t)
+ + 2 + IEEE80211_RATE_SIZE
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+ if (m == NULL)
+ return ENOMEM;
+ m->m_data += sizeof(struct ieee80211_frame);
+ frm = mtod(m, u_int8_t *);
+
+ capinfo = IEEE80211_CAPINFO_ESS;
+ if (ic->ic_flags & IEEE80211_F_WEPON)
+ capinfo |= IEEE80211_CAPINFO_PRIVACY;
+ *(u_int16_t *)frm = htole16(capinfo);
+ frm += 2;
+
+ *(u_int16_t *)frm = htole16(arg); /* status */
+ frm += 2;
+
+ if (arg == IEEE80211_STATUS_SUCCESS && ni != NULL)
+ *(u_int16_t *)frm = htole16(ni->ni_associd);
+ else
+ *(u_int16_t *)frm = htole16(0);
+ frm += 2;
+
+ if (ni != NULL) {
+ frm = ieee80211_add_rates(frm, &ni->ni_rates);
+ frm = ieee80211_add_xrates(frm, &ni->ni_rates);
+ } else {
+ frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates);
+ frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates);
+ }
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_DISASSOC:
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s disassociate (reason %d)\n",
+ ether_sprintf(ni->ni_macaddr), arg);
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return ENOMEM;
+ MH_ALIGN(m, 2);
+ m->m_pkthdr.len = m->m_len = 2;
+ *mtod(m, u_int16_t *) = htole16(arg); /* reason */
+ break;
+
+ default:
+ IEEE80211_DPRINTF(("%s: invalid mgmt frame type %u\n",
+ __func__, type));
+ return EINVAL;
+ }
+
+ ret = ieee80211_mgmt_output(ifp, ni, m, type);
+ if (ret == 0 && timer)
+ ic->ic_mgt_timer = timer;
+ return ret;
+}
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c
new file mode 100644
index 0000000..a3dd481
--- /dev/null
+++ b/sys/net80211/ieee80211_proto.c
@@ -0,0 +1,508 @@
+/*-
+ * 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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may 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.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * IEEE 802.11 protocol support.
+ */
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <machine/atomic.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#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"
+};
+
+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);
+
+ /* 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)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_node *ni;
+ int error, ostate;
+#ifdef IEEE80211_DEBUG
+ static const char *stname[] =
+ { "INIT", "SCAN", "AUTH", "ASSOC", "RUN" };
+#endif
+
+ 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;
+ 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);
+ }
+ 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);
+ 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;
+}
diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h
new file mode 100644
index 0000000..384703a
--- /dev/null
+++ b/sys/net80211/ieee80211_proto.h
@@ -0,0 +1,68 @@
+/*-
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 */
+};
+
+#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 *);
+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);
+#endif /* _NET80211_IEEE80211_PROTO_H_ */
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
new file mode 100644
index 0000000..99bba55
--- /dev/null
+++ b/sys/net80211/ieee80211_var.h
@@ -0,0 +1,255 @@
+/*-
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 <net80211/ieee80211.h>
+#include <net80211/ieee80211_crypto.h>
+#include <net80211/ieee80211_node.h>
+#include <net80211/ieee80211_proto.h>
+
+#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);
+ 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_ */
OpenPOWER on IntegriCloud