xref: /illumos-gate/usr/src/uts/common/io/net80211/net80211_output.c (revision f48205be61a214698b763ff550ab9e657525104c)
1 /*
2  * Copyright 2007 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 	struct ieee80211_key *key;
179 
180 	ASSERT(mp != NULL && MBLKL(mp) >= sizeof (struct ieee80211_frame));
181 	wh = (struct ieee80211_frame *)mp->b_rptr;
182 	*(uint16_t *)wh->i_dur = 0;
183 	*(uint16_t *)wh->i_seq =
184 		LE_16(in->in_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
185 	in->in_txseqs[0]++;
186 
187 	if (ic->ic_flags & IEEE80211_F_PRIVACY)
188 		key = ieee80211_crypto_getkey(ic);
189 	else
190 		key = NULL;
191 
192 	/*
193 	 * IEEE 802.1X: send EAPOL frames always in the clear.
194 	 * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set.
195 	 */
196 	if (key != NULL && (ic->ic_flags & IEEE80211_F_WPA)) {
197 		wh->i_fc[1] |= IEEE80211_FC1_WEP;
198 		if (!ieee80211_crypto_enmic(isc, key, mp, 0)) {
199 			ieee80211_err("ieee80211_crypto_enmic failed.\n");
200 		}
201 	}
202 
203 	return (mp);
204 }
205 
206 /*
207  * Add supported rates information element to a frame.
208  */
209 static uint8_t *
210 ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs)
211 {
212 	uint8_t nrates;
213 
214 	*frm++ = IEEE80211_ELEMID_RATES;
215 	nrates = rs->ir_nrates;
216 	if (nrates > IEEE80211_RATE_SIZE)
217 		nrates = IEEE80211_RATE_SIZE;
218 	*frm++ = nrates;
219 	bcopy(rs->ir_rates, frm, nrates);
220 	return (frm + nrates);
221 }
222 
223 /*
224  * Add extended supported rates element to a frame, usually for 11g mode
225  */
226 static uint8_t *
227 ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
228 {
229 	if (rs->ir_nrates > IEEE80211_RATE_SIZE) {
230 		uint8_t nrates = rs->ir_nrates - IEEE80211_RATE_SIZE;
231 
232 		*frm++ = IEEE80211_ELEMID_XRATES;
233 		*frm++ = nrates;
234 		bcopy(rs->ir_rates + IEEE80211_RATE_SIZE, frm, nrates);
235 		frm += nrates;
236 	}
237 	return (frm);
238 }
239 
240 /*
241  * Add SSID element to a frame
242  */
243 static uint8_t *
244 ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, uint32_t len)
245 {
246 	*frm++ = IEEE80211_ELEMID_SSID;
247 	*frm++ = (uint8_t)len;
248 	bcopy(ssid, frm, len);
249 	return (frm + len);
250 }
251 
252 /*
253  * Add an erp element to a frame.
254  */
255 static uint8_t *
256 ieee80211_add_erp(uint8_t *frm, ieee80211com_t *ic)
257 {
258 	uint8_t erp;
259 
260 	*frm++ = IEEE80211_ELEMID_ERP;
261 	*frm++ = 1;
262 	erp = 0;
263 	if (ic->ic_flags & IEEE80211_F_USEPROT)
264 		erp |= IEEE80211_ERP_USE_PROTECTION;
265 	if (ic->ic_flags & IEEE80211_F_USEBARKER)
266 		erp |= IEEE80211_ERP_LONG_PREAMBLE;
267 	*frm++ = erp;
268 	return (frm);
269 }
270 
271 /*
272  * Get capability information from the interface softc, ic.
273  */
274 static uint16_t
275 ieee80211_get_capinfo(ieee80211com_t *ic)
276 {
277 	uint16_t capinfo;
278 
279 	if (ic->ic_opmode == IEEE80211_M_IBSS)
280 		capinfo = IEEE80211_CAPINFO_IBSS;
281 	else
282 		capinfo = IEEE80211_CAPINFO_ESS;
283 	if (ic->ic_flags & IEEE80211_F_PRIVACY)
284 		capinfo |= IEEE80211_CAPINFO_PRIVACY;
285 	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
286 	    IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) {
287 		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
288 	}
289 	if (ic->ic_flags & IEEE80211_F_SHSLOT)
290 		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
291 
292 	return (capinfo);
293 }
294 
295 /*
296  * Send a probe request frame with the specified ssid
297  * and any optional information element data.
298  */
299 int
300 ieee80211_send_probereq(ieee80211_node_t *in,
301     const uint8_t *sa, const uint8_t *da, const uint8_t *bssid,
302     const uint8_t *ssid, size_t ssidlen, const void *optie, size_t optielen)
303 {
304 	mblk_t *mp;
305 	ieee80211com_t *ic = in->in_ic;
306 	enum ieee80211_phymode mode;
307 	struct ieee80211_frame *wh;
308 	uint8_t *frm;
309 
310 	/*
311 	 * prreq frame format ([tlv] - 1 byte element ID + 1 byte length)
312 	 *	[tlv] ssid
313 	 *	[tlv] supported rates
314 	 *	[tlv] extended supported rates
315 	 *	[tlv] user-specified ie's
316 	 */
317 	mp = ieee80211_getmgtframe(&frm,
318 		2 + IEEE80211_NWID_LEN
319 		+ 2 + IEEE80211_RATE_SIZE +
320 		+ 2 + IEEE80211_XRATE_SIZE
321 		+ optielen);
322 	if (mp == NULL)
323 		return (ENOMEM);
324 
325 	frm = ieee80211_add_ssid(frm, ssid, ssidlen);
326 	mode = ieee80211_chan2mode(ic, ic->ic_curchan);
327 	frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
328 	frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
329 	if (optie != NULL) {
330 		(void) memcpy(frm, optie, optielen);
331 		frm += optielen;
332 	}
333 	mp->b_wptr = frm;
334 
335 	wh = (struct ieee80211_frame *)mp->b_rptr;
336 	ieee80211_send_setup(ic, in, wh,
337 		IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ,
338 		sa, da, bssid);
339 
340 	ieee80211_dbg(IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
341 		"[%s] send probe req on channel %u\n",
342 		ieee80211_macaddr_sprintf(wh->i_addr1),
343 		ieee80211_chan2ieee(ic, ic->ic_curchan));
344 
345 	(void) (*ic->ic_xmit)(ic, mp, IEEE80211_FC0_TYPE_MGT);
346 	return (0);
347 }
348 
349 /*
350  * Send a management frame.  The node is for the destination (or ic_bss
351  * when in station mode).  Nodes other than ic_bss have their reference
352  * count bumped to reflect our use for an indeterminant time.
353  */
354 int
355 ieee80211_send_mgmt(ieee80211com_t *ic, ieee80211_node_t *in, int type, int arg)
356 {
357 	mblk_t *mp;
358 	uint8_t *frm;
359 	uint16_t capinfo;
360 	struct ieee80211_key *key;
361 	boolean_t has_challenge;
362 	boolean_t is_shared_key;
363 	int ret;
364 	int timer;
365 	int status;
366 
367 	ASSERT(in != NULL);
368 
369 	timer = 0;
370 	switch (type) {
371 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
372 		/*
373 		 * probe response frame format
374 		 *	[8] time stamp
375 		 *	[2] beacon interval
376 		 *	[2] capability information
377 		 *	[tlv] ssid
378 		 *	[tlv] supported rates
379 		 *	[tlv] parameter set (FH/DS)
380 		 *	[tlv] parameter set (IBSS)
381 		 *	[tlv] extended rate phy (ERP)
382 		 *	[tlv] extended supported rates
383 		 *	[tlv] WPA
384 		 *	[tlv] WME (optional)
385 		 */
386 		mp = ieee80211_getmgtframe(&frm,
387 			8			/* time stamp  */
388 			+ sizeof (uint16_t)	/* beacon interval  */
389 			+ sizeof (uint16_t)	/* capability  */
390 			+ 2 + IEEE80211_NWID_LEN
391 			+ 2 + IEEE80211_RATE_SIZE
392 			+ 2 + IEEE80211_FH_LEN
393 			+ 2 + IEEE80211_IBSS_LEN
394 			+ 2 + IEEE80211_ERP_LEN
395 			+ 2 + IEEE80211_XRATE_SIZE
396 			+ (ic->ic_flags & IEEE80211_F_WPA ?
397 				2 * sizeof (struct ieee80211_ie_wpa) : 0)
398 						/* [tlv] WPA  */
399 			+ (ic->ic_flags & IEEE80211_F_WME ?
400 				sizeof (struct ieee80211_wme_param) : 0));
401 						/* [tlv] WME  */
402 		if (mp == NULL)
403 			return (ENOMEM);
404 
405 		bzero(frm, 8);	/* timestamp is set by hardware/driver */
406 		frm += 8;
407 		*(uint16_t *)frm = LE_16(in->in_intval);
408 		frm += 2;
409 		capinfo = ieee80211_get_capinfo(ic);
410 		*(uint16_t *)frm = LE_16(capinfo);
411 		frm += 2;
412 
413 		frm = ieee80211_add_ssid(frm, in->in_essid, in->in_esslen);
414 		frm = ieee80211_add_rates(frm, &in->in_rates);
415 
416 		if (ic->ic_phytype == IEEE80211_T_FH) {
417 			*frm++ = IEEE80211_ELEMID_FHPARMS;
418 			*frm++ = IEEE80211_FH_LEN;
419 			*frm++ = in->in_fhdwell & 0x00ff;
420 			*frm++ = (in->in_fhdwell >> 8) & 0x00ff;
421 			*frm++ = IEEE80211_FH_CHANSET(
422 				ieee80211_chan2ieee(ic, ic->ic_curchan));
423 			*frm++ = IEEE80211_FH_CHANPAT(
424 				ieee80211_chan2ieee(ic, ic->ic_curchan));
425 			*frm++ = in->in_fhindex;
426 		} else {
427 			*frm++ = IEEE80211_ELEMID_DSPARMS;
428 			*frm++ = IEEE80211_DS_LEN;
429 			*frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan);
430 		}
431 
432 		if (ic->ic_opmode == IEEE80211_M_IBSS) {
433 			*frm++ = IEEE80211_ELEMID_IBSSPARMS;
434 			*frm++ = IEEE80211_IBSS_LEN;
435 			*frm++ = 0; *frm++ = 0;		/* ATIM window */
436 		}
437 		frm = ieee80211_add_xrates(frm, &in->in_rates);
438 		break;
439 
440 	case IEEE80211_FC0_SUBTYPE_AUTH:
441 		status = arg >> 16;
442 		arg &= 0xffff;
443 		has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE ||
444 		    arg == IEEE80211_AUTH_SHARED_RESPONSE) &&
445 		    in->in_challenge != NULL);
446 
447 		/*
448 		 * Deduce whether we're doing open authentication or
449 		 * shared key authentication.  We do the latter if
450 		 * we're in the middle of a shared key authentication
451 		 * handshake or if we're initiating an authentication
452 		 * request and configured to use shared key.
453 		 */
454 		is_shared_key = has_challenge ||
455 		    arg >= IEEE80211_AUTH_SHARED_RESPONSE ||
456 		    (arg == IEEE80211_AUTH_SHARED_REQUEST &&
457 		    ic->ic_bss->in_authmode == IEEE80211_AUTH_SHARED);
458 
459 		if (has_challenge && status == IEEE80211_STATUS_SUCCESS)
460 			key = ieee80211_crypto_getkey(ic);
461 		else
462 			key = NULL;
463 
464 		mp = ieee80211_getmgtframe(&frm,
465 			3 * sizeof (uint16_t)
466 			+ (has_challenge && status == IEEE80211_STATUS_SUCCESS ?
467 				sizeof (uint16_t) + IEEE80211_CHALLENGE_LEN : 0)
468 			+ (key != NULL ? key->wk_cipher->ic_header : 0));
469 		if (mp == NULL)
470 			return (ENOMEM);
471 
472 		if (key != NULL)
473 			frm += key->wk_cipher->ic_header;
474 
475 		((uint16_t *)frm)[0] =
476 			(is_shared_key) ? LE_16(IEEE80211_AUTH_ALG_SHARED)
477 					: LE_16(IEEE80211_AUTH_ALG_OPEN);
478 		((uint16_t *)frm)[1] = LE_16(arg);	/* sequence number */
479 		((uint16_t *)frm)[2] = LE_16(status);	/* status */
480 
481 		if (has_challenge && status == IEEE80211_STATUS_SUCCESS) {
482 			frm += IEEE80211_AUTH_ELEM_MIN;
483 			*frm = IEEE80211_ELEMID_CHALLENGE;
484 			frm++;
485 			*frm = IEEE80211_CHALLENGE_LEN;
486 			frm++;
487 			bcopy(in->in_challenge, frm, IEEE80211_CHALLENGE_LEN);
488 		}
489 
490 		if (ic->ic_opmode == IEEE80211_M_STA)
491 			timer = IEEE80211_TRANS_WAIT;
492 		break;
493 
494 	case IEEE80211_FC0_SUBTYPE_DEAUTH:
495 		mp = ieee80211_getmgtframe(&frm, sizeof (uint16_t));
496 		if (mp == NULL)
497 			return (ENOMEM);
498 
499 		*(uint16_t *)frm = LE_16(arg);	/* reason */
500 
501 		ieee80211_node_unauthorize(in);	/* port closed */
502 		break;
503 
504 	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
505 	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
506 		/*
507 		 * asreq frame format
508 		 *	[2] capability information
509 		 *	[2] listen interval
510 		 *	[6*] current AP address (reassoc only)
511 		 *	[tlv] ssid
512 		 *	[tlv] supported rates
513 		 *	[tlv] extended supported rates
514 		 *	[tlv] WME
515 		 *	[tlv] user-specified ie's
516 		 */
517 		mp = ieee80211_getmgtframe(&frm,
518 			sizeof (uint16_t)
519 			+ sizeof (uint16_t) + IEEE80211_ADDR_LEN
520 			+ 2 + IEEE80211_NWID_LEN
521 			+ 2 + IEEE80211_RATE_SIZE
522 			+ 2 + IEEE80211_XRATE_SIZE
523 			+ ic->ic_opt_ie_len);
524 		if (mp == NULL)
525 			return (ENOMEM);
526 
527 		capinfo = ieee80211_get_capinfo(ic);
528 		if (!(in->in_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) ||
529 		    !(ic->ic_caps & IEEE80211_C_SHSLOT)) {
530 			capinfo &= ~IEEE80211_CAPINFO_SHORT_SLOTTIME;
531 		} else {
532 			capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
533 		}
534 		*(uint16_t *)frm = LE_16(capinfo);
535 		frm += 2;
536 
537 		*(uint16_t *)frm = LE_16(ic->ic_lintval);
538 		frm += 2;
539 
540 		if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
541 			IEEE80211_ADDR_COPY(frm, ic->ic_bss->in_bssid);
542 			frm += IEEE80211_ADDR_LEN;
543 		}
544 
545 		frm = ieee80211_add_ssid(frm, in->in_essid, in->in_esslen);
546 		frm = ieee80211_add_rates(frm, &in->in_rates);
547 		frm = ieee80211_add_xrates(frm, &in->in_rates);
548 		if (ic->ic_opt_ie != NULL) {
549 			bcopy(ic->ic_opt_ie, frm, ic->ic_opt_ie_len);
550 			frm += ic->ic_opt_ie_len;
551 		}
552 		mp->b_wptr = frm;	/* allocated is greater than used */
553 
554 		timer = IEEE80211_TRANS_WAIT;
555 		break;
556 
557 	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
558 	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
559 		/*
560 		 * asreq frame format
561 		 *	[2] capability information
562 		 *	[2] status
563 		 *	[2] association ID
564 		 *	[tlv] supported rates
565 		 *	[tlv] extended supported rates
566 		 *	[tlv] WME (if enabled and STA enabled)
567 		 */
568 		mp = ieee80211_getmgtframe(&frm,
569 			3 * sizeof (uint16_t)
570 			+ 2 + IEEE80211_RATE_SIZE
571 			+ 2 + IEEE80211_XRATE_SIZE);
572 		if (mp == NULL)
573 			return (ENOMEM);
574 
575 		capinfo = ieee80211_get_capinfo(ic);
576 		*(uint16_t *)frm = LE_16(capinfo);
577 		frm += 2;
578 
579 		*(uint16_t *)frm = LE_16(arg);	/* status */
580 		frm += 2;
581 
582 		if (arg == IEEE80211_STATUS_SUCCESS)
583 			*(uint16_t *)frm = LE_16(in->in_associd);
584 		else
585 			*(uint16_t *)frm = LE_16(0);
586 		frm += 2;
587 
588 		frm = ieee80211_add_rates(frm, &in->in_rates);
589 		frm = ieee80211_add_xrates(frm, &in->in_rates);
590 		break;
591 
592 	case IEEE80211_FC0_SUBTYPE_DISASSOC:
593 		mp = ieee80211_getmgtframe(&frm, sizeof (uint16_t));
594 		if (mp == NULL)
595 			return (ENOMEM);
596 		*(uint16_t *)frm = LE_16(arg);	/* reason */
597 		break;
598 
599 	default:
600 		ieee80211_dbg(IEEE80211_MSG_ANY,
601 			"[%s] invalid mgmt frame type %u\n",
602 			ieee80211_macaddr_sprintf(in->in_macaddr), type);
603 		return (EINVAL);
604 	} /* type */
605 	ret = ieee80211_mgmt_output(ic, in, mp, type, timer);
606 	return (ret);
607 }
608 
609 /*
610  * Allocate a beacon frame and fillin the appropriate bits.
611  */
612 mblk_t *
613 ieee80211_beacon_alloc(ieee80211com_t *ic, ieee80211_node_t *in,
614     struct ieee80211_beacon_offsets *bo)
615 {
616 	struct ieee80211_frame *wh;
617 	struct ieee80211_rateset *rs;
618 	mblk_t *m;
619 	uint8_t *frm;
620 	uint8_t *efrm;
621 	int pktlen;
622 	uint16_t capinfo;
623 
624 	IEEE80211_LOCK(ic);
625 	/*
626 	 * beacon frame format
627 	 *	[8] time stamp
628 	 *	[2] beacon interval
629 	 *	[2] cabability information
630 	 *	[tlv] ssid
631 	 *	[tlv] supported rates
632 	 *	[3] parameter set (DS)
633 	 *	[tlv] parameter set (IBSS/TIM)
634 	 *	[tlv] extended rate phy (ERP)
635 	 *	[tlv] extended supported rates
636 	 *	[tlv] WME parameters
637 	 *	[tlv] WPA/RSN parameters
638 	 * Vendor-specific OIDs (e.g. Atheros)
639 	 * NB: we allocate the max space required for the TIM bitmap.
640 	 */
641 	rs = &in->in_rates;
642 	pktlen =  8				/* time stamp */
643 		+ sizeof (uint16_t)		/* beacon interval */
644 		+ sizeof (uint16_t)		/* capabilities */
645 		+ 2 + in->in_esslen		/* ssid */
646 		+ 2 + IEEE80211_RATE_SIZE	/* supported rates */
647 		+ 2 + 1				/* DS parameters */
648 		+ 2 + 4 + ic->ic_tim_len	/* DTIM/IBSSPARMS */
649 		+ 2 + 1				/* ERP */
650 		+ 2 + IEEE80211_XRATE_SIZE;
651 	m = ieee80211_getmgtframe(&frm, pktlen);
652 	if (m == NULL) {
653 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_beacon_alloc: "
654 			"cannot get buf; size %u\n", pktlen);
655 		IEEE80211_UNLOCK(ic);
656 		return (NULL);
657 	}
658 
659 	/* timestamp is set by hardware/driver */
660 	(void) memset(frm, 0, 8);
661 	frm += 8;
662 	*(uint16_t *)frm = LE_16(in->in_intval);
663 	frm += 2;
664 	capinfo = ieee80211_get_capinfo(ic);
665 	bo->bo_caps = (uint16_t *)frm;
666 	*(uint16_t *)frm = LE_16(capinfo);
667 	frm += 2;
668 	*frm++ = IEEE80211_ELEMID_SSID;
669 	if (!(ic->ic_flags & IEEE80211_F_HIDESSID)) {
670 		*frm++ = in->in_esslen;
671 		bcopy(in->in_essid, frm, in->in_esslen);
672 		frm += in->in_esslen;
673 	} else {
674 		*frm++ = 0;
675 	}
676 	frm = ieee80211_add_rates(frm, rs);
677 	if (ic->ic_curmode != IEEE80211_MODE_FH) {
678 		*frm++ = IEEE80211_ELEMID_DSPARMS;
679 		*frm++ = 1;
680 		*frm++ = ieee80211_chan2ieee(ic, in->in_chan);
681 	}
682 	bo->bo_tim = frm;
683 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
684 		*frm++ = IEEE80211_ELEMID_IBSSPARMS;
685 		*frm++ = 2;
686 		*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
687 		bo->bo_tim_len = 0;
688 	} else {
689 		struct ieee80211_tim_ie *tie =
690 			(struct ieee80211_tim_ie *)frm;
691 
692 		tie->tim_ie = IEEE80211_ELEMID_TIM;
693 		tie->tim_len = 4;	/* length */
694 		tie->tim_count = 0;	/* DTIM count */
695 		tie->tim_period = IEEE80211_DTIM_DEFAULT;
696 		tie->tim_bitctl = 0;	/* bitmap control */
697 		tie->tim_bitmap[0] = 0;	/* Partial Virtual Bitmap */
698 		frm += sizeof (struct ieee80211_tim_ie);
699 		bo->bo_tim_len = 1;
700 	}
701 	bo->bo_trailer = frm;
702 
703 	if (ic->ic_curmode == IEEE80211_MODE_11G) {
704 		bo->bo_erp = frm;
705 		frm = ieee80211_add_erp(frm, ic);
706 	}
707 	efrm = ieee80211_add_xrates(frm, rs);
708 	bo->bo_trailer_len = efrm - bo->bo_trailer;
709 	m->b_wptr = efrm;
710 
711 	wh = (struct ieee80211_frame *)m->b_rptr;
712 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
713 		IEEE80211_FC0_SUBTYPE_BEACON;
714 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
715 	*(uint16_t *)wh->i_dur = 0;
716 	IEEE80211_ADDR_COPY(wh->i_addr1, wifi_bcastaddr);
717 	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_macaddr);
718 	IEEE80211_ADDR_COPY(wh->i_addr3, in->in_bssid);
719 	*(uint16_t *)wh->i_seq = 0;
720 
721 	IEEE80211_UNLOCK(ic);
722 	return (m);
723 }
724 
725 /*
726  * Update the dynamic parts of a beacon frame based on the current state.
727  */
728 /* ARGSUSED */
729 int
730 ieee80211_beacon_update(ieee80211com_t *ic, ieee80211_node_t *in,
731     struct ieee80211_beacon_offsets *bo, mblk_t *mp, int mcast)
732 {
733 	uint16_t capinfo;
734 
735 	IEEE80211_LOCK(ic);
736 
737 	capinfo = ieee80211_get_capinfo(ic);
738 	*bo->bo_caps = LE_16(capinfo);
739 
740 	IEEE80211_UNLOCK(ic);
741 	return (0);
742 }
743