summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/net80211/net80211_proto.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/net80211/net80211_proto.c')
-rw-r--r--usr/src/uts/common/io/net80211/net80211_proto.c669
1 files changed, 669 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/net80211/net80211_proto.c b/usr/src/uts/common/io/net80211/net80211_proto.c
new file mode 100644
index 0000000000..0f92262d5c
--- /dev/null
+++ b/usr/src/uts/common/io/net80211/net80211_proto.c
@@ -0,0 +1,669 @@
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * IEEE 802.11 protocol support
+ */
+
+#include "net80211_impl.h"
+
+const char *ieee80211_mgt_subtype_name[] = {
+ "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp",
+ "probe_req", "probe_resp", "reserved#6", "reserved#7",
+ "beacon", "atim", "disassoc", "auth",
+ "deauth", "reserved#13", "reserved#14", "reserved#15"
+};
+const char *ieee80211_ctl_subtype_name[] = {
+ "reserved#0", "reserved#1", "reserved#2", "reserved#3",
+ "reserved#3", "reserved#5", "reserved#6", "reserved#7",
+ "reserved#8", "reserved#9", "ps_poll", "rts",
+ "cts", "ack", "cf_end", "cf_end_ack"
+};
+const char *ieee80211_state_name[IEEE80211_S_MAX] = {
+ "INIT", /* IEEE80211_S_INIT */
+ "SCAN", /* IEEE80211_S_SCAN */
+ "AUTH", /* IEEE80211_S_AUTH */
+ "ASSOC", /* IEEE80211_S_ASSOC */
+ "RUN" /* IEEE80211_S_RUN */
+};
+
+static int ieee80211_newstate(ieee80211com_t *, enum ieee80211_state, int);
+
+/*
+ * Initialize the interface softc, ic, with protocol management
+ * related data structures and functions.
+ */
+void
+ieee80211_proto_attach(ieee80211com_t *ic)
+{
+ struct ieee80211_impl *im = ic->ic_private;
+
+ ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT;
+ ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT;
+ ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
+ ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT;
+ ic->ic_protmode = IEEE80211_PROT_CTSONLY;
+ im->im_bmiss_max = IEEE80211_BMISS_MAX;
+
+ /* protocol state change handler */
+ ic->ic_newstate = ieee80211_newstate;
+
+ /* initialize management frame handlers */
+ ic->ic_recv_mgmt = ieee80211_recv_mgmt;
+ ic->ic_send_mgmt = ieee80211_send_mgmt;
+}
+
+/*
+ * Print a 802.11 frame header
+ */
+void
+ieee80211_dump_pkt(const uint8_t *buf, int32_t len, int32_t rate, int32_t rssi)
+{
+ struct ieee80211_frame *wh;
+ int8_t buf1[100];
+ int8_t buf2[25];
+ int i;
+
+ bzero(buf1, sizeof (buf1));
+ bzero(buf2, sizeof (buf2));
+ wh = (struct ieee80211_frame *)buf;
+ switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+ case IEEE80211_FC1_DIR_NODS:
+ (void) snprintf(buf2, sizeof (buf2), "NODS %s",
+ ieee80211_macaddr_sprintf(wh->i_addr2));
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ (void) snprintf(buf2, sizeof (buf2), "->%s",
+ ieee80211_macaddr_sprintf(wh->i_addr1));
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ (void) snprintf(buf2, sizeof (buf2), "(%s)",
+ ieee80211_macaddr_sprintf(wh->i_addr3));
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ break;
+ case IEEE80211_FC1_DIR_TODS:
+ (void) snprintf(buf2, sizeof (buf2), "TODS %s",
+ ieee80211_macaddr_sprintf(wh->i_addr2));
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ (void) snprintf(buf2, sizeof (buf2), "->%s",
+ ieee80211_macaddr_sprintf(wh->i_addr3));
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ (void) snprintf(buf2, sizeof (buf2), "(%s)",
+ ieee80211_macaddr_sprintf(wh->i_addr1));
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ break;
+ case IEEE80211_FC1_DIR_FROMDS:
+ (void) snprintf(buf2, sizeof (buf2), "FRDS %s",
+ ieee80211_macaddr_sprintf(wh->i_addr3));
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ (void) snprintf(buf2, sizeof (buf2), "->%s",
+ ieee80211_macaddr_sprintf(wh->i_addr1));
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ (void) snprintf(buf2, sizeof (buf2), "(%s)",
+ ieee80211_macaddr_sprintf(wh->i_addr2));
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ break;
+ case IEEE80211_FC1_DIR_DSTODS:
+ (void) snprintf(buf2, sizeof (buf2), "DSDS %s",
+ ieee80211_macaddr_sprintf((uint8_t *)&wh[1]));
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ (void) snprintf(buf2, sizeof (buf2), "->%s ",
+ ieee80211_macaddr_sprintf(wh->i_addr3));
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ (void) snprintf(buf2, sizeof (buf2), "%s",
+ ieee80211_macaddr_sprintf(wh->i_addr2));
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ (void) snprintf(buf2, sizeof (buf2), "->%s",
+ ieee80211_macaddr_sprintf(wh->i_addr1));
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ break;
+ }
+ ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_dump_pkt(): %s", buf1);
+ bzero(buf1, sizeof (buf1));
+
+ switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+ case IEEE80211_FC0_TYPE_DATA:
+ (void) sprintf(buf2, "data");
+ break;
+ case IEEE80211_FC0_TYPE_MGT:
+ (void) snprintf(buf2, sizeof (buf2), "%s",
+ ieee80211_mgt_subtype_name[
+ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
+ >> IEEE80211_FC0_SUBTYPE_SHIFT]);
+ break;
+ default:
+ (void) snprintf(buf2, sizeof (buf2), "type#%d",
+ wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
+ break;
+ }
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ (void) sprintf(buf2, " WEP");
+ (void) strcat(buf1, buf2);
+ }
+ if (rate >= 0) {
+ (void) snprintf(buf2, sizeof (buf2), " %dM", rate / 2);
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ }
+ if (rssi >= 0) {
+ (void) snprintf(buf2, sizeof (buf2), " +%d", rssi);
+ (void) strncat(buf1, buf2, sizeof (buf2));
+ }
+ ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_dump_pkt(): %s", buf1);
+ bzero(buf1, sizeof (buf1));
+
+ if (len > 0) {
+ for (i = 0; i < (len > 40 ? 40 : len); i++) {
+ if ((i & 0x03) == 0)
+ (void) strcat(buf1, " ");
+ (void) snprintf(buf2, 3, "%02x", buf[i]);
+ (void) strncat(buf1, buf2, 3);
+ }
+ ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_dump_pkt(): %s",
+ buf1);
+ }
+}
+
+/*
+ * Adjust/Fix the specified node's rate table
+ *
+ * in node
+ * flag IEEE80211_F_DOSORT : sort the node's rate table
+ * IEEE80211_F_DONEGO : mark a rate as basic rate if it is
+ * a device's basic rate
+ * IEEE80211_F_DODEL : delete rates not supported by the device
+ * IEEE80211_F_DOFRATE: check if the fixed rate is supported by
+ * the device
+ *
+ * The highest bit of returned rate value is set to 1 on failure.
+ */
+int
+ieee80211_fix_rate(ieee80211_node_t *in, int flags)
+{
+ ieee80211com_t *ic = in->in_ic;
+ struct ieee80211_rateset *srs;
+ struct ieee80211_rateset *nrs;
+ boolean_t ignore;
+ int i;
+ int okrate;
+ int badrate;
+ int fixedrate;
+ uint8_t r;
+
+ /*
+ * If the fixed rate check was requested but no
+ * fixed has been defined then just remove it.
+ */
+ if ((flags & IEEE80211_F_DOFRATE) &&
+ (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)) {
+ flags &= ~IEEE80211_F_DOFRATE;
+ }
+ okrate = badrate = fixedrate = 0;
+ srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, in->in_chan)];
+ nrs = &in->in_rates;
+ for (i = 0; i < nrs->ir_nrates; ) {
+ int j;
+
+ ignore = B_FALSE;
+ if (flags & IEEE80211_F_DOSORT) {
+ /*
+ * Sort rates.
+ */
+ for (j = i + 1; j < nrs->ir_nrates; j++) {
+ if (IEEE80211_RV(nrs->ir_rates[i]) >
+ IEEE80211_RV(nrs->ir_rates[j])) {
+ r = nrs->ir_rates[i];
+ nrs->ir_rates[i] = nrs->ir_rates[j];
+ nrs->ir_rates[j] = r;
+ }
+ }
+ }
+ r = IEEE80211_RV(nrs->ir_rates[i]);
+ badrate = r;
+
+ /*
+ * Check against supported rates.
+ */
+ for (j = 0; j < srs->ir_nrates; j++) {
+ if (r == IEEE80211_RV(srs->ir_rates[j])) {
+ /*
+ * Overwrite with the supported rate
+ * value so any basic rate bit is set.
+ * This insures that response we send
+ * to stations have the necessary basic
+ * rate bit set.
+ */
+ if (flags & IEEE80211_F_DONEGO)
+ nrs->ir_rates[i] = srs->ir_rates[j];
+ break;
+ }
+ }
+ if (j == srs->ir_nrates) {
+ /*
+ * A rate in the node's rate set is not
+ * supported. We just discard/ignore the rate.
+ * Note that this is important for 11b stations
+ * when they want to associate with an 11g AP.
+ */
+ ignore = B_TRUE;
+ }
+
+ if (flags & IEEE80211_F_DODEL) {
+ /*
+ * Delete unacceptable rates.
+ */
+ if (ignore) {
+ nrs->ir_nrates--;
+ for (j = i; j < nrs->ir_nrates; j++)
+ nrs->ir_rates[j] = nrs->ir_rates[j + 1];
+ nrs->ir_rates[j] = 0;
+ continue;
+ }
+ }
+ if (flags & IEEE80211_F_DOFRATE) {
+ /*
+ * Check any fixed rate is included.
+ */
+ if (r == ic->ic_fixed_rate)
+ fixedrate = r;
+ }
+ if (!ignore)
+ okrate = nrs->ir_rates[i];
+ i++;
+ }
+ if (okrate == 0 || ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0))
+ return (badrate | IEEE80211_RATE_BASIC);
+ else
+ return (IEEE80211_RV(okrate));
+}
+
+/*
+ * Reset 11g-related state.
+ */
+void
+ieee80211_reset_erp(ieee80211com_t *ic)
+{
+ ic->ic_flags &= ~IEEE80211_F_USEPROT;
+ /*
+ * Short slot time is enabled only when operating in 11g
+ * and not in an IBSS. We must also honor whether or not
+ * the driver is capable of doing it.
+ */
+ ieee80211_set_shortslottime(ic,
+ ic->ic_curmode == IEEE80211_MODE_11A);
+ /*
+ * Set short preamble and ERP barker-preamble flags.
+ */
+ if (ic->ic_curmode == IEEE80211_MODE_11A ||
+ (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) {
+ ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+ ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+ } else {
+ ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+ ic->ic_flags |= IEEE80211_F_USEBARKER;
+ }
+}
+
+/*
+ * Change current channel to be the next available channel
+ */
+void
+ieee80211_reset_chan(ieee80211com_t *ic)
+{
+ struct ieee80211_channel *ch = ic->ic_curchan;
+
+ IEEE80211_LOCK(ic);
+ do {
+ if (++ch > &ic->ic_sup_channels[IEEE80211_CHAN_MAX])
+ ch = &ic->ic_sup_channels[0];
+ if (ieee80211_isset(ic->ic_chan_active,
+ ieee80211_chan2ieee(ic, ch))) {
+ break;
+ }
+ } while (ch != ic->ic_curchan);
+ ic->ic_curchan = ch;
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Set the short slot time state and notify the driver.
+ */
+void
+ieee80211_set_shortslottime(ieee80211com_t *ic, boolean_t on)
+{
+ if (on)
+ ic->ic_flags |= IEEE80211_F_SHSLOT;
+ else
+ ic->ic_flags &= ~IEEE80211_F_SHSLOT;
+ /* notify driver */
+ if (ic->ic_set_shortslot != NULL)
+ ic->ic_set_shortslot(ic, on);
+}
+
+/*
+ * Mark the basic rates for the 11g rate table based on the
+ * operating mode. For real 11g we mark all the 11b rates
+ * and 6, 12, and 24 OFDM. For 11b compatibility we mark only
+ * 11b rates. There's also a pseudo 11a-mode used to mark only
+ * the basic OFDM rates.
+ */
+void
+ieee80211_setbasicrates(struct ieee80211_rateset *rs,
+ enum ieee80211_phymode mode)
+{
+ static const struct ieee80211_rateset basic[] = {
+ { 0 }, /* IEEE80211_MODE_AUTO */
+ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */
+ { 2, { 2, 4} }, /* IEEE80211_MODE_11B */
+ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G mixed b/g */
+ { 0 }, /* IEEE80211_MODE_FH */
+ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_TURBO_A */
+ { 4, { 2, 4, 11, 22 } } /* IEEE80211_MODE_TURBO_G (mixed b/g) */
+ };
+ int i, j;
+
+ ASSERT(mode < IEEE80211_MODE_MAX);
+ for (i = 0; i < rs->ir_nrates; i++) {
+ rs->ir_rates[i] &= IEEE80211_RATE_VAL;
+ for (j = 0; j < basic[mode].ir_nrates; j++) {
+ if (basic[mode].ir_rates[j] == rs->ir_rates[i]) {
+ rs->ir_rates[i] |= IEEE80211_RATE_BASIC;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Process STA mode beacon miss events. Send a direct probe request
+ * frame to the current ap bmiss_max times (w/o answer) before
+ * scanning for a new ap.
+ */
+void
+ieee80211_beacon_miss(ieee80211com_t *ic)
+{
+ ieee80211_impl_t *im = ic->ic_private;
+
+ if (ic->ic_flags & IEEE80211_F_SCAN)
+ return;
+ ieee80211_dbg(IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "%s\n", "beacon miss");
+
+ /*
+ * Our handling is only meaningful for stations that are
+ * associated; any other conditions else will be handled
+ * through different means (e.g. the tx timeout on mgt frames).
+ */
+ if (ic->ic_opmode != IEEE80211_M_STA ||
+ ic->ic_state != IEEE80211_S_RUN) {
+ return;
+ }
+
+ IEEE80211_LOCK(ic);
+ if (++im->im_bmiss_count < im->im_bmiss_max) {
+ /*
+ * Send a directed probe req before falling back to a scan;
+ * if we receive a response ic_bmiss_count will be reset.
+ * Some cards mistakenly report beacon miss so this avoids
+ * the expensive scan if the ap is still there.
+ */
+ IEEE80211_UNLOCK(ic);
+ (void) ieee80211_send_probereq(ic->ic_bss, ic->ic_macaddr,
+ ic->ic_bss->in_bssid, ic->ic_bss->in_bssid,
+ ic->ic_bss->in_essid, ic->ic_bss->in_esslen,
+ ic->ic_opt_ie, ic->ic_opt_ie_len);
+ return;
+ }
+ im->im_bmiss_count = 0;
+ IEEE80211_UNLOCK(ic);
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+}
+
+/*
+ * Manage state transition between INIT | AUTH | ASSOC | RUN.
+ */
+static int
+ieee80211_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211_impl *im = ic->ic_private;
+ ieee80211_node_t *in;
+ enum ieee80211_state ostate;
+ wifi_data_t wd = { 0 };
+
+ IEEE80211_LOCK(ic);
+ ostate = ic->ic_state;
+ ieee80211_dbg(IEEE80211_MSG_STATE, "ieee80211_newstate(): "
+ "%s -> %s\n",
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
+ ic->ic_state = nstate;
+ in = ic->ic_bss;
+ im->im_swbmiss_period = 0; /* Reset software beacon miss period */
+
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ IEEE80211_UNLOCK(ic);
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ return (0);
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(ic);
+ break;
+ case IEEE80211_S_AUTH:
+ break;
+ case IEEE80211_S_ASSOC:
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ IEEE80211_SEND_MGMT(ic, in,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
+ }
+ break;
+ case IEEE80211_S_RUN:
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ IEEE80211_SEND_MGMT(ic, in,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_ASSOC_LEAVE);
+ ieee80211_sta_leave(ic, in);
+ }
+ break;
+ }
+ IEEE80211_LOCK(ic);
+ im->im_mgt_timer = 0;
+ ieee80211_reset_bss(ic);
+ break;
+ case IEEE80211_S_SCAN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ if (ic->ic_opmode == IEEE80211_M_IBSS &&
+ 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_UNLOCK(ic);
+ ieee80211_begin_scan(ic,
+ (arg == 0) ? B_FALSE : B_TRUE);
+ return (0);
+ }
+ break;
+ case IEEE80211_S_SCAN:
+ /*
+ * Scan next. If doing an active scan and the
+ * channel is not marked passive-only then send
+ * a probe request. Otherwise just listen for
+ * beacons on the channel.
+ */
+ if ((ic->ic_flags & IEEE80211_F_ASCAN) &&
+ !IEEE80211_IS_CHAN_PASSIVE(ic->ic_curchan)) {
+ IEEE80211_UNLOCK(ic);
+ (void) ieee80211_send_probereq(in,
+ ic->ic_macaddr, wifi_bcastaddr,
+ wifi_bcastaddr,
+ ic->ic_des_essid, ic->ic_des_esslen,
+ ic->ic_opt_ie, ic->ic_opt_ie_len);
+ return (0);
+ }
+ break;
+ case IEEE80211_S_RUN:
+ /* beacon miss */
+ ieee80211_dbg(IEEE80211_MSG_STATE,
+ "no recent beacons from %s, rescanning\n",
+ ieee80211_macaddr_sprintf(in->in_macaddr));
+ IEEE80211_UNLOCK(ic);
+ ieee80211_sta_leave(ic, in);
+ IEEE80211_LOCK(ic);
+ ic->ic_flags &= ~IEEE80211_F_SIBSS;
+ /* FALLTHRU */
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ /* timeout restart scan */
+ in = ieee80211_find_node(&ic->ic_scan,
+ ic->ic_bss->in_macaddr);
+ if (in != NULL) {
+ in->in_fails++;
+ ieee80211_unref_node(&in);
+ }
+ break;
+ }
+ break;
+ case IEEE80211_S_AUTH:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ IEEE80211_UNLOCK(ic);
+ IEEE80211_SEND_MGMT(ic, in, IEEE80211_FC0_SUBTYPE_AUTH,
+ 1);
+ return (0);
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ switch (arg) {
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ IEEE80211_UNLOCK(ic);
+ IEEE80211_SEND_MGMT(ic, in,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ return (0);
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ /* ignore and retry scan on timeout */
+ break;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ switch (arg) {
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ ic->ic_state = ostate; /* stay RUN */
+ IEEE80211_UNLOCK(ic);
+ IEEE80211_SEND_MGMT(ic, in,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ return (0);
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ IEEE80211_UNLOCK(ic);
+ ieee80211_sta_leave(ic, in);
+ /* try to re-auth */
+ IEEE80211_SEND_MGMT(ic, in,
+ IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ return (0);
+ }
+ break;
+ }
+ break;
+ case IEEE80211_S_ASSOC:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ case IEEE80211_S_ASSOC:
+ ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_newstate: "
+ "invalid transition\n");
+ break;
+ case IEEE80211_S_AUTH:
+ IEEE80211_UNLOCK(ic);
+ IEEE80211_SEND_MGMT(ic, in,
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
+ return (0);
+ case IEEE80211_S_RUN:
+ IEEE80211_UNLOCK(ic);
+ ieee80211_sta_leave(ic, in);
+ IEEE80211_SEND_MGMT(ic, in,
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
+ return (0);
+ }
+ break;
+ case IEEE80211_S_RUN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ ieee80211_err("ieee80211_newstate: "
+ "invalid transition\n");
+ break;
+ case IEEE80211_S_AUTH:
+ ieee80211_err("ieee80211_newstate: "
+ "invalid transition\n");
+ break;
+ case IEEE80211_S_SCAN: /* adhoc/hostap mode */
+ case IEEE80211_S_ASSOC: /* infra mode */
+ ASSERT(in->in_txrate < in->in_rates.ir_nrates);
+ im->im_mgt_timer = 0;
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ ieee80211_notify_node_join(ic, in);
+
+ /*
+ * We can send data now; update the fastpath with our
+ * current associated BSSID and other relevant settings.
+ */
+ wd.wd_secalloc = ieee80211_crypto_getciphertype(ic);
+ wd.wd_opmode = ic->ic_opmode;
+ IEEE80211_ADDR_COPY(wd.wd_bssid, in->in_bssid);
+ (void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
+ break;
+ }
+
+ /*
+ * When 802.1x is not in use mark the port authorized
+ * at this point so traffic can flow.
+ */
+ if (in->in_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(in);
+ /*
+ * Enable inactivity processing.
+ */
+ ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT;
+ ic->ic_sta.nt_inact_timer = IEEE80211_INACT_WAIT;
+ break; /* IEEE80211_S_RUN */
+ } /* switch nstate */
+ IEEE80211_UNLOCK(ic);
+
+ return (0);
+}