xref: /illumos-gate/usr/src/uts/common/io/net80211/net80211_proto.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 2001 Atsushi Onoe
8  * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * Alternatively, this software may be distributed under the terms of the
23  * GNU General Public License ("GPL") version 2 as published by the Free
24  * Software Foundation.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 #pragma ident	"%Z%%M%	%I%	%E% SMI"
39 
40 /*
41  * IEEE 802.11 protocol support
42  */
43 
44 #include "net80211_impl.h"
45 
46 const char *ieee80211_mgt_subtype_name[] = {
47 	"assoc_req",	"assoc_resp",	"reassoc_req",	"reassoc_resp",
48 	"probe_req",	"probe_resp",	"reserved#6",	"reserved#7",
49 	"beacon",	"atim",		"disassoc",	"auth",
50 	"deauth",	"reserved#13",	"reserved#14",	"reserved#15"
51 };
52 const char *ieee80211_ctl_subtype_name[] = {
53 	"reserved#0",	"reserved#1",	"reserved#2",	"reserved#3",
54 	"reserved#3",	"reserved#5",	"reserved#6",	"reserved#7",
55 	"reserved#8",	"reserved#9",	"ps_poll",	"rts",
56 	"cts",		"ack",		"cf_end",	"cf_end_ack"
57 };
58 const char *ieee80211_state_name[IEEE80211_S_MAX] = {
59 	"INIT",		/* IEEE80211_S_INIT */
60 	"SCAN",		/* IEEE80211_S_SCAN */
61 	"AUTH",		/* IEEE80211_S_AUTH */
62 	"ASSOC",	/* IEEE80211_S_ASSOC */
63 	"RUN"		/* IEEE80211_S_RUN */
64 };
65 
66 static int ieee80211_newstate(ieee80211com_t *, enum ieee80211_state, int);
67 
68 /*
69  * Initialize the interface softc, ic, with protocol management
70  * related data structures and functions.
71  */
72 void
73 ieee80211_proto_attach(ieee80211com_t *ic)
74 {
75 	struct ieee80211_impl *im = ic->ic_private;
76 
77 	ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT;
78 	ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT;
79 	ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
80 	ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT;
81 	ic->ic_protmode = IEEE80211_PROT_CTSONLY;
82 	im->im_bmiss_max = IEEE80211_BMISS_MAX;
83 
84 	/* protocol state change handler */
85 	ic->ic_newstate = ieee80211_newstate;
86 
87 	/* initialize management frame handlers */
88 	ic->ic_recv_mgmt = ieee80211_recv_mgmt;
89 	ic->ic_send_mgmt = ieee80211_send_mgmt;
90 }
91 
92 /*
93  * Print a 802.11 frame header
94  */
95 void
96 ieee80211_dump_pkt(const uint8_t *buf, int32_t len, int32_t rate, int32_t rssi)
97 {
98 	struct ieee80211_frame *wh;
99 	int8_t buf1[100];
100 	int8_t buf2[25];
101 	int i;
102 
103 	bzero(buf1, sizeof (buf1));
104 	bzero(buf2, sizeof (buf2));
105 	wh = (struct ieee80211_frame *)buf;
106 	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
107 	case IEEE80211_FC1_DIR_NODS:
108 		(void) snprintf(buf2, sizeof (buf2), "NODS %s",
109 			ieee80211_macaddr_sprintf(wh->i_addr2));
110 		(void) strncat(buf1, buf2, sizeof (buf2));
111 		(void) snprintf(buf2, sizeof (buf2), "->%s",
112 			ieee80211_macaddr_sprintf(wh->i_addr1));
113 		(void) strncat(buf1, buf2, sizeof (buf2));
114 		(void) snprintf(buf2, sizeof (buf2), "(%s)",
115 			ieee80211_macaddr_sprintf(wh->i_addr3));
116 		(void) strncat(buf1, buf2, sizeof (buf2));
117 		break;
118 	case IEEE80211_FC1_DIR_TODS:
119 		(void) snprintf(buf2, sizeof (buf2), "TODS %s",
120 			ieee80211_macaddr_sprintf(wh->i_addr2));
121 		(void) strncat(buf1, buf2, sizeof (buf2));
122 		(void) snprintf(buf2, sizeof (buf2), "->%s",
123 			ieee80211_macaddr_sprintf(wh->i_addr3));
124 		(void) strncat(buf1, buf2, sizeof (buf2));
125 		(void) snprintf(buf2, sizeof (buf2), "(%s)",
126 			ieee80211_macaddr_sprintf(wh->i_addr1));
127 		(void) strncat(buf1, buf2, sizeof (buf2));
128 		break;
129 	case IEEE80211_FC1_DIR_FROMDS:
130 		(void) snprintf(buf2, sizeof (buf2), "FRDS %s",
131 			ieee80211_macaddr_sprintf(wh->i_addr3));
132 		(void) strncat(buf1, buf2, sizeof (buf2));
133 		(void) snprintf(buf2, sizeof (buf2), "->%s",
134 			ieee80211_macaddr_sprintf(wh->i_addr1));
135 		(void) strncat(buf1, buf2, sizeof (buf2));
136 		(void) snprintf(buf2, sizeof (buf2), "(%s)",
137 			ieee80211_macaddr_sprintf(wh->i_addr2));
138 		(void) strncat(buf1, buf2, sizeof (buf2));
139 		break;
140 	case IEEE80211_FC1_DIR_DSTODS:
141 		(void) snprintf(buf2, sizeof (buf2), "DSDS %s",
142 			ieee80211_macaddr_sprintf((uint8_t *)&wh[1]));
143 		(void) strncat(buf1, buf2, sizeof (buf2));
144 		(void) snprintf(buf2, sizeof (buf2), "->%s  ",
145 			ieee80211_macaddr_sprintf(wh->i_addr3));
146 		(void) strncat(buf1, buf2, sizeof (buf2));
147 		(void) snprintf(buf2, sizeof (buf2), "%s",
148 			ieee80211_macaddr_sprintf(wh->i_addr2));
149 		(void) strncat(buf1, buf2, sizeof (buf2));
150 		(void) snprintf(buf2, sizeof (buf2), "->%s",
151 			ieee80211_macaddr_sprintf(wh->i_addr1));
152 		(void) strncat(buf1, buf2, sizeof (buf2));
153 		break;
154 	}
155 	ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_dump_pkt(): %s", buf1);
156 	bzero(buf1, sizeof (buf1));
157 
158 	switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
159 	case IEEE80211_FC0_TYPE_DATA:
160 		(void) sprintf(buf2, "data");
161 		break;
162 	case IEEE80211_FC0_TYPE_MGT:
163 		(void) snprintf(buf2, sizeof (buf2), "%s",
164 			ieee80211_mgt_subtype_name[
165 				(wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
166 				>> IEEE80211_FC0_SUBTYPE_SHIFT]);
167 		break;
168 	default:
169 		(void) snprintf(buf2, sizeof (buf2), "type#%d",
170 			wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
171 		break;
172 	}
173 	(void) strncat(buf1, buf2, sizeof (buf2));
174 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
175 		(void) sprintf(buf2, " WEP");
176 		(void) strcat(buf1, buf2);
177 	}
178 	if (rate >= 0) {
179 		(void) snprintf(buf2,  sizeof (buf2), " %dM", rate / 2);
180 		(void) strncat(buf1, buf2, sizeof (buf2));
181 	}
182 	if (rssi >= 0) {
183 		(void) snprintf(buf2,  sizeof (buf2), " +%d", rssi);
184 		(void) strncat(buf1, buf2, sizeof (buf2));
185 	}
186 	ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_dump_pkt(): %s", buf1);
187 	bzero(buf1, sizeof (buf1));
188 
189 	if (len > 0) {
190 		for (i = 0; i < (len > 40 ? 40 : len); i++) {
191 			if ((i & 0x03) == 0)
192 				(void) strcat(buf1, " ");
193 			(void) snprintf(buf2, 3, "%02x", buf[i]);
194 			(void) strncat(buf1, buf2, 3);
195 		}
196 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_dump_pkt(): %s",
197 			buf1);
198 	}
199 }
200 
201 /*
202  * Adjust/Fix the specified node's rate table
203  *
204  * in   node
205  * flag IEEE80211_F_DOSORT : sort the node's rate table
206  *      IEEE80211_F_DONEGO : mark a rate as basic rate if it is
207  *                           a device's basic rate
208  *      IEEE80211_F_DODEL  : delete rates not supported by the device
209  *      IEEE80211_F_DOFRATE: check if the fixed rate is supported by
210  *                           the device
211  *
212  * The highest bit of returned rate value is set to 1 on failure.
213  */
214 int
215 ieee80211_fix_rate(ieee80211_node_t *in, int flags)
216 {
217 	ieee80211com_t *ic = in->in_ic;
218 	struct ieee80211_rateset *srs;
219 	struct ieee80211_rateset *nrs;
220 	boolean_t ignore;
221 	int i;
222 	int okrate;
223 	int badrate;
224 	int fixedrate;
225 	uint8_t r;
226 
227 	/*
228 	 * If the fixed rate check was requested but no
229 	 * fixed has been defined then just remove it.
230 	 */
231 	if ((flags & IEEE80211_F_DOFRATE) &&
232 	    (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)) {
233 		flags &= ~IEEE80211_F_DOFRATE;
234 	}
235 	if (in->in_chan == IEEE80211_CHAN_ANYC) {
236 		return (IEEE80211_RATE_BASIC);
237 	}
238 	okrate = badrate = fixedrate = 0;
239 	srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, in->in_chan)];
240 	nrs = &in->in_rates;
241 	for (i = 0; i < nrs->ir_nrates; ) {
242 		int j;
243 
244 		ignore = B_FALSE;
245 		if (flags & IEEE80211_F_DOSORT) {
246 			/*
247 			 * Sort rates.
248 			 */
249 			for (j = i + 1; j < nrs->ir_nrates; j++) {
250 				if (IEEE80211_RV(nrs->ir_rates[i]) >
251 				    IEEE80211_RV(nrs->ir_rates[j])) {
252 					r = nrs->ir_rates[i];
253 					nrs->ir_rates[i] = nrs->ir_rates[j];
254 					nrs->ir_rates[j] = r;
255 				}
256 			}
257 		}
258 		r = IEEE80211_RV(nrs->ir_rates[i]);
259 		badrate = r;
260 
261 		/*
262 		 * Check against supported rates.
263 		 */
264 		for (j = 0; j < srs->ir_nrates; j++) {
265 			if (r == IEEE80211_RV(srs->ir_rates[j])) {
266 				/*
267 				 * Overwrite with the supported rate
268 				 * value so any basic rate bit is set.
269 				 * This insures that response we send
270 				 * to stations have the necessary basic
271 				 * rate bit set.
272 				 */
273 				if (flags & IEEE80211_F_DONEGO)
274 					nrs->ir_rates[i] = srs->ir_rates[j];
275 				break;
276 			}
277 		}
278 		if (j == srs->ir_nrates) {
279 			/*
280 			 * A rate in the node's rate set is not
281 			 * supported. We just discard/ignore the rate.
282 			 * Note that this is important for 11b stations
283 			 * when they want to associate with an 11g AP.
284 			 */
285 			ignore = B_TRUE;
286 		}
287 
288 		if (flags & IEEE80211_F_DODEL) {
289 			/*
290 			 * Delete unacceptable rates.
291 			 */
292 			if (ignore) {
293 				nrs->ir_nrates--;
294 				for (j = i; j < nrs->ir_nrates; j++)
295 					nrs->ir_rates[j] = nrs->ir_rates[j + 1];
296 				nrs->ir_rates[j] = 0;
297 				continue;
298 			}
299 		}
300 		if (flags & IEEE80211_F_DOFRATE) {
301 			/*
302 			 * Check any fixed rate is included.
303 			 */
304 			if (r == ic->ic_fixed_rate)
305 				fixedrate = r;
306 		}
307 		if (!ignore)
308 			okrate = nrs->ir_rates[i];
309 		i++;
310 	}
311 	if (okrate == 0 || ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0))
312 		return (badrate | IEEE80211_RATE_BASIC);
313 	else
314 		return (IEEE80211_RV(okrate));
315 }
316 
317 /*
318  * Reset 11g-related state.
319  */
320 void
321 ieee80211_reset_erp(ieee80211com_t *ic)
322 {
323 	ic->ic_flags &= ~IEEE80211_F_USEPROT;
324 	/*
325 	 * Short slot time is enabled only when operating in 11g
326 	 * and not in an IBSS.  We must also honor whether or not
327 	 * the driver is capable of doing it.
328 	 */
329 	ieee80211_set_shortslottime(ic,
330 		ic->ic_curmode == IEEE80211_MODE_11A);
331 	/*
332 	 * Set short preamble and ERP barker-preamble flags.
333 	 */
334 	if (ic->ic_curmode == IEEE80211_MODE_11A ||
335 	    (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) {
336 		ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
337 		ic->ic_flags &= ~IEEE80211_F_USEBARKER;
338 	} else {
339 		ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
340 		ic->ic_flags |= IEEE80211_F_USEBARKER;
341 	}
342 }
343 
344 /*
345  * Change current channel to be the next available channel
346  */
347 void
348 ieee80211_reset_chan(ieee80211com_t *ic)
349 {
350 	struct ieee80211_channel *ch = ic->ic_curchan;
351 
352 	IEEE80211_LOCK(ic);
353 	do {
354 		if (++ch > &ic->ic_sup_channels[IEEE80211_CHAN_MAX])
355 			ch = &ic->ic_sup_channels[0];
356 		if (ieee80211_isset(ic->ic_chan_active,
357 		    ieee80211_chan2ieee(ic, ch))) {
358 			break;
359 		}
360 	} while (ch != ic->ic_curchan);
361 	ic->ic_curchan = ch;
362 	IEEE80211_UNLOCK(ic);
363 }
364 
365 /*
366  * Set the short slot time state and notify the driver.
367  */
368 void
369 ieee80211_set_shortslottime(ieee80211com_t *ic, boolean_t on)
370 {
371 	if (on)
372 		ic->ic_flags |= IEEE80211_F_SHSLOT;
373 	else
374 		ic->ic_flags &= ~IEEE80211_F_SHSLOT;
375 	/* notify driver */
376 	if (ic->ic_set_shortslot != NULL)
377 		ic->ic_set_shortslot(ic, on);
378 }
379 
380 /*
381  * Mark the basic rates for the 11g rate table based on the
382  * operating mode.  For real 11g we mark all the 11b rates
383  * and 6, 12, and 24 OFDM.  For 11b compatibility we mark only
384  * 11b rates.  There's also a pseudo 11a-mode used to mark only
385  * the basic OFDM rates.
386  */
387 void
388 ieee80211_setbasicrates(struct ieee80211_rateset *rs,
389     enum ieee80211_phymode mode)
390 {
391 	static const struct ieee80211_rateset basic[] = {
392 		{ 0 },			/* IEEE80211_MODE_AUTO */
393 		{ 3, { 12, 24, 48 } },	/* IEEE80211_MODE_11A */
394 		{ 2, { 2, 4} },		/* IEEE80211_MODE_11B */
395 		{ 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G mixed b/g */
396 		{ 0 },			/* IEEE80211_MODE_FH */
397 		{ 3, { 12, 24, 48 } },	/* IEEE80211_MODE_TURBO_A */
398 		{ 4, { 2, 4, 11, 22 } }	/* IEEE80211_MODE_TURBO_G (mixed b/g) */
399 	};
400 	int i, j;
401 
402 	ASSERT(mode < IEEE80211_MODE_MAX);
403 	for (i = 0; i < rs->ir_nrates; i++) {
404 		rs->ir_rates[i] &= IEEE80211_RATE_VAL;
405 		for (j = 0; j < basic[mode].ir_nrates; j++) {
406 			if (basic[mode].ir_rates[j] == rs->ir_rates[i]) {
407 				rs->ir_rates[i] |= IEEE80211_RATE_BASIC;
408 				break;
409 			}
410 		}
411 	}
412 }
413 
414 /*
415  * Process STA mode beacon miss events. Send a direct probe request
416  * frame to the current ap bmiss_max times (w/o answer) before
417  * scanning for a new ap.
418  */
419 void
420 ieee80211_beacon_miss(ieee80211com_t *ic)
421 {
422 	ieee80211_impl_t *im = ic->ic_private;
423 
424 	if (ic->ic_flags & IEEE80211_F_SCAN)
425 		return;
426 	ieee80211_dbg(IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
427 		"%s\n", "beacon miss");
428 
429 	/*
430 	 * Our handling is only meaningful for stations that are
431 	 * associated; any other conditions else will be handled
432 	 * through different means (e.g. the tx timeout on mgt frames).
433 	 */
434 	if (ic->ic_opmode != IEEE80211_M_STA ||
435 	    ic->ic_state != IEEE80211_S_RUN) {
436 		return;
437 	}
438 
439 	IEEE80211_LOCK(ic);
440 	if (++im->im_bmiss_count < im->im_bmiss_max) {
441 		/*
442 		 * Send a directed probe req before falling back to a scan;
443 		 * if we receive a response ic_bmiss_count will be reset.
444 		 * Some cards mistakenly report beacon miss so this avoids
445 		 * the expensive scan if the ap is still there.
446 		 */
447 		IEEE80211_UNLOCK(ic);
448 		(void) ieee80211_send_probereq(ic->ic_bss, ic->ic_macaddr,
449 			ic->ic_bss->in_bssid, ic->ic_bss->in_bssid,
450 			ic->ic_bss->in_essid, ic->ic_bss->in_esslen,
451 			ic->ic_opt_ie, ic->ic_opt_ie_len);
452 		return;
453 	}
454 	im->im_bmiss_count = 0;
455 	IEEE80211_UNLOCK(ic);
456 	ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
457 }
458 
459 /*
460  * Manage state transition between INIT | AUTH | ASSOC | RUN.
461  */
462 static int
463 ieee80211_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
464 {
465 	struct ieee80211_impl *im = ic->ic_private;
466 	ieee80211_node_t *in;
467 	enum ieee80211_state ostate;
468 	wifi_data_t wd = { 0 };
469 
470 	IEEE80211_LOCK(ic);
471 	ostate = ic->ic_state;
472 	ieee80211_dbg(IEEE80211_MSG_STATE, "ieee80211_newstate(): "
473 		"%s -> %s\n",
474 		ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
475 	ic->ic_state = nstate;
476 	in = ic->ic_bss;
477 	im->im_swbmiss_period = 0;	/* Reset software beacon miss period */
478 
479 	switch (nstate) {
480 	case IEEE80211_S_INIT:
481 		IEEE80211_UNLOCK(ic);
482 		switch (ostate) {
483 		case IEEE80211_S_INIT:
484 			return (0);
485 		case IEEE80211_S_SCAN:
486 			ieee80211_cancel_scan(ic);
487 			break;
488 		case IEEE80211_S_AUTH:
489 			break;
490 		case IEEE80211_S_ASSOC:
491 			if (ic->ic_opmode == IEEE80211_M_STA) {
492 				IEEE80211_SEND_MGMT(ic, in,
493 					IEEE80211_FC0_SUBTYPE_DEAUTH,
494 					IEEE80211_REASON_AUTH_LEAVE);
495 			}
496 			break;
497 		case IEEE80211_S_RUN:
498 			if (ic->ic_opmode == IEEE80211_M_STA) {
499 				IEEE80211_SEND_MGMT(ic, in,
500 					IEEE80211_FC0_SUBTYPE_DISASSOC,
501 					IEEE80211_REASON_ASSOC_LEAVE);
502 				ieee80211_sta_leave(ic, in);
503 			}
504 			break;
505 		}
506 		IEEE80211_LOCK(ic);
507 		im->im_mgt_timer = 0;
508 		ieee80211_reset_bss(ic);
509 		break;
510 	case IEEE80211_S_SCAN:
511 		switch (ostate) {
512 		case IEEE80211_S_INIT:
513 			if (ic->ic_opmode == IEEE80211_M_IBSS &&
514 			    ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
515 				/*
516 				 * AP operation and we already have a channel;
517 				 * bypass the scan and startup immediately.
518 				 */
519 				ieee80211_create_ibss(ic, ic->ic_des_chan);
520 			} else {
521 				IEEE80211_UNLOCK(ic);
522 				ieee80211_begin_scan(ic,
523 					(arg == 0) ? B_FALSE : B_TRUE);
524 				return (0);
525 			}
526 			break;
527 		case IEEE80211_S_SCAN:
528 			/*
529 			 * Scan next. If doing an active scan and the
530 			 * channel is not marked passive-only then send
531 			 * a probe request.  Otherwise just listen for
532 			 * beacons on the channel.
533 			 */
534 			if ((ic->ic_flags & IEEE80211_F_ASCAN) &&
535 			    !IEEE80211_IS_CHAN_PASSIVE(ic->ic_curchan)) {
536 				IEEE80211_UNLOCK(ic);
537 				(void) ieee80211_send_probereq(in,
538 					ic->ic_macaddr, wifi_bcastaddr,
539 					wifi_bcastaddr,
540 					ic->ic_des_essid, ic->ic_des_esslen,
541 					ic->ic_opt_ie, ic->ic_opt_ie_len);
542 				return (0);
543 			}
544 			break;
545 		case IEEE80211_S_RUN:
546 			/* beacon miss */
547 			ieee80211_dbg(IEEE80211_MSG_STATE,
548 				"no recent beacons from %s, rescanning\n",
549 				ieee80211_macaddr_sprintf(in->in_macaddr));
550 			IEEE80211_UNLOCK(ic);
551 			ieee80211_sta_leave(ic, in);
552 			IEEE80211_LOCK(ic);
553 			ic->ic_flags &= ~IEEE80211_F_SIBSS;
554 			/* FALLTHRU */
555 		case IEEE80211_S_AUTH:
556 		case IEEE80211_S_ASSOC:
557 			/* timeout restart scan */
558 			in = ieee80211_find_node(&ic->ic_scan,
559 				ic->ic_bss->in_macaddr);
560 			if (in != NULL) {
561 				in->in_fails++;
562 				ieee80211_unref_node(&in);
563 			}
564 			break;
565 		}
566 		break;
567 	case IEEE80211_S_AUTH:
568 		switch (ostate) {
569 		case IEEE80211_S_INIT:
570 		case IEEE80211_S_SCAN:
571 			IEEE80211_UNLOCK(ic);
572 			IEEE80211_SEND_MGMT(ic, in, IEEE80211_FC0_SUBTYPE_AUTH,
573 				1);
574 			return (0);
575 		case IEEE80211_S_AUTH:
576 		case IEEE80211_S_ASSOC:
577 			switch (arg) {
578 			case IEEE80211_FC0_SUBTYPE_AUTH:
579 				IEEE80211_UNLOCK(ic);
580 				IEEE80211_SEND_MGMT(ic, in,
581 					IEEE80211_FC0_SUBTYPE_AUTH, 2);
582 				return (0);
583 			case IEEE80211_FC0_SUBTYPE_DEAUTH:
584 				/* ignore and retry scan on timeout */
585 				break;
586 			}
587 			break;
588 		case IEEE80211_S_RUN:
589 			switch (arg) {
590 			case IEEE80211_FC0_SUBTYPE_AUTH:
591 				ic->ic_state = ostate;	/* stay RUN */
592 				IEEE80211_UNLOCK(ic);
593 				IEEE80211_SEND_MGMT(ic, in,
594 					IEEE80211_FC0_SUBTYPE_AUTH, 2);
595 				return (0);
596 			case IEEE80211_FC0_SUBTYPE_DEAUTH:
597 				IEEE80211_UNLOCK(ic);
598 				ieee80211_sta_leave(ic, in);
599 				/* try to re-auth */
600 				IEEE80211_SEND_MGMT(ic, in,
601 					IEEE80211_FC0_SUBTYPE_AUTH, 1);
602 				return (0);
603 			}
604 			break;
605 		}
606 		break;
607 	case IEEE80211_S_ASSOC:
608 		switch (ostate) {
609 		case IEEE80211_S_INIT:
610 		case IEEE80211_S_SCAN:
611 		case IEEE80211_S_ASSOC:
612 			ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_newstate: "
613 				"invalid transition\n");
614 			break;
615 		case IEEE80211_S_AUTH:
616 			IEEE80211_UNLOCK(ic);
617 			IEEE80211_SEND_MGMT(ic, in,
618 				IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
619 			return (0);
620 		case IEEE80211_S_RUN:
621 			IEEE80211_UNLOCK(ic);
622 			ieee80211_sta_leave(ic, in);
623 			IEEE80211_SEND_MGMT(ic, in,
624 				IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
625 			return (0);
626 		}
627 		break;
628 	case IEEE80211_S_RUN:
629 		switch (ostate) {
630 		case IEEE80211_S_INIT:
631 			ieee80211_err("ieee80211_newstate: "
632 				"invalid transition\n");
633 			break;
634 		case IEEE80211_S_AUTH:
635 			ieee80211_err("ieee80211_newstate: "
636 				"invalid transition\n");
637 			break;
638 		case IEEE80211_S_SCAN:		/* adhoc/hostap mode */
639 		case IEEE80211_S_ASSOC:		/* infra mode */
640 			ASSERT(in->in_txrate < in->in_rates.ir_nrates);
641 			im->im_mgt_timer = 0;
642 			if (ic->ic_opmode == IEEE80211_M_STA)
643 				ieee80211_notify_node_join(ic, in);
644 
645 			/*
646 			 * We can send data now; update the fastpath with our
647 			 * current associated BSSID and other relevant settings.
648 			 */
649 			wd.wd_secalloc = ieee80211_crypto_getciphertype(ic);
650 			wd.wd_opmode = ic->ic_opmode;
651 			IEEE80211_ADDR_COPY(wd.wd_bssid, in->in_bssid);
652 			(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
653 			break;
654 		}
655 
656 		/*
657 		 * When 802.1x is not in use mark the port authorized
658 		 * at this point so traffic can flow.
659 		 */
660 		if (in->in_authmode != IEEE80211_AUTH_8021X)
661 			ieee80211_node_authorize(in);
662 		/*
663 		 * Enable inactivity processing.
664 		 */
665 		ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT;
666 		ic->ic_sta.nt_inact_timer = IEEE80211_INACT_WAIT;
667 		break;	/* IEEE80211_S_RUN */
668 	} /* switch nstate */
669 	IEEE80211_UNLOCK(ic);
670 
671 	return (0);
672 }
673