1*3147Sxc151355 /*
2*3147Sxc151355  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
3*3147Sxc151355  * Use is subject to license terms.
4*3147Sxc151355  */
5*3147Sxc151355 
6*3147Sxc151355 /*
7*3147Sxc151355  * Copyright (c) 2001 Atsushi Onoe
8*3147Sxc151355  * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
9*3147Sxc151355  * All rights reserved.
10*3147Sxc151355  *
11*3147Sxc151355  * Redistribution and use in source and binary forms, with or without
12*3147Sxc151355  * modification, are permitted provided that the following conditions
13*3147Sxc151355  * are met:
14*3147Sxc151355  * 1. Redistributions of source code must retain the above copyright
15*3147Sxc151355  *    notice, this list of conditions and the following disclaimer.
16*3147Sxc151355  * 2. Redistributions in binary form must reproduce the above copyright
17*3147Sxc151355  *    notice, this list of conditions and the following disclaimer in the
18*3147Sxc151355  *    documentation and/or other materials provided with the distribution.
19*3147Sxc151355  * 3. The name of the author may not be used to endorse or promote products
20*3147Sxc151355  *    derived from this software without specific prior written permission.
21*3147Sxc151355  *
22*3147Sxc151355  * Alternatively, this software may be distributed under the terms of the
23*3147Sxc151355  * GNU General Public License ("GPL") version 2 as published by the Free
24*3147Sxc151355  * Software Foundation.
25*3147Sxc151355  *
26*3147Sxc151355  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27*3147Sxc151355  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28*3147Sxc151355  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29*3147Sxc151355  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30*3147Sxc151355  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31*3147Sxc151355  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32*3147Sxc151355  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33*3147Sxc151355  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34*3147Sxc151355  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35*3147Sxc151355  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36*3147Sxc151355  */
37*3147Sxc151355 
38*3147Sxc151355 #pragma ident	"%Z%%M%	%I%	%E% SMI"
39*3147Sxc151355 
40*3147Sxc151355 /*
41*3147Sxc151355  * Process received frame
42*3147Sxc151355  */
43*3147Sxc151355 
44*3147Sxc151355 #include <sys/byteorder.h>
45*3147Sxc151355 #include "net80211_impl.h"
46*3147Sxc151355 
47*3147Sxc151355 static mblk_t *ieee80211_defrag(ieee80211com_t *, ieee80211_node_t *,
48*3147Sxc151355     mblk_t *, int);
49*3147Sxc151355 
50*3147Sxc151355 /*
51*3147Sxc151355  * Process a received frame.  The node associated with the sender
52*3147Sxc151355  * should be supplied.  If nothing was found in the node table then
53*3147Sxc151355  * the caller is assumed to supply a reference to ic_bss instead.
54*3147Sxc151355  * The RSSI and a timestamp are also supplied.  The RSSI data is used
55*3147Sxc151355  * during AP scanning to select a AP to associate with; it can have
56*3147Sxc151355  * any units so long as values have consistent units and higher values
57*3147Sxc151355  * mean ``better signal''.  The receive timestamp is currently not used
58*3147Sxc151355  * by the 802.11 layer.
59*3147Sxc151355  */
60*3147Sxc151355 int
61*3147Sxc151355 ieee80211_input(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
62*3147Sxc151355     int32_t rssi, uint32_t rstamp)
63*3147Sxc151355 {
64*3147Sxc151355 	struct ieee80211_frame *wh;
65*3147Sxc151355 	struct ieee80211_key *key;
66*3147Sxc151355 	uint8_t *bssid;
67*3147Sxc151355 	int hdrspace;
68*3147Sxc151355 	int len;
69*3147Sxc151355 	uint16_t rxseq;
70*3147Sxc151355 	uint8_t dir;
71*3147Sxc151355 	uint8_t type;
72*3147Sxc151355 	uint8_t subtype;
73*3147Sxc151355 	uint8_t tid;
74*3147Sxc151355 
75*3147Sxc151355 	ASSERT(in != NULL);
76*3147Sxc151355 	type = (uint8_t)-1;		/* undefined */
77*3147Sxc151355 	len = mp->b_wptr - mp->b_rptr;
78*3147Sxc151355 	if (len < sizeof (struct ieee80211_frame_min)) {
79*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_input: "
80*3147Sxc151355 			"too short (1): len %u", len);
81*3147Sxc151355 		goto out;
82*3147Sxc151355 	}
83*3147Sxc151355 	/*
84*3147Sxc151355 	 * Bit of a cheat here, we use a pointer for a 3-address
85*3147Sxc151355 	 * frame format but don't reference fields past outside
86*3147Sxc151355 	 * ieee80211_frame_min w/o first validating the data is
87*3147Sxc151355 	 * present.
88*3147Sxc151355 	 */
89*3147Sxc151355 	wh = (struct ieee80211_frame *)mp->b_rptr;
90*3147Sxc151355 	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
91*3147Sxc151355 	    IEEE80211_FC0_VERSION_0) {
92*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_input: "
93*3147Sxc151355 			"discard pkt with wrong version %x", wh->i_fc[0]);
94*3147Sxc151355 		goto out;
95*3147Sxc151355 	}
96*3147Sxc151355 
97*3147Sxc151355 	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
98*3147Sxc151355 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
99*3147Sxc151355 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
100*3147Sxc151355 
101*3147Sxc151355 	IEEE80211_LOCK(ic);
102*3147Sxc151355 	if (!(ic->ic_flags & IEEE80211_F_SCAN)) {
103*3147Sxc151355 		switch (ic->ic_opmode) {
104*3147Sxc151355 		case IEEE80211_M_STA:
105*3147Sxc151355 			bssid = wh->i_addr2;
106*3147Sxc151355 			if (!IEEE80211_ADDR_EQ(bssid, in->in_bssid))
107*3147Sxc151355 				goto out_exit_mutex;
108*3147Sxc151355 			break;
109*3147Sxc151355 		case IEEE80211_M_IBSS:
110*3147Sxc151355 		case IEEE80211_M_AHDEMO:
111*3147Sxc151355 			if (dir != IEEE80211_FC1_DIR_NODS) {
112*3147Sxc151355 				bssid = wh->i_addr1;
113*3147Sxc151355 			} else if (type == IEEE80211_FC0_TYPE_CTL) {
114*3147Sxc151355 				bssid = wh->i_addr1;
115*3147Sxc151355 			} else {
116*3147Sxc151355 				if (len < sizeof (struct ieee80211_frame)) {
117*3147Sxc151355 					ieee80211_dbg(IEEE80211_MSG_ANY,
118*3147Sxc151355 						"ieee80211_input: too short(2):"
119*3147Sxc151355 						"len %u\n", len);
120*3147Sxc151355 					goto out_exit_mutex;
121*3147Sxc151355 				}
122*3147Sxc151355 				bssid = wh->i_addr3;
123*3147Sxc151355 			}
124*3147Sxc151355 			if (type != IEEE80211_FC0_TYPE_DATA)
125*3147Sxc151355 				break;
126*3147Sxc151355 			/*
127*3147Sxc151355 			 * Data frame, validate the bssid.
128*3147Sxc151355 			 */
129*3147Sxc151355 			if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->in_bssid) &&
130*3147Sxc151355 			    !IEEE80211_ADDR_EQ(bssid, wifi_bcastaddr)) {
131*3147Sxc151355 				/* not interested in */
132*3147Sxc151355 				ieee80211_dbg(IEEE80211_MSG_INPUT,
133*3147Sxc151355 					"ieee80211_input: not to bss %s\n",
134*3147Sxc151355 					ieee80211_macaddr_sprintf(bssid));
135*3147Sxc151355 				goto out_exit_mutex;
136*3147Sxc151355 			}
137*3147Sxc151355 			/*
138*3147Sxc151355 			 * For adhoc mode we cons up a node when it doesn't
139*3147Sxc151355 			 * exist. This should probably done after an ACL check.
140*3147Sxc151355 			 */
141*3147Sxc151355 			if (in == ic->ic_bss &&
142*3147Sxc151355 			    ic->ic_opmode != IEEE80211_M_HOSTAP &&
143*3147Sxc151355 			    !IEEE80211_ADDR_EQ(wh->i_addr2, in->in_macaddr)) {
144*3147Sxc151355 				/*
145*3147Sxc151355 				 * Fake up a node for this newly
146*3147Sxc151355 				 * discovered member of the IBSS.
147*3147Sxc151355 				 */
148*3147Sxc151355 				in = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
149*3147Sxc151355 					wh->i_addr2);
150*3147Sxc151355 				if (in == NULL) {
151*3147Sxc151355 					/* NB: stat kept for alloc failure */
152*3147Sxc151355 					goto out_exit_mutex;
153*3147Sxc151355 				}
154*3147Sxc151355 			}
155*3147Sxc151355 			break;
156*3147Sxc151355 		default:
157*3147Sxc151355 			goto out_exit_mutex;
158*3147Sxc151355 		}
159*3147Sxc151355 		in->in_rssi = (uint8_t)rssi;
160*3147Sxc151355 		in->in_rstamp = rstamp;
161*3147Sxc151355 		if (!(type & IEEE80211_FC0_TYPE_CTL)) {
162*3147Sxc151355 			tid = 0;
163*3147Sxc151355 			rxseq = (*(uint16_t *)wh->i_seq);
164*3147Sxc151355 			if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
165*3147Sxc151355 			    (rxseq - in->in_rxseqs[tid]) <= 0) {
166*3147Sxc151355 				/* duplicate, discard */
167*3147Sxc151355 				ieee80211_dbg(IEEE80211_MSG_INPUT,
168*3147Sxc151355 					"ieee80211_input: duplicate",
169*3147Sxc151355 					"seqno <%u,%u> fragno <%u,%u> tid %u",
170*3147Sxc151355 					rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
171*3147Sxc151355 					in->in_rxseqs[tid] >>
172*3147Sxc151355 						IEEE80211_SEQ_SEQ_SHIFT,
173*3147Sxc151355 					rxseq & IEEE80211_SEQ_FRAG_MASK,
174*3147Sxc151355 					in->in_rxseqs[tid] &
175*3147Sxc151355 						IEEE80211_SEQ_FRAG_MASK,
176*3147Sxc151355 					tid);
177*3147Sxc151355 				ic->ic_stats.is_rx_dups++;
178*3147Sxc151355 				goto out_exit_mutex;
179*3147Sxc151355 			}
180*3147Sxc151355 			in->in_rxseqs[tid] = rxseq;
181*3147Sxc151355 		}
182*3147Sxc151355 		in->in_inact = 0;
183*3147Sxc151355 	}
184*3147Sxc151355 
185*3147Sxc151355 	hdrspace = ieee80211_hdrspace(wh);
186*3147Sxc151355 	switch (type) {
187*3147Sxc151355 	case IEEE80211_FC0_TYPE_DATA:
188*3147Sxc151355 		if (len < hdrspace) {
189*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_input: "
190*3147Sxc151355 				"data too short: expecting %u", hdrspace);
191*3147Sxc151355 			goto out_exit_mutex;
192*3147Sxc151355 		}
193*3147Sxc151355 		switch (ic->ic_opmode) {
194*3147Sxc151355 		case IEEE80211_M_STA:
195*3147Sxc151355 			if (dir != IEEE80211_FC1_DIR_FROMDS) {
196*3147Sxc151355 				ieee80211_dbg(IEEE80211_MSG_INPUT,
197*3147Sxc151355 					"ieee80211_input: data ",
198*3147Sxc151355 					"unknown dir 0x%x", dir);
199*3147Sxc151355 				goto out_exit_mutex;
200*3147Sxc151355 			}
201*3147Sxc151355 			if (IEEE80211_IS_MULTICAST(wh->i_addr1) &&
202*3147Sxc151355 			    IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_macaddr)) {
203*3147Sxc151355 				/*
204*3147Sxc151355 				 * In IEEE802.11 network, multicast packet
205*3147Sxc151355 				 * sent from me is broadcasted from AP.
206*3147Sxc151355 				 * It should be silently discarded for
207*3147Sxc151355 				 * SIMPLEX interface.
208*3147Sxc151355 				 */
209*3147Sxc151355 				ieee80211_dbg(IEEE80211_MSG_INPUT,
210*3147Sxc151355 					"ieee80211_input: multicast echo\n");
211*3147Sxc151355 				goto out_exit_mutex;
212*3147Sxc151355 			}
213*3147Sxc151355 			break;
214*3147Sxc151355 		case IEEE80211_M_IBSS:
215*3147Sxc151355 		case IEEE80211_M_AHDEMO:
216*3147Sxc151355 			if (dir != IEEE80211_FC1_DIR_NODS) {
217*3147Sxc151355 				ieee80211_dbg(IEEE80211_MSG_INPUT,
218*3147Sxc151355 					"ieee80211_input: unknown dir 0x%x",
219*3147Sxc151355 					dir);
220*3147Sxc151355 				goto out_exit_mutex;
221*3147Sxc151355 			}
222*3147Sxc151355 			break;
223*3147Sxc151355 		default:
224*3147Sxc151355 			ieee80211_err("ieee80211_input: "
225*3147Sxc151355 				"receive data, unknown opmode %u, skip\n",
226*3147Sxc151355 				ic->ic_opmode);
227*3147Sxc151355 			goto out_exit_mutex;
228*3147Sxc151355 		}
229*3147Sxc151355 
230*3147Sxc151355 		/*
231*3147Sxc151355 		 * Handle privacy requirements.
232*3147Sxc151355 		 */
233*3147Sxc151355 		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
234*3147Sxc151355 			if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
235*3147Sxc151355 				/*
236*3147Sxc151355 				 * Discard encrypted frames when privacy off.
237*3147Sxc151355 				 */
238*3147Sxc151355 				ieee80211_dbg(IEEE80211_MSG_INPUT,
239*3147Sxc151355 					"ieee80211_input: ""WEP PRIVACY off");
240*3147Sxc151355 				ic->ic_stats.is_wep_errors++;
241*3147Sxc151355 				goto out_exit_mutex;
242*3147Sxc151355 			}
243*3147Sxc151355 			key = ieee80211_crypto_decap(ic, mp, hdrspace);
244*3147Sxc151355 			if (key == NULL) {
245*3147Sxc151355 				/* NB: stats+msgs handled in crypto_decap */
246*3147Sxc151355 				ic->ic_stats.is_wep_errors++;
247*3147Sxc151355 				goto out_exit_mutex;
248*3147Sxc151355 			}
249*3147Sxc151355 			wh = (struct ieee80211_frame *)mp->b_rptr;
250*3147Sxc151355 			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
251*3147Sxc151355 		} else {
252*3147Sxc151355 			key = NULL;
253*3147Sxc151355 		}
254*3147Sxc151355 
255*3147Sxc151355 		/*
256*3147Sxc151355 		 * Next up, any fragmentation
257*3147Sxc151355 		 */
258*3147Sxc151355 		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
259*3147Sxc151355 			mp = ieee80211_defrag(ic, in, mp, hdrspace);
260*3147Sxc151355 			if (mp == NULL) {
261*3147Sxc151355 				/* Fragment dropped or frame not complete yet */
262*3147Sxc151355 				goto out_exit_mutex;
263*3147Sxc151355 			}
264*3147Sxc151355 		}
265*3147Sxc151355 		wh = NULL;	/* no longer valid, catch any uses */
266*3147Sxc151355 
267*3147Sxc151355 		/*
268*3147Sxc151355 		 * Next strip any MSDU crypto bits.
269*3147Sxc151355 		 */
270*3147Sxc151355 		if (key != NULL && !ieee80211_crypto_demic(ic, key, mp, 0)) {
271*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_INPUT, "ieee80211_input: "
272*3147Sxc151355 				"data demic error\n");
273*3147Sxc151355 			goto out_exit_mutex;
274*3147Sxc151355 		}
275*3147Sxc151355 
276*3147Sxc151355 		ic->ic_stats.is_rx_frags++;
277*3147Sxc151355 		ic->ic_stats.is_rx_bytes += len;
278*3147Sxc151355 		IEEE80211_UNLOCK(ic);
279*3147Sxc151355 		mac_rx(ic->ic_mach, NULL, mp);
280*3147Sxc151355 		return (IEEE80211_FC0_TYPE_DATA);
281*3147Sxc151355 
282*3147Sxc151355 	case IEEE80211_FC0_TYPE_MGT:
283*3147Sxc151355 		if (dir != IEEE80211_FC1_DIR_NODS)
284*3147Sxc151355 			goto out_exit_mutex;
285*3147Sxc151355 		if (len < sizeof (struct ieee80211_frame))
286*3147Sxc151355 			goto out_exit_mutex;
287*3147Sxc151355 		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
288*3147Sxc151355 			if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
289*3147Sxc151355 				/*
290*3147Sxc151355 				 * Only shared key auth frames with a challenge
291*3147Sxc151355 				 * should be encrypted, discard all others.
292*3147Sxc151355 				 */
293*3147Sxc151355 				ieee80211_dbg(IEEE80211_MSG_INPUT,
294*3147Sxc151355 					"ieee80211_input: "
295*3147Sxc151355 					"%s WEP set but not permitted",
296*3147Sxc151355 					IEEE80211_SUBTYPE_NAME(subtype));
297*3147Sxc151355 				ic->ic_stats.is_wep_errors++;
298*3147Sxc151355 				goto out_exit_mutex;
299*3147Sxc151355 			}
300*3147Sxc151355 			if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
301*3147Sxc151355 				/*
302*3147Sxc151355 				 * Discard encrypted frames when privacy off.
303*3147Sxc151355 				 */
304*3147Sxc151355 				ieee80211_dbg(IEEE80211_MSG_INPUT,
305*3147Sxc151355 					"ieee80211_input: "
306*3147Sxc151355 					"mgt WEP set but PRIVACY off");
307*3147Sxc151355 				ic->ic_stats.is_wep_errors++;
308*3147Sxc151355 				goto out_exit_mutex;
309*3147Sxc151355 			}
310*3147Sxc151355 			key = ieee80211_crypto_decap(ic, mp, hdrspace);
311*3147Sxc151355 			if (key == NULL) {
312*3147Sxc151355 				/* NB: stats+msgs handled in crypto_decap */
313*3147Sxc151355 				goto out_exit_mutex;
314*3147Sxc151355 			}
315*3147Sxc151355 			wh = (struct ieee80211_frame *)mp->b_rptr;
316*3147Sxc151355 			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
317*3147Sxc151355 		}
318*3147Sxc151355 		IEEE80211_UNLOCK(ic);
319*3147Sxc151355 		ic->ic_recv_mgmt(ic, mp, in, subtype, rssi, rstamp);
320*3147Sxc151355 		goto out;
321*3147Sxc151355 
322*3147Sxc151355 	case IEEE80211_FC0_TYPE_CTL:
323*3147Sxc151355 	default:
324*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_input: "
325*3147Sxc151355 			"bad frame type 0x%x", type);
326*3147Sxc151355 		/* should not come here */
327*3147Sxc151355 		break;
328*3147Sxc151355 	}
329*3147Sxc151355 out_exit_mutex:
330*3147Sxc151355 	IEEE80211_UNLOCK(ic);
331*3147Sxc151355 out:
332*3147Sxc151355 	if (mp != NULL)
333*3147Sxc151355 		freemsg(mp);
334*3147Sxc151355 
335*3147Sxc151355 	return (type);
336*3147Sxc151355 }
337*3147Sxc151355 
338*3147Sxc151355 /*
339*3147Sxc151355  * This function reassemble fragments.
340*3147Sxc151355  * More fragments bit in the frame control means the packet is fragmented.
341*3147Sxc151355  * While the sequence control field consists of 4-bit fragment number
342*3147Sxc151355  * field and a 12-bit sequence number field.
343*3147Sxc151355  */
344*3147Sxc151355 /* ARGSUSED */
345*3147Sxc151355 static mblk_t *
346*3147Sxc151355 ieee80211_defrag(ieee80211com_t *ic, struct ieee80211_node *in, mblk_t *mp,
347*3147Sxc151355     int hdrspace)
348*3147Sxc151355 {
349*3147Sxc151355 	struct ieee80211_frame *wh = (struct ieee80211_frame *)mp->b_rptr;
350*3147Sxc151355 	struct ieee80211_frame *lwh;
351*3147Sxc151355 	mblk_t *mfrag;
352*3147Sxc151355 	uint16_t rxseq;
353*3147Sxc151355 	uint8_t fragno;
354*3147Sxc151355 	uint8_t more_frag;
355*3147Sxc151355 
356*3147Sxc151355 	ASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1));
357*3147Sxc151355 	more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG;
358*3147Sxc151355 	rxseq = LE_16(*(uint16_t *)wh->i_seq);
359*3147Sxc151355 	fragno = rxseq & IEEE80211_SEQ_FRAG_MASK;
360*3147Sxc151355 
361*3147Sxc151355 	/* Quick way out, if there's nothing to defragment */
362*3147Sxc151355 	if (!more_frag && fragno == 0 && in->in_rxfrag == NULL)
363*3147Sxc151355 		return (mp);
364*3147Sxc151355 
365*3147Sxc151355 	/*
366*3147Sxc151355 	 * Remove frag to insure it doesn't get reaped by timer.
367*3147Sxc151355 	 */
368*3147Sxc151355 	if (in->in_table == NULL) {
369*3147Sxc151355 		/*
370*3147Sxc151355 		 * Should never happen.  If the node is orphaned (not in
371*3147Sxc151355 		 * the table) then input packets should not reach here.
372*3147Sxc151355 		 * Otherwise, a concurrent request that yanks the table
373*3147Sxc151355 		 * should be blocked by other interlocking and/or by first
374*3147Sxc151355 		 * shutting the driver down.  Regardless, be defensive
375*3147Sxc151355 		 * here and just bail
376*3147Sxc151355 		 */
377*3147Sxc151355 		freemsg(mp);
378*3147Sxc151355 		return (NULL);
379*3147Sxc151355 	}
380*3147Sxc151355 	IEEE80211_NODE_LOCK(in->in_table);
381*3147Sxc151355 	mfrag = in->in_rxfrag;
382*3147Sxc151355 	in->in_rxfrag = NULL;
383*3147Sxc151355 	IEEE80211_NODE_UNLOCK(in->in_table);
384*3147Sxc151355 
385*3147Sxc151355 	/*
386*3147Sxc151355 	 * Validate new fragment is in order and
387*3147Sxc151355 	 * related to the previous ones.
388*3147Sxc151355 	 */
389*3147Sxc151355 	if (mfrag != NULL) {
390*3147Sxc151355 		uint16_t last_rxseq;
391*3147Sxc151355 
392*3147Sxc151355 		lwh = (struct ieee80211_frame *)mfrag->b_rptr;
393*3147Sxc151355 		last_rxseq = LE_16(*(uint16_t *)lwh->i_seq);
394*3147Sxc151355 		/*
395*3147Sxc151355 		 * Sequence control field contains 12-bit sequence no
396*3147Sxc151355 		 * and 4-bit fragment number. For fragemnts, the
397*3147Sxc151355 		 * sequence no is not changed.
398*3147Sxc151355 		 * NB: check seq # and frag together
399*3147Sxc151355 		 */
400*3147Sxc151355 		if (rxseq != last_rxseq + 1 ||
401*3147Sxc151355 		    !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) ||
402*3147Sxc151355 		    !IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) {
403*3147Sxc151355 			/*
404*3147Sxc151355 			 * Unrelated fragment or no space for it,
405*3147Sxc151355 			 * clear current fragments.
406*3147Sxc151355 			 */
407*3147Sxc151355 			freemsg(mfrag);
408*3147Sxc151355 			mfrag = NULL;
409*3147Sxc151355 		}
410*3147Sxc151355 	}
411*3147Sxc151355 
412*3147Sxc151355 	if (mfrag == NULL) {
413*3147Sxc151355 		if (fragno != 0) {	/* !first fragment, discard */
414*3147Sxc151355 			freemsg(mp);
415*3147Sxc151355 			return (NULL);
416*3147Sxc151355 		}
417*3147Sxc151355 		mfrag = mp;
418*3147Sxc151355 	} else {			/* concatenate */
419*3147Sxc151355 		(void) adjmsg(mp, hdrspace);
420*3147Sxc151355 		linkb(mfrag, mp);
421*3147Sxc151355 		/* track last seqnum and fragno */
422*3147Sxc151355 		lwh = (struct ieee80211_frame *)mfrag->b_rptr;
423*3147Sxc151355 		*(uint16_t *)lwh->i_seq = *(uint16_t *)wh->i_seq;
424*3147Sxc151355 	}
425*3147Sxc151355 	if (more_frag != 0) {		/* more to come, save */
426*3147Sxc151355 		in->in_rxfragstamp = ddi_get_lbolt();
427*3147Sxc151355 		in->in_rxfrag = mfrag;
428*3147Sxc151355 		mfrag = NULL;
429*3147Sxc151355 	}
430*3147Sxc151355 
431*3147Sxc151355 	return (mfrag);
432*3147Sxc151355 }
433*3147Sxc151355 
434*3147Sxc151355 /*
435*3147Sxc151355  * Install received rate set information in the node's state block.
436*3147Sxc151355  */
437*3147Sxc151355 int
438*3147Sxc151355 ieee80211_setup_rates(struct ieee80211_node *in, const uint8_t *rates,
439*3147Sxc151355     const uint8_t *xrates, int flags)
440*3147Sxc151355 {
441*3147Sxc151355 	struct ieee80211_rateset *rs = &in->in_rates;
442*3147Sxc151355 
443*3147Sxc151355 	bzero(rs, sizeof (*rs));
444*3147Sxc151355 	rs->ir_nrates = rates[1];
445*3147Sxc151355 	/* skip 1 byte element ID and 1 byte length */
446*3147Sxc151355 	bcopy(rates + 2, rs->ir_rates, rs->ir_nrates);
447*3147Sxc151355 	if (xrates != NULL) {
448*3147Sxc151355 		uint8_t nxrates;
449*3147Sxc151355 
450*3147Sxc151355 		/*
451*3147Sxc151355 		 * Tack on 11g extended supported rate element.
452*3147Sxc151355 		 */
453*3147Sxc151355 		nxrates = xrates[1];
454*3147Sxc151355 		if (rs->ir_nrates + nxrates > IEEE80211_RATE_MAXSIZE) {
455*3147Sxc151355 			nxrates = IEEE80211_RATE_MAXSIZE - rs->ir_nrates;
456*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_XRATE,
457*3147Sxc151355 				"ieee80211_setup_rates: %s",
458*3147Sxc151355 				"[%s] extended rate set too large;"
459*3147Sxc151355 				" only using %u of %u rates\n",
460*3147Sxc151355 				ieee80211_macaddr_sprintf(in->in_macaddr),
461*3147Sxc151355 				nxrates, xrates[1]);
462*3147Sxc151355 		}
463*3147Sxc151355 		bcopy(xrates + 2, rs->ir_rates + rs->ir_nrates, nxrates);
464*3147Sxc151355 		rs->ir_nrates += nxrates;
465*3147Sxc151355 	}
466*3147Sxc151355 	return (ieee80211_fix_rate(in, flags));
467*3147Sxc151355 }
468*3147Sxc151355 
469*3147Sxc151355 /*
470*3147Sxc151355  * Process open-system authentication response frame and start
471*3147Sxc151355  * association if the authentication request is accepted.
472*3147Sxc151355  */
473*3147Sxc151355 static void
474*3147Sxc151355 ieee80211_auth_open(ieee80211com_t *ic, struct ieee80211_frame *wh,
475*3147Sxc151355     struct ieee80211_node *in, uint16_t seq, uint16_t status)
476*3147Sxc151355 {
477*3147Sxc151355 	IEEE80211_LOCK_ASSERT(ic);
478*3147Sxc151355 	if (in->in_authmode == IEEE80211_AUTH_SHARED) {
479*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_AUTH,
480*3147Sxc151355 			"open auth: bad sta auth mode %u", in->in_authmode);
481*3147Sxc151355 		return;
482*3147Sxc151355 	}
483*3147Sxc151355 	if (ic->ic_opmode == IEEE80211_M_STA) {
484*3147Sxc151355 		if (ic->ic_state != IEEE80211_S_AUTH ||
485*3147Sxc151355 		    seq != IEEE80211_AUTH_OPEN_RESPONSE) {
486*3147Sxc151355 			return;
487*3147Sxc151355 		}
488*3147Sxc151355 		IEEE80211_UNLOCK(ic);
489*3147Sxc151355 		if (status != 0) {
490*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
491*3147Sxc151355 				"open auth failed (reason %d)\n", status);
492*3147Sxc151355 			if (in != ic->ic_bss)
493*3147Sxc151355 				in->in_fails++;
494*3147Sxc151355 			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
495*3147Sxc151355 		} else {
496*3147Sxc151355 			/* i_fc[0] - frame control's type & subtype field */
497*3147Sxc151355 			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
498*3147Sxc151355 				wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
499*3147Sxc151355 		}
500*3147Sxc151355 		IEEE80211_LOCK(ic);
501*3147Sxc151355 	} else {
502*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_AUTH, "ieee80211_auth_open: "
503*3147Sxc151355 			"bad operating mode %u", ic->ic_opmode);
504*3147Sxc151355 	}
505*3147Sxc151355 }
506*3147Sxc151355 
507*3147Sxc151355 /*
508*3147Sxc151355  * Allocate challenge text for use by shared-key authentication
509*3147Sxc151355  * Return B_TRUE on success, B_FALST otherwise.
510*3147Sxc151355  */
511*3147Sxc151355 static boolean_t
512*3147Sxc151355 ieee80211_alloc_challenge(struct ieee80211_node *in)
513*3147Sxc151355 {
514*3147Sxc151355 	if (in->in_challenge == NULL) {
515*3147Sxc151355 		in->in_challenge = kmem_alloc(IEEE80211_CHALLENGE_LEN,
516*3147Sxc151355 			KM_NOSLEEP);
517*3147Sxc151355 	}
518*3147Sxc151355 	if (in->in_challenge == NULL) {
519*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
520*3147Sxc151355 			"[%s] shared key challenge alloc failed\n",
521*3147Sxc151355 			ieee80211_macaddr_sprintf(in->in_macaddr));
522*3147Sxc151355 	}
523*3147Sxc151355 	return (in->in_challenge != NULL);
524*3147Sxc151355 }
525*3147Sxc151355 
526*3147Sxc151355 /*
527*3147Sxc151355  * Process shared-key authentication response frames. If authentication
528*3147Sxc151355  * succeeds, start association; otherwise, restart scan.
529*3147Sxc151355  */
530*3147Sxc151355 static void
531*3147Sxc151355 ieee80211_auth_shared(ieee80211com_t *ic, struct ieee80211_frame *wh,
532*3147Sxc151355     uint8_t *frm, uint8_t *efrm, struct ieee80211_node *in, uint16_t seq,
533*3147Sxc151355     uint16_t status)
534*3147Sxc151355 {
535*3147Sxc151355 	uint8_t *challenge;
536*3147Sxc151355 
537*3147Sxc151355 	/*
538*3147Sxc151355 	 * Pre-shared key authentication is evil; accept
539*3147Sxc151355 	 * it only if explicitly configured (it is supported
540*3147Sxc151355 	 * mainly for compatibility with clients like OS X).
541*3147Sxc151355 	 */
542*3147Sxc151355 	IEEE80211_LOCK_ASSERT(ic);
543*3147Sxc151355 	if (in->in_authmode != IEEE80211_AUTH_AUTO &&
544*3147Sxc151355 	    in->in_authmode != IEEE80211_AUTH_SHARED) {
545*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_AUTH, "ieee80211_auth_shared: "
546*3147Sxc151355 			"bad sta auth mode %u", in->in_authmode);
547*3147Sxc151355 		goto bad;
548*3147Sxc151355 	}
549*3147Sxc151355 
550*3147Sxc151355 	challenge = NULL;
551*3147Sxc151355 	if (frm + 1 < efrm) {
552*3147Sxc151355 		/*
553*3147Sxc151355 		 * Challenge text information element
554*3147Sxc151355 		 * frm[0] - element ID
555*3147Sxc151355 		 * frm[1] - length
556*3147Sxc151355 		 * frm[2]... - challenge text
557*3147Sxc151355 		 */
558*3147Sxc151355 		if ((frm[1] + 2) > (efrm - frm)) {
559*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_AUTH,
560*3147Sxc151355 				"ieee80211_auth_shared: ie %d%d too long\n",
561*3147Sxc151355 				frm[0], (frm[1] + 2) - (efrm - frm));
562*3147Sxc151355 			goto bad;
563*3147Sxc151355 		}
564*3147Sxc151355 		if (*frm == IEEE80211_ELEMID_CHALLENGE)
565*3147Sxc151355 			challenge = frm;
566*3147Sxc151355 		frm += frm[1] + 2;
567*3147Sxc151355 	}
568*3147Sxc151355 	switch (seq) {
569*3147Sxc151355 	case IEEE80211_AUTH_SHARED_CHALLENGE:
570*3147Sxc151355 	case IEEE80211_AUTH_SHARED_RESPONSE:
571*3147Sxc151355 		if (challenge == NULL) {
572*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_AUTH,
573*3147Sxc151355 				"ieee80211_auth_shared: no challenge\n");
574*3147Sxc151355 			goto bad;
575*3147Sxc151355 		}
576*3147Sxc151355 		if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
577*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_AUTH,
578*3147Sxc151355 				"ieee80211_auth_shared: bad challenge len %d\n",
579*3147Sxc151355 				challenge[1]);
580*3147Sxc151355 			goto bad;
581*3147Sxc151355 		}
582*3147Sxc151355 	default:
583*3147Sxc151355 		break;
584*3147Sxc151355 	}
585*3147Sxc151355 	switch (ic->ic_opmode) {
586*3147Sxc151355 	case IEEE80211_M_STA:
587*3147Sxc151355 		if (ic->ic_state != IEEE80211_S_AUTH)
588*3147Sxc151355 			return;
589*3147Sxc151355 		switch (seq) {
590*3147Sxc151355 		case IEEE80211_AUTH_SHARED_PASS:
591*3147Sxc151355 			if (in->in_challenge != NULL) {
592*3147Sxc151355 				kmem_free(in->in_challenge,
593*3147Sxc151355 				    IEEE80211_CHALLENGE_LEN);
594*3147Sxc151355 				in->in_challenge = NULL;
595*3147Sxc151355 			}
596*3147Sxc151355 			if (status != 0) {
597*3147Sxc151355 				ieee80211_dbg(IEEE80211_MSG_DEBUG |
598*3147Sxc151355 					IEEE80211_MSG_AUTH,
599*3147Sxc151355 					"shared key auth failed (reason %d)\n",
600*3147Sxc151355 					status);
601*3147Sxc151355 				if (in != ic->ic_bss)
602*3147Sxc151355 					in->in_fails++;
603*3147Sxc151355 				return;
604*3147Sxc151355 			}
605*3147Sxc151355 			IEEE80211_UNLOCK(ic);
606*3147Sxc151355 			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
607*3147Sxc151355 				wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
608*3147Sxc151355 			IEEE80211_LOCK(ic);
609*3147Sxc151355 			break;
610*3147Sxc151355 		case IEEE80211_AUTH_SHARED_CHALLENGE:
611*3147Sxc151355 			if (!ieee80211_alloc_challenge(in))
612*3147Sxc151355 				return;
613*3147Sxc151355 			bcopy(&challenge[2], in->in_challenge, challenge[1]);
614*3147Sxc151355 			IEEE80211_UNLOCK(ic);
615*3147Sxc151355 			IEEE80211_SEND_MGMT(ic, in, IEEE80211_FC0_SUBTYPE_AUTH,
616*3147Sxc151355 				seq + 1);
617*3147Sxc151355 			IEEE80211_LOCK(ic);
618*3147Sxc151355 			break;
619*3147Sxc151355 		default:
620*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_AUTH, "80211_auth_shared: "
621*3147Sxc151355 				"shared key auth: bad seq %d", seq);
622*3147Sxc151355 			return;
623*3147Sxc151355 		}
624*3147Sxc151355 		break;
625*3147Sxc151355 
626*3147Sxc151355 	default:
627*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_AUTH,
628*3147Sxc151355 			"ieee80211_auth_shared: bad opmode %u\n",
629*3147Sxc151355 			ic->ic_opmode);
630*3147Sxc151355 		break;
631*3147Sxc151355 	}
632*3147Sxc151355 	return;
633*3147Sxc151355 bad:
634*3147Sxc151355 	if (ic->ic_opmode == IEEE80211_M_STA) {
635*3147Sxc151355 		/*
636*3147Sxc151355 		 * Kick the state machine.  This short-circuits
637*3147Sxc151355 		 * using the mgt frame timeout to trigger the
638*3147Sxc151355 		 * state transition.
639*3147Sxc151355 		 */
640*3147Sxc151355 		if (ic->ic_state == IEEE80211_S_AUTH) {
641*3147Sxc151355 			IEEE80211_UNLOCK(ic);
642*3147Sxc151355 			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
643*3147Sxc151355 			IEEE80211_LOCK(ic);
644*3147Sxc151355 		}
645*3147Sxc151355 	}
646*3147Sxc151355 }
647*3147Sxc151355 
648*3147Sxc151355 /*
649*3147Sxc151355  * Process a beacon/probe response frame.
650*3147Sxc151355  * When the device is in station mode, create a node and add it
651*3147Sxc151355  * to the node database for a new ESS or update node info if it's
652*3147Sxc151355  * already there.
653*3147Sxc151355  */
654*3147Sxc151355 static void
655*3147Sxc151355 ieee80211_recv_beacon(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
656*3147Sxc151355     int subtype, int rssi, uint32_t rstamp)
657*3147Sxc151355 {
658*3147Sxc151355 	ieee80211_impl_t *im = ic->ic_private;
659*3147Sxc151355 	struct ieee80211_frame *wh;
660*3147Sxc151355 	uint8_t *frm;
661*3147Sxc151355 	uint8_t *efrm;	/* end of frame body */
662*3147Sxc151355 	struct ieee80211_scanparams scan;
663*3147Sxc151355 
664*3147Sxc151355 	wh = (struct ieee80211_frame *)mp->b_rptr;
665*3147Sxc151355 	frm = (uint8_t *)&wh[1];
666*3147Sxc151355 	efrm = (uint8_t *)mp->b_wptr;
667*3147Sxc151355 
668*3147Sxc151355 	/*
669*3147Sxc151355 	 * We process beacon/probe response frames:
670*3147Sxc151355 	 *    o when scanning, or
671*3147Sxc151355 	 *    o station mode when associated (to collect state
672*3147Sxc151355 	 *	updates such as 802.11g slot time), or
673*3147Sxc151355 	 *    o adhoc mode (to discover neighbors)
674*3147Sxc151355 	 * Frames otherwise received are discarded.
675*3147Sxc151355 	 */
676*3147Sxc151355 	if (!((ic->ic_flags & IEEE80211_F_SCAN) ||
677*3147Sxc151355 	    (ic->ic_opmode == IEEE80211_M_STA && in->in_associd != 0) ||
678*3147Sxc151355 	    ic->ic_opmode == IEEE80211_M_IBSS)) {
679*3147Sxc151355 		return;
680*3147Sxc151355 	}
681*3147Sxc151355 
682*3147Sxc151355 	/*
683*3147Sxc151355 	 * beacon/probe response frame format
684*3147Sxc151355 	 *	[8] time stamp
685*3147Sxc151355 	 *	[2] beacon interval
686*3147Sxc151355 	 *	[2] capability information
687*3147Sxc151355 	 *	[tlv] ssid
688*3147Sxc151355 	 *	[tlv] supported rates
689*3147Sxc151355 	 *	[tlv] country information
690*3147Sxc151355 	 *	[tlv] parameter set (FH/DS)
691*3147Sxc151355 	 *	[tlv] erp information
692*3147Sxc151355 	 *	[tlv] extended supported rates
693*3147Sxc151355 	 *	[tlv] WME
694*3147Sxc151355 	 *	[tlv] WPA or RSN
695*3147Sxc151355 	 */
696*3147Sxc151355 	IEEE80211_VERIFY_LENGTH(efrm - frm, IEEE80211_BEACON_ELEM_MIN, return);
697*3147Sxc151355 	bzero(&scan, sizeof (scan));
698*3147Sxc151355 	scan.tstamp  = frm;
699*3147Sxc151355 	frm += 8;
700*3147Sxc151355 	scan.bintval = (*(uint16_t *)frm);
701*3147Sxc151355 	frm += 2;
702*3147Sxc151355 	scan.capinfo = (*(uint16_t *)frm);
703*3147Sxc151355 	frm += 2;
704*3147Sxc151355 	scan.bchan = ieee80211_chan2ieee(ic, ic->ic_curchan);
705*3147Sxc151355 	scan.chan = scan.bchan;
706*3147Sxc151355 
707*3147Sxc151355 	while (frm < efrm) {
708*3147Sxc151355 		/* Agere element in beacon */
709*3147Sxc151355 		if ((*frm == IEEE80211_ELEMID_AGERE1) ||
710*3147Sxc151355 		    (*frm == IEEE80211_ELEMID_AGERE2)) {
711*3147Sxc151355 			frm = efrm;
712*3147Sxc151355 			break;
713*3147Sxc151355 		}
714*3147Sxc151355 
715*3147Sxc151355 		IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1], return);
716*3147Sxc151355 		switch (*frm) {
717*3147Sxc151355 		case IEEE80211_ELEMID_SSID:
718*3147Sxc151355 			scan.ssid = frm;
719*3147Sxc151355 			break;
720*3147Sxc151355 		case IEEE80211_ELEMID_RATES:
721*3147Sxc151355 			scan.rates = frm;
722*3147Sxc151355 			break;
723*3147Sxc151355 		case IEEE80211_ELEMID_COUNTRY:
724*3147Sxc151355 			scan.country = frm;
725*3147Sxc151355 			break;
726*3147Sxc151355 		case IEEE80211_ELEMID_FHPARMS:
727*3147Sxc151355 			if (ic->ic_phytype == IEEE80211_T_FH) {
728*3147Sxc151355 				scan.fhdwell = LE_16(*(uint16_t *)(frm + 2));
729*3147Sxc151355 				scan.chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
730*3147Sxc151355 				scan.fhindex = frm[6];
731*3147Sxc151355 				scan.phytype = IEEE80211_T_FH;
732*3147Sxc151355 			}
733*3147Sxc151355 			break;
734*3147Sxc151355 		case IEEE80211_ELEMID_DSPARMS:
735*3147Sxc151355 			if (ic->ic_phytype != IEEE80211_T_FH) {
736*3147Sxc151355 				scan.chan = frm[2];
737*3147Sxc151355 				scan.phytype = IEEE80211_T_DS;
738*3147Sxc151355 			}
739*3147Sxc151355 			break;
740*3147Sxc151355 		case IEEE80211_ELEMID_TIM:
741*3147Sxc151355 			scan.tim = frm;
742*3147Sxc151355 			scan.timoff = frm - mp->b_rptr;
743*3147Sxc151355 			break;
744*3147Sxc151355 		case IEEE80211_ELEMID_IBSSPARMS:
745*3147Sxc151355 			break;
746*3147Sxc151355 		case IEEE80211_ELEMID_XRATES:
747*3147Sxc151355 			scan.xrates = frm;
748*3147Sxc151355 			break;
749*3147Sxc151355 		case IEEE80211_ELEMID_ERP:
750*3147Sxc151355 			if (frm[1] != 1) {
751*3147Sxc151355 				ieee80211_dbg(IEEE80211_MSG_ELEMID,
752*3147Sxc151355 					"ieee80211_recv_mgmt: ignore %s, "
753*3147Sxc151355 					"invalid ERP element; "
754*3147Sxc151355 					"length %u, expecting 1\n",
755*3147Sxc151355 					IEEE80211_SUBTYPE_NAME(subtype),
756*3147Sxc151355 					frm[1]);
757*3147Sxc151355 				break;
758*3147Sxc151355 			}
759*3147Sxc151355 			scan.erp = frm[2];
760*3147Sxc151355 			scan.phytype = IEEE80211_T_OFDM;
761*3147Sxc151355 			break;
762*3147Sxc151355 		case IEEE80211_ELEMID_RSN:
763*3147Sxc151355 			scan.wpa = frm;
764*3147Sxc151355 			break;
765*3147Sxc151355 		default:
766*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_ELEMID,
767*3147Sxc151355 				"ieee80211_recv_mgmt: ignore %s,"
768*3147Sxc151355 				"unhandled id %u, len %u, totallen %u",
769*3147Sxc151355 				IEEE80211_SUBTYPE_NAME(subtype),
770*3147Sxc151355 				*frm, frm[1],
771*3147Sxc151355 				mp->b_wptr - mp->b_rptr);
772*3147Sxc151355 			break;
773*3147Sxc151355 		}
774*3147Sxc151355 		/* frm[1] - component length */
775*3147Sxc151355 		frm += IEEE80211_ELEM_LEN(frm[1]);
776*3147Sxc151355 	}
777*3147Sxc151355 	IEEE80211_VERIFY_ELEMENT(scan.rates, IEEE80211_RATE_MAXSIZE, return);
778*3147Sxc151355 	IEEE80211_VERIFY_ELEMENT(scan.ssid, IEEE80211_NWID_LEN, return);
779*3147Sxc151355 	if (ieee80211_isclr(ic->ic_chan_active, scan.chan)) {
780*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
781*3147Sxc151355 			"ieee80211_recv_mgmt: ignore %s ,"
782*3147Sxc151355 			"invalid channel %u\n",
783*3147Sxc151355 			IEEE80211_SUBTYPE_NAME(subtype), scan.chan);
784*3147Sxc151355 		return;
785*3147Sxc151355 	}
786*3147Sxc151355 	if (scan.chan != scan.bchan &&
787*3147Sxc151355 	    ic->ic_phytype != IEEE80211_T_FH) {
788*3147Sxc151355 		/*
789*3147Sxc151355 		 * Frame was received on a channel different from the
790*3147Sxc151355 		 * one indicated in the DS params element id;
791*3147Sxc151355 		 * silently discard it.
792*3147Sxc151355 		 *
793*3147Sxc151355 		 * NB:	this can happen due to signal leakage.
794*3147Sxc151355 		 *	But we should take it for FH phy because
795*3147Sxc151355 		 *	the rssi value should be correct even for
796*3147Sxc151355 		 *	different hop pattern in FH.
797*3147Sxc151355 		 */
798*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_ELEMID,
799*3147Sxc151355 			"ieee80211_recv_mgmt: ignore %s ,"
800*3147Sxc151355 			"phytype %u channel %u marked for %u\n",
801*3147Sxc151355 			IEEE80211_SUBTYPE_NAME(subtype),
802*3147Sxc151355 			ic->ic_phytype, scan.bchan, scan.chan);
803*3147Sxc151355 		return;
804*3147Sxc151355 	}
805*3147Sxc151355 	if (!(IEEE80211_BINTVAL_MIN <= scan.bintval &&
806*3147Sxc151355 	    scan.bintval <= IEEE80211_BINTVAL_MAX)) {
807*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
808*3147Sxc151355 			"ieee80211_recv_mgmt: ignore %s ,"
809*3147Sxc151355 			"bogus beacon interval %u\n",
810*3147Sxc151355 			IEEE80211_SUBTYPE_NAME(subtype), scan.bintval);
811*3147Sxc151355 		return;
812*3147Sxc151355 	}
813*3147Sxc151355 
814*3147Sxc151355 	/*
815*3147Sxc151355 	 * When operating in station mode, check for state updates.
816*3147Sxc151355 	 * Be careful to ignore beacons received while doing a
817*3147Sxc151355 	 * background scan.  We consider only 11g/WMM stuff right now.
818*3147Sxc151355 	 */
819*3147Sxc151355 	if (ic->ic_opmode == IEEE80211_M_STA &&
820*3147Sxc151355 	    in->in_associd != 0 &&
821*3147Sxc151355 	    (!(ic->ic_flags & IEEE80211_F_SCAN) ||
822*3147Sxc151355 	    IEEE80211_ADDR_EQ(wh->i_addr2, in->in_bssid))) {
823*3147Sxc151355 		/* record tsf of last beacon */
824*3147Sxc151355 		bcopy(scan.tstamp, in->in_tstamp.data,
825*3147Sxc151355 		    sizeof (in->in_tstamp));
826*3147Sxc151355 		/* count beacon frame for s/w bmiss handling */
827*3147Sxc151355 		im->im_swbmiss_count++;
828*3147Sxc151355 		im->im_bmiss_count = 0;
829*3147Sxc151355 
830*3147Sxc151355 		if ((in->in_capinfo ^ scan.capinfo) &
831*3147Sxc151355 		    IEEE80211_CAPINFO_SHORT_SLOTTIME) {
832*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_ASSOC,
833*3147Sxc151355 				"ieee80211_recv_mgmt: "
834*3147Sxc151355 				"[%s] cap change: before 0x%x, now 0x%x\n",
835*3147Sxc151355 				ieee80211_macaddr_sprintf(wh->i_addr2),
836*3147Sxc151355 				in->in_capinfo, scan.capinfo);
837*3147Sxc151355 			/*
838*3147Sxc151355 			 * NB:	we assume short preamble doesn't
839*3147Sxc151355 			 *	change dynamically
840*3147Sxc151355 			 */
841*3147Sxc151355 			ieee80211_set_shortslottime(ic,
842*3147Sxc151355 				ic->ic_curmode == IEEE80211_MODE_11A ||
843*3147Sxc151355 					(scan.capinfo &
844*3147Sxc151355 					IEEE80211_CAPINFO_SHORT_SLOTTIME));
845*3147Sxc151355 			in->in_capinfo = scan.capinfo;
846*3147Sxc151355 		}
847*3147Sxc151355 
848*3147Sxc151355 		if (scan.tim != NULL) {
849*3147Sxc151355 			struct ieee80211_tim_ie *ie;
850*3147Sxc151355 
851*3147Sxc151355 			ie = (struct ieee80211_tim_ie *)scan.tim;
852*3147Sxc151355 			in->in_dtim_count = ie->tim_count;
853*3147Sxc151355 			in->in_dtim_period = ie->tim_period;
854*3147Sxc151355 		}
855*3147Sxc151355 		if (ic->ic_flags & IEEE80211_F_SCAN) {
856*3147Sxc151355 			ieee80211_add_scan(ic, &scan, wh, subtype, rssi,
857*3147Sxc151355 				rstamp);
858*3147Sxc151355 		}
859*3147Sxc151355 		return;
860*3147Sxc151355 	}
861*3147Sxc151355 	/*
862*3147Sxc151355 	 * If scanning, just pass information to the scan module.
863*3147Sxc151355 	 */
864*3147Sxc151355 	if (ic->ic_flags & IEEE80211_F_SCAN) {
865*3147Sxc151355 		ieee80211_add_scan(ic, &scan, wh, subtype, rssi, rstamp);
866*3147Sxc151355 		return;
867*3147Sxc151355 	}
868*3147Sxc151355 
869*3147Sxc151355 	if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
870*3147Sxc151355 		if (!IEEE80211_ADDR_EQ(wh->i_addr2, in->in_macaddr)) {
871*3147Sxc151355 			/*
872*3147Sxc151355 			 * Create a new entry in the neighbor table.
873*3147Sxc151355 			 */
874*3147Sxc151355 			in = ieee80211_add_neighbor(ic, wh, &scan);
875*3147Sxc151355 		} else if (in->in_capinfo == 0) {
876*3147Sxc151355 			/*
877*3147Sxc151355 			 * Update faked node created on transmit.
878*3147Sxc151355 			 * Note this also updates the tsf.
879*3147Sxc151355 			 */
880*3147Sxc151355 			ieee80211_init_neighbor(in, wh, &scan);
881*3147Sxc151355 		} else {
882*3147Sxc151355 			/*
883*3147Sxc151355 			 * Record tsf for potential resync.
884*3147Sxc151355 			 */
885*3147Sxc151355 			bcopy(scan.tstamp, in->in_tstamp.data,
886*3147Sxc151355 			    sizeof (in->in_tstamp));
887*3147Sxc151355 		}
888*3147Sxc151355 		if (in != NULL) {
889*3147Sxc151355 			in->in_rssi = (uint8_t)rssi;
890*3147Sxc151355 			in->in_rstamp = rstamp;
891*3147Sxc151355 		}
892*3147Sxc151355 	}
893*3147Sxc151355 }
894*3147Sxc151355 
895*3147Sxc151355 /*
896*3147Sxc151355  * Perform input processing for 802.11 management frames.
897*3147Sxc151355  * It's the default ic_recv_mgmt callback function for the interface
898*3147Sxc151355  * softc, ic. Tipically ic_recv_mgmt is called within ieee80211_input()
899*3147Sxc151355  */
900*3147Sxc151355 void
901*3147Sxc151355 ieee80211_recv_mgmt(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
902*3147Sxc151355     int subtype, int rssi, uint32_t rstamp)
903*3147Sxc151355 {
904*3147Sxc151355 	struct ieee80211_frame *wh;
905*3147Sxc151355 	uint8_t *frm;		/* pointer to start of the frame */
906*3147Sxc151355 	uint8_t *efrm;		/* pointer to end of the frame */
907*3147Sxc151355 	uint8_t *ssid;
908*3147Sxc151355 	uint8_t *rates;
909*3147Sxc151355 	uint8_t *xrates;	/* extended rates */
910*3147Sxc151355 	boolean_t allocbs = B_FALSE;
911*3147Sxc151355 	uint8_t rate;
912*3147Sxc151355 	uint16_t algo;		/* authentication algorithm */
913*3147Sxc151355 	uint16_t seq;		/* sequence no */
914*3147Sxc151355 	uint16_t status;
915*3147Sxc151355 	uint16_t capinfo;
916*3147Sxc151355 	uint16_t associd;	/* association ID */
917*3147Sxc151355 
918*3147Sxc151355 	IEEE80211_LOCK(ic);
919*3147Sxc151355 	wh = (struct ieee80211_frame *)mp->b_rptr;
920*3147Sxc151355 	frm = (uint8_t *)&wh[1];
921*3147Sxc151355 	efrm = (uint8_t *)mp->b_wptr;
922*3147Sxc151355 	switch (subtype) {
923*3147Sxc151355 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
924*3147Sxc151355 	case IEEE80211_FC0_SUBTYPE_BEACON:
925*3147Sxc151355 		ieee80211_recv_beacon(ic, mp, in, subtype, rssi, rstamp);
926*3147Sxc151355 		break;
927*3147Sxc151355 
928*3147Sxc151355 	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
929*3147Sxc151355 		if (ic->ic_opmode == IEEE80211_M_STA ||
930*3147Sxc151355 		    ic->ic_state != IEEE80211_S_RUN ||
931*3147Sxc151355 		    IEEE80211_IS_MULTICAST(wh->i_addr2)) {
932*3147Sxc151355 			break;
933*3147Sxc151355 		}
934*3147Sxc151355 
935*3147Sxc151355 		/*
936*3147Sxc151355 		 * prreq frame format
937*3147Sxc151355 		 *	[tlv] ssid
938*3147Sxc151355 		 *	[tlv] supported rates
939*3147Sxc151355 		 *	[tlv] extended supported rates
940*3147Sxc151355 		 */
941*3147Sxc151355 		ssid = rates = xrates = NULL;
942*3147Sxc151355 		while (frm < efrm) {
943*3147Sxc151355 			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1], goto out);
944*3147Sxc151355 			switch (*frm) {
945*3147Sxc151355 			case IEEE80211_ELEMID_SSID:
946*3147Sxc151355 				ssid = frm;
947*3147Sxc151355 				break;
948*3147Sxc151355 			case IEEE80211_ELEMID_RATES:
949*3147Sxc151355 				rates = frm;
950*3147Sxc151355 				break;
951*3147Sxc151355 			case IEEE80211_ELEMID_XRATES:
952*3147Sxc151355 				xrates = frm;
953*3147Sxc151355 				break;
954*3147Sxc151355 			}
955*3147Sxc151355 			frm += frm[1] + 2;
956*3147Sxc151355 		}
957*3147Sxc151355 		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, break);
958*3147Sxc151355 		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, break);
959*3147Sxc151355 		IEEE80211_VERIFY_SSID(ic->ic_bss, ssid, break);
960*3147Sxc151355 		if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
961*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_INPUT,
962*3147Sxc151355 				"ieee80211_recv_mgmt: ignore %s, "
963*3147Sxc151355 				"no ssid with ssid suppression enabled",
964*3147Sxc151355 				IEEE80211_SUBTYPE_NAME(subtype));
965*3147Sxc151355 			break;
966*3147Sxc151355 		}
967*3147Sxc151355 
968*3147Sxc151355 		if (in == ic->ic_bss) {
969*3147Sxc151355 			if (ic->ic_opmode != IEEE80211_M_IBSS) {
970*3147Sxc151355 				in = ieee80211_tmp_node(ic, wh->i_addr2);
971*3147Sxc151355 				allocbs = B_TRUE;
972*3147Sxc151355 			} else if (!IEEE80211_ADDR_EQ(wh->i_addr2,
973*3147Sxc151355 			    in->in_macaddr)) {
974*3147Sxc151355 				/*
975*3147Sxc151355 				 * Cannot tell if the sender is operating
976*3147Sxc151355 				 * in ibss mode.  But we need a new node to
977*3147Sxc151355 				 * send the response so blindly add them to the
978*3147Sxc151355 				 * neighbor table.
979*3147Sxc151355 				 */
980*3147Sxc151355 				in = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
981*3147Sxc151355 					wh->i_addr2);
982*3147Sxc151355 			}
983*3147Sxc151355 			if (in == NULL)
984*3147Sxc151355 				break;
985*3147Sxc151355 		}
986*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_ASSOC, "ieee80211_recv_mgmt: "
987*3147Sxc151355 			"[%s] recv probe req\n",
988*3147Sxc151355 			ieee80211_macaddr_sprintf(wh->i_addr2));
989*3147Sxc151355 		in->in_rssi = (uint8_t)rssi;
990*3147Sxc151355 		in->in_rstamp = rstamp;
991*3147Sxc151355 		/*
992*3147Sxc151355 		 * Adjust and check station's rate list with device's
993*3147Sxc151355 		 * supported rate.  Send back response if there is at
994*3147Sxc151355 		 * least one rate or the fixed rate(if being set) is
995*3147Sxc151355 		 * supported by both station and the device
996*3147Sxc151355 		 */
997*3147Sxc151355 		rate = ieee80211_setup_rates(in, rates, xrates,
998*3147Sxc151355 			IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
999*3147Sxc151355 			IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
1000*3147Sxc151355 		if (rate & IEEE80211_RATE_BASIC) {
1001*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_XRATE, "ieee80211_recv_mgmt"
1002*3147Sxc151355 				"%s recv'd rate set invalid",
1003*3147Sxc151355 				IEEE80211_SUBTYPE_NAME(subtype));
1004*3147Sxc151355 		} else {
1005*3147Sxc151355 			IEEE80211_UNLOCK(ic);
1006*3147Sxc151355 			IEEE80211_SEND_MGMT(ic, in,
1007*3147Sxc151355 				IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
1008*3147Sxc151355 			IEEE80211_LOCK(ic);
1009*3147Sxc151355 		}
1010*3147Sxc151355 		if (allocbs) {
1011*3147Sxc151355 			/*
1012*3147Sxc151355 			 * Temporary node created just to send a
1013*3147Sxc151355 			 * response, reclaim immediately.
1014*3147Sxc151355 			 */
1015*3147Sxc151355 			ieee80211_free_node(in);
1016*3147Sxc151355 		}
1017*3147Sxc151355 		break;
1018*3147Sxc151355 
1019*3147Sxc151355 	case IEEE80211_FC0_SUBTYPE_AUTH:
1020*3147Sxc151355 		/*
1021*3147Sxc151355 		 * auth frame format
1022*3147Sxc151355 		 *	[2] algorithm
1023*3147Sxc151355 		 *	[2] sequence
1024*3147Sxc151355 		 *	[2] status
1025*3147Sxc151355 		 *	[tlv*] challenge
1026*3147Sxc151355 		 */
1027*3147Sxc151355 		IEEE80211_VERIFY_LENGTH(efrm - frm, IEEE80211_AUTH_ELEM_MIN,
1028*3147Sxc151355 			break);
1029*3147Sxc151355 		algo   = (*(uint16_t *)frm);
1030*3147Sxc151355 		seq    = (*(uint16_t *)(frm + 2));
1031*3147Sxc151355 		status = (*(uint16_t *)(frm + 4));
1032*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_AUTH, "ieee80211_recv_mgmt: "
1033*3147Sxc151355 			"[%s] recv auth frame with algorithm %d seq %d\n",
1034*3147Sxc151355 			ieee80211_macaddr_sprintf(wh->i_addr2), algo, seq);
1035*3147Sxc151355 
1036*3147Sxc151355 		if (ic->ic_flags & IEEE80211_F_COUNTERM) {
1037*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
1038*3147Sxc151355 				"ieee80211_recv_mgmt: ignore auth, %s\n",
1039*3147Sxc151355 				"TKIP countermeasures enabled");
1040*3147Sxc151355 			break;
1041*3147Sxc151355 		}
1042*3147Sxc151355 		switch (algo) {
1043*3147Sxc151355 		case IEEE80211_AUTH_ALG_SHARED:
1044*3147Sxc151355 			ieee80211_auth_shared(ic, wh, frm + 6, efrm, in,
1045*3147Sxc151355 				seq, status);
1046*3147Sxc151355 			break;
1047*3147Sxc151355 		case IEEE80211_AUTH_ALG_OPEN:
1048*3147Sxc151355 			ieee80211_auth_open(ic, wh, in, seq, status);
1049*3147Sxc151355 			break;
1050*3147Sxc151355 		default:
1051*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_recv_mgmt: "
1052*3147Sxc151355 				"ignore auth, unsupported alg %d", algo);
1053*3147Sxc151355 			break;
1054*3147Sxc151355 		}
1055*3147Sxc151355 		break;
1056*3147Sxc151355 
1057*3147Sxc151355 	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
1058*3147Sxc151355 	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
1059*3147Sxc151355 		if (ic->ic_opmode != IEEE80211_M_STA ||
1060*3147Sxc151355 		    ic->ic_state != IEEE80211_S_ASSOC)
1061*3147Sxc151355 			break;
1062*3147Sxc151355 
1063*3147Sxc151355 		/*
1064*3147Sxc151355 		 * asresp frame format
1065*3147Sxc151355 		 *	[2] capability information
1066*3147Sxc151355 		 *	[2] status
1067*3147Sxc151355 		 *	[2] association ID
1068*3147Sxc151355 		 *	[tlv] supported rates
1069*3147Sxc151355 		 *	[tlv] extended supported rates
1070*3147Sxc151355 		 *	[tlv] WME
1071*3147Sxc151355 		 */
1072*3147Sxc151355 		IEEE80211_VERIFY_LENGTH(efrm - frm,
1073*3147Sxc151355 			IEEE80211_ASSOC_RESP_ELEM_MIN, break);
1074*3147Sxc151355 		in = ic->ic_bss;
1075*3147Sxc151355 		capinfo = (*(uint16_t *)frm);
1076*3147Sxc151355 		frm += 2;
1077*3147Sxc151355 		status = (*(uint16_t *)frm);
1078*3147Sxc151355 		frm += 2;
1079*3147Sxc151355 		if (status != 0) {
1080*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_ASSOC,
1081*3147Sxc151355 				"assoc failed (reason %d)\n", status);
1082*3147Sxc151355 			in = ieee80211_find_node(&ic->ic_scan, wh->i_addr2);
1083*3147Sxc151355 			if (in != NULL) {
1084*3147Sxc151355 				in->in_fails++;
1085*3147Sxc151355 				ieee80211_free_node(in);
1086*3147Sxc151355 			}
1087*3147Sxc151355 			break;
1088*3147Sxc151355 		}
1089*3147Sxc151355 		associd = (*(uint16_t *)frm);
1090*3147Sxc151355 		frm += 2;
1091*3147Sxc151355 
1092*3147Sxc151355 		rates = xrates = NULL;
1093*3147Sxc151355 		while (frm < efrm) {
1094*3147Sxc151355 			/*
1095*3147Sxc151355 			 * Do not discard frames containing proprietary Agere
1096*3147Sxc151355 			 * elements 128 and 129, as the reported element length
1097*3147Sxc151355 			 * is often wrong. Skip rest of the frame, since we can
1098*3147Sxc151355 			 * not rely on the given element length making it
1099*3147Sxc151355 			 * impossible to know where the next element starts
1100*3147Sxc151355 			 */
1101*3147Sxc151355 			if ((*frm == IEEE80211_ELEMID_AGERE1) ||
1102*3147Sxc151355 			    (*frm == IEEE80211_ELEMID_AGERE2)) {
1103*3147Sxc151355 				frm = efrm;
1104*3147Sxc151355 				break;
1105*3147Sxc151355 			}
1106*3147Sxc151355 
1107*3147Sxc151355 			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1], goto out);
1108*3147Sxc151355 			switch (*frm) {
1109*3147Sxc151355 			case IEEE80211_ELEMID_RATES:
1110*3147Sxc151355 				rates = frm;
1111*3147Sxc151355 				break;
1112*3147Sxc151355 			case IEEE80211_ELEMID_XRATES:
1113*3147Sxc151355 				xrates = frm;
1114*3147Sxc151355 				break;
1115*3147Sxc151355 			}
1116*3147Sxc151355 			frm += frm[1] + 2;
1117*3147Sxc151355 		}
1118*3147Sxc151355 
1119*3147Sxc151355 		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, break);
1120*3147Sxc151355 		/*
1121*3147Sxc151355 		 * Adjust and check AP's rate list with device's
1122*3147Sxc151355 		 * supported rate. Re-start scan if no rate is or the
1123*3147Sxc151355 		 * fixed rate(if being set) cannot be supported by
1124*3147Sxc151355 		 * either AP or the device.
1125*3147Sxc151355 		 */
1126*3147Sxc151355 		rate = ieee80211_setup_rates(in, rates, xrates,
1127*3147Sxc151355 			IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
1128*3147Sxc151355 			IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
1129*3147Sxc151355 		if (rate & IEEE80211_RATE_BASIC) {
1130*3147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_ASSOC,
1131*3147Sxc151355 				"assoc failed (rate set mismatch)\n");
1132*3147Sxc151355 			if (in != ic->ic_bss)
1133*3147Sxc151355 				in->in_fails++;
1134*3147Sxc151355 			IEEE80211_UNLOCK(ic);
1135*3147Sxc151355 			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
1136*3147Sxc151355 			return;
1137*3147Sxc151355 		}
1138*3147Sxc151355 
1139*3147Sxc151355 		in->in_capinfo = capinfo;
1140*3147Sxc151355 		in->in_associd = associd;
1141*3147Sxc151355 		in->in_flags &= ~IEEE80211_NODE_QOS;
1142*3147Sxc151355 		/*
1143*3147Sxc151355 		 * Configure state now that we are associated.
1144*3147Sxc151355 		 */
1145*3147Sxc151355 		if (ic->ic_curmode == IEEE80211_MODE_11A ||
1146*3147Sxc151355 		    (in->in_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
1147*3147Sxc151355 			ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
1148*3147Sxc151355 			ic->ic_flags &= ~IEEE80211_F_USEBARKER;
1149*3147Sxc151355 		} else {
1150*3147Sxc151355 			ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
1151*3147Sxc151355 			ic->ic_flags |= IEEE80211_F_USEBARKER;
1152*3147Sxc151355 		}
1153*3147Sxc151355 		ieee80211_set_shortslottime(ic,
1154*3147Sxc151355 			ic->ic_curmode == IEEE80211_MODE_11A ||
1155*3147Sxc151355 			(in->in_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
1156*3147Sxc151355 		/*
1157*3147Sxc151355 		 * Honor ERP protection.
1158*3147Sxc151355 		 *
1159*3147Sxc151355 		 * NB:	in_erp should zero for non-11g operation.
1160*3147Sxc151355 		 *	check ic_curmode anyway
1161*3147Sxc151355 		 */
1162*3147Sxc151355 		if (ic->ic_curmode == IEEE80211_MODE_11G &&
1163*3147Sxc151355 		    (in->in_erp & IEEE80211_ERP_USE_PROTECTION))
1164*3147Sxc151355 			ic->ic_flags |= IEEE80211_F_USEPROT;
1165*3147Sxc151355 		else
1166*3147Sxc151355 			ic->ic_flags &= ~IEEE80211_F_USEPROT;
1167*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_ASSOC,
1168*3147Sxc151355 			"assoc success: %s preamble, %s slot time%s%s\n",
1169*3147Sxc151355 			ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
1170*3147Sxc151355 			ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
1171*3147Sxc151355 			ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
1172*3147Sxc151355 			in->in_flags & IEEE80211_NODE_QOS ? ", QoS" : "");
1173*3147Sxc151355 		IEEE80211_UNLOCK(ic);
1174*3147Sxc151355 		ieee80211_new_state(ic, IEEE80211_S_RUN, subtype);
1175*3147Sxc151355 		return;
1176*3147Sxc151355 
1177*3147Sxc151355 	case IEEE80211_FC0_SUBTYPE_DEAUTH:
1178*3147Sxc151355 		if (ic->ic_state == IEEE80211_S_SCAN)
1179*3147Sxc151355 			break;
1180*3147Sxc151355 
1181*3147Sxc151355 		/*
1182*3147Sxc151355 		 * deauth frame format
1183*3147Sxc151355 		 *	[2] reason
1184*3147Sxc151355 		 */
1185*3147Sxc151355 		IEEE80211_VERIFY_LENGTH(efrm - frm, 2, break);
1186*3147Sxc151355 		status = (*(uint16_t *)frm);
1187*3147Sxc151355 
1188*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_AUTH,
1189*3147Sxc151355 			"recv deauthenticate (reason %d)\n", status);
1190*3147Sxc151355 		switch (ic->ic_opmode) {
1191*3147Sxc151355 		case IEEE80211_M_STA:
1192*3147Sxc151355 			IEEE80211_UNLOCK(ic);
1193*3147Sxc151355 			ieee80211_new_state(ic, IEEE80211_S_AUTH,
1194*3147Sxc151355 				wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
1195*3147Sxc151355 			return;
1196*3147Sxc151355 		default:
1197*3147Sxc151355 			break;
1198*3147Sxc151355 		}
1199*3147Sxc151355 		break;
1200*3147Sxc151355 
1201*3147Sxc151355 	case IEEE80211_FC0_SUBTYPE_DISASSOC:
1202*3147Sxc151355 		if (ic->ic_state != IEEE80211_S_RUN &&
1203*3147Sxc151355 		    ic->ic_state != IEEE80211_S_ASSOC &&
1204*3147Sxc151355 		    ic->ic_state != IEEE80211_S_AUTH)
1205*3147Sxc151355 			break;
1206*3147Sxc151355 		/*
1207*3147Sxc151355 		 * disassoc frame format
1208*3147Sxc151355 		 *	[2] reason
1209*3147Sxc151355 		 */
1210*3147Sxc151355 		IEEE80211_VERIFY_LENGTH(efrm - frm, 2, break);
1211*3147Sxc151355 		status = (*(uint16_t *)frm);
1212*3147Sxc151355 
1213*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_ASSOC,
1214*3147Sxc151355 			"recv disassociate (reason %d)\n", status);
1215*3147Sxc151355 		switch (ic->ic_opmode) {
1216*3147Sxc151355 		case IEEE80211_M_STA:
1217*3147Sxc151355 			IEEE80211_UNLOCK(ic);
1218*3147Sxc151355 			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
1219*3147Sxc151355 				wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
1220*3147Sxc151355 			return;
1221*3147Sxc151355 		default:
1222*3147Sxc151355 			break;
1223*3147Sxc151355 		}
1224*3147Sxc151355 		break;
1225*3147Sxc151355 
1226*3147Sxc151355 	default:
1227*3147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_recv_mgmt: "
1228*3147Sxc151355 			"subtype 0x%x not handled\n", subtype);
1229*3147Sxc151355 		break;
1230*3147Sxc151355 	} /* switch subtype */
1231*3147Sxc151355 out:
1232*3147Sxc151355 	IEEE80211_UNLOCK(ic);
1233*3147Sxc151355 }
1234