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