xref: /titanic_44/usr/src/uts/common/io/net80211/net80211_output.c (revision 5151fb1220e0ceafdc172203863c73da4285c170)
1 /*
2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 2001 Atsushi Onoe
8  * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * Alternatively, this software may be distributed under the terms of the
23  * GNU General Public License ("GPL") version 2 as published by the Free
24  * Software Foundation.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 #pragma ident	"%Z%%M%	%I%	%E% SMI"
39 
40 
41 /*
42  * Send out 802.11 frames
43  */
44 
45 #include <sys/byteorder.h>
46 #include <sys/strsun.h>
47 #include "net80211_impl.h"
48 
49 /*
50  * Set the direction field and address fields of an outgoing
51  * non-QoS frame.  Note this should be called early on in
52  * constructing a frame as it sets i_fc[1]; other bits can
53  * then be or'd in.
54  */
55 static void
56 ieee80211_send_setup(ieee80211com_t *ic, ieee80211_node_t *in,
57     struct ieee80211_frame *wh, int type, const uint8_t *sa, const uint8_t *da,
58     const uint8_t *bssid)
59 {
60 	wh->i_fc[0] = (uint8_t)(IEEE80211_FC0_VERSION_0 | type);
61 	if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
62 		switch (ic->ic_opmode) {
63 		case IEEE80211_M_STA:
64 			wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
65 			IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
66 			IEEE80211_ADDR_COPY(wh->i_addr2, sa);
67 			IEEE80211_ADDR_COPY(wh->i_addr3, da);
68 			break;
69 		case IEEE80211_M_IBSS:
70 		case IEEE80211_M_AHDEMO:
71 			wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
72 			IEEE80211_ADDR_COPY(wh->i_addr1, da);
73 			IEEE80211_ADDR_COPY(wh->i_addr2, sa);
74 			IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
75 			break;
76 		default:
77 			ieee80211_err("ieee80211_send_setup: "
78 				"Invalid mode %u\n", ic->ic_opmode);
79 			return;
80 		}
81 	} else {
82 		wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
83 		IEEE80211_ADDR_COPY(wh->i_addr1, da);
84 		IEEE80211_ADDR_COPY(wh->i_addr2, sa);
85 		IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
86 	}
87 	*(uint16_t *)&wh->i_dur[0] = 0;	/* set duration */
88 	*(uint16_t *)&wh->i_seq[0] =	/* set sequence number */
89 		LE_16(in->in_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
90 	in->in_txseqs[0]++;		/* increase sequence number by 1 */
91 }
92 
93 /*
94  * Send a management frame to the specified node.  The node pointer
95  * must have a reference as the pointer will be passed to the driver
96  * and potentially held for a long time.  If the frame is successfully
97  * dispatched to the driver, then it is responsible for freeing the
98  * reference (and potentially free'ing up any associated storage).
99  *
100  * Return 0 on success
101  */
102 static int
103 ieee80211_mgmt_output(ieee80211com_t *ic, ieee80211_node_t *in, mblk_t *mp,
104     int type, int timer)
105 {
106 	ieee80211_impl_t *im = ic->ic_private;
107 	struct ieee80211_frame *wh;
108 
109 	ASSERT(in != NULL);
110 
111 	wh = (struct ieee80211_frame *)mp->b_rptr;
112 	ieee80211_send_setup(ic, in, wh, IEEE80211_FC0_TYPE_MGT | type,
113 		ic->ic_macaddr, in->in_macaddr, in->in_bssid);
114 	if (in->in_challenge != NULL)
115 		wh->i_fc[1] |= IEEE80211_FC1_WEP;
116 
117 	if (timer > 0) {
118 		/*
119 		 * Set the mgt frame timeout.
120 		 */
121 		im->im_mgt_timer = timer;
122 		ieee80211_start_watchdog(ic, 1);
123 	}
124 	return ((*ic->ic_xmit)(ic, mp, IEEE80211_FC0_TYPE_MGT));
125 }
126 
127 /*
128  * Send a null data frame to the specified node.
129  *
130  * NB: the caller is assumed to have setup a node reference
131  *     for use; this is necessary to deal with a race condition
132  *     when probing for inactive stations.
133  */
134 int
135 ieee80211_send_nulldata(ieee80211_node_t *in)
136 {
137 	ieee80211com_t *ic = in->in_ic;
138 	mblk_t *m;
139 	struct ieee80211_frame *wh;
140 	uint8_t *frm;
141 
142 	m = ieee80211_getmgtframe(&frm, 0);
143 	if (m == NULL) {
144 		ic->ic_stats.is_tx_nobuf++;
145 		return (ENOMEM);
146 	}
147 
148 	wh = (struct ieee80211_frame *)m->b_rptr;
149 	ieee80211_send_setup(ic, in, wh,
150 		IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA,
151 		ic->ic_macaddr, in->in_macaddr, in->in_bssid);
152 	/* NB: power management bit is never sent by an AP */
153 	if ((in->in_flags & IEEE80211_NODE_PWR_MGT) &&
154 	    ic->ic_opmode != IEEE80211_M_HOSTAP)
155 		wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
156 	m->b_wptr = m->b_rptr + sizeof (struct ieee80211_frame);
157 
158 	ieee80211_dbg(IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, "net80211: "
159 		"send null data frame on channel %u, pwr mgt %s\n",
160 		ieee80211_macaddr_sprintf(in->in_macaddr),
161 		ieee80211_chan2ieee(ic, ic->ic_curchan),
162 		wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis");
163 
164 	(void) (*ic->ic_xmit)(ic, m, IEEE80211_FC0_TYPE_MGT);
165 
166 	return (0);
167 }
168 
169 /*
170  * Encapsulate an outbound data frame for GLDv3 based driver.
171  * Fill in the variable part of the 80211 frame
172  */
173 /* ARGSUSED */
174 mblk_t *
175 ieee80211_encap(ieee80211com_t *ic, mblk_t *mp, ieee80211_node_t *in)
176 {
177 	struct ieee80211_frame	*wh;
178 
179 	ASSERT(mp != NULL && MBLKL(mp) >= sizeof (struct ieee80211_frame));
180 	wh = (struct ieee80211_frame *)mp->b_rptr;
181 	*(uint16_t *)wh->i_dur = 0;
182 	*(uint16_t *)wh->i_seq =
183 		LE_16(in->in_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
184 	in->in_txseqs[0]++;
185 
186 	return (mp);
187 }
188 
189 /*
190  * Add supported rates information element to a frame.
191  */
192 static uint8_t *
193 ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs)
194 {
195 	uint8_t nrates;
196 
197 	*frm++ = IEEE80211_ELEMID_RATES;
198 	nrates = rs->ir_nrates;
199 	if (nrates > IEEE80211_RATE_SIZE)
200 		nrates = IEEE80211_RATE_SIZE;
201 	*frm++ = nrates;
202 	bcopy(rs->ir_rates, frm, nrates);
203 	return (frm + nrates);
204 }
205 
206 /*
207  * Add extended supported rates element to a frame, usually for 11g mode
208  */
209 static uint8_t *
210 ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
211 {
212 	if (rs->ir_nrates > IEEE80211_RATE_SIZE) {
213 		uint8_t nrates = rs->ir_nrates - IEEE80211_RATE_SIZE;
214 
215 		*frm++ = IEEE80211_ELEMID_XRATES;
216 		*frm++ = nrates;
217 		bcopy(rs->ir_rates + IEEE80211_RATE_SIZE, frm, nrates);
218 		frm += nrates;
219 	}
220 	return (frm);
221 }
222 
223 /*
224  * Add SSID element to a frame
225  */
226 static uint8_t *
227 ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, uint32_t len)
228 {
229 	*frm++ = IEEE80211_ELEMID_SSID;
230 	*frm++ = (uint8_t)len;
231 	bcopy(ssid, frm, len);
232 	return (frm + len);
233 }
234 
235 /*
236  * Add an erp element to a frame.
237  */
238 static uint8_t *
239 ieee80211_add_erp(uint8_t *frm, ieee80211com_t *ic)
240 {
241 	uint8_t erp;
242 
243 	*frm++ = IEEE80211_ELEMID_ERP;
244 	*frm++ = 1;
245 	erp = 0;
246 	if (ic->ic_flags & IEEE80211_F_USEPROT)
247 		erp |= IEEE80211_ERP_USE_PROTECTION;
248 	if (ic->ic_flags & IEEE80211_F_USEBARKER)
249 		erp |= IEEE80211_ERP_LONG_PREAMBLE;
250 	*frm++ = erp;
251 	return (frm);
252 }
253 
254 /*
255  * Get capability information from the interface softc, ic.
256  */
257 static uint16_t
258 ieee80211_get_capinfo(ieee80211com_t *ic)
259 {
260 	uint16_t capinfo;
261 
262 	if (ic->ic_opmode == IEEE80211_M_IBSS)
263 		capinfo = IEEE80211_CAPINFO_IBSS;
264 	else
265 		capinfo = IEEE80211_CAPINFO_ESS;
266 	if (ic->ic_flags & IEEE80211_F_PRIVACY)
267 		capinfo |= IEEE80211_CAPINFO_PRIVACY;
268 	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
269 	    IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) {
270 		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
271 	}
272 	if (ic->ic_flags & IEEE80211_F_SHSLOT)
273 		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
274 
275 	return (capinfo);
276 }
277 
278 /*
279  * Send a probe request frame with the specified ssid
280  * and any optional information element data.
281  */
282 int
283 ieee80211_send_probereq(ieee80211_node_t *in,
284     const uint8_t *sa, const uint8_t *da, const uint8_t *bssid,
285     const uint8_t *ssid, size_t ssidlen, const void *optie, size_t optielen)
286 {
287 	mblk_t *mp;
288 	ieee80211com_t *ic = in->in_ic;
289 	enum ieee80211_phymode mode;
290 	struct ieee80211_frame *wh;
291 	uint8_t *frm;
292 
293 	/*
294 	 * prreq frame format ([tlv] - 1 byte element ID + 1 byte length)
295 	 *	[tlv] ssid
296 	 *	[tlv] supported rates
297 	 *	[tlv] extended supported rates
298 	 *	[tlv] user-specified ie's
299 	 */
300 	mp = ieee80211_getmgtframe(&frm,
301 		2 + IEEE80211_NWID_LEN
302 		+ 2 + IEEE80211_RATE_SIZE +
303 		+ 2 + IEEE80211_XRATE_SIZE
304 		+ optielen);
305 	if (mp == NULL)
306 		return (ENOMEM);
307 
308 	frm = ieee80211_add_ssid(frm, ssid, ssidlen);
309 	mode = ieee80211_chan2mode(ic, ic->ic_curchan);
310 	frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
311 	frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
312 	if (optie != NULL) {
313 		(void) memcpy(frm, optie, optielen);
314 		frm += optielen;
315 	}
316 	mp->b_wptr = frm;
317 
318 	wh = (struct ieee80211_frame *)mp->b_rptr;
319 	ieee80211_send_setup(ic, in, wh,
320 		IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ,
321 		sa, da, bssid);
322 
323 	ieee80211_dbg(IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
324 		"[%s] send probe req on channel %u\n",
325 		ieee80211_macaddr_sprintf(wh->i_addr1),
326 		ieee80211_chan2ieee(ic, ic->ic_curchan));
327 
328 	(void) (*ic->ic_xmit)(ic, mp, IEEE80211_FC0_TYPE_MGT);
329 	return (0);
330 }
331 
332 /*
333  * Send a management frame.  The node is for the destination (or ic_bss
334  * when in station mode).  Nodes other than ic_bss have their reference
335  * count bumped to reflect our use for an indeterminant time.
336  */
337 int
338 ieee80211_send_mgmt(ieee80211com_t *ic, ieee80211_node_t *in, int type, int arg)
339 {
340 	mblk_t *mp;
341 	uint8_t *frm;
342 	uint16_t capinfo;
343 	struct ieee80211_key *key;
344 	boolean_t has_challenge;
345 	boolean_t is_shared_key;
346 	int ret;
347 	int timer;
348 	int status;
349 
350 	ASSERT(in != NULL);
351 
352 	timer = 0;
353 	switch (type) {
354 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
355 		/*
356 		 * probe response frame format
357 		 *	[8] time stamp
358 		 *	[2] beacon interval
359 		 *	[2] capability information
360 		 *	[tlv] ssid
361 		 *	[tlv] supported rates
362 		 *	[tlv] parameter set (FH/DS)
363 		 *	[tlv] parameter set (IBSS)
364 		 *	[tlv] extended rate phy (ERP)
365 		 *	[tlv] extended supported rates
366 		 *	[tlv] WPA
367 		 *	[tlv] WME (optional)
368 		 */
369 		mp = ieee80211_getmgtframe(&frm,
370 			8			/* time stamp  */
371 			+ sizeof (uint16_t)	/* beacon interval  */
372 			+ sizeof (uint16_t)	/* capability  */
373 			+ 2 + IEEE80211_NWID_LEN
374 			+ 2 + IEEE80211_RATE_SIZE
375 			+ 2 + IEEE80211_FH_LEN
376 			+ 2 + IEEE80211_IBSS_LEN
377 			+ 2 + IEEE80211_ERP_LEN
378 			+ 2 + IEEE80211_XRATE_SIZE
379 			+ (ic->ic_flags & IEEE80211_F_WPA ?
380 				2 * sizeof (struct ieee80211_ie_wpa) : 0)
381 						/* [tlv] WPA  */
382 			+ (ic->ic_flags & IEEE80211_F_WME ?
383 				sizeof (struct ieee80211_wme_param) : 0));
384 						/* [tlv] WME  */
385 		if (mp == NULL)
386 			return (ENOMEM);
387 
388 		bzero(frm, 8);	/* timestamp is set by hardware/driver */
389 		frm += 8;
390 		*(uint16_t *)frm = LE_16(in->in_intval);
391 		frm += 2;
392 		capinfo = ieee80211_get_capinfo(ic);
393 		*(uint16_t *)frm = LE_16(capinfo);
394 		frm += 2;
395 
396 		frm = ieee80211_add_ssid(frm, in->in_essid, in->in_esslen);
397 		frm = ieee80211_add_rates(frm, &in->in_rates);
398 
399 		if (ic->ic_phytype == IEEE80211_T_FH) {
400 			*frm++ = IEEE80211_ELEMID_FHPARMS;
401 			*frm++ = IEEE80211_FH_LEN;
402 			*frm++ = in->in_fhdwell & 0x00ff;
403 			*frm++ = (in->in_fhdwell >> 8) & 0x00ff;
404 			*frm++ = IEEE80211_FH_CHANSET(
405 				ieee80211_chan2ieee(ic, ic->ic_curchan));
406 			*frm++ = IEEE80211_FH_CHANPAT(
407 				ieee80211_chan2ieee(ic, ic->ic_curchan));
408 			*frm++ = in->in_fhindex;
409 		} else {
410 			*frm++ = IEEE80211_ELEMID_DSPARMS;
411 			*frm++ = IEEE80211_DS_LEN;
412 			*frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan);
413 		}
414 
415 		if (ic->ic_opmode == IEEE80211_M_IBSS) {
416 			*frm++ = IEEE80211_ELEMID_IBSSPARMS;
417 			*frm++ = IEEE80211_IBSS_LEN;
418 			*frm++ = 0; *frm++ = 0;		/* ATIM window */
419 		}
420 		frm = ieee80211_add_xrates(frm, &in->in_rates);
421 		break;
422 
423 	case IEEE80211_FC0_SUBTYPE_AUTH:
424 		status = arg >> 16;
425 		arg &= 0xffff;
426 		has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE ||
427 		    arg == IEEE80211_AUTH_SHARED_RESPONSE) &&
428 		    in->in_challenge != NULL);
429 
430 		/*
431 		 * Deduce whether we're doing open authentication or
432 		 * shared key authentication.  We do the latter if
433 		 * we're in the middle of a shared key authentication
434 		 * handshake or if we're initiating an authentication
435 		 * request and configured to use shared key.
436 		 */
437 		is_shared_key = has_challenge ||
438 		    arg >= IEEE80211_AUTH_SHARED_RESPONSE ||
439 		    (arg == IEEE80211_AUTH_SHARED_REQUEST &&
440 		    ic->ic_bss->in_authmode == IEEE80211_AUTH_SHARED);
441 
442 		if (has_challenge && status == IEEE80211_STATUS_SUCCESS)
443 			key = ieee80211_crypto_getkey(ic);
444 		else
445 			key = NULL;
446 
447 		mp = ieee80211_getmgtframe(&frm,
448 			3 * sizeof (uint16_t)
449 			+ (has_challenge && status == IEEE80211_STATUS_SUCCESS ?
450 				sizeof (uint16_t) + IEEE80211_CHALLENGE_LEN : 0)
451 			+ (key != NULL ? key->wk_cipher->ic_header : 0));
452 		if (mp == NULL)
453 			return (ENOMEM);
454 
455 		if (key != NULL)
456 			frm += key->wk_cipher->ic_header;
457 
458 		((uint16_t *)frm)[0] =
459 			(is_shared_key) ? LE_16(IEEE80211_AUTH_ALG_SHARED)
460 					: LE_16(IEEE80211_AUTH_ALG_OPEN);
461 		((uint16_t *)frm)[1] = LE_16(arg);	/* sequence number */
462 		((uint16_t *)frm)[2] = LE_16(status);	/* status */
463 
464 		if (has_challenge && status == IEEE80211_STATUS_SUCCESS) {
465 			frm += IEEE80211_AUTH_ELEM_MIN;
466 			*frm = IEEE80211_ELEMID_CHALLENGE;
467 			frm++;
468 			*frm = IEEE80211_CHALLENGE_LEN;
469 			frm++;
470 			bcopy(in->in_challenge, frm, IEEE80211_CHALLENGE_LEN);
471 		}
472 
473 		if (ic->ic_opmode == IEEE80211_M_STA)
474 			timer = IEEE80211_TRANS_WAIT;
475 		break;
476 
477 	case IEEE80211_FC0_SUBTYPE_DEAUTH:
478 		mp = ieee80211_getmgtframe(&frm, sizeof (uint16_t));
479 		if (mp == NULL)
480 			return (ENOMEM);
481 
482 		*(uint16_t *)frm = LE_16(arg);	/* reason */
483 
484 		ieee80211_node_unauthorize(in);	/* port closed */
485 		break;
486 
487 	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
488 	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
489 		/*
490 		 * asreq frame format
491 		 *	[2] capability information
492 		 *	[2] listen interval
493 		 *	[6*] current AP address (reassoc only)
494 		 *	[tlv] ssid
495 		 *	[tlv] supported rates
496 		 *	[tlv] extended supported rates
497 		 *	[tlv] WME
498 		 *	[tlv] user-specified ie's
499 		 */
500 		mp = ieee80211_getmgtframe(&frm,
501 			sizeof (uint16_t)
502 			+ sizeof (uint16_t) + IEEE80211_ADDR_LEN
503 			+ 2 + IEEE80211_NWID_LEN
504 			+ 2 + IEEE80211_RATE_SIZE
505 			+ 2 + IEEE80211_XRATE_SIZE
506 			+ ic->ic_opt_ie_len);
507 		if (mp == NULL)
508 			return (ENOMEM);
509 
510 		capinfo = ieee80211_get_capinfo(ic);
511 		if (!(in->in_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) ||
512 		    !(ic->ic_caps & IEEE80211_C_SHSLOT)) {
513 			capinfo &= ~IEEE80211_CAPINFO_SHORT_SLOTTIME;
514 		}
515 		*(uint16_t *)frm = LE_16(capinfo);
516 		frm += 2;
517 
518 		*(uint16_t *)frm = LE_16(ic->ic_lintval);
519 		frm += 2;
520 
521 		if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
522 			IEEE80211_ADDR_COPY(frm, ic->ic_bss->in_bssid);
523 			frm += IEEE80211_ADDR_LEN;
524 		}
525 
526 		frm = ieee80211_add_ssid(frm, in->in_essid, in->in_esslen);
527 		frm = ieee80211_add_rates(frm, &in->in_rates);
528 		frm = ieee80211_add_xrates(frm, &in->in_rates);
529 		if (ic->ic_opt_ie != NULL) {
530 			bcopy(ic->ic_opt_ie, frm, ic->ic_opt_ie_len);
531 			frm += ic->ic_opt_ie_len;
532 		}
533 		mp->b_wptr = frm;	/* allocated is greater than used */
534 
535 		timer = IEEE80211_TRANS_WAIT;
536 		break;
537 
538 	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
539 	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
540 		/*
541 		 * asreq frame format
542 		 *	[2] capability information
543 		 *	[2] status
544 		 *	[2] association ID
545 		 *	[tlv] supported rates
546 		 *	[tlv] extended supported rates
547 		 *	[tlv] WME (if enabled and STA enabled)
548 		 */
549 		mp = ieee80211_getmgtframe(&frm,
550 			3 * sizeof (uint16_t)
551 			+ 2 + IEEE80211_RATE_SIZE
552 			+ 2 + IEEE80211_XRATE_SIZE);
553 		if (mp == NULL)
554 			return (ENOMEM);
555 
556 		capinfo = ieee80211_get_capinfo(ic);
557 		*(uint16_t *)frm = LE_16(capinfo);
558 		frm += 2;
559 
560 		*(uint16_t *)frm = LE_16(arg);	/* status */
561 		frm += 2;
562 
563 		if (arg == IEEE80211_STATUS_SUCCESS)
564 			*(uint16_t *)frm = LE_16(in->in_associd);
565 		else
566 			*(uint16_t *)frm = LE_16(0);
567 		frm += 2;
568 
569 		frm = ieee80211_add_rates(frm, &in->in_rates);
570 		frm = ieee80211_add_xrates(frm, &in->in_rates);
571 		break;
572 
573 	case IEEE80211_FC0_SUBTYPE_DISASSOC:
574 		mp = ieee80211_getmgtframe(&frm, sizeof (uint16_t));
575 		if (mp == NULL)
576 			return (ENOMEM);
577 		*(uint16_t *)frm = LE_16(arg);	/* reason */
578 		break;
579 
580 	default:
581 		ieee80211_dbg(IEEE80211_MSG_ANY,
582 			"[%s] invalid mgmt frame type %u\n",
583 			ieee80211_macaddr_sprintf(in->in_macaddr), type);
584 		return (EINVAL);
585 	} /* type */
586 	ret = ieee80211_mgmt_output(ic, in, mp, type, timer);
587 	return (ret);
588 }
589 
590 /*
591  * Allocate a beacon frame and fillin the appropriate bits.
592  */
593 mblk_t *
594 ieee80211_beacon_alloc(ieee80211com_t *ic, ieee80211_node_t *in,
595     struct ieee80211_beacon_offsets *bo)
596 {
597 	struct ieee80211_frame *wh;
598 	struct ieee80211_rateset *rs;
599 	mblk_t *m;
600 	uint8_t *frm;
601 	uint8_t *efrm;
602 	int pktlen;
603 	uint16_t capinfo;
604 
605 	IEEE80211_LOCK(ic);
606 	/*
607 	 * beacon frame format
608 	 *	[8] time stamp
609 	 *	[2] beacon interval
610 	 *	[2] cabability information
611 	 *	[tlv] ssid
612 	 *	[tlv] supported rates
613 	 *	[3] parameter set (DS)
614 	 *	[tlv] parameter set (IBSS/TIM)
615 	 *	[tlv] extended rate phy (ERP)
616 	 *	[tlv] extended supported rates
617 	 *	[tlv] WME parameters
618 	 *	[tlv] WPA/RSN parameters
619 	 * Vendor-specific OIDs (e.g. Atheros)
620 	 * NB: we allocate the max space required for the TIM bitmap.
621 	 */
622 	rs = &in->in_rates;
623 	pktlen =  8				/* time stamp */
624 		+ sizeof (uint16_t)		/* beacon interval */
625 		+ sizeof (uint16_t)		/* capabilities */
626 		+ 2 + in->in_esslen		/* ssid */
627 		+ 2 + IEEE80211_RATE_SIZE	/* supported rates */
628 		+ 2 + 1				/* DS parameters */
629 		+ 2 + 4 + ic->ic_tim_len	/* DTIM/IBSSPARMS */
630 		+ 2 + 1				/* ERP */
631 		+ 2 + IEEE80211_XRATE_SIZE;
632 	m = ieee80211_getmgtframe(&frm, pktlen);
633 	if (m == NULL) {
634 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_beacon_alloc: "
635 			"cannot get buf; size %u\n", pktlen);
636 		IEEE80211_UNLOCK(ic);
637 		return (NULL);
638 	}
639 
640 	/* timestamp is set by hardware/driver */
641 	(void) memset(frm, 0, 8);
642 	frm += 8;
643 	*(uint16_t *)frm = LE_16(in->in_intval);
644 	frm += 2;
645 	capinfo = ieee80211_get_capinfo(ic);
646 	bo->bo_caps = (uint16_t *)frm;
647 	*(uint16_t *)frm = LE_16(capinfo);
648 	frm += 2;
649 	*frm++ = IEEE80211_ELEMID_SSID;
650 	if (!(ic->ic_flags & IEEE80211_F_HIDESSID)) {
651 		*frm++ = in->in_esslen;
652 		bcopy(in->in_essid, frm, in->in_esslen);
653 		frm += in->in_esslen;
654 	} else {
655 		*frm++ = 0;
656 	}
657 	frm = ieee80211_add_rates(frm, rs);
658 	if (ic->ic_curmode != IEEE80211_MODE_FH) {
659 		*frm++ = IEEE80211_ELEMID_DSPARMS;
660 		*frm++ = 1;
661 		*frm++ = ieee80211_chan2ieee(ic, in->in_chan);
662 	}
663 	bo->bo_tim = frm;
664 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
665 		*frm++ = IEEE80211_ELEMID_IBSSPARMS;
666 		*frm++ = 2;
667 		*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
668 		bo->bo_tim_len = 0;
669 	} else {
670 		struct ieee80211_tim_ie *tie =
671 			(struct ieee80211_tim_ie *)frm;
672 
673 		tie->tim_ie = IEEE80211_ELEMID_TIM;
674 		tie->tim_len = 4;	/* length */
675 		tie->tim_count = 0;	/* DTIM count */
676 		tie->tim_period = IEEE80211_DTIM_DEFAULT;
677 		tie->tim_bitctl = 0;	/* bitmap control */
678 		tie->tim_bitmap[0] = 0;	/* Partial Virtual Bitmap */
679 		frm += sizeof (struct ieee80211_tim_ie);
680 		bo->bo_tim_len = 1;
681 	}
682 	bo->bo_trailer = frm;
683 
684 	if (ic->ic_curmode == IEEE80211_MODE_11G) {
685 		bo->bo_erp = frm;
686 		frm = ieee80211_add_erp(frm, ic);
687 	}
688 	efrm = ieee80211_add_xrates(frm, rs);
689 	bo->bo_trailer_len = efrm - bo->bo_trailer;
690 	m->b_wptr = efrm;
691 
692 	wh = (struct ieee80211_frame *)m->b_rptr;
693 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
694 		IEEE80211_FC0_SUBTYPE_BEACON;
695 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
696 	*(uint16_t *)wh->i_dur = 0;
697 	IEEE80211_ADDR_COPY(wh->i_addr1, wifi_bcastaddr);
698 	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_macaddr);
699 	IEEE80211_ADDR_COPY(wh->i_addr3, in->in_bssid);
700 	*(uint16_t *)wh->i_seq = 0;
701 
702 	IEEE80211_UNLOCK(ic);
703 	return (m);
704 }
705 
706 /*
707  * Update the dynamic parts of a beacon frame based on the current state.
708  */
709 /* ARGSUSED */
710 int
711 ieee80211_beacon_update(ieee80211com_t *ic, ieee80211_node_t *in,
712     struct ieee80211_beacon_offsets *bo, mblk_t *mp, int mcast)
713 {
714 	uint16_t capinfo;
715 
716 	IEEE80211_LOCK(ic);
717 
718 	capinfo = ieee80211_get_capinfo(ic);
719 	*bo->bo_caps = LE_16(capinfo);
720 
721 	IEEE80211_UNLOCK(ic);
722 	return (0);
723 }
724