xref: /illumos-gate/usr/src/uts/common/io/net80211/net80211_output.c (revision 7247f8883be6bcac5fe4735b6f87f873387dbbef)
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 		if (!(in->in_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) ||
535 		    !(ic->ic_caps & IEEE80211_C_SHPREAMBLE)) {
536 			capinfo &= ~IEEE80211_CAPINFO_SHORT_PREAMBLE;
537 		} else {
538 			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
539 		}
540 		*(uint16_t *)frm = LE_16(capinfo);
541 		frm += 2;
542 
543 		*(uint16_t *)frm = LE_16(ic->ic_lintval);
544 		frm += 2;
545 
546 		if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
547 			IEEE80211_ADDR_COPY(frm, ic->ic_bss->in_bssid);
548 			frm += IEEE80211_ADDR_LEN;
549 		}
550 
551 		frm = ieee80211_add_ssid(frm, in->in_essid, in->in_esslen);
552 		frm = ieee80211_add_rates(frm, &in->in_rates);
553 		frm = ieee80211_add_xrates(frm, &in->in_rates);
554 		if (ic->ic_opt_ie != NULL) {
555 			bcopy(ic->ic_opt_ie, frm, ic->ic_opt_ie_len);
556 			frm += ic->ic_opt_ie_len;
557 		}
558 		mp->b_wptr = frm;	/* allocated is greater than used */
559 
560 		timer = IEEE80211_TRANS_WAIT;
561 		break;
562 
563 	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
564 	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
565 		/*
566 		 * asreq frame format
567 		 *	[2] capability information
568 		 *	[2] status
569 		 *	[2] association ID
570 		 *	[tlv] supported rates
571 		 *	[tlv] extended supported rates
572 		 *	[tlv] WME (if enabled and STA enabled)
573 		 */
574 		mp = ieee80211_getmgtframe(&frm,
575 		    3 * sizeof (uint16_t)
576 		    + 2 + IEEE80211_RATE_SIZE
577 		    + 2 + IEEE80211_XRATE_SIZE);
578 		if (mp == NULL)
579 			return (ENOMEM);
580 
581 		capinfo = ieee80211_get_capinfo(ic);
582 		*(uint16_t *)frm = LE_16(capinfo);
583 		frm += 2;
584 
585 		*(uint16_t *)frm = LE_16(arg);	/* status */
586 		frm += 2;
587 
588 		if (arg == IEEE80211_STATUS_SUCCESS)
589 			*(uint16_t *)frm = LE_16(in->in_associd);
590 		else
591 			*(uint16_t *)frm = LE_16(0);
592 		frm += 2;
593 
594 		frm = ieee80211_add_rates(frm, &in->in_rates);
595 		frm = ieee80211_add_xrates(frm, &in->in_rates);
596 		break;
597 
598 	case IEEE80211_FC0_SUBTYPE_DISASSOC:
599 		mp = ieee80211_getmgtframe(&frm, sizeof (uint16_t));
600 		if (mp == NULL)
601 			return (ENOMEM);
602 		*(uint16_t *)frm = LE_16(arg);	/* reason */
603 		break;
604 
605 	default:
606 		ieee80211_dbg(IEEE80211_MSG_ANY,
607 		    "[%s] invalid mgmt frame type %u\n",
608 		    ieee80211_macaddr_sprintf(in->in_macaddr), type);
609 		return (EINVAL);
610 	} /* type */
611 	ret = ieee80211_mgmt_output(ic, in, mp, type, timer);
612 	return (ret);
613 }
614 
615 /*
616  * Allocate a beacon frame and fillin the appropriate bits.
617  */
618 mblk_t *
619 ieee80211_beacon_alloc(ieee80211com_t *ic, ieee80211_node_t *in,
620     struct ieee80211_beacon_offsets *bo)
621 {
622 	struct ieee80211_frame *wh;
623 	struct ieee80211_rateset *rs;
624 	mblk_t *m;
625 	uint8_t *frm;
626 	uint8_t *efrm;
627 	int pktlen;
628 	uint16_t capinfo;
629 
630 	IEEE80211_LOCK(ic);
631 	/*
632 	 * beacon frame format
633 	 *	[8] time stamp
634 	 *	[2] beacon interval
635 	 *	[2] cabability information
636 	 *	[tlv] ssid
637 	 *	[tlv] supported rates
638 	 *	[3] parameter set (DS)
639 	 *	[tlv] parameter set (IBSS/TIM)
640 	 *	[tlv] extended rate phy (ERP)
641 	 *	[tlv] extended supported rates
642 	 *	[tlv] WME parameters
643 	 *	[tlv] WPA/RSN parameters
644 	 * Vendor-specific OIDs (e.g. Atheros)
645 	 * NB: we allocate the max space required for the TIM bitmap.
646 	 */
647 	rs = &in->in_rates;
648 	pktlen =  8			/* time stamp */
649 	    + sizeof (uint16_t)		/* beacon interval */
650 	    + sizeof (uint16_t)		/* capabilities */
651 	    + 2 + in->in_esslen		/* ssid */
652 	    + 2 + IEEE80211_RATE_SIZE	/* supported rates */
653 	    + 2 + 1			/* DS parameters */
654 	    + 2 + 4 + ic->ic_tim_len	/* DTIM/IBSSPARMS */
655 	    + 2 + 1			/* ERP */
656 	    + 2 + IEEE80211_XRATE_SIZE;
657 	m = ieee80211_getmgtframe(&frm, pktlen);
658 	if (m == NULL) {
659 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_beacon_alloc: "
660 		    "cannot get buf; size %u\n", pktlen);
661 		IEEE80211_UNLOCK(ic);
662 		return (NULL);
663 	}
664 
665 	/* timestamp is set by hardware/driver */
666 	(void) memset(frm, 0, 8);
667 	frm += 8;
668 	*(uint16_t *)frm = LE_16(in->in_intval);
669 	frm += 2;
670 	capinfo = ieee80211_get_capinfo(ic);
671 	bo->bo_caps = (uint16_t *)frm;
672 	*(uint16_t *)frm = LE_16(capinfo);
673 	frm += 2;
674 	*frm++ = IEEE80211_ELEMID_SSID;
675 	if (!(ic->ic_flags & IEEE80211_F_HIDESSID)) {
676 		*frm++ = in->in_esslen;
677 		bcopy(in->in_essid, frm, in->in_esslen);
678 		frm += in->in_esslen;
679 	} else {
680 		*frm++ = 0;
681 	}
682 	frm = ieee80211_add_rates(frm, rs);
683 	if (ic->ic_curmode != IEEE80211_MODE_FH) {
684 		*frm++ = IEEE80211_ELEMID_DSPARMS;
685 		*frm++ = 1;
686 		*frm++ = ieee80211_chan2ieee(ic, in->in_chan);
687 	}
688 	bo->bo_tim = frm;
689 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
690 		*frm++ = IEEE80211_ELEMID_IBSSPARMS;
691 		*frm++ = 2;
692 		*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
693 		bo->bo_tim_len = 0;
694 	} else {
695 		struct ieee80211_tim_ie *tie =
696 		    (struct ieee80211_tim_ie *)frm;
697 
698 		tie->tim_ie = IEEE80211_ELEMID_TIM;
699 		tie->tim_len = 4;	/* length */
700 		tie->tim_count = 0;	/* DTIM count */
701 		tie->tim_period = IEEE80211_DTIM_DEFAULT;
702 		tie->tim_bitctl = 0;	/* bitmap control */
703 		tie->tim_bitmap[0] = 0;	/* Partial Virtual Bitmap */
704 		frm += sizeof (struct ieee80211_tim_ie);
705 		bo->bo_tim_len = 1;
706 	}
707 	bo->bo_trailer = frm;
708 
709 	if (ic->ic_curmode == IEEE80211_MODE_11G) {
710 		bo->bo_erp = frm;
711 		frm = ieee80211_add_erp(frm, ic);
712 	}
713 	efrm = ieee80211_add_xrates(frm, rs);
714 	bo->bo_trailer_len = efrm - bo->bo_trailer;
715 	m->b_wptr = efrm;
716 
717 	wh = (struct ieee80211_frame *)m->b_rptr;
718 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
719 	    IEEE80211_FC0_SUBTYPE_BEACON;
720 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
721 	*(uint16_t *)wh->i_dur = 0;
722 	IEEE80211_ADDR_COPY(wh->i_addr1, wifi_bcastaddr);
723 	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_macaddr);
724 	IEEE80211_ADDR_COPY(wh->i_addr3, in->in_bssid);
725 	*(uint16_t *)wh->i_seq = 0;
726 
727 	IEEE80211_UNLOCK(ic);
728 	return (m);
729 }
730 
731 /*
732  * Update the dynamic parts of a beacon frame based on the current state.
733  */
734 /* ARGSUSED */
735 int
736 ieee80211_beacon_update(ieee80211com_t *ic, ieee80211_node_t *in,
737     struct ieee80211_beacon_offsets *bo, mblk_t *mp, int mcast)
738 {
739 	uint16_t capinfo;
740 
741 	IEEE80211_LOCK(ic);
742 
743 	capinfo = ieee80211_get_capinfo(ic);
744 	*bo->bo_caps = LE_16(capinfo);
745 
746 	IEEE80211_UNLOCK(ic);
747 	return (0);
748 }
749