ieee80211_proto.c revision 116904
1116742Ssam/*-
2116904Ssam * Copyright (c) 2001 Atsushi Onoe
3116742Ssam * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
4116742Ssam * All rights reserved.
5116742Ssam *
6116742Ssam * Redistribution and use in source and binary forms, with or without
7116742Ssam * modification, are permitted provided that the following conditions
8116742Ssam * are met:
9116742Ssam * 1. Redistributions of source code must retain the above copyright
10116904Ssam *    notice, this list of conditions and the following disclaimer.
11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright
12116904Ssam *    notice, this list of conditions and the following disclaimer in the
13116904Ssam *    documentation and/or other materials provided with the distribution.
14116904Ssam * 3. The name of the author may not be used to endorse or promote products
15116904Ssam *    derived from this software without specific prior written permission.
16116742Ssam *
17116742Ssam * Alternatively, this software may be distributed under the terms of the
18116742Ssam * GNU General Public License ("GPL") version 2 as published by the Free
19116742Ssam * Software Foundation.
20116742Ssam *
21116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31116742Ssam */
32116742Ssam
33116742Ssam#include <sys/cdefs.h>
34116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_proto.c 116904 2003-06-27 05:13:52Z sam $");
35116742Ssam
36116742Ssam/*
37116742Ssam * IEEE 802.11 protocol support.
38116742Ssam */
39116742Ssam
40116742Ssam#include "opt_inet.h"
41116742Ssam
42116742Ssam#include <sys/param.h>
43116742Ssam#include <sys/systm.h>
44116742Ssam#include <sys/mbuf.h>
45116742Ssam#include <sys/malloc.h>
46116742Ssam#include <sys/kernel.h>
47116742Ssam#include <sys/socket.h>
48116742Ssam#include <sys/sockio.h>
49116742Ssam#include <sys/endian.h>
50116742Ssam#include <sys/errno.h>
51116742Ssam#include <sys/bus.h>
52116742Ssam#include <sys/proc.h>
53116742Ssam#include <sys/sysctl.h>
54116742Ssam
55116742Ssam#include <machine/atomic.h>
56116742Ssam
57116742Ssam#include <net/if.h>
58116742Ssam#include <net/if_dl.h>
59116742Ssam#include <net/if_media.h>
60116742Ssam#include <net/if_arp.h>
61116742Ssam#include <net/ethernet.h>
62116742Ssam#include <net/if_llc.h>
63116742Ssam
64116742Ssam#include <net80211/ieee80211_var.h>
65116742Ssam
66116742Ssam#include <net/bpf.h>
67116742Ssam
68116742Ssam#ifdef INET
69116742Ssam#include <netinet/in.h>
70116742Ssam#include <netinet/if_ether.h>
71116742Ssam#endif
72116742Ssam
73116742Ssam#define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
74116742Ssam
75116742Ssamconst char *ieee80211_mgt_subtype_name[] = {
76116742Ssam	"assoc_req",	"assoc_resp",	"reassoc_req",	"reassoc_resp",
77116742Ssam	"probe_req",	"probe_resp",	"reserved#6",	"reserved#7",
78116742Ssam	"beacon",	"atim",		"disassoc",	"auth",
79116742Ssam	"deauth",	"reserved#13",	"reserved#14",	"reserved#15"
80116742Ssam};
81116742Ssam
82116742Ssamvoid
83116742Ssamieee80211_proto_attach(struct ifnet *ifp)
84116742Ssam{
85116742Ssam	struct ieee80211com *ic = (void *)ifp;
86116742Ssam
87116742Ssam	ifp->if_hdrlen = sizeof(struct ieee80211_frame);
88116742Ssam
89116742Ssam#ifdef notdef
90116742Ssam	ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT;
91116742Ssam#else
92116742Ssam	ic->ic_rtsthreshold = IEEE80211_RTS_MAX;
93116742Ssam#endif
94116742Ssam	ic->ic_fragthreshold = 2346;		/* XXX not used yet */
95116742Ssam	ic->ic_fixed_rate = -1;			/* no fixed rate */
96116742Ssam
97116742Ssam	mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_name, "mgmt send q", MTX_DEF);
98116742Ssam
99116742Ssam	/* initialize management frame handlers */
100116742Ssam	ic->ic_recv_mgmt = ieee80211_recv_mgmt;
101116742Ssam	ic->ic_send_mgmt = ieee80211_send_mgmt;
102116742Ssam}
103116742Ssam
104116742Ssamvoid
105116742Ssamieee80211_proto_detach(struct ifnet *ifp)
106116742Ssam{
107116742Ssam	struct ieee80211com *ic = (void *)ifp;
108116742Ssam
109116742Ssam	IF_DRAIN(&ic->ic_mgtq);
110116742Ssam	mtx_destroy(&ic->ic_mgtq.ifq_mtx);
111116742Ssam}
112116742Ssam
113116742Ssamvoid
114116742Ssamieee80211_print_essid(u_int8_t *essid, int len)
115116742Ssam{
116116742Ssam	int i;
117116742Ssam	u_int8_t *p;
118116742Ssam
119116742Ssam	if (len > IEEE80211_NWID_LEN)
120116742Ssam		len = IEEE80211_NWID_LEN;
121116742Ssam	/* determine printable or not */
122116742Ssam	for (i = 0, p = essid; i < len; i++, p++) {
123116742Ssam		if (*p < ' ' || *p > 0x7e)
124116742Ssam			break;
125116742Ssam	}
126116742Ssam	if (i == len) {
127116742Ssam		printf("\"");
128116742Ssam		for (i = 0, p = essid; i < len; i++, p++)
129116742Ssam			printf("%c", *p);
130116742Ssam		printf("\"");
131116742Ssam	} else {
132116742Ssam		printf("0x");
133116742Ssam		for (i = 0, p = essid; i < len; i++, p++)
134116742Ssam			printf("%02x", *p);
135116742Ssam	}
136116742Ssam}
137116742Ssam
138116742Ssamvoid
139116742Ssamieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi)
140116742Ssam{
141116742Ssam	struct ieee80211_frame *wh;
142116742Ssam	int i;
143116742Ssam
144116742Ssam	wh = (struct ieee80211_frame *)buf;
145116742Ssam	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
146116742Ssam	case IEEE80211_FC1_DIR_NODS:
147116742Ssam		printf("NODS %s", ether_sprintf(wh->i_addr2));
148116742Ssam		printf("->%s", ether_sprintf(wh->i_addr1));
149116742Ssam		printf("(%s)", ether_sprintf(wh->i_addr3));
150116742Ssam		break;
151116742Ssam	case IEEE80211_FC1_DIR_TODS:
152116742Ssam		printf("TODS %s", ether_sprintf(wh->i_addr2));
153116742Ssam		printf("->%s", ether_sprintf(wh->i_addr3));
154116742Ssam		printf("(%s)", ether_sprintf(wh->i_addr1));
155116742Ssam		break;
156116742Ssam	case IEEE80211_FC1_DIR_FROMDS:
157116742Ssam		printf("FRDS %s", ether_sprintf(wh->i_addr3));
158116742Ssam		printf("->%s", ether_sprintf(wh->i_addr1));
159116742Ssam		printf("(%s)", ether_sprintf(wh->i_addr2));
160116742Ssam		break;
161116742Ssam	case IEEE80211_FC1_DIR_DSTODS:
162116742Ssam		printf("DSDS %s", ether_sprintf((u_int8_t *)&wh[1]));
163116742Ssam		printf("->%s", ether_sprintf(wh->i_addr3));
164116742Ssam		printf("(%s", ether_sprintf(wh->i_addr2));
165116742Ssam		printf("->%s)", ether_sprintf(wh->i_addr1));
166116742Ssam		break;
167116742Ssam	}
168116742Ssam	switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
169116742Ssam	case IEEE80211_FC0_TYPE_DATA:
170116742Ssam		printf(" data");
171116742Ssam		break;
172116742Ssam	case IEEE80211_FC0_TYPE_MGT:
173116742Ssam		printf(" %s", ieee80211_mgt_subtype_name[
174116742Ssam		    (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
175116742Ssam		    >> IEEE80211_FC0_SUBTYPE_SHIFT]);
176116742Ssam		break;
177116742Ssam	default:
178116742Ssam		printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
179116742Ssam		break;
180116742Ssam	}
181116742Ssam	if (wh->i_fc[1] & IEEE80211_FC1_WEP)
182116742Ssam		printf(" WEP");
183116742Ssam	if (rate >= 0)
184116742Ssam		printf(" %dM", rate / 2);
185116742Ssam	if (rssi >= 0)
186116742Ssam		printf(" +%d", rssi);
187116742Ssam	printf("\n");
188116742Ssam	if (len > 0) {
189116742Ssam		for (i = 0; i < len; i++) {
190116742Ssam			if ((i & 1) == 0)
191116742Ssam				printf(" ");
192116742Ssam			printf("%02x", buf[i]);
193116742Ssam		}
194116742Ssam		printf("\n");
195116742Ssam	}
196116742Ssam}
197116742Ssam
198116742Ssamint
199116742Ssamieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags)
200116742Ssam{
201116742Ssam#define	RV(v)	((v) & IEEE80211_RATE_VAL)
202116742Ssam	int i, j, ignore, error;
203116742Ssam	int okrate, badrate;
204116742Ssam	struct ieee80211_rateset *srs, *nrs;
205116742Ssam	u_int8_t r;
206116742Ssam
207116742Ssam	error = 0;
208116742Ssam	okrate = badrate = 0;
209116742Ssam	srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
210116742Ssam	nrs = &ni->ni_rates;
211116742Ssam	for (i = 0; i < ni->ni_rates.rs_nrates; ) {
212116742Ssam		ignore = 0;
213116742Ssam		if (flags & IEEE80211_F_DOSORT) {
214116742Ssam			/*
215116742Ssam			 * Sort rates.
216116742Ssam			 */
217116742Ssam			for (j = i + 1; j < nrs->rs_nrates; j++) {
218116742Ssam				if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) {
219116742Ssam					r = nrs->rs_rates[i];
220116742Ssam					nrs->rs_rates[i] = nrs->rs_rates[j];
221116742Ssam					nrs->rs_rates[j] = r;
222116742Ssam				}
223116742Ssam			}
224116742Ssam		}
225116742Ssam		r = nrs->rs_rates[i] & IEEE80211_RATE_VAL;
226116742Ssam		badrate = r;
227116742Ssam		if (flags & IEEE80211_F_DOFRATE) {
228116742Ssam			/*
229116742Ssam			 * Apply fixed rate constraint.  Note that we do
230116742Ssam			 * not apply the constraint to basic rates as
231116742Ssam			 * otherwise we may not be able to associate if
232116742Ssam			 * the rate set we submit to the AP is invalid
233116742Ssam			 * (e.g. fix rate at 36Mb/s which is not a basic
234116742Ssam			 * rate for 11a operation).
235116742Ssam			 */
236116742Ssam			if ((nrs->rs_rates[i] & IEEE80211_RATE_BASIC) == 0 &&
237116742Ssam			    ic->ic_fixed_rate >= 0 &&
238116742Ssam			    r != RV(srs->rs_rates[ic->ic_fixed_rate]))
239116742Ssam				ignore++;
240116742Ssam		}
241116742Ssam		if (flags & IEEE80211_F_DONEGO) {
242116742Ssam			/*
243116742Ssam			 * Check against supported rates.
244116742Ssam			 */
245116742Ssam			for (j = 0; j < srs->rs_nrates; j++) {
246116742Ssam				if (r == RV(srs->rs_rates[j]))
247116742Ssam					break;
248116742Ssam			}
249116742Ssam			if (j == srs->rs_nrates) {
250116742Ssam				if (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)
251116742Ssam					error++;
252116742Ssam				ignore++;
253116742Ssam			}
254116742Ssam		}
255116742Ssam		if (flags & IEEE80211_F_DODEL) {
256116742Ssam			/*
257116742Ssam			 * Delete unacceptable rates.
258116742Ssam			 */
259116742Ssam			if (ignore) {
260116742Ssam				nrs->rs_nrates--;
261116742Ssam				for (j = i; j < nrs->rs_nrates; j++)
262116742Ssam					nrs->rs_rates[j] = nrs->rs_rates[j + 1];
263116742Ssam				nrs->rs_rates[j] = 0;
264116742Ssam				continue;
265116742Ssam			}
266116742Ssam		}
267116742Ssam		if (!ignore)
268116742Ssam			okrate = nrs->rs_rates[i];
269116742Ssam		i++;
270116742Ssam	}
271116742Ssam	if (okrate == 0 || error != 0)
272116742Ssam		return badrate | IEEE80211_RATE_BASIC;
273116742Ssam	else
274116742Ssam		return RV(okrate);
275116742Ssam#undef RV
276116742Ssam}
277116742Ssam
278116742Ssamint
279116742Ssamieee80211_new_state(struct ifnet *ifp, enum ieee80211_state nstate, int mgt)
280116742Ssam{
281116742Ssam	struct ieee80211com *ic = (void *)ifp;
282116742Ssam	struct ieee80211_node *ni;
283116742Ssam	int error, ostate;
284116742Ssam#ifdef IEEE80211_DEBUG
285116742Ssam	static const char *stname[] =
286116742Ssam	    { "INIT", "SCAN", "AUTH", "ASSOC", "RUN" };
287116742Ssam#endif
288116742Ssam
289116742Ssam	ostate = ic->ic_state;
290116742Ssam	IEEE80211_DPRINTF(("%s: %s -> %s\n", __func__,
291116742Ssam		stname[ostate], stname[nstate]));
292116742Ssam	if (ic->ic_newstate) {
293116742Ssam		error = (*ic->ic_newstate)(ic->ic_softc, nstate);
294116742Ssam		if (error == EINPROGRESS)
295116742Ssam			return 0;
296116742Ssam		if (error != 0)
297116742Ssam			return error;
298116742Ssam	}
299116742Ssam
300116742Ssam	/* state transition */
301116742Ssam	ic->ic_state = nstate;
302116742Ssam	ni = ic->ic_bss;			/* NB: no reference held */
303116742Ssam	switch (nstate) {
304116742Ssam	case IEEE80211_S_INIT:
305116742Ssam		switch (ostate) {
306116742Ssam		case IEEE80211_S_INIT:
307116742Ssam			break;
308116742Ssam		case IEEE80211_S_RUN:
309116742Ssam			switch (ic->ic_opmode) {
310116742Ssam			case IEEE80211_M_STA:
311116742Ssam				IEEE80211_SEND_MGMT(ic, ni,
312116742Ssam				    IEEE80211_FC0_SUBTYPE_DISASSOC,
313116742Ssam				    IEEE80211_REASON_ASSOC_LEAVE);
314116742Ssam				break;
315116742Ssam			case IEEE80211_M_HOSTAP:
316116742Ssam				mtx_lock(&ic->ic_nodelock);
317116742Ssam				TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
318116742Ssam					if (ni->ni_associd == 0)
319116742Ssam						continue;
320116742Ssam					IEEE80211_SEND_MGMT(ic, ni,
321116742Ssam					    IEEE80211_FC0_SUBTYPE_DISASSOC,
322116742Ssam					    IEEE80211_REASON_ASSOC_LEAVE);
323116742Ssam				}
324116742Ssam				mtx_unlock(&ic->ic_nodelock);
325116742Ssam				break;
326116742Ssam			default:
327116742Ssam				break;
328116742Ssam			}
329116742Ssam			/* FALLTHRU */
330116742Ssam		case IEEE80211_S_ASSOC:
331116742Ssam			switch (ic->ic_opmode) {
332116742Ssam			case IEEE80211_M_STA:
333116742Ssam				IEEE80211_SEND_MGMT(ic, ni,
334116742Ssam				    IEEE80211_FC0_SUBTYPE_DEAUTH,
335116742Ssam				    IEEE80211_REASON_AUTH_LEAVE);
336116742Ssam				break;
337116742Ssam			case IEEE80211_M_HOSTAP:
338116742Ssam				mtx_lock(&ic->ic_nodelock);
339116742Ssam				TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
340116742Ssam					IEEE80211_SEND_MGMT(ic, ni,
341116742Ssam					    IEEE80211_FC0_SUBTYPE_DEAUTH,
342116742Ssam					    IEEE80211_REASON_AUTH_LEAVE);
343116742Ssam				}
344116742Ssam				mtx_unlock(&ic->ic_nodelock);
345116742Ssam				break;
346116742Ssam			default:
347116742Ssam				break;
348116742Ssam			}
349116742Ssam			/* FALLTHRU */
350116742Ssam		case IEEE80211_S_AUTH:
351116742Ssam		case IEEE80211_S_SCAN:
352116742Ssam			ic->ic_mgt_timer = 0;
353116742Ssam			IF_DRAIN(&ic->ic_mgtq);
354116742Ssam			if (ic->ic_wep_ctx != NULL) {
355116742Ssam				free(ic->ic_wep_ctx, M_DEVBUF);
356116742Ssam				ic->ic_wep_ctx = NULL;
357116742Ssam			}
358116742Ssam			ieee80211_free_allnodes(ic);
359116742Ssam			break;
360116742Ssam		}
361116742Ssam		break;
362116742Ssam	case IEEE80211_S_SCAN:
363116742Ssam		ic->ic_flags &= ~IEEE80211_F_SIBSS;
364116742Ssam		/* initialize bss for probe request */
365116742Ssam		IEEE80211_ADDR_COPY(ni->ni_macaddr, ifp->if_broadcastaddr);
366116742Ssam		IEEE80211_ADDR_COPY(ni->ni_bssid, ifp->if_broadcastaddr);
367116742Ssam		ni->ni_rates = ic->ic_sup_rates[
368116742Ssam			ieee80211_chan2mode(ic, ni->ni_chan)];
369116742Ssam		ni->ni_associd = 0;
370116742Ssam		ni->ni_rstamp = 0;
371116742Ssam		switch (ostate) {
372116742Ssam		case IEEE80211_S_INIT:
373116742Ssam			if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
374116742Ssam			    ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
375116742Ssam				/*
376116742Ssam				 * AP operation and we already have a channel;
377116742Ssam				 * bypass the scan and startup immediately.
378116742Ssam				 */
379116742Ssam				ieee80211_create_ibss(ic, ic->ic_des_chan);
380116742Ssam			} else {
381116742Ssam				ieee80211_begin_scan(ifp, ni);
382116742Ssam			}
383116742Ssam			break;
384116742Ssam		case IEEE80211_S_SCAN:
385116742Ssam			/* scan next */
386116742Ssam			if (ic->ic_flags & IEEE80211_F_ASCAN) {
387116742Ssam				IEEE80211_SEND_MGMT(ic, ni,
388116742Ssam				    IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
389116742Ssam			}
390116742Ssam			break;
391116742Ssam		case IEEE80211_S_RUN:
392116742Ssam			/* beacon miss */
393116742Ssam			if (ifp->if_flags & IFF_DEBUG) {
394116742Ssam				/* XXX bssid clobbered above */
395116742Ssam				if_printf(ifp, "no recent beacons from %s;"
396116742Ssam				    " rescanning\n",
397116742Ssam				    ether_sprintf(ic->ic_bss->ni_bssid));
398116742Ssam			}
399116742Ssam			ieee80211_free_allnodes(ic);
400116742Ssam			/* FALLTHRU */
401116742Ssam		case IEEE80211_S_AUTH:
402116742Ssam		case IEEE80211_S_ASSOC:
403116742Ssam			/* timeout restart scan */
404116742Ssam			ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr);
405116742Ssam			if (ni != NULL) {
406116742Ssam				ni->ni_fails++;
407116742Ssam				ieee80211_unref_node(&ni);
408116742Ssam			}
409116742Ssam			ieee80211_begin_scan(ifp, ic->ic_bss);
410116742Ssam			break;
411116742Ssam		}
412116742Ssam		break;
413116742Ssam	case IEEE80211_S_AUTH:
414116742Ssam		switch (ostate) {
415116742Ssam		case IEEE80211_S_INIT:
416116742Ssam			IEEE80211_DPRINTF(("%s: invalid transition\n",
417116742Ssam				__func__));
418116742Ssam			break;
419116742Ssam		case IEEE80211_S_SCAN:
420116742Ssam			IEEE80211_SEND_MGMT(ic, ni,
421116742Ssam			    IEEE80211_FC0_SUBTYPE_AUTH, 1);
422116742Ssam			break;
423116742Ssam		case IEEE80211_S_AUTH:
424116742Ssam		case IEEE80211_S_ASSOC:
425116742Ssam			switch (mgt) {
426116742Ssam			case IEEE80211_FC0_SUBTYPE_AUTH:
427116742Ssam				/* ??? */
428116742Ssam				IEEE80211_SEND_MGMT(ic, ni,
429116742Ssam				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
430116742Ssam				break;
431116742Ssam			case IEEE80211_FC0_SUBTYPE_DEAUTH:
432116742Ssam				/* ignore and retry scan on timeout */
433116742Ssam				break;
434116742Ssam			}
435116742Ssam			break;
436116742Ssam		case IEEE80211_S_RUN:
437116742Ssam			switch (mgt) {
438116742Ssam			case IEEE80211_FC0_SUBTYPE_AUTH:
439116742Ssam				IEEE80211_SEND_MGMT(ic, ni,
440116742Ssam				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
441116742Ssam				ic->ic_state = ostate;	/* stay RUN */
442116742Ssam				break;
443116742Ssam			case IEEE80211_FC0_SUBTYPE_DEAUTH:
444116742Ssam				/* try to reauth */
445116742Ssam				IEEE80211_SEND_MGMT(ic, ni,
446116742Ssam				    IEEE80211_FC0_SUBTYPE_AUTH, 1);
447116742Ssam				break;
448116742Ssam			}
449116742Ssam			break;
450116742Ssam		}
451116742Ssam		break;
452116742Ssam	case IEEE80211_S_ASSOC:
453116742Ssam		switch (ostate) {
454116742Ssam		case IEEE80211_S_INIT:
455116742Ssam		case IEEE80211_S_SCAN:
456116742Ssam		case IEEE80211_S_ASSOC:
457116742Ssam			IEEE80211_DPRINTF(("%s: invalid transition\n",
458116742Ssam				__func__));
459116742Ssam			break;
460116742Ssam		case IEEE80211_S_AUTH:
461116742Ssam			IEEE80211_SEND_MGMT(ic, ni,
462116742Ssam			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
463116742Ssam			break;
464116742Ssam		case IEEE80211_S_RUN:
465116742Ssam			IEEE80211_SEND_MGMT(ic, ni,
466116742Ssam			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
467116742Ssam			break;
468116742Ssam		}
469116742Ssam		break;
470116742Ssam	case IEEE80211_S_RUN:
471116742Ssam		switch (ostate) {
472116742Ssam		case IEEE80211_S_INIT:
473116742Ssam		case IEEE80211_S_AUTH:
474116742Ssam		case IEEE80211_S_RUN:
475116742Ssam			IEEE80211_DPRINTF(("%s: invalid transition\n",
476116742Ssam				__func__));
477116742Ssam			break;
478116742Ssam		case IEEE80211_S_SCAN:		/* adhoc/hostap mode */
479116742Ssam		case IEEE80211_S_ASSOC:		/* infra mode */
480116742Ssam			KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates,
481116742Ssam				("%s: bogus xmit rate %u setup\n", __func__,
482116742Ssam					ni->ni_txrate));
483116742Ssam			if (ifp->if_flags & IFF_DEBUG) {
484116742Ssam				if_printf(ifp, " ");
485116742Ssam				if (ic->ic_opmode == IEEE80211_M_STA)
486116742Ssam					printf("associated ");
487116742Ssam				else
488116742Ssam					printf("synchronized ");
489116742Ssam				printf("with %s ssid ",
490116742Ssam				    ether_sprintf(ni->ni_bssid));
491116742Ssam				ieee80211_print_essid(ic->ic_bss->ni_essid,
492116742Ssam				    ni->ni_esslen);
493116742Ssam				printf(" channel %d start %uMb\n",
494116742Ssam					ieee80211_chan2ieee(ic, ni->ni_chan),
495116742Ssam					IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate]));
496116742Ssam			}
497116742Ssam			ic->ic_mgt_timer = 0;
498116742Ssam			(*ifp->if_start)(ifp);
499116742Ssam			break;
500116742Ssam		}
501116742Ssam		break;
502116742Ssam	}
503116742Ssam	return 0;
504116742Ssam}
505