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