ieee80211_output.c revision 165569
1116742Ssam/*-
2116904Ssam * Copyright (c) 2001 Atsushi Onoe
3139530Ssam * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
4116742Ssam * All rights reserved.
5116742Ssam *
6116742Ssam * Redistribution and use in source and binary forms, with or without
7116742Ssam * modification, are permitted provided that the following conditions
8116742Ssam * are met:
9116742Ssam * 1. Redistributions of source code must retain the above copyright
10116904Ssam *    notice, this list of conditions and the following disclaimer.
11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright
12116904Ssam *    notice, this list of conditions and the following disclaimer in the
13116904Ssam *    documentation and/or other materials provided with the distribution.
14116904Ssam * 3. The name of the author may not be used to endorse or promote products
15116904Ssam *    derived from this software without specific prior written permission.
16116742Ssam *
17116742Ssam * Alternatively, this software may be distributed under the terms of the
18116742Ssam * GNU General Public License ("GPL") version 2 as published by the Free
19116742Ssam * Software Foundation.
20116742Ssam *
21116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31116742Ssam */
32116742Ssam
33116742Ssam#include <sys/cdefs.h>
34116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_output.c 165569 2006-12-27 18:46:18Z sam $");
35116742Ssam
36116742Ssam#include "opt_inet.h"
37116742Ssam
38116742Ssam#include <sys/param.h>
39116742Ssam#include <sys/systm.h>
40116742Ssam#include <sys/mbuf.h>
41116742Ssam#include <sys/kernel.h>
42116742Ssam#include <sys/endian.h>
43116742Ssam
44138568Ssam#include <sys/socket.h>
45116742Ssam
46138568Ssam#include <net/bpf.h>
47138568Ssam#include <net/ethernet.h>
48116742Ssam#include <net/if.h>
49138568Ssam#include <net/if_llc.h>
50116742Ssam#include <net/if_media.h>
51138568Ssam#include <net/if_vlan_var.h>
52116742Ssam
53116742Ssam#include <net80211/ieee80211_var.h>
54116742Ssam
55116742Ssam#ifdef INET
56116742Ssam#include <netinet/in.h>
57116742Ssam#include <netinet/if_ether.h>
58138568Ssam#include <netinet/in_systm.h>
59138568Ssam#include <netinet/ip.h>
60116742Ssam#endif
61116742Ssam
62138568Ssam#ifdef IEEE80211_DEBUG
63119150Ssam/*
64138568Ssam * Decide if an outbound management frame should be
65138568Ssam * printed when debugging is enabled.  This filters some
66138568Ssam * of the less interesting frames that come frequently
67138568Ssam * (e.g. beacons).
68138568Ssam */
69138568Ssamstatic __inline int
70138568Ssamdoprint(struct ieee80211com *ic, int subtype)
71138568Ssam{
72138568Ssam	switch (subtype) {
73138568Ssam	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
74138568Ssam		return (ic->ic_opmode == IEEE80211_M_IBSS);
75138568Ssam	}
76138568Ssam	return 1;
77138568Ssam}
78138568Ssam#endif
79138568Ssam
80138568Ssam/*
81148314Ssam * Set the direction field and address fields of an outgoing
82148314Ssam * non-QoS frame.  Note this should be called early on in
83148314Ssam * constructing a frame as it sets i_fc[1]; other bits can
84148314Ssam * then be or'd in.
85148314Ssam */
86148314Ssamstatic void
87148314Ssamieee80211_send_setup(struct ieee80211com *ic,
88148314Ssam	struct ieee80211_node *ni,
89148314Ssam	struct ieee80211_frame *wh,
90148314Ssam	int type,
91148314Ssam	const u_int8_t sa[IEEE80211_ADDR_LEN],
92148314Ssam	const u_int8_t da[IEEE80211_ADDR_LEN],
93148314Ssam	const u_int8_t bssid[IEEE80211_ADDR_LEN])
94148314Ssam{
95148314Ssam#define	WH4(wh)	((struct ieee80211_frame_addr4 *)wh)
96148314Ssam
97148314Ssam	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type;
98148314Ssam	if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
99148314Ssam		switch (ic->ic_opmode) {
100148314Ssam		case IEEE80211_M_STA:
101148314Ssam			wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
102148314Ssam			IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
103148314Ssam			IEEE80211_ADDR_COPY(wh->i_addr2, sa);
104148314Ssam			IEEE80211_ADDR_COPY(wh->i_addr3, da);
105148314Ssam			break;
106148314Ssam		case IEEE80211_M_IBSS:
107148314Ssam		case IEEE80211_M_AHDEMO:
108148314Ssam			wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
109148314Ssam			IEEE80211_ADDR_COPY(wh->i_addr1, da);
110148314Ssam			IEEE80211_ADDR_COPY(wh->i_addr2, sa);
111148314Ssam			IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
112148314Ssam			break;
113148314Ssam		case IEEE80211_M_HOSTAP:
114148314Ssam			wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
115148314Ssam			IEEE80211_ADDR_COPY(wh->i_addr1, da);
116148314Ssam			IEEE80211_ADDR_COPY(wh->i_addr2, bssid);
117148314Ssam			IEEE80211_ADDR_COPY(wh->i_addr3, sa);
118148314Ssam			break;
119148314Ssam		case IEEE80211_M_MONITOR:	/* NB: to quiet compiler */
120148314Ssam			break;
121148314Ssam		}
122148314Ssam	} else {
123148314Ssam		wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
124148314Ssam		IEEE80211_ADDR_COPY(wh->i_addr1, da);
125148314Ssam		IEEE80211_ADDR_COPY(wh->i_addr2, sa);
126148314Ssam		IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
127148314Ssam	}
128148314Ssam	*(u_int16_t *)&wh->i_dur[0] = 0;
129148314Ssam	/* NB: use non-QoS tid */
130148314Ssam	*(u_int16_t *)&wh->i_seq[0] =
131148314Ssam	    htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
132148314Ssam	ni->ni_txseqs[0]++;
133148314Ssam#undef WH4
134148314Ssam}
135148314Ssam
136148314Ssam/*
137119150Ssam * Send a management frame to the specified node.  The node pointer
138119150Ssam * must have a reference as the pointer will be passed to the driver
139119150Ssam * and potentially held for a long time.  If the frame is successfully
140119150Ssam * dispatched to the driver, then it is responsible for freeing the
141119150Ssam * reference (and potentially free'ing up any associated storage).
142119150Ssam */
143119150Ssamstatic int
144138568Ssamieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni,
145155460Ssam    struct mbuf *m, int type, int timer)
146116742Ssam{
147138568Ssam	struct ifnet *ifp = ic->ic_ifp;
148116742Ssam	struct ieee80211_frame *wh;
149116742Ssam
150119150Ssam	KASSERT(ni != NULL, ("null node"));
151116742Ssam
152119150Ssam	/*
153119150Ssam	 * Yech, hack alert!  We want to pass the node down to the
154119150Ssam	 * driver's start routine.  If we don't do so then the start
155119150Ssam	 * routine must immediately look it up again and that can
156119150Ssam	 * cause a lock order reversal if, for example, this frame
157119150Ssam	 * is being sent because the station is being timedout and
158119150Ssam	 * the frame being sent is a DEAUTH message.  We could stick
159119150Ssam	 * this in an m_tag and tack that on to the mbuf.  However
160119150Ssam	 * that's rather expensive to do for every frame so instead
161119150Ssam	 * we stuff it in the rcvif field since outbound frames do
162119150Ssam	 * not (presently) use this.
163119150Ssam	 */
164116742Ssam	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
165116742Ssam	if (m == NULL)
166116742Ssam		return ENOMEM;
167119150Ssam	KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null"));
168119150Ssam	m->m_pkthdr.rcvif = (void *)ni;
169119150Ssam
170116742Ssam	wh = mtod(m, struct ieee80211_frame *);
171148314Ssam	ieee80211_send_setup(ic, ni, wh,
172148314Ssam		IEEE80211_FC0_TYPE_MGT | type,
173148314Ssam		ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid);
174138568Ssam	if ((m->m_flags & M_LINK0) != 0 && ni->ni_challenge != NULL) {
175138568Ssam		m->m_flags &= ~M_LINK0;
176138568Ssam		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
177138568Ssam			"[%s] encrypting frame (%s)\n",
178138568Ssam			ether_sprintf(wh->i_addr1), __func__);
179138568Ssam		wh->i_fc[1] |= IEEE80211_FC1_WEP;
180138568Ssam	}
181116742Ssam#ifdef IEEE80211_DEBUG
182138568Ssam	/* avoid printing too many frames */
183138568Ssam	if ((ieee80211_msg_debug(ic) && doprint(ic, type)) ||
184138568Ssam	    ieee80211_msg_dumppkts(ic)) {
185138568Ssam		printf("[%s] send %s on channel %u\n",
186138568Ssam		    ether_sprintf(wh->i_addr1),
187138568Ssam		    ieee80211_mgt_subtype_name[
188138568Ssam			(type & IEEE80211_FC0_SUBTYPE_MASK) >>
189138568Ssam				IEEE80211_FC0_SUBTYPE_SHIFT],
190148936Ssam		    ieee80211_chan2ieee(ic, ic->ic_curchan));
191138568Ssam	}
192116742Ssam#endif
193138568Ssam	IEEE80211_NODE_STAT(ni, tx_mgmt);
194116742Ssam	IF_ENQUEUE(&ic->ic_mgtq, m);
195155460Ssam	if (timer) {
196155460Ssam		/*
197155460Ssam		 * Set the mgt frame timeout.
198155460Ssam		 */
199155460Ssam		ic->ic_mgt_timer = timer;
200155460Ssam		ifp->if_timer = 1;
201155460Ssam	}
202132712Srwatson	if_start(ifp);
203116742Ssam	return 0;
204116742Ssam}
205116742Ssam
206119150Ssam/*
207160690Ssam * Raw packet transmit stub for legacy drivers.
208160690Ssam * Send the packet through the mgt q so we bypass
209160690Ssam * the normal encapsulation work.
210160690Ssam */
211160690Ssamint
212160690Ssamieee80211_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
213160690Ssam	const struct ieee80211_bpf_params *params)
214160690Ssam{
215160690Ssam	struct ieee80211com *ic = ni->ni_ic;
216160690Ssam	struct ifnet *ifp = ic->ic_ifp;
217160690Ssam
218160690Ssam	m->m_pkthdr.rcvif = (void *) ni;
219160690Ssam	IF_ENQUEUE(&ic->ic_mgtq, m);
220160690Ssam	if_start(ifp);
221160690Ssam	ifp->if_opackets++;
222160690Ssam
223160690Ssam	return 0;
224160690Ssam}
225160690Ssam
226160690Ssam/*
227160690Ssam * 802.11 output routine. This is (currently) used only to
228160690Ssam * connect bpf write calls to the 802.11 layer for injecting
229160690Ssam * raw 802.11 frames.  Note we locate the ieee80211com from
230160690Ssam * the ifnet using a spare field setup at attach time.  This
231160690Ssam * will go away when the virtual ap support comes in.
232160690Ssam */
233160690Ssamint
234160690Ssamieee80211_output(struct ifnet *ifp, struct mbuf *m,
235160690Ssam	struct sockaddr *dst, struct rtentry *rt0)
236160690Ssam{
237160690Ssam#define senderr(e) do { error = (e); goto bad;} while (0)
238160690Ssam	struct ieee80211com *ic = ifp->if_spare2;	/* XXX */
239160690Ssam	struct ieee80211_node *ni = NULL;
240160690Ssam	struct ieee80211_frame *wh;
241160690Ssam	int error;
242160690Ssam
243160690Ssam	/*
244160690Ssam	 * Hand to the 802.3 code if not tagged as
245160690Ssam	 * a raw 802.11 frame.
246160690Ssam	 */
247160690Ssam	if (dst->sa_family != AF_IEEE80211)
248160690Ssam		return ether_output(ifp, m, dst, rt0);
249160690Ssam#ifdef MAC
250160690Ssam	error = mac_check_ifnet_transmit(ifp, m);
251160690Ssam	if (error)
252160690Ssam		senderr(error);
253160690Ssam#endif
254160690Ssam	if (ifp->if_flags & IFF_MONITOR)
255160690Ssam		senderr(ENETDOWN);
256160690Ssam	if ((ifp->if_flags & IFF_UP) == 0)
257160690Ssam		senderr(ENETDOWN);
258160690Ssam
259160690Ssam	/* XXX bypass bridge, pfil, carp, etc. */
260160690Ssam
261160690Ssam	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack))
262160690Ssam		senderr(EIO);	/* XXX */
263160690Ssam	wh = mtod(m, struct ieee80211_frame *);
264160690Ssam	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
265160690Ssam	    IEEE80211_FC0_VERSION_0)
266160690Ssam		senderr(EIO);	/* XXX */
267160690Ssam
268160690Ssam	/* locate destination node */
269160690Ssam	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
270160690Ssam	case IEEE80211_FC1_DIR_NODS:
271160690Ssam	case IEEE80211_FC1_DIR_FROMDS:
272160690Ssam		ni = ieee80211_find_txnode(ic, wh->i_addr1);
273160690Ssam		break;
274160690Ssam	case IEEE80211_FC1_DIR_TODS:
275160690Ssam	case IEEE80211_FC1_DIR_DSTODS:
276160690Ssam		if (m->m_pkthdr.len < sizeof(struct ieee80211_frame))
277160690Ssam			senderr(EIO);	/* XXX */
278160690Ssam		ni = ieee80211_find_txnode(ic, wh->i_addr3);
279160690Ssam		break;
280160690Ssam	default:
281160690Ssam		senderr(EIO);	/* XXX */
282160690Ssam	}
283160690Ssam	if (ni == NULL) {
284160690Ssam		/*
285160690Ssam		 * Permit packets w/ bpf params through regardless
286160690Ssam		 * (see below about sa_len).
287160690Ssam		 */
288160690Ssam		if (dst->sa_len == 0)
289160690Ssam			senderr(EHOSTUNREACH);
290160690Ssam		ni = ieee80211_ref_node(ic->ic_bss);
291160690Ssam	}
292160690Ssam
293160690Ssam	/* XXX ctrl frames should go through */
294160690Ssam	if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
295160690Ssam	    (m->m_flags & M_PWR_SAV) == 0) {
296160690Ssam		/*
297160690Ssam		 * Station in power save mode; pass the frame
298160690Ssam		 * to the 802.11 layer and continue.  We'll get
299160690Ssam		 * the frame back when the time is right.
300160690Ssam		 */
301160690Ssam		ieee80211_pwrsave(ic, ni, m);
302160690Ssam		error = 0;
303160690Ssam		goto reclaim;
304160690Ssam	}
305160690Ssam
306160690Ssam	/* calculate priority so drivers can find the tx queue */
307160690Ssam	/* XXX assumes an 802.3 frame */
308160690Ssam	if (ieee80211_classify(ic, m, ni))
309160690Ssam		senderr(EIO);		/* XXX */
310160690Ssam
311160690Ssam	BPF_MTAP(ifp, m);
312160690Ssam	/*
313160690Ssam	 * NB: DLT_IEEE802_11_RADIO identifies the parameters are
314160690Ssam	 * present by setting the sa_len field of the sockaddr (yes,
315160690Ssam	 * this is a hack).
316160690Ssam	 * NB: we assume sa_data is suitably aligned to cast.
317160690Ssam	 */
318160690Ssam	return ic->ic_raw_xmit(ni, m, (const struct ieee80211_bpf_params *)
319160690Ssam		(dst->sa_len ? dst->sa_data : NULL));
320160690Ssambad:
321160690Ssam	if (m != NULL)
322160690Ssam		m_freem(m);
323160690Ssamreclaim:
324160690Ssam	if (ni != NULL)
325160690Ssam		ieee80211_free_node(ni);
326160690Ssam	return error;
327160690Ssam#undef senderr
328160690Ssam}
329160690Ssam
330160690Ssam/*
331138568Ssam * Send a null data frame to the specified node.
332148582Ssam *
333148582Ssam * NB: the caller is assumed to have setup a node reference
334148582Ssam *     for use; this is necessary to deal with a race condition
335148582Ssam *     when probing for inactive stations.
336119150Ssam */
337138568Ssamint
338148301Ssamieee80211_send_nulldata(struct ieee80211_node *ni)
339138568Ssam{
340148301Ssam	struct ieee80211com *ic = ni->ni_ic;
341138568Ssam	struct ifnet *ifp = ic->ic_ifp;
342138568Ssam	struct mbuf *m;
343138568Ssam	struct ieee80211_frame *wh;
344138568Ssam
345151967Sandre	MGETHDR(m, M_NOWAIT, MT_DATA);
346138568Ssam	if (m == NULL) {
347138568Ssam		/* XXX debug msg */
348138568Ssam		ic->ic_stats.is_tx_nobuf++;
349148582Ssam		ieee80211_unref_node(&ni);
350138568Ssam		return ENOMEM;
351138568Ssam	}
352148582Ssam	m->m_pkthdr.rcvif = (void *) ni;
353138568Ssam
354138568Ssam	wh = mtod(m, struct ieee80211_frame *);
355148314Ssam	ieee80211_send_setup(ic, ni, wh,
356148314Ssam		IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA,
357148314Ssam		ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid);
358148314Ssam	/* NB: power management bit is never sent by an AP */
359148314Ssam	if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
360148314Ssam	    ic->ic_opmode != IEEE80211_M_HOSTAP)
361148314Ssam		wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
362138568Ssam	m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame);
363138568Ssam
364138568Ssam	IEEE80211_NODE_STAT(ni, tx_data);
365138568Ssam
366148314Ssam	IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
367148314Ssam	    "[%s] send null data frame on channel %u, pwr mgt %s\n",
368148314Ssam	    ether_sprintf(ni->ni_macaddr),
369148936Ssam	    ieee80211_chan2ieee(ic, ic->ic_curchan),
370148314Ssam	    wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis");
371148314Ssam
372138568Ssam	IF_ENQUEUE(&ic->ic_mgtq, m);		/* cheat */
373138568Ssam	if_start(ifp);
374138568Ssam
375138568Ssam	return 0;
376138568Ssam}
377138568Ssam
378138568Ssam/*
379138568Ssam * Assign priority to a frame based on any vlan tag assigned
380138568Ssam * to the station and/or any Diffserv setting in an IP header.
381138568Ssam * Finally, if an ACM policy is setup (in station mode) it's
382138568Ssam * applied.
383138568Ssam */
384138568Ssamint
385138568Ssamieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni)
386138568Ssam{
387138568Ssam	int v_wme_ac, d_wme_ac, ac;
388138568Ssam#ifdef INET
389138568Ssam	struct ether_header *eh;
390138568Ssam#endif
391138568Ssam
392138568Ssam	if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) {
393138568Ssam		ac = WME_AC_BE;
394138568Ssam		goto done;
395138568Ssam	}
396138568Ssam
397138568Ssam	/*
398138568Ssam	 * If node has a vlan tag then all traffic
399138568Ssam	 * to it must have a matching tag.
400138568Ssam	 */
401138568Ssam	v_wme_ac = 0;
402138568Ssam	if (ni->ni_vlan != 0) {
403162375Sandre		 if ((m->m_flags & M_VLANTAG) == 0) {
404138568Ssam			IEEE80211_NODE_STAT(ni, tx_novlantag);
405138568Ssam			return 1;
406138568Ssam		}
407162375Sandre		if (EVL_VLANOFTAG(m->m_pkthdr.ether_vtag) !=
408138568Ssam		    EVL_VLANOFTAG(ni->ni_vlan)) {
409138568Ssam			IEEE80211_NODE_STAT(ni, tx_vlanmismatch);
410138568Ssam			return 1;
411138568Ssam		}
412138568Ssam		/* map vlan priority to AC */
413138568Ssam		switch (EVL_PRIOFTAG(ni->ni_vlan)) {
414138568Ssam		case 1:
415138568Ssam		case 2:
416138568Ssam			v_wme_ac = WME_AC_BK;
417138568Ssam			break;
418138568Ssam		case 0:
419138568Ssam		case 3:
420138568Ssam			v_wme_ac = WME_AC_BE;
421138568Ssam			break;
422138568Ssam		case 4:
423138568Ssam		case 5:
424138568Ssam			v_wme_ac = WME_AC_VI;
425138568Ssam			break;
426138568Ssam		case 6:
427138568Ssam		case 7:
428138568Ssam			v_wme_ac = WME_AC_VO;
429138568Ssam			break;
430138568Ssam		}
431138568Ssam	}
432138568Ssam
433138568Ssam#ifdef INET
434138568Ssam	eh = mtod(m, struct ether_header *);
435138568Ssam	if (eh->ether_type == htons(ETHERTYPE_IP)) {
436138568Ssam		const struct ip *ip = (struct ip *)
437138568Ssam			(mtod(m, u_int8_t *) + sizeof (*eh));
438138568Ssam		/*
439138568Ssam		 * IP frame, map the TOS field.
440138568Ssam		 */
441138568Ssam		switch (ip->ip_tos) {
442138568Ssam		case 0x08:
443138568Ssam		case 0x20:
444138568Ssam			d_wme_ac = WME_AC_BK;	/* background */
445138568Ssam			break;
446138568Ssam		case 0x28:
447138568Ssam		case 0xa0:
448138568Ssam			d_wme_ac = WME_AC_VI;	/* video */
449138568Ssam			break;
450138568Ssam		case 0x30:			/* voice */
451138568Ssam		case 0xe0:
452138568Ssam		case 0x88:			/* XXX UPSD */
453138568Ssam		case 0xb8:
454138568Ssam			d_wme_ac = WME_AC_VO;
455138568Ssam			break;
456138568Ssam		default:
457138568Ssam			d_wme_ac = WME_AC_BE;
458138568Ssam			break;
459138568Ssam		}
460138568Ssam	} else {
461138568Ssam#endif /* INET */
462138568Ssam		d_wme_ac = WME_AC_BE;
463138568Ssam#ifdef INET
464138568Ssam	}
465138568Ssam#endif
466138568Ssam	/*
467138568Ssam	 * Use highest priority AC.
468138568Ssam	 */
469138568Ssam	if (v_wme_ac > d_wme_ac)
470138568Ssam		ac = v_wme_ac;
471138568Ssam	else
472138568Ssam		ac = d_wme_ac;
473138568Ssam
474138568Ssam	/*
475138568Ssam	 * Apply ACM policy.
476138568Ssam	 */
477138568Ssam	if (ic->ic_opmode == IEEE80211_M_STA) {
478138568Ssam		static const int acmap[4] = {
479138568Ssam			WME_AC_BK,	/* WME_AC_BE */
480138568Ssam			WME_AC_BK,	/* WME_AC_BK */
481138568Ssam			WME_AC_BE,	/* WME_AC_VI */
482138568Ssam			WME_AC_VI,	/* WME_AC_VO */
483138568Ssam		};
484138568Ssam		while (ac != WME_AC_BK &&
485138568Ssam		    ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm)
486138568Ssam			ac = acmap[ac];
487138568Ssam	}
488138568Ssamdone:
489138568Ssam	M_WME_SETAC(m, ac);
490138568Ssam	return 0;
491138568Ssam}
492138568Ssam
493138568Ssam/*
494139527Ssam * Insure there is sufficient contiguous space to encapsulate the
495139527Ssam * 802.11 data frame.  If room isn't already there, arrange for it.
496139527Ssam * Drivers and cipher modules assume we have done the necessary work
497139527Ssam * and fail rudely if they don't find the space they need.
498139527Ssam */
499139527Ssamstatic struct mbuf *
500139527Ssamieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
501139527Ssam	struct ieee80211_key *key, struct mbuf *m)
502139527Ssam{
503164805Ssam#define	TO_BE_RECLAIMED	(sizeof(struct ether_header) - sizeof(struct llc))
504139527Ssam	int needed_space = hdrsize;
505139527Ssam
506139527Ssam	if (key != NULL) {
507139527Ssam		/* XXX belongs in crypto code? */
508139527Ssam		needed_space += key->wk_cipher->ic_header;
509139527Ssam		/* XXX frags */
510156758Ssam		/*
511156758Ssam		 * When crypto is being done in the host we must insure
512156758Ssam		 * the data are writable for the cipher routines; clone
513156758Ssam		 * a writable mbuf chain.
514156758Ssam		 * XXX handle SWMIC specially
515156758Ssam		 */
516156758Ssam		if (key->wk_flags & (IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC)) {
517156758Ssam			m = m_unshare(m, M_NOWAIT);
518156758Ssam			if (m == NULL) {
519156758Ssam				IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
520156758Ssam				    "%s: cannot get writable mbuf\n", __func__);
521156758Ssam				ic->ic_stats.is_tx_nobuf++; /* XXX new stat */
522156758Ssam				return NULL;
523156758Ssam			}
524156758Ssam		}
525139527Ssam	}
526139527Ssam	/*
527139527Ssam	 * We know we are called just before stripping an Ethernet
528139527Ssam	 * header and prepending an LLC header.  This means we know
529139527Ssam	 * there will be
530164805Ssam	 *	sizeof(struct ether_header) - sizeof(struct llc)
531139527Ssam	 * bytes recovered to which we need additional space for the
532139527Ssam	 * 802.11 header and any crypto header.
533139527Ssam	 */
534139527Ssam	/* XXX check trailing space and copy instead? */
535139527Ssam	if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) {
536139527Ssam		struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type);
537139527Ssam		if (n == NULL) {
538139527Ssam			IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
539139527Ssam			    "%s: cannot expand storage\n", __func__);
540139527Ssam			ic->ic_stats.is_tx_nobuf++;
541139527Ssam			m_freem(m);
542139527Ssam			return NULL;
543139527Ssam		}
544139527Ssam		KASSERT(needed_space <= MHLEN,
545139527Ssam		    ("not enough room, need %u got %zu\n", needed_space, MHLEN));
546139527Ssam		/*
547139527Ssam		 * Setup new mbuf to have leading space to prepend the
548139527Ssam		 * 802.11 header and any crypto header bits that are
549139527Ssam		 * required (the latter are added when the driver calls
550139527Ssam		 * back to ieee80211_crypto_encap to do crypto encapsulation).
551139527Ssam		 */
552139527Ssam		/* NB: must be first 'cuz it clobbers m_data */
553139527Ssam		m_move_pkthdr(n, m);
554139527Ssam		n->m_len = 0;			/* NB: m_gethdr does not set */
555139527Ssam		n->m_data += needed_space;
556139527Ssam		/*
557139527Ssam		 * Pull up Ethernet header to create the expected layout.
558139527Ssam		 * We could use m_pullup but that's overkill (i.e. we don't
559139527Ssam		 * need the actual data) and it cannot fail so do it inline
560139527Ssam		 * for speed.
561139527Ssam		 */
562139527Ssam		/* NB: struct ether_header is known to be contiguous */
563139527Ssam		n->m_len += sizeof(struct ether_header);
564139527Ssam		m->m_len -= sizeof(struct ether_header);
565139527Ssam		m->m_data += sizeof(struct ether_header);
566139527Ssam		/*
567139527Ssam		 * Replace the head of the chain.
568139527Ssam		 */
569139527Ssam		n->m_next = m;
570139527Ssam		m = n;
571139527Ssam	}
572139527Ssam	return m;
573139527Ssam#undef TO_BE_RECLAIMED
574139527Ssam}
575139527Ssam
576139527Ssam#define	KEY_UNDEFINED(k)	((k).wk_cipher == &ieee80211_cipher_none)
577139527Ssam/*
578139527Ssam * Return the transmit key to use in sending a unicast frame.
579139527Ssam * If a unicast key is set we use that.  When no unicast key is set
580139527Ssam * we fall back to the default transmit key.
581138568Ssam */
582138568Ssamstatic __inline struct ieee80211_key *
583139527Ssamieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
584138568Ssam{
585139527Ssam	if (KEY_UNDEFINED(ni->ni_ucastkey)) {
586138568Ssam		if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
587138568Ssam		    KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey]))
588138568Ssam			return NULL;
589138568Ssam		return &ic->ic_nw_keys[ic->ic_def_txkey];
590138568Ssam	} else {
591138568Ssam		return &ni->ni_ucastkey;
592138568Ssam	}
593138568Ssam}
594138568Ssam
595138568Ssam/*
596139527Ssam * Return the transmit key to use in sending a multicast frame.
597139527Ssam * Multicast traffic always uses the group key which is installed as
598139527Ssam * the default tx key.
599139527Ssam */
600139527Ssamstatic __inline struct ieee80211_key *
601139527Ssamieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
602139527Ssam{
603139527Ssam	if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
604139527Ssam	    KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey]))
605139527Ssam		return NULL;
606139527Ssam	return &ic->ic_nw_keys[ic->ic_def_txkey];
607139527Ssam}
608139527Ssam
609139527Ssam/*
610138568Ssam * Encapsulate an outbound data frame.  The mbuf chain is updated.
611138568Ssam * If an error is encountered NULL is returned.  The caller is required
612138568Ssam * to provide a node reference and pullup the ethernet header in the
613138568Ssam * first mbuf.
614138568Ssam */
615116742Ssamstruct mbuf *
616138568Ssamieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
617138568Ssam	struct ieee80211_node *ni)
618116742Ssam{
619116742Ssam	struct ether_header eh;
620116742Ssam	struct ieee80211_frame *wh;
621138568Ssam	struct ieee80211_key *key;
622116742Ssam	struct llc *llc;
623139527Ssam	int hdrsize, datalen, addqos;
624116742Ssam
625138568Ssam	KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!"));
626116742Ssam	memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
627116742Ssam
628138568Ssam	/*
629138568Ssam	 * Insure space for additional headers.  First identify
630138568Ssam	 * transmit key to use in calculating any buffer adjustments
631138568Ssam	 * required.  This is also used below to do privacy
632138568Ssam	 * encapsulation work.  Then calculate the 802.11 header
633138568Ssam	 * size and any padding required by the driver.
634138568Ssam	 *
635138568Ssam	 * Note key may be NULL if we fall back to the default
636138568Ssam	 * transmit key and that is not set.  In that case the
637138568Ssam	 * buffer may not be expanded as needed by the cipher
638138568Ssam	 * routines, but they will/should discard it.
639138568Ssam	 */
640138568Ssam	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
641139527Ssam		if (ic->ic_opmode == IEEE80211_M_STA ||
642139527Ssam		    !IEEE80211_IS_MULTICAST(eh.ether_dhost))
643139527Ssam			key = ieee80211_crypto_getucastkey(ic, ni);
644139527Ssam		else
645139527Ssam			key = ieee80211_crypto_getmcastkey(ic, ni);
646138568Ssam		if (key == NULL && eh.ether_type != htons(ETHERTYPE_PAE)) {
647138568Ssam			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
648139527Ssam			    "[%s] no default transmit key (%s) deftxkey %u\n",
649139527Ssam			    ether_sprintf(eh.ether_dhost), __func__,
650139527Ssam			    ic->ic_def_txkey);
651139527Ssam			ic->ic_stats.is_tx_nodefkey++;
652138568Ssam		}
653138568Ssam	} else
654138568Ssam		key = NULL;
655138568Ssam	/* XXX 4-address format */
656139527Ssam	/*
657139527Ssam	 * XXX Some ap's don't handle QoS-encapsulated EAPOL
658139527Ssam	 * frames so suppress use.  This may be an issue if other
659139527Ssam	 * ap's require all data frames to be QoS-encapsulated
660139527Ssam	 * once negotiated in which case we'll need to make this
661139527Ssam	 * configurable.
662139527Ssam	 */
663139527Ssam	addqos = (ni->ni_flags & IEEE80211_NODE_QOS) &&
664139527Ssam		 eh.ether_type != htons(ETHERTYPE_PAE);
665139527Ssam	if (addqos)
666138568Ssam		hdrsize = sizeof(struct ieee80211_qosframe);
667138568Ssam	else
668138568Ssam		hdrsize = sizeof(struct ieee80211_frame);
669138568Ssam	if (ic->ic_flags & IEEE80211_F_DATAPAD)
670138568Ssam		hdrsize = roundup(hdrsize, sizeof(u_int32_t));
671138568Ssam	m = ieee80211_mbuf_adjust(ic, hdrsize, key, m);
672138568Ssam	if (m == NULL) {
673138568Ssam		/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
674127772Ssam		goto bad;
675127772Ssam	}
676116742Ssam
677138568Ssam	/* NB: this could be optimized because of ieee80211_mbuf_adjust */
678164805Ssam	m_adj(m, sizeof(struct ether_header) - sizeof(struct llc));
679116742Ssam	llc = mtod(m, struct llc *);
680116742Ssam	llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
681116742Ssam	llc->llc_control = LLC_UI;
682116742Ssam	llc->llc_snap.org_code[0] = 0;
683116742Ssam	llc->llc_snap.org_code[1] = 0;
684116742Ssam	llc->llc_snap.org_code[2] = 0;
685116742Ssam	llc->llc_snap.ether_type = eh.ether_type;
686138568Ssam	datalen = m->m_pkthdr.len;		/* NB: w/o 802.11 header */
687138568Ssam
688138568Ssam	M_PREPEND(m, hdrsize, M_DONTWAIT);
689121180Ssam	if (m == NULL) {
690138568Ssam		ic->ic_stats.is_tx_nobuf++;
691119150Ssam		goto bad;
692121180Ssam	}
693116742Ssam	wh = mtod(m, struct ieee80211_frame *);
694116742Ssam	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
695116742Ssam	*(u_int16_t *)wh->i_dur = 0;
696116742Ssam	switch (ic->ic_opmode) {
697116742Ssam	case IEEE80211_M_STA:
698116742Ssam		wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
699116742Ssam		IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid);
700116742Ssam		IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
701116742Ssam		IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
702116742Ssam		break;
703116742Ssam	case IEEE80211_M_IBSS:
704116742Ssam	case IEEE80211_M_AHDEMO:
705116742Ssam		wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
706116742Ssam		IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
707116742Ssam		IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
708140636Ssam		/*
709140636Ssam		 * NB: always use the bssid from ic_bss as the
710140636Ssam		 *     neighbor's may be stale after an ibss merge
711140636Ssam		 */
712140636Ssam		IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
713116742Ssam		break;
714116742Ssam	case IEEE80211_M_HOSTAP:
715116742Ssam		wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
716116742Ssam		IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
717116742Ssam		IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid);
718116742Ssam		IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
719116742Ssam		break;
720117817Ssam	case IEEE80211_M_MONITOR:
721119150Ssam		goto bad;
722116742Ssam	}
723147789Ssam	if (m->m_flags & M_MORE_DATA)
724147789Ssam		wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
725139527Ssam	if (addqos) {
726138568Ssam		struct ieee80211_qosframe *qwh =
727138568Ssam			(struct ieee80211_qosframe *) wh;
728138568Ssam		int ac, tid;
729138568Ssam
730138568Ssam		ac = M_WME_GETAC(m);
731138568Ssam		/* map from access class/queue to 11e header priorty value */
732138568Ssam		tid = WME_AC_TO_TID(ac);
733138568Ssam		qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
734138568Ssam		if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy)
735138568Ssam			qwh->i_qos[0] |= 1 << IEEE80211_QOS_ACKPOLICY_S;
736138568Ssam		qwh->i_qos[1] = 0;
737138568Ssam		qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
738138568Ssam
739138568Ssam		*(u_int16_t *)wh->i_seq =
740138568Ssam		    htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
741138568Ssam		ni->ni_txseqs[tid]++;
742138568Ssam	} else {
743138568Ssam		*(u_int16_t *)wh->i_seq =
744138568Ssam		    htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
745138568Ssam		ni->ni_txseqs[0]++;
746138568Ssam	}
747139527Ssam	if (key != NULL) {
748139527Ssam		/*
749139527Ssam		 * IEEE 802.1X: send EAPOL frames always in the clear.
750139527Ssam		 * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set.
751139527Ssam		 */
752139527Ssam		if (eh.ether_type != htons(ETHERTYPE_PAE) ||
753139527Ssam		    ((ic->ic_flags & IEEE80211_F_WPA) &&
754141660Ssam		     (ic->ic_opmode == IEEE80211_M_STA ?
755141660Ssam		      !KEY_UNDEFINED(*key) : !KEY_UNDEFINED(ni->ni_ucastkey)))) {
756139527Ssam			wh->i_fc[1] |= IEEE80211_FC1_WEP;
757139527Ssam			/* XXX do fragmentation */
758147045Ssam			if (!ieee80211_crypto_enmic(ic, key, m, 0)) {
759139527Ssam				IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
760139527Ssam				    "[%s] enmic failed, discard frame\n",
761139527Ssam				    ether_sprintf(eh.ether_dhost));
762139527Ssam				ic->ic_stats.is_crypto_enmicfail++;
763139527Ssam				goto bad;
764139527Ssam			}
765139527Ssam		}
766139527Ssam	}
767138568Ssam
768138568Ssam	IEEE80211_NODE_STAT(ni, tx_data);
769161144Ssam	if (IEEE80211_IS_MULTICAST(wh->i_addr1))
770161144Ssam		IEEE80211_NODE_STAT(ni, tx_mcast);
771161144Ssam	else
772161144Ssam		IEEE80211_NODE_STAT(ni, tx_ucast);
773138568Ssam	IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen);
774138568Ssam
775116742Ssam	return m;
776119150Ssambad:
777119150Ssam	if (m != NULL)
778119150Ssam		m_freem(m);
779119150Ssam	return NULL;
780116742Ssam}
781116742Ssam
782116742Ssam/*
783116742Ssam * Add a supported rates element id to a frame.
784116742Ssam */
785138568Ssamstatic u_int8_t *
786116742Ssamieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs)
787116742Ssam{
788116742Ssam	int nrates;
789116742Ssam
790116742Ssam	*frm++ = IEEE80211_ELEMID_RATES;
791116742Ssam	nrates = rs->rs_nrates;
792116742Ssam	if (nrates > IEEE80211_RATE_SIZE)
793116742Ssam		nrates = IEEE80211_RATE_SIZE;
794116742Ssam	*frm++ = nrates;
795116742Ssam	memcpy(frm, rs->rs_rates, nrates);
796116742Ssam	return frm + nrates;
797116742Ssam}
798116742Ssam
799116742Ssam/*
800116742Ssam * Add an extended supported rates element id to a frame.
801116742Ssam */
802138568Ssamstatic u_int8_t *
803116742Ssamieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs)
804116742Ssam{
805116742Ssam	/*
806116742Ssam	 * Add an extended supported rates element if operating in 11g mode.
807116742Ssam	 */
808116742Ssam	if (rs->rs_nrates > IEEE80211_RATE_SIZE) {
809116742Ssam		int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE;
810116742Ssam		*frm++ = IEEE80211_ELEMID_XRATES;
811116742Ssam		*frm++ = nrates;
812116742Ssam		memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates);
813116742Ssam		frm += nrates;
814116742Ssam	}
815116742Ssam	return frm;
816116742Ssam}
817116742Ssam
818116742Ssam/*
819116742Ssam * Add an ssid elemet to a frame.
820116742Ssam */
821116742Ssamstatic u_int8_t *
822116742Ssamieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len)
823116742Ssam{
824116742Ssam	*frm++ = IEEE80211_ELEMID_SSID;
825116742Ssam	*frm++ = len;
826116742Ssam	memcpy(frm, ssid, len);
827116742Ssam	return frm + len;
828116742Ssam}
829116742Ssam
830138568Ssam/*
831138568Ssam * Add an erp element to a frame.
832138568Ssam */
833138568Ssamstatic u_int8_t *
834138568Ssamieee80211_add_erp(u_int8_t *frm, struct ieee80211com *ic)
835116742Ssam{
836138568Ssam	u_int8_t erp;
837116742Ssam
838138568Ssam	*frm++ = IEEE80211_ELEMID_ERP;
839138568Ssam	*frm++ = 1;
840138568Ssam	erp = 0;
841138568Ssam	if (ic->ic_nonerpsta != 0)
842138568Ssam		erp |= IEEE80211_ERP_NON_ERP_PRESENT;
843138568Ssam	if (ic->ic_flags & IEEE80211_F_USEPROT)
844138568Ssam		erp |= IEEE80211_ERP_USE_PROTECTION;
845138568Ssam	if (ic->ic_flags & IEEE80211_F_USEBARKER)
846138568Ssam		erp |= IEEE80211_ERP_LONG_PREAMBLE;
847138568Ssam	*frm++ = erp;
848138568Ssam	return frm;
849138568Ssam}
850138568Ssam
851138568Ssamstatic u_int8_t *
852138568Ssamieee80211_setup_wpa_ie(struct ieee80211com *ic, u_int8_t *ie)
853138568Ssam{
854138568Ssam#define	WPA_OUI_BYTES		0x00, 0x50, 0xf2
855138568Ssam#define	ADDSHORT(frm, v) do {			\
856138568Ssam	frm[0] = (v) & 0xff;			\
857138568Ssam	frm[1] = (v) >> 8;			\
858138568Ssam	frm += 2;				\
859138568Ssam} while (0)
860138568Ssam#define	ADDSELECTOR(frm, sel) do {		\
861138568Ssam	memcpy(frm, sel, 4);			\
862138568Ssam	frm += 4;				\
863138568Ssam} while (0)
864138568Ssam	static const u_int8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE };
865138568Ssam	static const u_int8_t cipher_suite[][4] = {
866138568Ssam		{ WPA_OUI_BYTES, WPA_CSE_WEP40 },	/* NB: 40-bit */
867138568Ssam		{ WPA_OUI_BYTES, WPA_CSE_TKIP },
868138568Ssam		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX WRAP */
869138568Ssam		{ WPA_OUI_BYTES, WPA_CSE_CCMP },
870138568Ssam		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX CKIP */
871138568Ssam		{ WPA_OUI_BYTES, WPA_CSE_NULL },
872138568Ssam	};
873138568Ssam	static const u_int8_t wep104_suite[4] =
874138568Ssam		{ WPA_OUI_BYTES, WPA_CSE_WEP104 };
875138568Ssam	static const u_int8_t key_mgt_unspec[4] =
876138568Ssam		{ WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC };
877138568Ssam	static const u_int8_t key_mgt_psk[4] =
878138568Ssam		{ WPA_OUI_BYTES, WPA_ASE_8021X_PSK };
879138568Ssam	const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
880138568Ssam	u_int8_t *frm = ie;
881138568Ssam	u_int8_t *selcnt;
882138568Ssam
883138568Ssam	*frm++ = IEEE80211_ELEMID_VENDOR;
884138568Ssam	*frm++ = 0;				/* length filled in below */
885138568Ssam	memcpy(frm, oui, sizeof(oui));		/* WPA OUI */
886138568Ssam	frm += sizeof(oui);
887138568Ssam	ADDSHORT(frm, WPA_VERSION);
888138568Ssam
889138568Ssam	/* XXX filter out CKIP */
890138568Ssam
891138568Ssam	/* multicast cipher */
892138568Ssam	if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
893138568Ssam	    rsn->rsn_mcastkeylen >= 13)
894138568Ssam		ADDSELECTOR(frm, wep104_suite);
895116742Ssam	else
896138568Ssam		ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
897138568Ssam
898138568Ssam	/* unicast cipher list */
899138568Ssam	selcnt = frm;
900138568Ssam	ADDSHORT(frm, 0);			/* selector count */
901138568Ssam	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) {
902138568Ssam		selcnt[0]++;
903138568Ssam		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
904138568Ssam	}
905138568Ssam	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) {
906138568Ssam		selcnt[0]++;
907138568Ssam		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
908138568Ssam	}
909138568Ssam
910138568Ssam	/* authenticator selector list */
911138568Ssam	selcnt = frm;
912138568Ssam	ADDSHORT(frm, 0);			/* selector count */
913138568Ssam	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
914138568Ssam		selcnt[0]++;
915138568Ssam		ADDSELECTOR(frm, key_mgt_unspec);
916138568Ssam	}
917138568Ssam	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
918138568Ssam		selcnt[0]++;
919138568Ssam		ADDSELECTOR(frm, key_mgt_psk);
920138568Ssam	}
921138568Ssam
922138568Ssam	/* optional capabilities */
923147066Ssam	if (rsn->rsn_caps != 0 && rsn->rsn_caps != RSN_CAP_PREAUTH)
924138568Ssam		ADDSHORT(frm, rsn->rsn_caps);
925138568Ssam
926138568Ssam	/* calculate element length */
927138568Ssam	ie[1] = frm - ie - 2;
928138568Ssam	KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa),
929138609Ssam		("WPA IE too big, %u > %zu",
930138568Ssam		ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
931138568Ssam	return frm;
932138568Ssam#undef ADDSHORT
933138568Ssam#undef ADDSELECTOR
934138568Ssam#undef WPA_OUI_BYTES
935116742Ssam}
936116742Ssam
937138568Ssamstatic u_int8_t *
938138568Ssamieee80211_setup_rsn_ie(struct ieee80211com *ic, u_int8_t *ie)
939138568Ssam{
940138568Ssam#define	RSN_OUI_BYTES		0x00, 0x0f, 0xac
941138568Ssam#define	ADDSHORT(frm, v) do {			\
942138568Ssam	frm[0] = (v) & 0xff;			\
943138568Ssam	frm[1] = (v) >> 8;			\
944138568Ssam	frm += 2;				\
945138568Ssam} while (0)
946138568Ssam#define	ADDSELECTOR(frm, sel) do {		\
947138568Ssam	memcpy(frm, sel, 4);			\
948138568Ssam	frm += 4;				\
949138568Ssam} while (0)
950138568Ssam	static const u_int8_t cipher_suite[][4] = {
951138568Ssam		{ RSN_OUI_BYTES, RSN_CSE_WEP40 },	/* NB: 40-bit */
952138568Ssam		{ RSN_OUI_BYTES, RSN_CSE_TKIP },
953138568Ssam		{ RSN_OUI_BYTES, RSN_CSE_WRAP },
954138568Ssam		{ RSN_OUI_BYTES, RSN_CSE_CCMP },
955138568Ssam		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX CKIP */
956138568Ssam		{ RSN_OUI_BYTES, RSN_CSE_NULL },
957138568Ssam	};
958138568Ssam	static const u_int8_t wep104_suite[4] =
959138568Ssam		{ RSN_OUI_BYTES, RSN_CSE_WEP104 };
960138568Ssam	static const u_int8_t key_mgt_unspec[4] =
961138568Ssam		{ RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC };
962138568Ssam	static const u_int8_t key_mgt_psk[4] =
963138568Ssam		{ RSN_OUI_BYTES, RSN_ASE_8021X_PSK };
964138568Ssam	const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
965138568Ssam	u_int8_t *frm = ie;
966138568Ssam	u_int8_t *selcnt;
967138568Ssam
968138568Ssam	*frm++ = IEEE80211_ELEMID_RSN;
969138568Ssam	*frm++ = 0;				/* length filled in below */
970138568Ssam	ADDSHORT(frm, RSN_VERSION);
971138568Ssam
972138568Ssam	/* XXX filter out CKIP */
973138568Ssam
974138568Ssam	/* multicast cipher */
975138568Ssam	if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
976138568Ssam	    rsn->rsn_mcastkeylen >= 13)
977138568Ssam		ADDSELECTOR(frm, wep104_suite);
978138568Ssam	else
979138568Ssam		ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
980138568Ssam
981138568Ssam	/* unicast cipher list */
982138568Ssam	selcnt = frm;
983138568Ssam	ADDSHORT(frm, 0);			/* selector count */
984138568Ssam	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) {
985138568Ssam		selcnt[0]++;
986138568Ssam		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
987138568Ssam	}
988138568Ssam	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) {
989138568Ssam		selcnt[0]++;
990138568Ssam		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
991138568Ssam	}
992138568Ssam
993138568Ssam	/* authenticator selector list */
994138568Ssam	selcnt = frm;
995138568Ssam	ADDSHORT(frm, 0);			/* selector count */
996138568Ssam	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
997138568Ssam		selcnt[0]++;
998138568Ssam		ADDSELECTOR(frm, key_mgt_unspec);
999138568Ssam	}
1000138568Ssam	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
1001138568Ssam		selcnt[0]++;
1002138568Ssam		ADDSELECTOR(frm, key_mgt_psk);
1003138568Ssam	}
1004138568Ssam
1005138568Ssam	/* optional capabilities */
1006147066Ssam	ADDSHORT(frm, rsn->rsn_caps);
1007138568Ssam	/* XXX PMKID */
1008138568Ssam
1009138568Ssam	/* calculate element length */
1010138568Ssam	ie[1] = frm - ie - 2;
1011138568Ssam	KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa),
1012138609Ssam		("RSN IE too big, %u > %zu",
1013138568Ssam		ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
1014138568Ssam	return frm;
1015138568Ssam#undef ADDSELECTOR
1016138568Ssam#undef ADDSHORT
1017138568Ssam#undef RSN_OUI_BYTES
1018138568Ssam}
1019138568Ssam
1020119150Ssam/*
1021138568Ssam * Add a WPA/RSN element to a frame.
1022138568Ssam */
1023138568Ssamstatic u_int8_t *
1024138568Ssamieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *ic)
1025138568Ssam{
1026138568Ssam
1027138568Ssam	KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!"));
1028138568Ssam	if (ic->ic_flags & IEEE80211_F_WPA2)
1029138568Ssam		frm = ieee80211_setup_rsn_ie(ic, frm);
1030138568Ssam	if (ic->ic_flags & IEEE80211_F_WPA1)
1031138568Ssam		frm = ieee80211_setup_wpa_ie(ic, frm);
1032138568Ssam	return frm;
1033138568Ssam}
1034138568Ssam
1035138568Ssam#define	WME_OUI_BYTES		0x00, 0x50, 0xf2
1036138568Ssam/*
1037138568Ssam * Add a WME information element to a frame.
1038138568Ssam */
1039138568Ssamstatic u_int8_t *
1040138568Ssamieee80211_add_wme_info(u_int8_t *frm, struct ieee80211_wme_state *wme)
1041138568Ssam{
1042138568Ssam	static const struct ieee80211_wme_info info = {
1043138568Ssam		.wme_id		= IEEE80211_ELEMID_VENDOR,
1044138568Ssam		.wme_len	= sizeof(struct ieee80211_wme_info) - 2,
1045138568Ssam		.wme_oui	= { WME_OUI_BYTES },
1046138568Ssam		.wme_type	= WME_OUI_TYPE,
1047138568Ssam		.wme_subtype	= WME_INFO_OUI_SUBTYPE,
1048138568Ssam		.wme_version	= WME_VERSION,
1049138568Ssam		.wme_info	= 0,
1050138568Ssam	};
1051138568Ssam	memcpy(frm, &info, sizeof(info));
1052138568Ssam	return frm + sizeof(info);
1053138568Ssam}
1054138568Ssam
1055138568Ssam/*
1056138568Ssam * Add a WME parameters element to a frame.
1057138568Ssam */
1058138568Ssamstatic u_int8_t *
1059138568Ssamieee80211_add_wme_param(u_int8_t *frm, struct ieee80211_wme_state *wme)
1060138568Ssam{
1061138568Ssam#define	SM(_v, _f)	(((_v) << _f##_S) & _f)
1062138568Ssam#define	ADDSHORT(frm, v) do {			\
1063138568Ssam	frm[0] = (v) & 0xff;			\
1064138568Ssam	frm[1] = (v) >> 8;			\
1065138568Ssam	frm += 2;				\
1066138568Ssam} while (0)
1067138568Ssam	/* NB: this works 'cuz a param has an info at the front */
1068138568Ssam	static const struct ieee80211_wme_info param = {
1069138568Ssam		.wme_id		= IEEE80211_ELEMID_VENDOR,
1070138568Ssam		.wme_len	= sizeof(struct ieee80211_wme_param) - 2,
1071138568Ssam		.wme_oui	= { WME_OUI_BYTES },
1072138568Ssam		.wme_type	= WME_OUI_TYPE,
1073138568Ssam		.wme_subtype	= WME_PARAM_OUI_SUBTYPE,
1074138568Ssam		.wme_version	= WME_VERSION,
1075138568Ssam	};
1076138568Ssam	int i;
1077138568Ssam
1078138568Ssam	memcpy(frm, &param, sizeof(param));
1079138568Ssam	frm += __offsetof(struct ieee80211_wme_info, wme_info);
1080138568Ssam	*frm++ = wme->wme_bssChanParams.cap_info;	/* AC info */
1081138568Ssam	*frm++ = 0;					/* reserved field */
1082138568Ssam	for (i = 0; i < WME_NUM_AC; i++) {
1083138568Ssam		const struct wmeParams *ac =
1084138568Ssam		       &wme->wme_bssChanParams.cap_wmeParams[i];
1085138568Ssam		*frm++ = SM(i, WME_PARAM_ACI)
1086138568Ssam		       | SM(ac->wmep_acm, WME_PARAM_ACM)
1087138568Ssam		       | SM(ac->wmep_aifsn, WME_PARAM_AIFSN)
1088138568Ssam		       ;
1089138568Ssam		*frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX)
1090138568Ssam		       | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN)
1091138568Ssam		       ;
1092138568Ssam		ADDSHORT(frm, ac->wmep_txopLimit);
1093138568Ssam	}
1094138568Ssam	return frm;
1095138568Ssam#undef SM
1096138568Ssam#undef ADDSHORT
1097138568Ssam}
1098138568Ssam#undef WME_OUI_BYTES
1099138568Ssam
1100138568Ssam/*
1101148315Ssam * Send a probe request frame with the specified ssid
1102148315Ssam * and any optional information element data.
1103148315Ssam */
1104148315Ssamint
1105148315Ssamieee80211_send_probereq(struct ieee80211_node *ni,
1106148315Ssam	const u_int8_t sa[IEEE80211_ADDR_LEN],
1107148315Ssam	const u_int8_t da[IEEE80211_ADDR_LEN],
1108148315Ssam	const u_int8_t bssid[IEEE80211_ADDR_LEN],
1109148315Ssam	const u_int8_t *ssid, size_t ssidlen,
1110148315Ssam	const void *optie, size_t optielen)
1111148315Ssam{
1112148315Ssam	struct ieee80211com *ic = ni->ni_ic;
1113148315Ssam	struct ieee80211_frame *wh;
1114165569Ssam	const struct ieee80211_rateset *rs;
1115148315Ssam	struct mbuf *m;
1116148315Ssam	u_int8_t *frm;
1117148315Ssam
1118148315Ssam	/*
1119148315Ssam	 * Hold a reference on the node so it doesn't go away until after
1120148315Ssam	 * the xmit is complete all the way in the driver.  On error we
1121148315Ssam	 * will remove our reference.
1122148315Ssam	 */
1123148315Ssam	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
1124148315Ssam		"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
1125148315Ssam		__func__, __LINE__,
1126148315Ssam		ni, ether_sprintf(ni->ni_macaddr),
1127148315Ssam		ieee80211_node_refcnt(ni)+1);
1128148315Ssam	ieee80211_ref_node(ni);
1129148315Ssam
1130148315Ssam	/*
1131148315Ssam	 * prreq frame format
1132148315Ssam	 *	[tlv] ssid
1133148315Ssam	 *	[tlv] supported rates
1134148315Ssam	 *	[tlv] extended supported rates
1135148315Ssam	 *	[tlv] user-specified ie's
1136148315Ssam	 */
1137148315Ssam	m = ieee80211_getmgtframe(&frm,
1138148315Ssam		 2 + IEEE80211_NWID_LEN
1139148315Ssam	       + 2 + IEEE80211_RATE_SIZE
1140148315Ssam	       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
1141148315Ssam	       + (optie != NULL ? optielen : 0)
1142148315Ssam	);
1143148315Ssam	if (m == NULL) {
1144148315Ssam		ic->ic_stats.is_tx_nobuf++;
1145148315Ssam		ieee80211_free_node(ni);
1146148315Ssam		return ENOMEM;
1147148315Ssam	}
1148148315Ssam
1149148315Ssam	frm = ieee80211_add_ssid(frm, ssid, ssidlen);
1150165569Ssam	rs = ieee80211_get_suprates(ic, ic->ic_curchan);
1151165569Ssam	frm = ieee80211_add_rates(frm, rs);
1152165569Ssam	frm = ieee80211_add_xrates(frm, rs);
1153148315Ssam
1154148315Ssam	if (optie != NULL) {
1155148315Ssam		memcpy(frm, optie, optielen);
1156148315Ssam		frm += optielen;
1157148315Ssam	}
1158148315Ssam	m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
1159148315Ssam
1160148315Ssam	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
1161148315Ssam	if (m == NULL)
1162148315Ssam		return ENOMEM;
1163148315Ssam	KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null"));
1164148315Ssam	m->m_pkthdr.rcvif = (void *)ni;
1165148315Ssam
1166148315Ssam	wh = mtod(m, struct ieee80211_frame *);
1167148315Ssam	ieee80211_send_setup(ic, ni, wh,
1168148315Ssam		IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ,
1169148315Ssam		sa, da, bssid);
1170148315Ssam	/* XXX power management? */
1171148315Ssam
1172148315Ssam	IEEE80211_NODE_STAT(ni, tx_probereq);
1173148315Ssam	IEEE80211_NODE_STAT(ni, tx_mgmt);
1174148315Ssam
1175148315Ssam	IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
1176148315Ssam	    "[%s] send probe req on channel %u\n",
1177148315Ssam	    ether_sprintf(wh->i_addr1),
1178148936Ssam	    ieee80211_chan2ieee(ic, ic->ic_curchan));
1179148315Ssam
1180148315Ssam	IF_ENQUEUE(&ic->ic_mgtq, m);
1181148315Ssam	if_start(ic->ic_ifp);
1182148315Ssam	return 0;
1183148315Ssam}
1184148315Ssam
1185148315Ssam/*
1186155999Ssam * Calculate capability information for mgt frames.
1187155999Ssam */
1188155999Ssamstatic u_int16_t
1189155999Ssamgetcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan)
1190155999Ssam{
1191155999Ssam	u_int16_t capinfo;
1192155999Ssam
1193155999Ssam	KASSERT(ic->ic_opmode != IEEE80211_M_STA, ("station mode"));
1194155999Ssam
1195155999Ssam	if (ic->ic_opmode == IEEE80211_M_HOSTAP)
1196155999Ssam		capinfo = IEEE80211_CAPINFO_ESS;
1197155999Ssam	else if (ic->ic_opmode == IEEE80211_M_IBSS)
1198155999Ssam		capinfo = IEEE80211_CAPINFO_IBSS;
1199155999Ssam	else
1200155999Ssam		capinfo = 0;
1201155999Ssam	if (ic->ic_flags & IEEE80211_F_PRIVACY)
1202155999Ssam		capinfo |= IEEE80211_CAPINFO_PRIVACY;
1203155999Ssam	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
1204155999Ssam	    IEEE80211_IS_CHAN_2GHZ(chan))
1205155999Ssam		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
1206155999Ssam	if (ic->ic_flags & IEEE80211_F_SHSLOT)
1207155999Ssam		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
1208155999Ssam	return capinfo;
1209155999Ssam}
1210155999Ssam
1211155999Ssam/*
1212119150Ssam * Send a management frame.  The node is for the destination (or ic_bss
1213119150Ssam * when in station mode).  Nodes other than ic_bss have their reference
1214119150Ssam * count bumped to reflect our use for an indeterminant time.
1215119150Ssam */
1216116742Ssamint
1217116742Ssamieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
1218116742Ssam	int type, int arg)
1219116742Ssam{
1220121180Ssam#define	senderr(_x, _v)	do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
1221116742Ssam	struct mbuf *m;
1222116742Ssam	u_int8_t *frm;
1223116742Ssam	u_int16_t capinfo;
1224138568Ssam	int has_challenge, is_shared_key, ret, timer, status;
1225116742Ssam
1226119150Ssam	KASSERT(ni != NULL, ("null node"));
1227119150Ssam
1228119150Ssam	/*
1229119150Ssam	 * Hold a reference on the node so it doesn't go away until after
1230119150Ssam	 * the xmit is complete all the way in the driver.  On error we
1231119150Ssam	 * will remove our reference.
1232119150Ssam	 */
1233138568Ssam	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
1234140766Ssam		"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
1235138568Ssam		__func__, __LINE__,
1236140766Ssam		ni, ether_sprintf(ni->ni_macaddr),
1237140766Ssam		ieee80211_node_refcnt(ni)+1);
1238138568Ssam	ieee80211_ref_node(ni);
1239138568Ssam
1240116742Ssam	timer = 0;
1241116742Ssam	switch (type) {
1242116742Ssam	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
1243116742Ssam		/*
1244116742Ssam		 * probe response frame format
1245116742Ssam		 *	[8] time stamp
1246116742Ssam		 *	[2] beacon interval
1247116742Ssam		 *	[2] cabability information
1248116742Ssam		 *	[tlv] ssid
1249116742Ssam		 *	[tlv] supported rates
1250121178Ssam		 *	[tlv] parameter set (FH/DS)
1251116742Ssam		 *	[tlv] parameter set (IBSS)
1252138568Ssam		 *	[tlv] extended rate phy (ERP)
1253116742Ssam		 *	[tlv] extended supported rates
1254138568Ssam		 *	[tlv] WPA
1255144136Ssam		 *	[tlv] WME (optional)
1256116742Ssam		 */
1257138568Ssam		m = ieee80211_getmgtframe(&frm,
1258138568Ssam			 8
1259138568Ssam		       + sizeof(u_int16_t)
1260138568Ssam		       + sizeof(u_int16_t)
1261138568Ssam		       + 2 + IEEE80211_NWID_LEN
1262116742Ssam		       + 2 + IEEE80211_RATE_SIZE
1263138568Ssam		       + 7	/* max(7,3) */
1264116742Ssam		       + 6
1265138568Ssam		       + 3
1266138568Ssam		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
1267138568Ssam		       /* XXX !WPA1+WPA2 fits w/o a cluster */
1268138568Ssam		       + (ic->ic_flags & IEEE80211_F_WPA ?
1269138568Ssam				2*sizeof(struct ieee80211_ie_wpa) : 0)
1270144136Ssam		       + sizeof(struct ieee80211_wme_param)
1271138568Ssam		);
1272116742Ssam		if (m == NULL)
1273138568Ssam			senderr(ENOMEM, is_tx_nobuf);
1274116742Ssam
1275116742Ssam		memset(frm, 0, 8);	/* timestamp should be filled later */
1276116742Ssam		frm += 8;
1277116742Ssam		*(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval);
1278116742Ssam		frm += 2;
1279155999Ssam		capinfo = getcapinfo(ic, ic->ic_curchan);
1280116742Ssam		*(u_int16_t *)frm = htole16(capinfo);
1281116742Ssam		frm += 2;
1282116742Ssam
1283116742Ssam		frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid,
1284116742Ssam				ic->ic_bss->ni_esslen);
1285138568Ssam		frm = ieee80211_add_rates(frm, &ni->ni_rates);
1286116742Ssam
1287121178Ssam		if (ic->ic_phytype == IEEE80211_T_FH) {
1288121178Ssam                        *frm++ = IEEE80211_ELEMID_FHPARMS;
1289121178Ssam                        *frm++ = 5;
1290121178Ssam                        *frm++ = ni->ni_fhdwell & 0x00ff;
1291121178Ssam                        *frm++ = (ni->ni_fhdwell >> 8) & 0x00ff;
1292121178Ssam                        *frm++ = IEEE80211_FH_CHANSET(
1293148936Ssam			    ieee80211_chan2ieee(ic, ic->ic_curchan));
1294121178Ssam                        *frm++ = IEEE80211_FH_CHANPAT(
1295148936Ssam			    ieee80211_chan2ieee(ic, ic->ic_curchan));
1296121178Ssam                        *frm++ = ni->ni_fhindex;
1297121178Ssam		} else {
1298121178Ssam			*frm++ = IEEE80211_ELEMID_DSPARMS;
1299121178Ssam			*frm++ = 1;
1300148936Ssam			*frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan);
1301121178Ssam		}
1302121178Ssam
1303116742Ssam		if (ic->ic_opmode == IEEE80211_M_IBSS) {
1304116742Ssam			*frm++ = IEEE80211_ELEMID_IBSSPARMS;
1305116742Ssam			*frm++ = 2;
1306116742Ssam			*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
1307116742Ssam		}
1308138568Ssam		if (ic->ic_flags & IEEE80211_F_WPA)
1309138568Ssam			frm = ieee80211_add_wpa(frm, ic);
1310138568Ssam		if (ic->ic_curmode == IEEE80211_MODE_11G)
1311138568Ssam			frm = ieee80211_add_erp(frm, ic);
1312138568Ssam		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
1313144136Ssam		if (ic->ic_flags & IEEE80211_F_WME)
1314144136Ssam			frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
1315116742Ssam		m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
1316116742Ssam		break;
1317116742Ssam
1318116742Ssam	case IEEE80211_FC0_SUBTYPE_AUTH:
1319138568Ssam		status = arg >> 16;
1320138568Ssam		arg &= 0xffff;
1321138568Ssam		has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE ||
1322138568Ssam		    arg == IEEE80211_AUTH_SHARED_RESPONSE) &&
1323138568Ssam		    ni->ni_challenge != NULL);
1324138568Ssam
1325138568Ssam		/*
1326138568Ssam		 * Deduce whether we're doing open authentication or
1327138568Ssam		 * shared key authentication.  We do the latter if
1328138568Ssam		 * we're in the middle of a shared key authentication
1329138568Ssam		 * handshake or if we're initiating an authentication
1330138568Ssam		 * request and configured to use shared key.
1331138568Ssam		 */
1332138568Ssam		is_shared_key = has_challenge ||
1333138568Ssam		     arg >= IEEE80211_AUTH_SHARED_RESPONSE ||
1334138568Ssam		     (arg == IEEE80211_AUTH_SHARED_REQUEST &&
1335138568Ssam		      ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED);
1336138568Ssam
1337138568Ssam		m = ieee80211_getmgtframe(&frm,
1338138568Ssam			  3 * sizeof(u_int16_t)
1339138568Ssam			+ (has_challenge && status == IEEE80211_STATUS_SUCCESS ?
1340138568Ssam				sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0)
1341138568Ssam		);
1342116742Ssam		if (m == NULL)
1343138568Ssam			senderr(ENOMEM, is_tx_nobuf);
1344138568Ssam
1345138568Ssam		((u_int16_t *)frm)[0] =
1346138568Ssam		    (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED)
1347138568Ssam		                    : htole16(IEEE80211_AUTH_ALG_OPEN);
1348116742Ssam		((u_int16_t *)frm)[1] = htole16(arg);	/* sequence number */
1349138568Ssam		((u_int16_t *)frm)[2] = htole16(status);/* status */
1350138568Ssam
1351138568Ssam		if (has_challenge && status == IEEE80211_STATUS_SUCCESS) {
1352138568Ssam			((u_int16_t *)frm)[3] =
1353138568Ssam			    htole16((IEEE80211_CHALLENGE_LEN << 8) |
1354138568Ssam			    IEEE80211_ELEMID_CHALLENGE);
1355138568Ssam			memcpy(&((u_int16_t *)frm)[4], ni->ni_challenge,
1356138568Ssam			    IEEE80211_CHALLENGE_LEN);
1357138568Ssam			m->m_pkthdr.len = m->m_len =
1358138568Ssam				4 * sizeof(u_int16_t) + IEEE80211_CHALLENGE_LEN;
1359138568Ssam			if (arg == IEEE80211_AUTH_SHARED_RESPONSE) {
1360138568Ssam				IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
1361138568Ssam				    "[%s] request encrypt frame (%s)\n",
1362138568Ssam				    ether_sprintf(ni->ni_macaddr), __func__);
1363138568Ssam				m->m_flags |= M_LINK0; /* WEP-encrypt, please */
1364138568Ssam			}
1365138568Ssam		} else
1366138568Ssam			m->m_pkthdr.len = m->m_len = 3 * sizeof(u_int16_t);
1367138568Ssam
1368138568Ssam		/* XXX not right for shared key */
1369138568Ssam		if (status == IEEE80211_STATUS_SUCCESS)
1370138568Ssam			IEEE80211_NODE_STAT(ni, tx_auth);
1371138568Ssam		else
1372138568Ssam			IEEE80211_NODE_STAT(ni, tx_auth_fail);
1373138568Ssam
1374116742Ssam		if (ic->ic_opmode == IEEE80211_M_STA)
1375116742Ssam			timer = IEEE80211_TRANS_WAIT;
1376116742Ssam		break;
1377116742Ssam
1378116742Ssam	case IEEE80211_FC0_SUBTYPE_DEAUTH:
1379138568Ssam		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
1380138568Ssam			"[%s] send station deauthenticate (reason %d)\n",
1381138568Ssam			ether_sprintf(ni->ni_macaddr), arg);
1382138568Ssam		m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t));
1383116742Ssam		if (m == NULL)
1384138568Ssam			senderr(ENOMEM, is_tx_nobuf);
1385138568Ssam		*(u_int16_t *)frm = htole16(arg);	/* reason */
1386138568Ssam		m->m_pkthdr.len = m->m_len = sizeof(u_int16_t);
1387138568Ssam
1388138568Ssam		IEEE80211_NODE_STAT(ni, tx_deauth);
1389138568Ssam		IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg);
1390138568Ssam
1391148302Ssam		ieee80211_node_unauthorize(ni);		/* port closed */
1392116742Ssam		break;
1393116742Ssam
1394116742Ssam	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
1395116742Ssam	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
1396116742Ssam		/*
1397116742Ssam		 * asreq frame format
1398116742Ssam		 *	[2] capability information
1399116742Ssam		 *	[2] listen interval
1400116742Ssam		 *	[6*] current AP address (reassoc only)
1401116742Ssam		 *	[tlv] ssid
1402116742Ssam		 *	[tlv] supported rates
1403116742Ssam		 *	[tlv] extended supported rates
1404138568Ssam		 *	[tlv] WME
1405138568Ssam		 *	[tlv] user-specified ie's
1406116742Ssam		 */
1407138568Ssam		m = ieee80211_getmgtframe(&frm,
1408138568Ssam			 sizeof(u_int16_t)
1409116742Ssam		       + sizeof(u_int16_t)
1410116742Ssam		       + IEEE80211_ADDR_LEN
1411138568Ssam		       + 2 + IEEE80211_NWID_LEN
1412116742Ssam		       + 2 + IEEE80211_RATE_SIZE
1413138568Ssam		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
1414138568Ssam		       + sizeof(struct ieee80211_wme_info)
1415138568Ssam		       + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0)
1416138568Ssam		);
1417116742Ssam		if (m == NULL)
1418138568Ssam			senderr(ENOMEM, is_tx_nobuf);
1419116742Ssam
1420155999Ssam		KASSERT(ic->ic_opmode == IEEE80211_M_STA,
1421155999Ssam		    ("wrong mode %u", ic->ic_opmode));
1422155999Ssam		capinfo = IEEE80211_CAPINFO_ESS;
1423138568Ssam		if (ic->ic_flags & IEEE80211_F_PRIVACY)
1424116742Ssam			capinfo |= IEEE80211_CAPINFO_PRIVACY;
1425120070Ssam		/*
1426120070Ssam		 * NB: Some 11a AP's reject the request when
1427120070Ssam		 *     short premable is set.
1428120070Ssam		 */
1429120070Ssam		if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
1430148936Ssam		    IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
1431116742Ssam			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
1432138568Ssam		if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) &&
1433138568Ssam		    (ic->ic_caps & IEEE80211_C_SHSLOT))
1434116742Ssam			capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
1435116742Ssam		*(u_int16_t *)frm = htole16(capinfo);
1436116742Ssam		frm += 2;
1437116742Ssam
1438116742Ssam		*(u_int16_t *)frm = htole16(ic->ic_lintval);
1439116742Ssam		frm += 2;
1440116742Ssam
1441116742Ssam		if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
1442116742Ssam			IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid);
1443116742Ssam			frm += IEEE80211_ADDR_LEN;
1444116742Ssam		}
1445116742Ssam
1446116742Ssam		frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
1447116742Ssam		frm = ieee80211_add_rates(frm, &ni->ni_rates);
1448116742Ssam		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
1449138568Ssam		if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
1450138568Ssam			frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
1451138568Ssam		if (ic->ic_opt_ie != NULL) {
1452138568Ssam			memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len);
1453138568Ssam			frm += ic->ic_opt_ie_len;
1454138568Ssam		}
1455116742Ssam		m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
1456116742Ssam
1457116742Ssam		timer = IEEE80211_TRANS_WAIT;
1458116742Ssam		break;
1459116742Ssam
1460116742Ssam	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
1461116742Ssam	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
1462116742Ssam		/*
1463116742Ssam		 * asreq frame format
1464116742Ssam		 *	[2] capability information
1465116742Ssam		 *	[2] status
1466116742Ssam		 *	[2] association ID
1467116742Ssam		 *	[tlv] supported rates
1468116742Ssam		 *	[tlv] extended supported rates
1469138568Ssam		 *	[tlv] WME (if enabled and STA enabled)
1470116742Ssam		 */
1471138568Ssam		m = ieee80211_getmgtframe(&frm,
1472138568Ssam			 sizeof(u_int16_t)
1473116742Ssam		       + sizeof(u_int16_t)
1474116742Ssam		       + sizeof(u_int16_t)
1475116742Ssam		       + 2 + IEEE80211_RATE_SIZE
1476138568Ssam		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
1477138568Ssam		       + sizeof(struct ieee80211_wme_param)
1478138568Ssam		);
1479116742Ssam		if (m == NULL)
1480138568Ssam			senderr(ENOMEM, is_tx_nobuf);
1481116742Ssam
1482155999Ssam		capinfo = getcapinfo(ic, ic->ic_curchan);
1483116742Ssam		*(u_int16_t *)frm = htole16(capinfo);
1484116742Ssam		frm += 2;
1485116742Ssam
1486116742Ssam		*(u_int16_t *)frm = htole16(arg);	/* status */
1487116742Ssam		frm += 2;
1488116742Ssam
1489138568Ssam		if (arg == IEEE80211_STATUS_SUCCESS) {
1490116742Ssam			*(u_int16_t *)frm = htole16(ni->ni_associd);
1491138568Ssam			IEEE80211_NODE_STAT(ni, tx_assoc);
1492138568Ssam		} else
1493138568Ssam			IEEE80211_NODE_STAT(ni, tx_assoc_fail);
1494116742Ssam		frm += 2;
1495116742Ssam
1496119150Ssam		frm = ieee80211_add_rates(frm, &ni->ni_rates);
1497119150Ssam		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
1498138568Ssam		if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
1499138568Ssam			frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
1500116742Ssam		m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
1501116742Ssam		break;
1502116742Ssam
1503116742Ssam	case IEEE80211_FC0_SUBTYPE_DISASSOC:
1504138568Ssam		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
1505138568Ssam			"[%s] send station disassociate (reason %d)\n",
1506138568Ssam			ether_sprintf(ni->ni_macaddr), arg);
1507138568Ssam		m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t));
1508116742Ssam		if (m == NULL)
1509138568Ssam			senderr(ENOMEM, is_tx_nobuf);
1510138568Ssam		*(u_int16_t *)frm = htole16(arg);	/* reason */
1511138568Ssam		m->m_pkthdr.len = m->m_len = sizeof(u_int16_t);
1512138568Ssam
1513138568Ssam		IEEE80211_NODE_STAT(ni, tx_disassoc);
1514138568Ssam		IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg);
1515116742Ssam		break;
1516116742Ssam
1517116742Ssam	default:
1518138568Ssam		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
1519138568Ssam			"[%s] invalid mgmt frame type %u\n",
1520138568Ssam			ether_sprintf(ni->ni_macaddr), type);
1521121180Ssam		senderr(EINVAL, is_tx_unknownmgt);
1522119150Ssam		/* NOTREACHED */
1523116742Ssam	}
1524155460Ssam	ret = ieee80211_mgmt_output(ic, ni, m, type, timer);
1525155460Ssam	if (ret != 0) {
1526119150Ssambad:
1527138568Ssam		ieee80211_free_node(ni);
1528119150Ssam	}
1529116742Ssam	return ret;
1530119150Ssam#undef senderr
1531116742Ssam}
1532138568Ssam
1533138568Ssam/*
1534138568Ssam * Allocate a beacon frame and fillin the appropriate bits.
1535138568Ssam */
1536138568Ssamstruct mbuf *
1537138568Ssamieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni,
1538138568Ssam	struct ieee80211_beacon_offsets *bo)
1539138568Ssam{
1540138568Ssam	struct ifnet *ifp = ic->ic_ifp;
1541138568Ssam	struct ieee80211_frame *wh;
1542138568Ssam	struct mbuf *m;
1543138568Ssam	int pktlen;
1544138568Ssam	u_int8_t *frm, *efrm;
1545138568Ssam	u_int16_t capinfo;
1546138568Ssam	struct ieee80211_rateset *rs;
1547138568Ssam
1548138568Ssam	/*
1549138568Ssam	 * beacon frame format
1550138568Ssam	 *	[8] time stamp
1551138568Ssam	 *	[2] beacon interval
1552138568Ssam	 *	[2] cabability information
1553138568Ssam	 *	[tlv] ssid
1554138568Ssam	 *	[tlv] supported rates
1555138568Ssam	 *	[3] parameter set (DS)
1556138568Ssam	 *	[tlv] parameter set (IBSS/TIM)
1557138568Ssam	 *	[tlv] extended rate phy (ERP)
1558138568Ssam	 *	[tlv] extended supported rates
1559138568Ssam	 *	[tlv] WME parameters
1560138568Ssam	 *	[tlv] WPA/RSN parameters
1561138568Ssam	 * XXX Vendor-specific OIDs (e.g. Atheros)
1562138568Ssam	 * NB: we allocate the max space required for the TIM bitmap.
1563138568Ssam	 */
1564138568Ssam	rs = &ni->ni_rates;
1565138568Ssam	pktlen =   8					/* time stamp */
1566138568Ssam		 + sizeof(u_int16_t)			/* beacon interval */
1567138568Ssam		 + sizeof(u_int16_t)			/* capabilities */
1568138568Ssam		 + 2 + ni->ni_esslen			/* ssid */
1569138568Ssam	         + 2 + IEEE80211_RATE_SIZE		/* supported rates */
1570138568Ssam	         + 2 + 1				/* DS parameters */
1571138568Ssam		 + 2 + 4 + ic->ic_tim_len		/* DTIM/IBSSPARMS */
1572138568Ssam		 + 2 + 1				/* ERP */
1573138568Ssam	         + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
1574138568Ssam		 + (ic->ic_caps & IEEE80211_C_WME ?	/* WME */
1575138568Ssam			sizeof(struct ieee80211_wme_param) : 0)
1576138568Ssam		 + (ic->ic_caps & IEEE80211_C_WPA ?	/* WPA 1+2 */
1577138568Ssam			2*sizeof(struct ieee80211_ie_wpa) : 0)
1578138568Ssam		 ;
1579138568Ssam	m = ieee80211_getmgtframe(&frm, pktlen);
1580138568Ssam	if (m == NULL) {
1581138568Ssam		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
1582138568Ssam			"%s: cannot get buf; size %u\n", __func__, pktlen);
1583138568Ssam		ic->ic_stats.is_tx_nobuf++;
1584138568Ssam		return NULL;
1585138568Ssam	}
1586138568Ssam
1587138568Ssam	memset(frm, 0, 8);	/* XXX timestamp is set by hardware/driver */
1588138568Ssam	frm += 8;
1589138568Ssam	*(u_int16_t *)frm = htole16(ni->ni_intval);
1590138568Ssam	frm += 2;
1591155999Ssam	capinfo = getcapinfo(ic, ni->ni_chan);
1592138568Ssam	bo->bo_caps = (u_int16_t *)frm;
1593138568Ssam	*(u_int16_t *)frm = htole16(capinfo);
1594138568Ssam	frm += 2;
1595138568Ssam	*frm++ = IEEE80211_ELEMID_SSID;
1596138568Ssam	if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) {
1597138568Ssam		*frm++ = ni->ni_esslen;
1598138568Ssam		memcpy(frm, ni->ni_essid, ni->ni_esslen);
1599138568Ssam		frm += ni->ni_esslen;
1600138568Ssam	} else
1601138568Ssam		*frm++ = 0;
1602138568Ssam	frm = ieee80211_add_rates(frm, rs);
1603138568Ssam	if (ic->ic_curmode != IEEE80211_MODE_FH) {
1604138568Ssam		*frm++ = IEEE80211_ELEMID_DSPARMS;
1605138568Ssam		*frm++ = 1;
1606138568Ssam		*frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
1607138568Ssam	}
1608138568Ssam	bo->bo_tim = frm;
1609138568Ssam	if (ic->ic_opmode == IEEE80211_M_IBSS) {
1610138568Ssam		*frm++ = IEEE80211_ELEMID_IBSSPARMS;
1611138568Ssam		*frm++ = 2;
1612138568Ssam		*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
1613138568Ssam		bo->bo_tim_len = 0;
1614155999Ssam	} else if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
1615138568Ssam		struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm;
1616138568Ssam
1617138568Ssam		tie->tim_ie = IEEE80211_ELEMID_TIM;
1618138568Ssam		tie->tim_len = 4;	/* length */
1619138568Ssam		tie->tim_count = 0;	/* DTIM count */
1620138568Ssam		tie->tim_period = ic->ic_dtim_period;	/* DTIM period */
1621138568Ssam		tie->tim_bitctl = 0;	/* bitmap control */
1622138568Ssam		tie->tim_bitmap[0] = 0;	/* Partial Virtual Bitmap */
1623138568Ssam		frm += sizeof(struct ieee80211_tim_ie);
1624138568Ssam		bo->bo_tim_len = 1;
1625138568Ssam	}
1626138568Ssam	bo->bo_trailer = frm;
1627138568Ssam	if (ic->ic_flags & IEEE80211_F_WME) {
1628138568Ssam		bo->bo_wme = frm;
1629138568Ssam		frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
1630140764Ssam		ic->ic_flags &= ~IEEE80211_F_WMEUPDATE;
1631138568Ssam	}
1632138568Ssam	if (ic->ic_flags & IEEE80211_F_WPA)
1633138568Ssam		frm = ieee80211_add_wpa(frm, ic);
1634153973Ssam	if (ic->ic_curmode == IEEE80211_MODE_11G) {
1635153973Ssam		bo->bo_erp = frm;
1636138568Ssam		frm = ieee80211_add_erp(frm, ic);
1637153973Ssam	}
1638138568Ssam	efrm = ieee80211_add_xrates(frm, rs);
1639138568Ssam	bo->bo_trailer_len = efrm - bo->bo_trailer;
1640138568Ssam	m->m_pkthdr.len = m->m_len = efrm - mtod(m, u_int8_t *);
1641138568Ssam
1642138568Ssam	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
1643138568Ssam	KASSERT(m != NULL, ("no space for 802.11 header?"));
1644138568Ssam	wh = mtod(m, struct ieee80211_frame *);
1645138568Ssam	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
1646138568Ssam	    IEEE80211_FC0_SUBTYPE_BEACON;
1647138568Ssam	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
1648138568Ssam	*(u_int16_t *)wh->i_dur = 0;
1649138568Ssam	IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
1650138568Ssam	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
1651138568Ssam	IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
1652138568Ssam	*(u_int16_t *)wh->i_seq = 0;
1653138568Ssam
1654138568Ssam	return m;
1655138568Ssam}
1656138568Ssam
1657138568Ssam/*
1658138568Ssam * Update the dynamic parts of a beacon frame based on the current state.
1659138568Ssam */
1660138568Ssamint
1661138568Ssamieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni,
1662138568Ssam	struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast)
1663138568Ssam{
1664138568Ssam	int len_changed = 0;
1665138568Ssam	u_int16_t capinfo;
1666138568Ssam
1667138568Ssam	IEEE80211_BEACON_LOCK(ic);
1668138568Ssam	/* XXX faster to recalculate entirely or just changes? */
1669155999Ssam	capinfo = getcapinfo(ic, ni->ni_chan);
1670138568Ssam	*bo->bo_caps = htole16(capinfo);
1671138568Ssam
1672138568Ssam	if (ic->ic_flags & IEEE80211_F_WME) {
1673138568Ssam		struct ieee80211_wme_state *wme = &ic->ic_wme;
1674138568Ssam
1675138568Ssam		/*
1676138568Ssam		 * Check for agressive mode change.  When there is
1677138568Ssam		 * significant high priority traffic in the BSS
1678138568Ssam		 * throttle back BE traffic by using conservative
1679138568Ssam		 * parameters.  Otherwise BE uses agressive params
1680138568Ssam		 * to optimize performance of legacy/non-QoS traffic.
1681138568Ssam		 */
1682138568Ssam		if (wme->wme_flags & WME_F_AGGRMODE) {
1683138568Ssam			if (wme->wme_hipri_traffic >
1684138568Ssam			    wme->wme_hipri_switch_thresh) {
1685138568Ssam				IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
1686138568Ssam				    "%s: traffic %u, disable aggressive mode\n",
1687138568Ssam				    __func__, wme->wme_hipri_traffic);
1688138568Ssam				wme->wme_flags &= ~WME_F_AGGRMODE;
1689138568Ssam				ieee80211_wme_updateparams_locked(ic);
1690138568Ssam				wme->wme_hipri_traffic =
1691138568Ssam					wme->wme_hipri_switch_hysteresis;
1692138568Ssam			} else
1693138568Ssam				wme->wme_hipri_traffic = 0;
1694138568Ssam		} else {
1695138568Ssam			if (wme->wme_hipri_traffic <=
1696138568Ssam			    wme->wme_hipri_switch_thresh) {
1697138568Ssam				IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
1698138568Ssam				    "%s: traffic %u, enable aggressive mode\n",
1699138568Ssam				    __func__, wme->wme_hipri_traffic);
1700138568Ssam				wme->wme_flags |= WME_F_AGGRMODE;
1701138568Ssam				ieee80211_wme_updateparams_locked(ic);
1702138568Ssam				wme->wme_hipri_traffic = 0;
1703138568Ssam			} else
1704138568Ssam				wme->wme_hipri_traffic =
1705138568Ssam					wme->wme_hipri_switch_hysteresis;
1706138568Ssam		}
1707138568Ssam		if (ic->ic_flags & IEEE80211_F_WMEUPDATE) {
1708138568Ssam			(void) ieee80211_add_wme_param(bo->bo_wme, wme);
1709138568Ssam			ic->ic_flags &= ~IEEE80211_F_WMEUPDATE;
1710138568Ssam		}
1711138568Ssam	}
1712138568Ssam
1713138568Ssam	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {	/* NB: no IBSS support*/
1714138568Ssam		struct ieee80211_tim_ie *tie =
1715138568Ssam			(struct ieee80211_tim_ie *) bo->bo_tim;
1716138568Ssam		if (ic->ic_flags & IEEE80211_F_TIMUPDATE) {
1717138568Ssam			u_int timlen, timoff, i;
1718138568Ssam			/*
1719138568Ssam			 * ATIM/DTIM needs updating.  If it fits in the
1720138568Ssam			 * current space allocated then just copy in the
1721138568Ssam			 * new bits.  Otherwise we need to move any trailing
1722138568Ssam			 * data to make room.  Note that we know there is
1723138568Ssam			 * contiguous space because ieee80211_beacon_allocate
1724138568Ssam			 * insures there is space in the mbuf to write a
1725138568Ssam			 * maximal-size virtual bitmap (based on ic_max_aid).
1726138568Ssam			 */
1727138568Ssam			/*
1728138568Ssam			 * Calculate the bitmap size and offset, copy any
1729138568Ssam			 * trailer out of the way, and then copy in the
1730138568Ssam			 * new bitmap and update the information element.
1731138568Ssam			 * Note that the tim bitmap must contain at least
1732138568Ssam			 * one byte and any offset must be even.
1733138568Ssam			 */
1734138568Ssam			if (ic->ic_ps_pending != 0) {
1735138568Ssam				timoff = 128;		/* impossibly large */
1736138568Ssam				for (i = 0; i < ic->ic_tim_len; i++)
1737138568Ssam					if (ic->ic_tim_bitmap[i]) {
1738138568Ssam						timoff = i &~ 1;
1739138568Ssam						break;
1740138568Ssam					}
1741138568Ssam				KASSERT(timoff != 128, ("tim bitmap empty!"));
1742138568Ssam				for (i = ic->ic_tim_len-1; i >= timoff; i--)
1743138568Ssam					if (ic->ic_tim_bitmap[i])
1744138568Ssam						break;
1745138568Ssam				timlen = 1 + (i - timoff);
1746138568Ssam			} else {
1747138568Ssam				timoff = 0;
1748138568Ssam				timlen = 1;
1749138568Ssam			}
1750138568Ssam			if (timlen != bo->bo_tim_len) {
1751138568Ssam				/* copy up/down trailer */
1752153973Ssam				int adjust = tie->tim_bitmap+timlen
1753153973Ssam					   - bo->bo_trailer;
1754153973Ssam				ovbcopy(bo->bo_trailer, bo->bo_trailer+adjust,
1755138568Ssam					bo->bo_trailer_len);
1756153973Ssam				bo->bo_trailer += adjust;
1757153973Ssam				bo->bo_wme += adjust;
1758153973Ssam				bo->bo_erp += adjust;
1759138568Ssam				bo->bo_tim_len = timlen;
1760138568Ssam
1761138568Ssam				/* update information element */
1762138568Ssam				tie->tim_len = 3 + timlen;
1763138568Ssam				tie->tim_bitctl = timoff;
1764138568Ssam				len_changed = 1;
1765138568Ssam			}
1766138568Ssam			memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff,
1767138568Ssam				bo->bo_tim_len);
1768138568Ssam
1769138568Ssam			ic->ic_flags &= ~IEEE80211_F_TIMUPDATE;
1770138568Ssam
1771138568Ssam			IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
1772138568Ssam				"%s: TIM updated, pending %u, off %u, len %u\n",
1773138568Ssam				__func__, ic->ic_ps_pending, timoff, timlen);
1774138568Ssam		}
1775138568Ssam		/* count down DTIM period */
1776138568Ssam		if (tie->tim_count == 0)
1777138568Ssam			tie->tim_count = tie->tim_period - 1;
1778138568Ssam		else
1779138568Ssam			tie->tim_count--;
1780138568Ssam		/* update state for buffered multicast frames on DTIM */
1781153139Ssam		if (mcast && tie->tim_count == 0)
1782138568Ssam			tie->tim_bitctl |= 1;
1783138568Ssam		else
1784138568Ssam			tie->tim_bitctl &= ~1;
1785153973Ssam		if (ic->ic_flags_ext & IEEE80211_FEXT_ERPUPDATE) {
1786153973Ssam			/*
1787153973Ssam			 * ERP element needs updating.
1788153973Ssam			 */
1789153973Ssam			(void) ieee80211_add_erp(bo->bo_erp, ic);
1790153973Ssam			ic->ic_flags_ext &= ~IEEE80211_FEXT_ERPUPDATE;
1791153973Ssam		}
1792138568Ssam	}
1793138568Ssam	IEEE80211_BEACON_UNLOCK(ic);
1794138568Ssam
1795138568Ssam	return len_changed;
1796138568Ssam}
1797138568Ssam
1798138568Ssam/*
1799138568Ssam * Save an outbound packet for a node in power-save sleep state.
1800138568Ssam * The new packet is placed on the node's saved queue, and the TIM
1801138568Ssam * is changed, if necessary.
1802138568Ssam */
1803138568Ssamvoid
1804138568Ssamieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni,
1805138568Ssam		  struct mbuf *m)
1806138568Ssam{
1807138568Ssam	int qlen, age;
1808138568Ssam
1809138568Ssam	IEEE80211_NODE_SAVEQ_LOCK(ni);
1810138568Ssam	if (_IF_QFULL(&ni->ni_savedq)) {
1811138568Ssam		_IF_DROP(&ni->ni_savedq);
1812138568Ssam		IEEE80211_NODE_SAVEQ_UNLOCK(ni);
1813138568Ssam		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
1814138568Ssam			"[%s] pwr save q overflow, drops %d (size %d)\n",
1815138568Ssam			ether_sprintf(ni->ni_macaddr),
1816138568Ssam			ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE);
1817138568Ssam#ifdef IEEE80211_DEBUG
1818138568Ssam		if (ieee80211_msg_dumppkts(ic))
1819138568Ssam			ieee80211_dump_pkt(mtod(m, caddr_t), m->m_len, -1, -1);
1820138568Ssam#endif
1821138568Ssam		m_freem(m);
1822138568Ssam		return;
1823138568Ssam	}
1824138568Ssam	/*
1825138568Ssam	 * Tag the frame with it's expiry time and insert
1826138568Ssam	 * it in the queue.  The aging interval is 4 times
1827138568Ssam	 * the listen interval specified by the station.
1828138568Ssam	 * Frames that sit around too long are reclaimed
1829138568Ssam	 * using this information.
1830138568Ssam	 */
1831138568Ssam	/* XXX handle overflow? */
1832148843Ssam	age = ((ni->ni_intval * ic->ic_bintval) << 2) / 1024; /* TU -> secs */
1833138568Ssam	_IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age);
1834138568Ssam	IEEE80211_NODE_SAVEQ_UNLOCK(ni);
1835138568Ssam
1836138568Ssam	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
1837148843Ssam		"[%s] save frame with age %d, %u now queued\n",
1838148843Ssam		ether_sprintf(ni->ni_macaddr), age, qlen);
1839138568Ssam
1840138568Ssam	if (qlen == 1)
1841148304Ssam		ic->ic_set_tim(ni, 1);
1842138568Ssam}
1843