ieee80211_output.c revision 116742
1109998Smarkm/*-
2109998Smarkm * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
3109998Smarkm * All rights reserved.
4109998Smarkm *
5109998Smarkm * Redistribution and use in source and binary forms, with or without
6109998Smarkm * modification, are permitted provided that the following conditions
7109998Smarkm * are met:
8109998Smarkm * 1. Redistributions of source code must retain the above copyright
9109998Smarkm *    notice, this list of conditions and the following disclaimer,
10280297Sjkim *    without modification.
11109998Smarkm * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12109998Smarkm *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13109998Smarkm *    redistribution must be conditioned upon including a substantially
14109998Smarkm *    similar Disclaimer requirement for further binary redistribution.
15109998Smarkm * 3. Neither the names of the above-listed copyright holders nor the names
16109998Smarkm *    of any contributors may be used to endorse or promote products derived
17109998Smarkm *    from this software without specific prior written permission.
18109998Smarkm *
19109998Smarkm * Alternatively, this software may be distributed under the terms of the
20109998Smarkm * GNU General Public License ("GPL") version 2 as published by the Free
21109998Smarkm * Software Foundation.
22109998Smarkm *
23109998Smarkm * NO WARRANTY
24109998Smarkm * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25109998Smarkm * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26109998Smarkm * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
27109998Smarkm * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
28109998Smarkm * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
29109998Smarkm * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30109998Smarkm * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31109998Smarkm * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
32109998Smarkm * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33109998Smarkm * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
34109998Smarkm * THE POSSIBILITY OF SUCH DAMAGES.
35109998Smarkm */
36109998Smarkm
37109998Smarkm#include <sys/cdefs.h>
38109998Smarkm__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_output.c 116742 2003-06-23 16:55:01Z sam $");
39109998Smarkm
40109998Smarkm#include "opt_inet.h"
41109998Smarkm
42109998Smarkm#include <sys/param.h>
43109998Smarkm#include <sys/systm.h>
44109998Smarkm#include <sys/mbuf.h>
45109998Smarkm#include <sys/malloc.h>
46109998Smarkm#include <sys/kernel.h>
47109998Smarkm#include <sys/socket.h>
48109998Smarkm#include <sys/sockio.h>
49109998Smarkm#include <sys/endian.h>
50109998Smarkm#include <sys/errno.h>
51109998Smarkm#include <sys/bus.h>
52109998Smarkm#include <sys/proc.h>
53109998Smarkm#include <sys/sysctl.h>
54109998Smarkm
55280297Sjkim#include <machine/atomic.h>
56280297Sjkim
57280297Sjkim#include <net/if.h>
58280297Sjkim#include <net/if_dl.h>
59280297Sjkim#include <net/if_media.h>
60109998Smarkm#include <net/if_arp.h>
61109998Smarkm#include <net/ethernet.h>
62109998Smarkm#include <net/if_llc.h>
63109998Smarkm
64280297Sjkim#include <net80211/ieee80211_var.h>
65109998Smarkm
66280297Sjkim#include <net/bpf.h>
67280297Sjkim
68280297Sjkim#ifdef INET
69280297Sjkim#include <netinet/in.h>
70280297Sjkim#include <netinet/if_ether.h>
71280297Sjkim#endif
72280297Sjkim
73109998Smarkmint
74109998Smarkmieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni,
75280297Sjkim    struct mbuf *m, int type)
76280297Sjkim{
77109998Smarkm	struct ieee80211com *ic = (void *)ifp;
78280297Sjkim	struct ieee80211_frame *wh;
79280297Sjkim
80109998Smarkm	/* XXX this probably shouldn't be permitted */
81109998Smarkm	KASSERT(ni != NULL, ("%s: null node", __func__));
82109998Smarkm	ni->ni_inact = 0;
83109998Smarkm
84109998Smarkm	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
85109998Smarkm	if (m == NULL)
86280297Sjkim		return ENOMEM;
87109998Smarkm	wh = mtod(m, struct ieee80211_frame *);
88109998Smarkm	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type;
89280297Sjkim	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
90280297Sjkim	*(u_int16_t *)wh->i_dur = 0;
91280297Sjkim	*(u_int16_t *)wh->i_seq =
92109998Smarkm	    htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
93109998Smarkm	ni->ni_txseq++;
94280297Sjkim	IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
95280297Sjkim	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
96280297Sjkim	IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
97280297Sjkim
98280297Sjkim	if (ifp->if_flags & IFF_DEBUG) {
99109998Smarkm		/* avoid to print too many frames */
100280297Sjkim		if (ic->ic_opmode == IEEE80211_M_IBSS ||
101280297Sjkim#ifdef IEEE80211_DEBUG
102280297Sjkim		    ieee80211_debug > 1 ||
103280297Sjkim#endif
104280297Sjkim		    (type & IEEE80211_FC0_SUBTYPE_MASK) !=
105280297Sjkim		    IEEE80211_FC0_SUBTYPE_PROBE_RESP)
106280297Sjkim			if_printf(ifp, "sending %s to %s on channel %u\n",
107280297Sjkim			    ieee80211_mgt_subtype_name[
108280297Sjkim			    (type & IEEE80211_FC0_SUBTYPE_MASK)
109280297Sjkim			    >> IEEE80211_FC0_SUBTYPE_SHIFT],
110280297Sjkim			    ether_sprintf(ni->ni_macaddr),
111280297Sjkim			    ieee80211_chan2ieee(ic, ni->ni_chan));
112280297Sjkim	}
113109998Smarkm	IF_ENQUEUE(&ic->ic_mgtq, m);
114280297Sjkim	ifp->if_timer = 1;
115109998Smarkm	(*ifp->if_start)(ifp);
116109998Smarkm	return 0;
117280297Sjkim}
118109998Smarkm
119109998Smarkmstruct mbuf *
120280297Sjkimieee80211_encap(struct ifnet *ifp, struct mbuf *m)
121280297Sjkim{
122280297Sjkim	struct ieee80211com *ic = (void *)ifp;
123109998Smarkm	struct ether_header eh;
124109998Smarkm	struct ieee80211_frame *wh;
125109998Smarkm	struct llc *llc;
126109998Smarkm	struct ieee80211_node *ni;
127280297Sjkim
128109998Smarkm	if (m->m_len < sizeof(struct ether_header)) {
129280297Sjkim		m = m_pullup(m, sizeof(struct ether_header));
130280297Sjkim		if (m == NULL)
131280297Sjkim			return NULL;
132280297Sjkim	}
133280297Sjkim	memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
134109998Smarkm
135280297Sjkim	ni = ieee80211_find_node(ic, eh.ether_dhost);
136306195Sjkim	if (ni == NULL)			/*ic_opmode?? XXX*/
137306195Sjkim		ni = ieee80211_ref_node(ic->ic_bss);
138306195Sjkim	ni->ni_inact = 0;
139306195Sjkim
140109998Smarkm	m_adj(m, sizeof(struct ether_header) - sizeof(struct llc));
141109998Smarkm	llc = mtod(m, struct llc *);
142109998Smarkm	llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
143280297Sjkim	llc->llc_control = LLC_UI;
144109998Smarkm	llc->llc_snap.org_code[0] = 0;
145280297Sjkim	llc->llc_snap.org_code[1] = 0;
146280297Sjkim	llc->llc_snap.org_code[2] = 0;
147280297Sjkim	llc->llc_snap.ether_type = eh.ether_type;
148280297Sjkim	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
149280297Sjkim	if (m == NULL) {
150280297Sjkim		ieee80211_unref_node(&ni);
151280297Sjkim		return NULL;
152109998Smarkm	}
153280297Sjkim	wh = mtod(m, struct ieee80211_frame *);
154109998Smarkm	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
155280297Sjkim	*(u_int16_t *)wh->i_dur = 0;
156280297Sjkim	*(u_int16_t *)wh->i_seq =
157280297Sjkim	    htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
158109998Smarkm	ni->ni_txseq++;
159109998Smarkm	switch (ic->ic_opmode) {
160109998Smarkm	case IEEE80211_M_STA:
161280297Sjkim		wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
162280297Sjkim		IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid);
163109998Smarkm		IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
164109998Smarkm		IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
165109998Smarkm		break;
166109998Smarkm	case IEEE80211_M_IBSS:
167280297Sjkim	case IEEE80211_M_AHDEMO:
168280297Sjkim		wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
169280297Sjkim		IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
170280297Sjkim		IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
171280297Sjkim		IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
172280297Sjkim		break;
173280297Sjkim	case IEEE80211_M_HOSTAP:
174280297Sjkim		wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
175280297Sjkim		IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
176280297Sjkim		IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid);
177280297Sjkim		IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
178109998Smarkm		break;
179280297Sjkim	}
180280297Sjkim	ieee80211_unref_node(&ni);
181109998Smarkm	return m;
182280297Sjkim}
183280297Sjkim
184280297Sjkim/*
185280297Sjkim * Add a supported rates element id to a frame.
186280297Sjkim */
187109998Smarkmu_int8_t *
188280297Sjkimieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs)
189280297Sjkim{
190109998Smarkm	int nrates;
191280297Sjkim
192280297Sjkim	*frm++ = IEEE80211_ELEMID_RATES;
193306195Sjkim	nrates = rs->rs_nrates;
194306195Sjkim	if (nrates > IEEE80211_RATE_SIZE)
195306195Sjkim		nrates = IEEE80211_RATE_SIZE;
196306195Sjkim	*frm++ = nrates;
197306195Sjkim	memcpy(frm, rs->rs_rates, nrates);
198280297Sjkim	return frm + nrates;
199280297Sjkim}
200280297Sjkim
201109998Smarkm/*
202280297Sjkim * Add an extended supported rates element id to a frame.
203280297Sjkim */
204280297Sjkimu_int8_t *
205280297Sjkimieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs)
206109998Smarkm{
207280297Sjkim	/*
208109998Smarkm	 * Add an extended supported rates element if operating in 11g mode.
209280297Sjkim	 */
210280297Sjkim	if (rs->rs_nrates > IEEE80211_RATE_SIZE) {
211280297Sjkim		int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE;
212280297Sjkim		*frm++ = IEEE80211_ELEMID_XRATES;
213280297Sjkim		*frm++ = nrates;
214280297Sjkim		memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates);
215109998Smarkm		frm += nrates;
216280297Sjkim	}
217280297Sjkim	return frm;
218280297Sjkim}
219280297Sjkim
220280297Sjkim/*
221280297Sjkim * Add an ssid elemet to a frame.
222280297Sjkim */
223280297Sjkimstatic u_int8_t *
224280297Sjkimieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len)
225280297Sjkim{
226280297Sjkim	*frm++ = IEEE80211_ELEMID_SSID;
227280297Sjkim	*frm++ = len;
228280297Sjkim	memcpy(frm, ssid, len);
229280297Sjkim	return frm + len;
230280297Sjkim}
231280297Sjkim
232109998Smarkmstatic struct mbuf *
233280297Sjkimieee80211_getmbuf(int flags, int type, u_int pktlen)
234109998Smarkm{
235109998Smarkm	struct mbuf *m;
236280297Sjkim
237109998Smarkm	if (pktlen > MHLEN)
238109998Smarkm		MGETHDR(m, flags, type);
239280297Sjkim	else
240280297Sjkim		m = m_getcl(flags, type, M_PKTHDR);
241280297Sjkim	return m;
242280297Sjkim}
243280297Sjkim
244109998Smarkmint
245280297Sjkimieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
246109998Smarkm	int type, int arg)
247109998Smarkm{
248280297Sjkim	struct ifnet *ifp = &ic->ic_if;
249280297Sjkim	struct mbuf *m;
250280297Sjkim	u_int8_t *frm;
251109998Smarkm	enum ieee80211_phymode mode;
252109998Smarkm	u_int16_t capinfo;
253280297Sjkim	int ret, timer;
254280297Sjkim
255280297Sjkim	timer = 0;
256280297Sjkim	switch (type) {
257280297Sjkim	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
258109998Smarkm		/*
259280297Sjkim		 * prreq frame format
260109998Smarkm		 *	[tlv] ssid
261280297Sjkim		 *	[tlv] supported rates
262109998Smarkm		 *	[tlv] extended supported rates
263109998Smarkm		 */
264109998Smarkm		m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
265109998Smarkm			 2 + ic->ic_des_esslen
266109998Smarkm		       + 2 + IEEE80211_RATE_SIZE
267109998Smarkm		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
268109998Smarkm		if (m == NULL)
269109998Smarkm			return ENOMEM;
270280297Sjkim		m->m_data += sizeof(struct ieee80211_frame);
271109998Smarkm		frm = mtod(m, u_int8_t *);
272109998Smarkm		frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen);
273280297Sjkim		mode = ieee80211_chan2mode(ic, ni->ni_chan);
274280297Sjkim		frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
275280297Sjkim		frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
276109998Smarkm		m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
277280297Sjkim
278109998Smarkm		timer = IEEE80211_TRANS_WAIT;
279109998Smarkm		break;
280109998Smarkm
281109998Smarkm	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
282280297Sjkim		/*
283109998Smarkm		 * probe response frame format
284280297Sjkim		 *	[8] time stamp
285280297Sjkim		 *	[2] beacon interval
286280297Sjkim		 *	[2] cabability information
287280297Sjkim		 *	[tlv] ssid
288109998Smarkm		 *	[tlv] supported rates
289280297Sjkim		 *	[tlv] parameter set (IBSS)
290280297Sjkim		 *	[tlv] extended supported rates
291109998Smarkm		 */
292280297Sjkim		m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
293109998Smarkm			 8 + 2 + 2 + 2
294280297Sjkim		       + 2 + ni->ni_esslen
295280297Sjkim		       + 2 + IEEE80211_RATE_SIZE
296280297Sjkim		       + 6
297280297Sjkim		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
298109998Smarkm		if (m == NULL)
299280297Sjkim			return ENOMEM;
300109998Smarkm		m->m_data += sizeof(struct ieee80211_frame);
301109998Smarkm		frm = mtod(m, u_int8_t *);
302280297Sjkim
303280297Sjkim		memset(frm, 0, 8);	/* timestamp should be filled later */
304280297Sjkim		frm += 8;
305280297Sjkim		*(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval);
306280297Sjkim		frm += 2;
307109998Smarkm		if (ic->ic_opmode == IEEE80211_M_IBSS)
308109998Smarkm			capinfo = IEEE80211_CAPINFO_IBSS;
309280297Sjkim		else
310109998Smarkm			capinfo = IEEE80211_CAPINFO_ESS;
311109998Smarkm		if (ic->ic_flags & IEEE80211_F_WEPON)
312109998Smarkm			capinfo |= IEEE80211_CAPINFO_PRIVACY;
313109998Smarkm		*(u_int16_t *)frm = htole16(capinfo);
314280297Sjkim		frm += 2;
315280297Sjkim
316280297Sjkim		frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid,
317109998Smarkm				ic->ic_bss->ni_esslen);
318280297Sjkim		frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates);
319280297Sjkim
320109998Smarkm		if (ic->ic_opmode == IEEE80211_M_IBSS) {
321280297Sjkim			*frm++ = IEEE80211_ELEMID_IBSSPARMS;
322280297Sjkim			*frm++ = 2;
323280297Sjkim			*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
324280297Sjkim		} else {	/* IEEE80211_M_HOSTAP */
325109998Smarkm			/* TODO: TIM */
326109998Smarkm			*frm++ = IEEE80211_ELEMID_TIM;
327280297Sjkim			*frm++ = 4;	/* length */
328280297Sjkim			*frm++ = 0;	/* DTIM count */
329280297Sjkim			*frm++ = 1;	/* DTIM period */
330280297Sjkim			*frm++ = 0;	/* bitmap control */
331109998Smarkm			*frm++ = 0;	/* Partial Virtual Bitmap (variable length) */
332280297Sjkim		}
333280297Sjkim		frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates);
334280297Sjkim		m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
335280297Sjkim		break;
336280297Sjkim
337280297Sjkim	case IEEE80211_FC0_SUBTYPE_AUTH:
338280297Sjkim		MGETHDR(m, M_DONTWAIT, MT_DATA);
339280297Sjkim		if (m == NULL)
340109998Smarkm			return ENOMEM;
341280297Sjkim		MH_ALIGN(m, 2 * 3);
342280297Sjkim		m->m_pkthdr.len = m->m_len = 6;
343280297Sjkim		frm = mtod(m, u_int8_t *);
344109998Smarkm		/* TODO: shared key auth */
345280297Sjkim		((u_int16_t *)frm)[0] = htole16(IEEE80211_AUTH_ALG_OPEN);
346306195Sjkim		((u_int16_t *)frm)[1] = htole16(arg);	/* sequence number */
347306195Sjkim		((u_int16_t *)frm)[2] = 0;		/* status */
348306195Sjkim		if (ic->ic_opmode == IEEE80211_M_STA)
349306195Sjkim			timer = IEEE80211_TRANS_WAIT;
350306195Sjkim		break;
351280297Sjkim
352280297Sjkim	case IEEE80211_FC0_SUBTYPE_DEAUTH:
353280297Sjkim		if (ifp->if_flags & IFF_DEBUG)
354109998Smarkm			if_printf(ifp, "station %s deauthenticate (reason %d)\n",
355280297Sjkim			    ether_sprintf(ni->ni_macaddr), arg);
356280297Sjkim		MGETHDR(m, M_DONTWAIT, MT_DATA);
357280297Sjkim		if (m == NULL)
358280297Sjkim			return ENOMEM;
359280297Sjkim		MH_ALIGN(m, 2);
360280297Sjkim		m->m_pkthdr.len = m->m_len = 2;
361280297Sjkim		*mtod(m, u_int16_t *) = htole16(arg);	/* reason */
362280297Sjkim		break;
363280297Sjkim
364280297Sjkim	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
365280297Sjkim	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
366280297Sjkim		/*
367280297Sjkim		 * asreq frame format
368109998Smarkm		 *	[2] capability information
369109998Smarkm		 *	[2] listen interval
370280297Sjkim		 *	[6*] current AP address (reassoc only)
371109998Smarkm		 *	[tlv] ssid
372280297Sjkim		 *	[tlv] supported rates
373280297Sjkim		 *	[tlv] extended supported rates
374280297Sjkim		 */
375280297Sjkim		m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
376109998Smarkm			 sizeof(capinfo)
377280297Sjkim		       + sizeof(u_int16_t)
378280297Sjkim		       + IEEE80211_ADDR_LEN
379109998Smarkm		       + 2 + ni->ni_esslen
380280297Sjkim		       + 2 + IEEE80211_RATE_SIZE
381306195Sjkim		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
382306195Sjkim		if (m == NULL)
383306195Sjkim			return ENOMEM;
384306195Sjkim		m->m_data += sizeof(struct ieee80211_frame);
385280297Sjkim		frm = mtod(m, u_int8_t *);
386280297Sjkim
387109998Smarkm		capinfo = 0;
388109998Smarkm		if (ic->ic_opmode == IEEE80211_M_IBSS)
389280297Sjkim			capinfo |= IEEE80211_CAPINFO_IBSS;
390109998Smarkm		else		/* IEEE80211_M_STA */
391280297Sjkim			capinfo |= IEEE80211_CAPINFO_ESS;
392280297Sjkim		if (ic->ic_flags & IEEE80211_F_WEPON)
393109998Smarkm			capinfo |= IEEE80211_CAPINFO_PRIVACY;
394280297Sjkim		if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
395109998Smarkm			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
396280297Sjkim		if (ic->ic_flags & IEEE80211_F_SHSLOT)
397280297Sjkim			capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
398280297Sjkim		*(u_int16_t *)frm = htole16(capinfo);
399280297Sjkim		frm += 2;
400280297Sjkim
401280297Sjkim		*(u_int16_t *)frm = htole16(ic->ic_lintval);
402280297Sjkim		frm += 2;
403280297Sjkim
404280297Sjkim		if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
405109998Smarkm			IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid);
406109998Smarkm			frm += IEEE80211_ADDR_LEN;
407280297Sjkim		}
408109998Smarkm
409280297Sjkim		frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
410280297Sjkim		frm = ieee80211_add_rates(frm, &ni->ni_rates);
411280297Sjkim		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
412280297Sjkim		m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
413109998Smarkm
414109998Smarkm		timer = IEEE80211_TRANS_WAIT;
415280297Sjkim		break;
416109998Smarkm
417280297Sjkim	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
418306195Sjkim	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
419306195Sjkim		/*
420306195Sjkim		 * asreq frame format
421306195Sjkim		 *	[2] capability information
422280297Sjkim		 *	[2] status
423109998Smarkm		 *	[2] association ID
424109998Smarkm		 *	[tlv] supported rates
425280297Sjkim		 *	[tlv] extended supported rates
426109998Smarkm		 */
427109998Smarkm		m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
428280297Sjkim			 sizeof(capinfo)
429109998Smarkm		       + sizeof(u_int16_t)
430280297Sjkim		       + sizeof(u_int16_t)
431109998Smarkm		       + 2 + IEEE80211_RATE_SIZE
432280297Sjkim		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
433109998Smarkm		if (m == NULL)
434109998Smarkm			return ENOMEM;
435109998Smarkm		m->m_data += sizeof(struct ieee80211_frame);
436280297Sjkim		frm = mtod(m, u_int8_t *);
437109998Smarkm
438109998Smarkm		capinfo = IEEE80211_CAPINFO_ESS;
439109998Smarkm		if (ic->ic_flags & IEEE80211_F_WEPON)
440109998Smarkm			capinfo |= IEEE80211_CAPINFO_PRIVACY;
441109998Smarkm		*(u_int16_t *)frm = htole16(capinfo);
442109998Smarkm		frm += 2;
443109998Smarkm
444109998Smarkm		*(u_int16_t *)frm = htole16(arg);	/* status */
445280297Sjkim		frm += 2;
446109998Smarkm
447109998Smarkm		if (arg == IEEE80211_STATUS_SUCCESS && ni != NULL)
448280297Sjkim			*(u_int16_t *)frm = htole16(ni->ni_associd);
449280297Sjkim		else
450280297Sjkim			*(u_int16_t *)frm = htole16(0);
451109998Smarkm		frm += 2;
452280297Sjkim
453109998Smarkm		if (ni != NULL) {
454			frm = ieee80211_add_rates(frm, &ni->ni_rates);
455			frm = ieee80211_add_xrates(frm, &ni->ni_rates);
456		} else {
457			frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates);
458			frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates);
459		}
460		m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
461		break;
462
463	case IEEE80211_FC0_SUBTYPE_DISASSOC:
464		if (ifp->if_flags & IFF_DEBUG)
465			if_printf(ifp, "station %s disassociate (reason %d)\n",
466			    ether_sprintf(ni->ni_macaddr), arg);
467		MGETHDR(m, M_DONTWAIT, MT_DATA);
468		if (m == NULL)
469			return ENOMEM;
470		MH_ALIGN(m, 2);
471		m->m_pkthdr.len = m->m_len = 2;
472		*mtod(m, u_int16_t *) = htole16(arg);	/* reason */
473		break;
474
475	default:
476		IEEE80211_DPRINTF(("%s: invalid mgmt frame type %u\n",
477			__func__, type));
478		return EINVAL;
479	}
480
481	ret = ieee80211_mgmt_output(ifp, ni, m, type);
482	if (ret == 0 && timer)
483		ic->ic_mgt_timer = timer;
484	return ret;
485}
486