xref: /illumos-gate/usr/src/uts/common/io/net80211/net80211_proto.c (revision 03100a6332bd4edc7a53091fcf7c9a7131bcdaa7)
1 /*
2  * Copyright 2006 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 	okrate = badrate = fixedrate = 0;
236 	srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, in->in_chan)];
237 	nrs = &in->in_rates;
238 	for (i = 0; i < nrs->ir_nrates; ) {
239 		int j;
240 
241 		ignore = B_FALSE;
242 		if (flags & IEEE80211_F_DOSORT) {
243 			/*
244 			 * Sort rates.
245 			 */
246 			for (j = i + 1; j < nrs->ir_nrates; j++) {
247 				if (IEEE80211_RV(nrs->ir_rates[i]) >
248 				    IEEE80211_RV(nrs->ir_rates[j])) {
249 					r = nrs->ir_rates[i];
250 					nrs->ir_rates[i] = nrs->ir_rates[j];
251 					nrs->ir_rates[j] = r;
252 				}
253 			}
254 		}
255 		r = IEEE80211_RV(nrs->ir_rates[i]);
256 		badrate = r;
257 
258 		/*
259 		 * Check against supported rates.
260 		 */
261 		for (j = 0; j < srs->ir_nrates; j++) {
262 			if (r == IEEE80211_RV(srs->ir_rates[j])) {
263 				/*
264 				 * Overwrite with the supported rate
265 				 * value so any basic rate bit is set.
266 				 * This insures that response we send
267 				 * to stations have the necessary basic
268 				 * rate bit set.
269 				 */
270 				if (flags & IEEE80211_F_DONEGO)
271 					nrs->ir_rates[i] = srs->ir_rates[j];
272 				break;
273 			}
274 		}
275 		if (j == srs->ir_nrates) {
276 			/*
277 			 * A rate in the node's rate set is not
278 			 * supported. We just discard/ignore the rate.
279 			 * Note that this is important for 11b stations
280 			 * when they want to associate with an 11g AP.
281 			 */
282 			ignore = B_TRUE;
283 		}
284 
285 		if (flags & IEEE80211_F_DODEL) {
286 			/*
287 			 * Delete unacceptable rates.
288 			 */
289 			if (ignore) {
290 				nrs->ir_nrates--;
291 				for (j = i; j < nrs->ir_nrates; j++)
292 					nrs->ir_rates[j] = nrs->ir_rates[j + 1];
293 				nrs->ir_rates[j] = 0;
294 				continue;
295 			}
296 		}
297 		if (flags & IEEE80211_F_DOFRATE) {
298 			/*
299 			 * Check any fixed rate is included.
300 			 */
301 			if (r == ic->ic_fixed_rate)
302 				fixedrate = r;
303 		}
304 		if (!ignore)
305 			okrate = nrs->ir_rates[i];
306 		i++;
307 	}
308 	if (okrate == 0 || ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0))
309 		return (badrate | IEEE80211_RATE_BASIC);
310 	else
311 		return (IEEE80211_RV(okrate));
312 }
313 
314 /*
315  * Reset 11g-related state.
316  */
317 void
318 ieee80211_reset_erp(ieee80211com_t *ic)
319 {
320 	ic->ic_flags &= ~IEEE80211_F_USEPROT;
321 	/*
322 	 * Short slot time is enabled only when operating in 11g
323 	 * and not in an IBSS.  We must also honor whether or not
324 	 * the driver is capable of doing it.
325 	 */
326 	ieee80211_set_shortslottime(ic,
327 		ic->ic_curmode == IEEE80211_MODE_11A);
328 	/*
329 	 * Set short preamble and ERP barker-preamble flags.
330 	 */
331 	if (ic->ic_curmode == IEEE80211_MODE_11A ||
332 	    (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) {
333 		ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
334 		ic->ic_flags &= ~IEEE80211_F_USEBARKER;
335 	} else {
336 		ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
337 		ic->ic_flags |= IEEE80211_F_USEBARKER;
338 	}
339 }
340 
341 /*
342  * Change current channel to be the next available channel
343  */
344 void
345 ieee80211_reset_chan(ieee80211com_t *ic)
346 {
347 	struct ieee80211_channel *ch = ic->ic_curchan;
348 
349 	IEEE80211_LOCK(ic);
350 	do {
351 		if (++ch > &ic->ic_sup_channels[IEEE80211_CHAN_MAX])
352 			ch = &ic->ic_sup_channels[0];
353 		if (ieee80211_isset(ic->ic_chan_active,
354 		    ieee80211_chan2ieee(ic, ch))) {
355 			break;
356 		}
357 	} while (ch != ic->ic_curchan);
358 	ic->ic_curchan = ch;
359 	IEEE80211_UNLOCK(ic);
360 }
361 
362 /*
363  * Set the short slot time state and notify the driver.
364  */
365 void
366 ieee80211_set_shortslottime(ieee80211com_t *ic, boolean_t on)
367 {
368 	if (on)
369 		ic->ic_flags |= IEEE80211_F_SHSLOT;
370 	else
371 		ic->ic_flags &= ~IEEE80211_F_SHSLOT;
372 	/* notify driver */
373 	if (ic->ic_set_shortslot != NULL)
374 		ic->ic_set_shortslot(ic, on);
375 }
376 
377 /*
378  * Mark the basic rates for the 11g rate table based on the
379  * operating mode.  For real 11g we mark all the 11b rates
380  * and 6, 12, and 24 OFDM.  For 11b compatibility we mark only
381  * 11b rates.  There's also a pseudo 11a-mode used to mark only
382  * the basic OFDM rates.
383  */
384 void
385 ieee80211_setbasicrates(struct ieee80211_rateset *rs,
386     enum ieee80211_phymode mode)
387 {
388 	static const struct ieee80211_rateset basic[] = {
389 		{ 0 },			/* IEEE80211_MODE_AUTO */
390 		{ 3, { 12, 24, 48 } },	/* IEEE80211_MODE_11A */
391 		{ 2, { 2, 4} },		/* IEEE80211_MODE_11B */
392 		{ 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G mixed b/g */
393 		{ 0 },			/* IEEE80211_MODE_FH */
394 		{ 3, { 12, 24, 48 } },	/* IEEE80211_MODE_TURBO_A */
395 		{ 4, { 2, 4, 11, 22 } }	/* IEEE80211_MODE_TURBO_G (mixed b/g) */
396 	};
397 	int i, j;
398 
399 	ASSERT(mode < IEEE80211_MODE_MAX);
400 	for (i = 0; i < rs->ir_nrates; i++) {
401 		rs->ir_rates[i] &= IEEE80211_RATE_VAL;
402 		for (j = 0; j < basic[mode].ir_nrates; j++) {
403 			if (basic[mode].ir_rates[j] == rs->ir_rates[i]) {
404 				rs->ir_rates[i] |= IEEE80211_RATE_BASIC;
405 				break;
406 			}
407 		}
408 	}
409 }
410 
411 /*
412  * Process STA mode beacon miss events. Send a direct probe request
413  * frame to the current ap bmiss_max times (w/o answer) before
414  * scanning for a new ap.
415  */
416 void
417 ieee80211_beacon_miss(ieee80211com_t *ic)
418 {
419 	ieee80211_impl_t *im = ic->ic_private;
420 
421 	if (ic->ic_flags & IEEE80211_F_SCAN)
422 		return;
423 	ieee80211_dbg(IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
424 		"%s\n", "beacon miss");
425 
426 	/*
427 	 * Our handling is only meaningful for stations that are
428 	 * associated; any other conditions else will be handled
429 	 * through different means (e.g. the tx timeout on mgt frames).
430 	 */
431 	if (ic->ic_opmode != IEEE80211_M_STA ||
432 	    ic->ic_state != IEEE80211_S_RUN) {
433 		return;
434 	}
435 
436 	IEEE80211_LOCK(ic);
437 	if (++im->im_bmiss_count < im->im_bmiss_max) {
438 		/*
439 		 * Send a directed probe req before falling back to a scan;
440 		 * if we receive a response ic_bmiss_count will be reset.
441 		 * Some cards mistakenly report beacon miss so this avoids
442 		 * the expensive scan if the ap is still there.
443 		 */
444 		IEEE80211_UNLOCK(ic);
445 		(void) ieee80211_send_probereq(ic->ic_bss, ic->ic_macaddr,
446 			ic->ic_bss->in_bssid, ic->ic_bss->in_bssid,
447 			ic->ic_bss->in_essid, ic->ic_bss->in_esslen,
448 			ic->ic_opt_ie, ic->ic_opt_ie_len);
449 		return;
450 	}
451 	im->im_bmiss_count = 0;
452 	IEEE80211_UNLOCK(ic);
453 	ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
454 }
455 
456 /*
457  * Manage state transition between INIT | AUTH | ASSOC | RUN.
458  */
459 static int
460 ieee80211_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
461 {
462 	struct ieee80211_impl *im = ic->ic_private;
463 	ieee80211_node_t *in;
464 	enum ieee80211_state ostate;
465 	wifi_data_t wd = { 0 };
466 
467 	IEEE80211_LOCK(ic);
468 	ostate = ic->ic_state;
469 	ieee80211_dbg(IEEE80211_MSG_STATE, "ieee80211_newstate(): "
470 		"%s -> %s\n",
471 		ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
472 	ic->ic_state = nstate;
473 	in = ic->ic_bss;
474 	im->im_swbmiss_period = 0;	/* Reset software beacon miss period */
475 
476 	switch (nstate) {
477 	case IEEE80211_S_INIT:
478 		IEEE80211_UNLOCK(ic);
479 		switch (ostate) {
480 		case IEEE80211_S_INIT:
481 			return (0);
482 		case IEEE80211_S_SCAN:
483 			ieee80211_cancel_scan(ic);
484 			break;
485 		case IEEE80211_S_AUTH:
486 			break;
487 		case IEEE80211_S_ASSOC:
488 			if (ic->ic_opmode == IEEE80211_M_STA) {
489 				IEEE80211_SEND_MGMT(ic, in,
490 					IEEE80211_FC0_SUBTYPE_DEAUTH,
491 					IEEE80211_REASON_AUTH_LEAVE);
492 			}
493 			break;
494 		case IEEE80211_S_RUN:
495 			if (ic->ic_opmode == IEEE80211_M_STA) {
496 				IEEE80211_SEND_MGMT(ic, in,
497 					IEEE80211_FC0_SUBTYPE_DISASSOC,
498 					IEEE80211_REASON_ASSOC_LEAVE);
499 				ieee80211_sta_leave(ic, in);
500 			}
501 			break;
502 		}
503 		IEEE80211_LOCK(ic);
504 		im->im_mgt_timer = 0;
505 		ieee80211_reset_bss(ic);
506 		break;
507 	case IEEE80211_S_SCAN:
508 		switch (ostate) {
509 		case IEEE80211_S_INIT:
510 			if (ic->ic_opmode == IEEE80211_M_IBSS &&
511 			    ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
512 				/*
513 				 * AP operation and we already have a channel;
514 				 * bypass the scan and startup immediately.
515 				 */
516 				ieee80211_create_ibss(ic, ic->ic_des_chan);
517 			} else {
518 				IEEE80211_UNLOCK(ic);
519 				ieee80211_begin_scan(ic,
520 					(arg == 0) ? B_FALSE : B_TRUE);
521 				return (0);
522 			}
523 			break;
524 		case IEEE80211_S_SCAN:
525 			/*
526 			 * Scan next. If doing an active scan and the
527 			 * channel is not marked passive-only then send
528 			 * a probe request.  Otherwise just listen for
529 			 * beacons on the channel.
530 			 */
531 			if ((ic->ic_flags & IEEE80211_F_ASCAN) &&
532 			    !IEEE80211_IS_CHAN_PASSIVE(ic->ic_curchan)) {
533 				IEEE80211_UNLOCK(ic);
534 				(void) ieee80211_send_probereq(in,
535 					ic->ic_macaddr, wifi_bcastaddr,
536 					wifi_bcastaddr,
537 					ic->ic_des_essid, ic->ic_des_esslen,
538 					ic->ic_opt_ie, ic->ic_opt_ie_len);
539 				return (0);
540 			}
541 			break;
542 		case IEEE80211_S_RUN:
543 			/* beacon miss */
544 			ieee80211_dbg(IEEE80211_MSG_STATE,
545 				"no recent beacons from %s, rescanning\n",
546 				ieee80211_macaddr_sprintf(in->in_macaddr));
547 			IEEE80211_UNLOCK(ic);
548 			ieee80211_sta_leave(ic, in);
549 			IEEE80211_LOCK(ic);
550 			ic->ic_flags &= ~IEEE80211_F_SIBSS;
551 			/* FALLTHRU */
552 		case IEEE80211_S_AUTH:
553 		case IEEE80211_S_ASSOC:
554 			/* timeout restart scan */
555 			in = ieee80211_find_node(&ic->ic_scan,
556 				ic->ic_bss->in_macaddr);
557 			if (in != NULL) {
558 				in->in_fails++;
559 				ieee80211_unref_node(&in);
560 			}
561 			break;
562 		}
563 		break;
564 	case IEEE80211_S_AUTH:
565 		switch (ostate) {
566 		case IEEE80211_S_INIT:
567 		case IEEE80211_S_SCAN:
568 			IEEE80211_UNLOCK(ic);
569 			IEEE80211_SEND_MGMT(ic, in, IEEE80211_FC0_SUBTYPE_AUTH,
570 				1);
571 			return (0);
572 		case IEEE80211_S_AUTH:
573 		case IEEE80211_S_ASSOC:
574 			switch (arg) {
575 			case IEEE80211_FC0_SUBTYPE_AUTH:
576 				IEEE80211_UNLOCK(ic);
577 				IEEE80211_SEND_MGMT(ic, in,
578 					IEEE80211_FC0_SUBTYPE_AUTH, 2);
579 				return (0);
580 			case IEEE80211_FC0_SUBTYPE_DEAUTH:
581 				/* ignore and retry scan on timeout */
582 				break;
583 			}
584 			break;
585 		case IEEE80211_S_RUN:
586 			switch (arg) {
587 			case IEEE80211_FC0_SUBTYPE_AUTH:
588 				ic->ic_state = ostate;	/* stay RUN */
589 				IEEE80211_UNLOCK(ic);
590 				IEEE80211_SEND_MGMT(ic, in,
591 					IEEE80211_FC0_SUBTYPE_AUTH, 2);
592 				return (0);
593 			case IEEE80211_FC0_SUBTYPE_DEAUTH:
594 				IEEE80211_UNLOCK(ic);
595 				ieee80211_sta_leave(ic, in);
596 				/* try to re-auth */
597 				IEEE80211_SEND_MGMT(ic, in,
598 					IEEE80211_FC0_SUBTYPE_AUTH, 1);
599 				return (0);
600 			}
601 			break;
602 		}
603 		break;
604 	case IEEE80211_S_ASSOC:
605 		switch (ostate) {
606 		case IEEE80211_S_INIT:
607 		case IEEE80211_S_SCAN:
608 		case IEEE80211_S_ASSOC:
609 			ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_newstate: "
610 				"invalid transition\n");
611 			break;
612 		case IEEE80211_S_AUTH:
613 			IEEE80211_UNLOCK(ic);
614 			IEEE80211_SEND_MGMT(ic, in,
615 				IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
616 			return (0);
617 		case IEEE80211_S_RUN:
618 			IEEE80211_UNLOCK(ic);
619 			ieee80211_sta_leave(ic, in);
620 			IEEE80211_SEND_MGMT(ic, in,
621 				IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
622 			return (0);
623 		}
624 		break;
625 	case IEEE80211_S_RUN:
626 		switch (ostate) {
627 		case IEEE80211_S_INIT:
628 			ieee80211_err("ieee80211_newstate: "
629 				"invalid transition\n");
630 			break;
631 		case IEEE80211_S_AUTH:
632 			ieee80211_err("ieee80211_newstate: "
633 				"invalid transition\n");
634 			break;
635 		case IEEE80211_S_SCAN:		/* adhoc/hostap mode */
636 		case IEEE80211_S_ASSOC:		/* infra mode */
637 			ASSERT(in->in_txrate < in->in_rates.ir_nrates);
638 			im->im_mgt_timer = 0;
639 			if (ic->ic_opmode == IEEE80211_M_STA)
640 				ieee80211_notify_node_join(ic, in);
641 
642 			/*
643 			 * We can send data now; update the fastpath with our
644 			 * current associated BSSID and other relevant settings.
645 			 */
646 			wd.wd_secalloc = ieee80211_crypto_getciphertype(ic);
647 			wd.wd_opmode = ic->ic_opmode;
648 			IEEE80211_ADDR_COPY(wd.wd_bssid, in->in_bssid);
649 			(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
650 			break;
651 		}
652 
653 		/*
654 		 * When 802.1x is not in use mark the port authorized
655 		 * at this point so traffic can flow.
656 		 */
657 		if (in->in_authmode != IEEE80211_AUTH_8021X)
658 			ieee80211_node_authorize(in);
659 		/*
660 		 * Enable inactivity processing.
661 		 */
662 		ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT;
663 		ic->ic_sta.nt_inact_timer = IEEE80211_INACT_WAIT;
664 		break;	/* IEEE80211_S_RUN */
665 	} /* switch nstate */
666 	IEEE80211_UNLOCK(ic);
667 
668 	return (0);
669 }
670