ieee80211_ioctl.c revision 173273
1331722Seadler/*-
2341477Svmaffione * Copyright (c) 2001 Atsushi Onoe
3341477Svmaffione * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
4261909Sluigi * All rights reserved.
5261909Sluigi *
6261909Sluigi * Redistribution and use in source and binary forms, with or without
7261909Sluigi * modification, are permitted provided that the following conditions
8261909Sluigi * are met:
9261909Sluigi * 1. Redistributions of source code must retain the above copyright
10261909Sluigi *    notice, this list of conditions and the following disclaimer.
11261909Sluigi * 2. Redistributions in binary form must reproduce the above copyright
12261909Sluigi *    notice, this list of conditions and the following disclaimer in the
13261909Sluigi *    documentation and/or other materials provided with the distribution.
14261909Sluigi *
15261909Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16261909Sluigi * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17261909Sluigi * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18261909Sluigi * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19261909Sluigi * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20261909Sluigi * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21261909Sluigi * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22261909Sluigi * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23261909Sluigi * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24261909Sluigi * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25261909Sluigi */
26261909Sluigi
27261909Sluigi#include <sys/cdefs.h>
28261909Sluigi__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ioctl.c 173273 2007-11-02 05:22:25Z sam $");
29261909Sluigi
30261909Sluigi#include "opt_compat.h"
31261909Sluigi
32261909Sluigi/*
33261909Sluigi * IEEE 802.11 ioctl support (FreeBSD-specific)
34261909Sluigi */
35261909Sluigi
36261909Sluigi#include "opt_inet.h"
37261909Sluigi#include "opt_ipx.h"
38261909Sluigi
39261909Sluigi#include <sys/endian.h>
40261909Sluigi#include <sys/param.h>
41261909Sluigi#include <sys/kernel.h>
42261909Sluigi#include <sys/priv.h>
43261909Sluigi#include <sys/socket.h>
44261909Sluigi#include <sys/sockio.h>
45261909Sluigi#include <sys/systm.h>
46261909Sluigi
47261909Sluigi#include <net/if.h>
48261909Sluigi#include <net/if_dl.h>
49261909Sluigi#include <net/if_media.h>
50261909Sluigi#include <net/ethernet.h>
51261909Sluigi
52261909Sluigi#ifdef INET
53261909Sluigi#include <netinet/in.h>
54261909Sluigi#include <netinet/if_ether.h>
55261909Sluigi#endif
56261909Sluigi
57261909Sluigi#ifdef IPX
58341477Svmaffione#include <netipx/ipx.h>
59341477Svmaffione#include <netipx/ipx_if.h>
60341477Svmaffione#endif
61261909Sluigi
62261909Sluigi#include <net80211/ieee80211_var.h>
63261909Sluigi#include <net80211/ieee80211_ioctl.h>
64261909Sluigi
65261909Sluigi#define	IS_UP(_ic) \
66261909Sluigi	(((_ic)->ic_ifp->if_flags & IFF_UP) &&			\
67261909Sluigi	    ((_ic)->ic_ifp->if_drv_flags & IFF_DRV_RUNNING))
68261909Sluigi#define	IS_UP_AUTO(_ic) \
69261909Sluigi	(IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO)
70261909Sluigi#define	RESCAN	1
71261909Sluigi
72261909Sluigistatic struct ieee80211_channel *findchannel(struct ieee80211com *,
73261909Sluigi		int ieee, int mode);
74261909Sluigi
75261909Sluigistatic int
76261909Sluigicap2cipher(int flag)
77261909Sluigi{
78341477Svmaffione	switch (flag) {
79261909Sluigi	case IEEE80211_C_WEP:		return IEEE80211_CIPHER_WEP;
80341477Svmaffione	case IEEE80211_C_AES:		return IEEE80211_CIPHER_AES_OCB;
81341477Svmaffione	case IEEE80211_C_AES_CCM:	return IEEE80211_CIPHER_AES_CCM;
82261909Sluigi	case IEEE80211_C_CKIP:		return IEEE80211_CIPHER_CKIP;
83341477Svmaffione	case IEEE80211_C_TKIP:		return IEEE80211_CIPHER_TKIP;
84341477Svmaffione	}
85341477Svmaffione	return -1;
86261909Sluigi}
87261909Sluigi
88285349Sluigistatic int
89285349Sluigiieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq)
90261909Sluigi{
91341477Svmaffione	struct ieee80211_node *ni;
92285349Sluigi	struct ieee80211req_key ik;
93261909Sluigi	struct ieee80211_key *wk;
94285349Sluigi	const struct ieee80211_cipher *cip;
95285349Sluigi	u_int kid;
96261909Sluigi	int error;
97341477Svmaffione
98285349Sluigi	if (ireq->i_len != sizeof(ik))
99285349Sluigi		return EINVAL;
100261909Sluigi	error = copyin(ireq->i_data, &ik, sizeof(ik));
101341477Svmaffione	if (error)
102341477Svmaffione		return error;
103341477Svmaffione	kid = ik.ik_keyix;
104285349Sluigi	if (kid == IEEE80211_KEYIX_NONE) {
105261909Sluigi		ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
106261909Sluigi		if (ni == NULL)
107285349Sluigi			return EINVAL;		/* XXX */
108261909Sluigi		wk = &ni->ni_ucastkey;
109261909Sluigi	} else {
110261909Sluigi		if (kid >= IEEE80211_WEP_NKID)
111261909Sluigi			return EINVAL;
112261909Sluigi		wk = &ic->ic_nw_keys[kid];
113261909Sluigi		IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr);
114261909Sluigi		ni = NULL;
115261909Sluigi	}
116261909Sluigi	cip = wk->wk_cipher;
117261909Sluigi	ik.ik_type = cip->ic_cipher;
118285349Sluigi	ik.ik_keylen = wk->wk_keylen;
119344047Svmaffione	ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
120344047Svmaffione	if (wk->wk_keyix == ic->ic_def_txkey)
121285349Sluigi		ik.ik_flags |= IEEE80211_KEY_DEFAULT;
122341477Svmaffione	if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
123261909Sluigi		/* NB: only root can read key data */
124261909Sluigi		ik.ik_keyrsc = wk->wk_keyrsc;
125261909Sluigi		ik.ik_keytsc = wk->wk_keytsc;
126261909Sluigi		memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen);
127261909Sluigi		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) {
128261909Sluigi			memcpy(ik.ik_keydata+wk->wk_keylen,
129261909Sluigi				wk->wk_key + IEEE80211_KEYBUF_SIZE,
130261909Sluigi				IEEE80211_MICBUF_SIZE);
131341477Svmaffione			ik.ik_keylen += IEEE80211_MICBUF_SIZE;
132261909Sluigi		}
133261909Sluigi	} else {
134261909Sluigi		ik.ik_keyrsc = 0;
135261909Sluigi		ik.ik_keytsc = 0;
136261909Sluigi		memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata));
137341477Svmaffione	}
138261909Sluigi	if (ni != NULL)
139341477Svmaffione		ieee80211_free_node(ni);
140341477Svmaffione	return copyout(&ik, ireq->i_data, sizeof(ik));
141341477Svmaffione}
142341477Svmaffione
143341477Svmaffionestatic int
144261909Sluigiieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
145261909Sluigi{
146261909Sluigi
147261909Sluigi	if (sizeof(ic->ic_chan_active) < ireq->i_len)
148261909Sluigi		ireq->i_len = sizeof(ic->ic_chan_active);
149261909Sluigi	return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len);
150261909Sluigi}
151261909Sluigi
152261909Sluigistatic int
153261909Sluigiieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq)
154261909Sluigi{
155285349Sluigi	int space;
156285349Sluigi
157285349Sluigi	space = __offsetof(struct ieee80211req_chaninfo,
158285349Sluigi			ic_chans[ic->ic_nchans]);
159261909Sluigi	if (space > ireq->i_len)
160261909Sluigi		space = ireq->i_len;
161261909Sluigi	/* XXX assumes compatible layout */
162261909Sluigi	return copyout(&ic->ic_nchans, ireq->i_data, space);
163261909Sluigi}
164261909Sluigi
165261909Sluigistatic int
166261909Sluigiieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int req)
167261909Sluigi{
168261909Sluigi	struct ieee80211_node *ni;
169261909Sluigi	struct ieee80211req_wpaie2 wpaie;
170261909Sluigi	int error;
171261909Sluigi
172261909Sluigi	if (ireq->i_len < IEEE80211_ADDR_LEN)
173261909Sluigi		return EINVAL;
174285349Sluigi	error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN);
175285349Sluigi	if (error != 0)
176285349Sluigi		return error;
177285349Sluigi	ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr);
178261909Sluigi	if (ni == NULL)
179261909Sluigi		return ENOENT;		/* XXX */
180261909Sluigi	memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie));
181261909Sluigi	if (ni->ni_wpa_ie != NULL) {
182341477Svmaffione		int ielen = ni->ni_wpa_ie[1] + 2;
183270063Sluigi		if (ielen > sizeof(wpaie.wpa_ie))
184261909Sluigi			ielen = sizeof(wpaie.wpa_ie);
185341477Svmaffione		memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen);
186341477Svmaffione	}
187341477Svmaffione	if (req == IEEE80211_IOC_WPAIE2) {
188341477Svmaffione		memset(wpaie.rsn_ie, 0, sizeof(wpaie.rsn_ie));
189341477Svmaffione		if (ni->ni_rsn_ie != NULL) {
190261909Sluigi			int ielen = ni->ni_rsn_ie[1] + 2;
191344047Svmaffione			if (ielen > sizeof(wpaie.rsn_ie))
192344047Svmaffione				ielen = sizeof(wpaie.rsn_ie);
193341477Svmaffione			memcpy(wpaie.rsn_ie, ni->ni_rsn_ie, ielen);
194341477Svmaffione		}
195261909Sluigi		if (ireq->i_len > sizeof(struct ieee80211req_wpaie2))
196341477Svmaffione			ireq->i_len = sizeof(struct ieee80211req_wpaie2);
197341477Svmaffione	} else {
198261909Sluigi		/* compatibility op, may overwrite wpa ie */
199341477Svmaffione		/* XXX check ic_flags? */
200341477Svmaffione		if (ni->ni_rsn_ie != NULL) {
201341477Svmaffione			int ielen = ni->ni_rsn_ie[1] + 2;
202341477Svmaffione			if (ielen > sizeof(wpaie.wpa_ie))
203341477Svmaffione				ielen = sizeof(wpaie.wpa_ie);
204341477Svmaffione			memcpy(wpaie.wpa_ie, ni->ni_rsn_ie, ielen);
205261909Sluigi		}
206261909Sluigi		if (ireq->i_len > sizeof(struct ieee80211req_wpaie))
207261909Sluigi			ireq->i_len = sizeof(struct ieee80211req_wpaie);
208341477Svmaffione	}
209341477Svmaffione	ieee80211_free_node(ni);
210341477Svmaffione	return copyout(&wpaie, ireq->i_data, ireq->i_len);
211341477Svmaffione}
212261909Sluigi
213341477Svmaffionestatic int
214341477Svmaffioneieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
215341477Svmaffione{
216341477Svmaffione	struct ieee80211_node *ni;
217341477Svmaffione	uint8_t macaddr[IEEE80211_ADDR_LEN];
218341477Svmaffione	const int off = __offsetof(struct ieee80211req_sta_stats, is_stats);
219261909Sluigi	int error;
220341477Svmaffione
221261909Sluigi	if (ireq->i_len < off)
222344047Svmaffione		return EINVAL;
223341477Svmaffione	error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
224341477Svmaffione	if (error != 0)
225261909Sluigi		return error;
226341477Svmaffione	ni = ieee80211_find_node(&ic->ic_sta, macaddr);
227341477Svmaffione	if (ni == NULL)
228341477Svmaffione		return EINVAL;
229341477Svmaffione	if (ireq->i_len > sizeof(struct ieee80211req_sta_stats))
230341477Svmaffione		ireq->i_len = sizeof(struct ieee80211req_sta_stats);
231261909Sluigi	/* NB: copy out only the statistics */
232261909Sluigi	error = copyout(&ni->ni_stats, (uint8_t *) ireq->i_data + off,
233261909Sluigi			ireq->i_len - off);
234261909Sluigi	ieee80211_free_node(ni);
235341477Svmaffione	return error;
236270063Sluigi}
237261909Sluigi
238341477Svmaffionestatic __inline uint8_t *
239341477Svmaffionecopyie(uint8_t *cp, const uint8_t *ie)
240341477Svmaffione{
241341477Svmaffione	if (ie != NULL) {
242261909Sluigi		memcpy(cp, ie, 2+ie[1]);
243344047Svmaffione		cp += 2+ie[1];
244344047Svmaffione	}
245341477Svmaffione	return cp;
246341477Svmaffione}
247261909Sluigi
248341477Svmaffione#ifdef COMPAT_FREEBSD6
249341477Svmaffione#define	IEEE80211_IOC_SCAN_RESULTS_OLD	24
250341477Svmaffione
251341477Svmaffionestruct scan_result_old {
252341477Svmaffione	uint16_t	isr_len;		/* length (mult of 4) */
253341477Svmaffione	uint16_t	isr_freq;		/* MHz */
254341477Svmaffione	uint16_t	isr_flags;		/* channel flags */
255341477Svmaffione	uint8_t		isr_noise;
256341477Svmaffione	uint8_t		isr_rssi;
257341477Svmaffione	uint8_t		isr_intval;		/* beacon interval */
258261909Sluigi	uint8_t		isr_capinfo;		/* capabilities */
259341477Svmaffione	uint8_t		isr_erp;		/* ERP element */
260341477Svmaffione	uint8_t		isr_bssid[IEEE80211_ADDR_LEN];
261341477Svmaffione	uint8_t		isr_nrates;
262341477Svmaffione	uint8_t		isr_rates[IEEE80211_RATE_MAXSIZE];
263341477Svmaffione	uint8_t		isr_ssid_len;		/* SSID length */
264341477Svmaffione	uint8_t		isr_ie_len;		/* IE length */
265341477Svmaffione	uint8_t		isr_pad[5];
266341477Svmaffione	/* variable length SSID followed by IE data */
267341477Svmaffione};
268341477Svmaffione
269341477Svmaffionestruct oscanreq {
270341477Svmaffione	struct scan_result_old *sr;
271341477Svmaffione	size_t space;
272341477Svmaffione};
273341477Svmaffione
274341477Svmaffionestatic size_t
275344047Svmaffioneold_scan_space(const struct ieee80211_scan_entry *se, int *ielen)
276341477Svmaffione{
277341477Svmaffione	size_t len;
278341477Svmaffione
279341477Svmaffione	*ielen = 0;
280341477Svmaffione	if (se->se_wpa_ie != NULL)
281341477Svmaffione		*ielen += 2+se->se_wpa_ie[1];
282261909Sluigi	if (se->se_wme_ie != NULL)
283261909Sluigi		*ielen += 2+se->se_wme_ie[1];
284261909Sluigi	/*
285261909Sluigi	 * NB: ie's can be no more than 255 bytes and the max 802.11
286261909Sluigi	 * packet is <3Kbytes so we are sure this doesn't overflow
287261909Sluigi	 * 16-bits; if this is a concern we can drop the ie's.
288261909Sluigi	 */
289261909Sluigi	len = sizeof(struct scan_result_old) + se->se_ssid[1] + *ielen;
290261909Sluigi	return roundup(len, sizeof(uint32_t));
291261909Sluigi}
292261909Sluigi
293261909Sluigistatic void
294261909Sluigiold_get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
295261909Sluigi{
296261909Sluigi	struct oscanreq *req = arg;
297261909Sluigi	int ielen;
298261909Sluigi
299261909Sluigi	req->space += old_scan_space(se, &ielen);
300261909Sluigi}
301261909Sluigi
302261909Sluigistatic void
303261909Sluigiold_get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
304261909Sluigi{
305261909Sluigi	struct oscanreq *req = arg;
306261909Sluigi	struct scan_result_old *sr;
307261909Sluigi	int ielen, len, nr, nxr;
308261909Sluigi	uint8_t *cp;
309261909Sluigi
310261909Sluigi	len = old_scan_space(se, &ielen);
311261909Sluigi	if (len > req->space)
312261909Sluigi		return;
313344047Svmaffione
314344047Svmaffione	sr = req->sr;
315344047Svmaffione	memset(sr, 0, sizeof(*sr));
316344047Svmaffione	sr->isr_ssid_len = se->se_ssid[1];
317344047Svmaffione	/* NB: beware of overflow, isr_ie_len is 8 bits */
318344047Svmaffione	sr->isr_ie_len = (ielen > 255 ? 0 : ielen);
319344047Svmaffione	sr->isr_len = len;
320344047Svmaffione	sr->isr_freq = se->se_chan->ic_freq;
321344047Svmaffione	sr->isr_flags = se->se_chan->ic_flags;
322344047Svmaffione	sr->isr_rssi = se->se_rssi;
323344047Svmaffione	sr->isr_noise = se->se_noise;
324344047Svmaffione	sr->isr_intval = se->se_intval;
325344047Svmaffione	sr->isr_capinfo = se->se_capinfo;
326344047Svmaffione	sr->isr_erp = se->se_erp;
327344047Svmaffione	IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid);
328344047Svmaffione	nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE);
329344047Svmaffione	memcpy(sr->isr_rates, se->se_rates+2, nr);
330344047Svmaffione	nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr);
331344047Svmaffione	memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
332344047Svmaffione	sr->isr_nrates = nr + nxr;
333344047Svmaffione
334344047Svmaffione	cp = (uint8_t *)(sr+1);
335344047Svmaffione	memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
336344047Svmaffione	cp += sr->isr_ssid_len;
337344047Svmaffione	if (sr->isr_ie_len) {
338344047Svmaffione		cp = copyie(cp, se->se_wpa_ie);
339344047Svmaffione		cp = copyie(cp, se->se_wme_ie);
340344047Svmaffione	}
341344047Svmaffione
342344047Svmaffione	req->space -= len;
343344047Svmaffione	req->sr = (struct scan_result_old *)(((uint8_t *)sr) + len);
344344047Svmaffione}
345344047Svmaffione
346344047Svmaffionestatic int
347344047Svmaffioneold_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
348344047Svmaffione{
349344047Svmaffione	struct oscanreq req;
350344047Svmaffione	int error;
351344047Svmaffione
352344047Svmaffione	if (ireq->i_len < sizeof(struct scan_result_old))
353344047Svmaffione		return EFAULT;
354341477Svmaffione
355261909Sluigi	error = 0;
356261909Sluigi	req.space = 0;
357261909Sluigi	ieee80211_scan_iterate(ic, old_get_scan_space, &req);
358261909Sluigi	if (req.space > ireq->i_len)
359261909Sluigi		req.space = ireq->i_len;
360261909Sluigi	if (req.space > 0) {
361261909Sluigi		size_t space;
362261909Sluigi		void *p;
363261909Sluigi
364261909Sluigi		space = req.space;
365261909Sluigi		/* XXX M_WAITOK after driver lock released */
366261909Sluigi		MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
367261909Sluigi		if (p == NULL)
368261909Sluigi			return ENOMEM;
369261909Sluigi		req.sr = p;
370341477Svmaffione		ieee80211_scan_iterate(ic, old_get_scan_result, &req);
371261909Sluigi		ireq->i_len = space - req.space;
372261909Sluigi		error = copyout(p, ireq->i_data, ireq->i_len);
373261909Sluigi		FREE(p, M_TEMP);
374261909Sluigi	} else
375261909Sluigi		ireq->i_len = 0;
376261909Sluigi
377261909Sluigi	return error;
378344047Svmaffione}
379344047Svmaffione#endif /* COMPAT_FREEBSD6 */
380344047Svmaffione
381344047Svmaffionestruct scanreq {
382344047Svmaffione	struct ieee80211req_scan_result *sr;
383344047Svmaffione	size_t space;
384344047Svmaffione};
385344047Svmaffione
386344047Svmaffionestatic size_t
387344047Svmaffionescan_space(const struct ieee80211_scan_entry *se, int *ielen)
388344047Svmaffione{
389285349Sluigi	size_t len;
390285349Sluigi
391344047Svmaffione	*ielen = 0;
392344047Svmaffione	if (se->se_wpa_ie != NULL)
393344047Svmaffione		*ielen += 2+se->se_wpa_ie[1];
394261909Sluigi	if (se->se_rsn_ie != NULL)
395344047Svmaffione		*ielen += 2+se->se_rsn_ie[1];
396344047Svmaffione	if (se->se_wme_ie != NULL)
397344047Svmaffione		*ielen += 2+se->se_wme_ie[1];
398344047Svmaffione	if (se->se_ath_ie != NULL)
399344047Svmaffione		*ielen += 2+se->se_ath_ie[1];
400344047Svmaffione	/*
401261909Sluigi	 * NB: ie's can be no more than 255 bytes and the max 802.11
402344047Svmaffione	 * packet is <3Kbytes so we are sure this doesn't overflow
403344047Svmaffione	 * 16-bits; if this is a concern we can drop the ie's.
404344047Svmaffione	 */
405344047Svmaffione	len = sizeof(struct ieee80211req_scan_result) + se->se_ssid[1] + *ielen;
406344047Svmaffione	return roundup(len, sizeof(uint32_t));
407344047Svmaffione}
408344047Svmaffione
409261909Sluigistatic void
410344047Svmaffioneget_scan_space(void *arg, const struct ieee80211_scan_entry *se)
411344047Svmaffione{
412344047Svmaffione	struct scanreq *req = arg;
413344047Svmaffione	int ielen;
414344047Svmaffione
415344047Svmaffione	req->space += scan_space(se, &ielen);
416344047Svmaffione}
417344047Svmaffione
418344047Svmaffionestatic void
419344047Svmaffioneget_scan_result(void *arg, const struct ieee80211_scan_entry *se)
420344047Svmaffione{
421344047Svmaffione	struct scanreq *req = arg;
422344047Svmaffione	struct ieee80211req_scan_result *sr;
423344047Svmaffione	int ielen, len, nr, nxr;
424344047Svmaffione	uint8_t *cp;
425344047Svmaffione
426344047Svmaffione	len = scan_space(se, &ielen);
427344047Svmaffione	if (len > req->space)
428344047Svmaffione		return;
429344047Svmaffione
430344047Svmaffione	sr = req->sr;
431344047Svmaffione	KASSERT(len <= 65535 && ielen <= 65535,
432344047Svmaffione	    ("len %u ssid %u ie %u", len, se->se_ssid[1], ielen));
433344047Svmaffione	sr->isr_ie_off = sizeof(struct ieee80211req_scan_result);
434344047Svmaffione	sr->isr_ie_len = ielen;
435344047Svmaffione	sr->isr_len = len;
436344047Svmaffione	sr->isr_freq = se->se_chan->ic_freq;
437344047Svmaffione	sr->isr_flags = se->se_chan->ic_flags;
438344047Svmaffione	sr->isr_rssi = se->se_rssi;
439344047Svmaffione	sr->isr_noise = se->se_noise;
440344047Svmaffione	sr->isr_intval = se->se_intval;
441344047Svmaffione	sr->isr_capinfo = se->se_capinfo;
442344047Svmaffione	sr->isr_erp = se->se_erp;
443344047Svmaffione	IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid);
444344047Svmaffione	nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE);
445344047Svmaffione	memcpy(sr->isr_rates, se->se_rates+2, nr);
446344047Svmaffione	nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr);
447344047Svmaffione	memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
448344047Svmaffione	sr->isr_nrates = nr + nxr;
449344047Svmaffione
450285349Sluigi	sr->isr_ssid_len = se->se_ssid[1];
451261909Sluigi	cp = ((uint8_t *)sr) + sr->isr_ie_off;
452344047Svmaffione	memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
453341477Svmaffione
454261909Sluigi	if (ielen) {
455261909Sluigi		cp += sr->isr_ssid_len;
456261909Sluigi		cp = copyie(cp, se->se_wpa_ie);
457261909Sluigi		cp = copyie(cp, se->se_rsn_ie);
458261909Sluigi		cp = copyie(cp, se->se_wme_ie);
459261909Sluigi		cp = copyie(cp, se->se_ath_ie);
460267128Sluigi		cp = copyie(cp, se->se_htcap_ie);
461261909Sluigi	}
462261909Sluigi
463261909Sluigi	req->space -= len;
464261909Sluigi	req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len);
465341477Svmaffione}
466341477Svmaffione
467261909Sluigistatic int
468261909Sluigiieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
469261909Sluigi{
470261909Sluigi	struct scanreq req;
471261909Sluigi	int error;
472261909Sluigi
473267128Sluigi	if (ireq->i_len < sizeof(struct ieee80211req_scan_result))
474261909Sluigi		return EFAULT;
475261909Sluigi
476261909Sluigi	error = 0;
477261909Sluigi	req.space = 0;
478261909Sluigi	ieee80211_scan_iterate(ic, get_scan_space, &req);
479261909Sluigi	if (req.space > ireq->i_len)
480261909Sluigi		req.space = ireq->i_len;
481261909Sluigi	if (req.space > 0) {
482261909Sluigi		size_t space;
483261909Sluigi		void *p;
484261909Sluigi
485261909Sluigi		space = req.space;
486261909Sluigi		/* XXX M_WAITOK after driver lock released */
487261909Sluigi		MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
488341477Svmaffione		if (p == NULL)
489261909Sluigi			return ENOMEM;
490261909Sluigi		req.sr = p;
491261909Sluigi		ieee80211_scan_iterate(ic, get_scan_result, &req);
492261909Sluigi		ireq->i_len = space - req.space;
493261909Sluigi		error = copyout(p, ireq->i_data, ireq->i_len);
494261909Sluigi		FREE(p, M_TEMP);
495341477Svmaffione	} else
496344047Svmaffione		ireq->i_len = 0;
497285349Sluigi
498344047Svmaffione	return error;
499261909Sluigi}
500344047Svmaffione
501344047Svmaffionestruct stainforeq {
502341477Svmaffione	struct ieee80211com *ic;
503341477Svmaffione	struct ieee80211req_sta_info *si;
504341477Svmaffione	size_t	space;
505341477Svmaffione};
506261909Sluigi
507341477Svmaffionestatic size_t
508341477Svmaffionesta_space(const struct ieee80211_node *ni, size_t *ielen)
509344047Svmaffione{
510261909Sluigi	*ielen = 0;
511341477Svmaffione	if (ni->ni_wpa_ie != NULL)
512341477Svmaffione		*ielen += 2+ni->ni_wpa_ie[1];
513344047Svmaffione	if (ni->ni_rsn_ie != NULL)
514341477Svmaffione		*ielen += 2+ni->ni_rsn_ie[1];
515341477Svmaffione	if (ni->ni_wme_ie != NULL)
516341477Svmaffione		*ielen += 2+ni->ni_wme_ie[1];
517261909Sluigi	if (ni->ni_ath_ie != NULL)
518344047Svmaffione		*ielen += 2+ni->ni_ath_ie[1];
519261909Sluigi	return roundup(sizeof(struct ieee80211req_sta_info) + *ielen,
520261909Sluigi		      sizeof(uint32_t));
521261909Sluigi}
522344047Svmaffione
523261909Sluigistatic void
524261909Sluigiget_sta_space(void *arg, struct ieee80211_node *ni)
525261909Sluigi{
526344047Svmaffione	struct stainforeq *req = arg;
527261909Sluigi	struct ieee80211com *ic = ni->ni_ic;
528261909Sluigi	size_t ielen;
529261909Sluigi
530341477Svmaffione	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
531261909Sluigi	    ni->ni_associd == 0)	/* only associated stations */
532261909Sluigi		return;
533344047Svmaffione	req->space += sta_space(ni, &ielen);
534344047Svmaffione}
535344047Svmaffione
536261909Sluigistatic void
537344047Svmaffioneget_sta_info(void *arg, struct ieee80211_node *ni)
538341477Svmaffione{
539261909Sluigi	struct stainforeq *req = arg;
540261909Sluigi	struct ieee80211com *ic = ni->ni_ic;
541344047Svmaffione	struct ieee80211req_sta_info *si;
542344047Svmaffione	size_t ielen, len;
543341477Svmaffione	uint8_t *cp;
544341477Svmaffione
545341477Svmaffione	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
546341477Svmaffione	    ni->ni_associd == 0)	/* only associated stations */
547341477Svmaffione		return;
548341477Svmaffione	if (ni->ni_chan == IEEE80211_CHAN_ANYC)	/* XXX bogus entry */
549341477Svmaffione		return;
550341477Svmaffione	len = sta_space(ni, &ielen);
551341477Svmaffione	if (len > req->space)
552341477Svmaffione		return;
553342033Svmaffione	si = req->si;
554341477Svmaffione	si->isi_len = len;
555341477Svmaffione	si->isi_ie_off = sizeof(struct ieee80211req_sta_info);
556341477Svmaffione	si->isi_ie_len = ielen;
557341477Svmaffione	si->isi_freq = ni->ni_chan->ic_freq;
558344047Svmaffione	si->isi_flags = ni->ni_chan->ic_flags;
559341477Svmaffione	si->isi_state = ni->ni_flags;
560341477Svmaffione	si->isi_authmode = ni->ni_authmode;
561341477Svmaffione	ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise);
562341477Svmaffione	si->isi_noise = 0;		/* XXX */
563341477Svmaffione	si->isi_capinfo = ni->ni_capinfo;
564341477Svmaffione	si->isi_erp = ni->ni_erp;
565341477Svmaffione	IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr);
566341477Svmaffione	si->isi_nrates = ni->ni_rates.rs_nrates;
567341477Svmaffione	if (si->isi_nrates > 15)
568341477Svmaffione		si->isi_nrates = 15;
569341477Svmaffione	memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates);
570341477Svmaffione	si->isi_txrate = ni->ni_txrate;
571344047Svmaffione	si->isi_ie_len = ielen;
572341477Svmaffione	si->isi_associd = ni->ni_associd;
573341477Svmaffione	si->isi_txpower = ni->ni_txpower;
574341477Svmaffione	si->isi_vlan = ni->ni_vlan;
575341477Svmaffione	if (ni->ni_flags & IEEE80211_NODE_QOS) {
576341477Svmaffione		memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs));
577341477Svmaffione		memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs));
578341477Svmaffione	} else {
579341477Svmaffione		si->isi_txseqs[0] = ni->ni_txseqs[IEEE80211_NONQOS_TID];
580341477Svmaffione		si->isi_rxseqs[0] = ni->ni_rxseqs[IEEE80211_NONQOS_TID];
581341477Svmaffione	}
582341477Svmaffione	/* NB: leave all cases in case we relax ni_associd == 0 check */
583341477Svmaffione	if (ieee80211_node_is_authorized(ni))
584261909Sluigi		si->isi_inact = ic->ic_inact_run;
585341477Svmaffione	else if (ni->ni_associd != 0)
586261909Sluigi		si->isi_inact = ic->ic_inact_auth;
587261909Sluigi	else
588341477Svmaffione		si->isi_inact = ic->ic_inact_init;
589261909Sluigi	si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
590261909Sluigi
591261909Sluigi	if (ielen) {
592261909Sluigi		cp = ((uint8_t *)si) + si->isi_ie_off;
593261909Sluigi		cp = copyie(cp, ni->ni_wpa_ie);
594261909Sluigi		cp = copyie(cp, ni->ni_rsn_ie);
595344047Svmaffione		cp = copyie(cp, ni->ni_wme_ie);
596344047Svmaffione		cp = copyie(cp, ni->ni_ath_ie);
597344047Svmaffione	}
598344047Svmaffione
599344047Svmaffione	req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len);
600344047Svmaffione	req->space -= len;
601344047Svmaffione}
602344047Svmaffione
603344047Svmaffionestatic int
604344047Svmaffionegetstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq,
605344047Svmaffione	struct ieee80211_node *ni, int off)
606344047Svmaffione{
607344047Svmaffione	struct stainforeq req;
608344047Svmaffione	size_t space;
609344047Svmaffione	void *p;
610344047Svmaffione	int error;
611344047Svmaffione
612344047Svmaffione	error = 0;
613344047Svmaffione	req.space = 0;
614344047Svmaffione	if (ni == NULL)
615344047Svmaffione		ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req);
616344047Svmaffione	else
617344047Svmaffione		get_sta_space(&req, ni);
618344047Svmaffione	if (req.space > ireq->i_len)
619344047Svmaffione		req.space = ireq->i_len;
620344047Svmaffione	if (req.space > 0) {
621261909Sluigi		space = req.space;
622344047Svmaffione		/* XXX M_WAITOK after driver lock released */
623344047Svmaffione		MALLOC(p, void *, space, M_TEMP, M_NOWAIT);
624344047Svmaffione		if (p == NULL) {
625344047Svmaffione			error = ENOMEM;
626344047Svmaffione			goto bad;
627344047Svmaffione		}
628344047Svmaffione		req.si = p;
629344047Svmaffione		if (ni == NULL)
630344047Svmaffione			ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req);
631261909Sluigi		else
632261909Sluigi			get_sta_info(&req, ni);
633261909Sluigi		ireq->i_len = space - req.space;
634261909Sluigi		error = copyout(p, (uint8_t *) ireq->i_data+off, ireq->i_len);
635261909Sluigi		FREE(p, M_TEMP);
636344047Svmaffione	} else
637261909Sluigi		ireq->i_len = 0;
638344047Svmaffionebad:
639261909Sluigi	if (ni != NULL)
640261909Sluigi		ieee80211_free_node(ni);
641261909Sluigi	return error;
642341477Svmaffione}
643261909Sluigi
644341477Svmaffionestatic int
645341477Svmaffioneieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
646261909Sluigi{
647261909Sluigi	uint8_t macaddr[IEEE80211_ADDR_LEN];
648261909Sluigi	const int off = __offsetof(struct ieee80211req_sta_req, info);
649261909Sluigi	struct ieee80211_node *ni;
650261909Sluigi	int error;
651341477Svmaffione
652341477Svmaffione	if (ireq->i_len < sizeof(struct ieee80211req_sta_req))
653261909Sluigi		return EFAULT;
654341477Svmaffione	error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
655261909Sluigi	if (error != 0)
656341477Svmaffione		return error;
657341477Svmaffione	if (IEEE80211_ADDR_EQ(macaddr, ic->ic_ifp->if_broadcastaddr)) {
658341477Svmaffione		ni = NULL;
659341477Svmaffione	} else {
660341477Svmaffione		ni = ieee80211_find_node(&ic->ic_sta, macaddr);
661341477Svmaffione		if (ni == NULL)
662261909Sluigi			return EINVAL;
663341477Svmaffione	}
664341477Svmaffione	return getstainfo_common(ic, ireq, ni, off);
665341477Svmaffione}
666341477Svmaffione
667341477Svmaffione#ifdef COMPAT_FREEBSD6
668341477Svmaffione#define	IEEE80211_IOC_STA_INFO_OLD	45
669341477Svmaffione
670341477Svmaffionestatic int
671341477Svmaffioneold_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
672344047Svmaffione{
673341477Svmaffione	if (ireq->i_len < sizeof(struct ieee80211req_sta_info))
674341477Svmaffione		return EFAULT;
675341477Svmaffione	return getstainfo_common(ic, ireq, NULL, 0);
676341477Svmaffione}
677341477Svmaffione#endif /* COMPAT_FREEBSD6 */
678341477Svmaffione
679341477Svmaffionestatic int
680341477Svmaffioneieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
681341477Svmaffione{
682341477Svmaffione	struct ieee80211_node *ni;
683261909Sluigi	struct ieee80211req_sta_txpow txpow;
684341477Svmaffione	int error;
685341477Svmaffione
686341477Svmaffione	if (ireq->i_len != sizeof(txpow))
687261909Sluigi		return EINVAL;
688261909Sluigi	error = copyin(ireq->i_data, &txpow, sizeof(txpow));
689261909Sluigi	if (error != 0)
690341477Svmaffione		return error;
691341477Svmaffione	ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
692341477Svmaffione	if (ni == NULL)
693341477Svmaffione		return EINVAL;		/* XXX */
694341477Svmaffione	txpow.it_txpow = ni->ni_txpower;
695342033Svmaffione	error = copyout(&txpow, ireq->i_data, sizeof(txpow));
696341477Svmaffione	ieee80211_free_node(ni);
697341477Svmaffione	return error;
698341477Svmaffione}
699342033Svmaffione
700341477Svmaffionestatic int
701341477Svmaffioneieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
702341477Svmaffione{
703344047Svmaffione	struct ieee80211_wme_state *wme = &ic->ic_wme;
704341477Svmaffione	struct wmeParams *wmep;
705341477Svmaffione	int ac;
706344047Svmaffione
707341477Svmaffione	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
708341477Svmaffione		return EINVAL;
709341477Svmaffione
710341477Svmaffione	ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
711341477Svmaffione	if (ac >= WME_NUM_AC)
712342033Svmaffione		ac = WME_AC_BE;
713341477Svmaffione	if (ireq->i_len & IEEE80211_WMEPARAM_BSS)
714341477Svmaffione		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
715344047Svmaffione	else
716344047Svmaffione		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
717341477Svmaffione	switch (ireq->i_type) {
718341477Svmaffione	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
719341477Svmaffione		ireq->i_val = wmep->wmep_logcwmin;
720261909Sluigi		break;
721261909Sluigi	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
722261909Sluigi		ireq->i_val = wmep->wmep_logcwmax;
723344047Svmaffione		break;
724261909Sluigi	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
725261909Sluigi		ireq->i_val = wmep->wmep_aifsn;
726261909Sluigi		break;
727261909Sluigi	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
728261909Sluigi		ireq->i_val = wmep->wmep_txopLimit;
729341477Svmaffione		break;
730261909Sluigi	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
731261909Sluigi		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
732261909Sluigi		ireq->i_val = wmep->wmep_acm;
733344047Svmaffione		break;
734341477Svmaffione	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (!bss only)*/
735261909Sluigi		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
736344047Svmaffione		ireq->i_val = !wmep->wmep_noackPolicy;
737341477Svmaffione		break;
738261909Sluigi	}
739261909Sluigi	return 0;
740341477Svmaffione}
741341477Svmaffione
742341477Svmaffionestatic int
743261909Sluigiieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
744261909Sluigi{
745344047Svmaffione	const struct ieee80211_aclator *acl = ic->ic_acl;
746261909Sluigi
747261909Sluigi	return (acl == NULL ? EINVAL : acl->iac_getioctl(ic, ireq));
748261909Sluigi}
749261909Sluigi
750267128Sluigi/*
751341477Svmaffione * Return the current ``state'' of an Atheros capbility.
752341477Svmaffione * If associated in station mode report the negotiated
753341477Svmaffione * setting. Otherwise report the current setting.
754341477Svmaffione */
755261909Sluigistatic int
756261909Sluigigetathcap(struct ieee80211com *ic, int cap)
757270063Sluigi{
758261909Sluigi	if (ic->ic_opmode == IEEE80211_M_STA && ic->ic_state == IEEE80211_S_RUN)
759341477Svmaffione		return IEEE80211_ATH_CAP(ic, ic->ic_bss, cap) != 0;
760261909Sluigi	else
761341477Svmaffione		return (ic->ic_flags & cap) != 0;
762261909Sluigi}
763341477Svmaffione
764261909Sluigistatic int
765261909Sluigiieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq)
766261909Sluigi{
767261909Sluigi	if (ireq->i_len != sizeof(struct ieee80211_channel))
768261909Sluigi		return EINVAL;
769261909Sluigi	return copyout(ic->ic_curchan, ireq->i_data, sizeof(*ic->ic_curchan));
770261909Sluigi}
771341477Svmaffione
772341477Svmaffione/*
773261909Sluigi * When building the kernel with -O2 on the i386 architecture, gcc
774261909Sluigi * seems to want to inline this function into ieee80211_ioctl()
775341477Svmaffione * (which is the only routine that calls it). When this happens,
776341477Svmaffione * ieee80211_ioctl() ends up consuming an additional 2K of stack
777341477Svmaffione * space. (Exactly why it needs so much is unclear.) The problem
778341477Svmaffione * is that it's possible for ieee80211_ioctl() to invoke other
779341477Svmaffione * routines (including driver init functions) which could then find
780341477Svmaffione * themselves perilously close to exhausting the stack.
781341477Svmaffione *
782261909Sluigi * To avoid this, we deliberately prevent gcc from inlining this
783261909Sluigi * routine. Another way to avoid this is to use less agressive
784341477Svmaffione * optimization when compiling this file (i.e. -O instead of -O2)
785261909Sluigi * but special-casing the compilation of this one module in the
786261909Sluigi * build system would be awkward.
787261909Sluigi */
788261909Sluigi#ifdef __GNUC__
789270063Sluigi__attribute__ ((noinline))
790261909Sluigi#endif
791261909Sluigistatic int
792261909Sluigiieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
793261909Sluigi{
794261909Sluigi	const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
795261909Sluigi	int error = 0;
796341477Svmaffione	u_int kid, len, m;
797261909Sluigi	uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
798261909Sluigi	char tmpssid[IEEE80211_NWID_LEN];
799285697Sluigi
800261909Sluigi	switch (ireq->i_type) {
801261909Sluigi	case IEEE80211_IOC_SSID:
802261909Sluigi		switch (ic->ic_state) {
803341477Svmaffione		case IEEE80211_S_INIT:
804342394Svmaffione		case IEEE80211_S_SCAN:
805341477Svmaffione			ireq->i_len = ic->ic_des_ssid[0].len;
806342394Svmaffione			memcpy(tmpssid, ic->ic_des_ssid[0].ssid, ireq->i_len);
807341477Svmaffione			break;
808342394Svmaffione		default:
809341477Svmaffione			ireq->i_len = ic->ic_bss->ni_esslen;
810341477Svmaffione			memcpy(tmpssid, ic->ic_bss->ni_essid,
811261909Sluigi				ireq->i_len);
812261909Sluigi			break;
813261909Sluigi		}
814261909Sluigi		error = copyout(tmpssid, ireq->i_data, ireq->i_len);
815261909Sluigi		break;
816261909Sluigi	case IEEE80211_IOC_NUMSSIDS:
817261909Sluigi		ireq->i_val = 1;
818261909Sluigi		break;
819261909Sluigi	case IEEE80211_IOC_WEP:
820341477Svmaffione		if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
821341477Svmaffione			ireq->i_val = IEEE80211_WEP_OFF;
822261909Sluigi		else if (ic->ic_flags & IEEE80211_F_DROPUNENC)
823341477Svmaffione			ireq->i_val = IEEE80211_WEP_ON;
824341477Svmaffione		else
825341477Svmaffione			ireq->i_val = IEEE80211_WEP_MIXED;
826261909Sluigi		break;
827341477Svmaffione	case IEEE80211_IOC_WEPKEY:
828341477Svmaffione		kid = (u_int) ireq->i_val;
829261909Sluigi		if (kid >= IEEE80211_WEP_NKID)
830261909Sluigi			return EINVAL;
831261909Sluigi		len = (u_int) ic->ic_nw_keys[kid].wk_keylen;
832341477Svmaffione		/* NB: only root can read WEP keys */
833261909Sluigi		if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
834261909Sluigi			bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
835261909Sluigi		} else {
836344047Svmaffione			bzero(tmpkey, len);
837261909Sluigi		}
838261909Sluigi		ireq->i_len = len;
839344047Svmaffione		error = copyout(tmpkey, ireq->i_data, len);
840341477Svmaffione		break;
841341477Svmaffione	case IEEE80211_IOC_NUMWEPKEYS:
842261909Sluigi		ireq->i_val = IEEE80211_WEP_NKID;
843261909Sluigi		break;
844261909Sluigi	case IEEE80211_IOC_WEPTXKEY:
845341477Svmaffione		ireq->i_val = ic->ic_def_txkey;
846341477Svmaffione		break;
847261909Sluigi	case IEEE80211_IOC_AUTHMODE:
848261909Sluigi		if (ic->ic_flags & IEEE80211_F_WPA)
849261909Sluigi			ireq->i_val = IEEE80211_AUTH_WPA;
850261909Sluigi		else
851341477Svmaffione			ireq->i_val = ic->ic_bss->ni_authmode;
852285697Sluigi		break;
853285697Sluigi	case IEEE80211_IOC_CHANNEL:
854261909Sluigi		ireq->i_val = ieee80211_chan2ieee(ic, ic->ic_curchan);
855341477Svmaffione		break;
856261909Sluigi	case IEEE80211_IOC_POWERSAVE:
857341477Svmaffione		if (ic->ic_flags & IEEE80211_F_PMGTON)
858261909Sluigi			ireq->i_val = IEEE80211_POWERSAVE_ON;
859261909Sluigi		else
860261909Sluigi			ireq->i_val = IEEE80211_POWERSAVE_OFF;
861261909Sluigi		break;
862261909Sluigi	case IEEE80211_IOC_POWERSAVESLEEP:
863		ireq->i_val = ic->ic_lintval;
864		break;
865	case IEEE80211_IOC_RTSTHRESHOLD:
866		ireq->i_val = ic->ic_rtsthreshold;
867		break;
868	case IEEE80211_IOC_PROTMODE:
869		ireq->i_val = ic->ic_protmode;
870		break;
871	case IEEE80211_IOC_TXPOWER:
872		if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
873			return EINVAL;
874		ireq->i_val = ic->ic_txpowlimit;
875		break;
876	case IEEE80211_IOC_MCASTCIPHER:
877		ireq->i_val = rsn->rsn_mcastcipher;
878		break;
879	case IEEE80211_IOC_MCASTKEYLEN:
880		ireq->i_val = rsn->rsn_mcastkeylen;
881		break;
882	case IEEE80211_IOC_UCASTCIPHERS:
883		ireq->i_val = 0;
884		for (m = 0x1; m != 0; m <<= 1)
885			if (rsn->rsn_ucastcipherset & m)
886				ireq->i_val |= 1<<cap2cipher(m);
887		break;
888	case IEEE80211_IOC_UCASTCIPHER:
889		ireq->i_val = rsn->rsn_ucastcipher;
890		break;
891	case IEEE80211_IOC_UCASTKEYLEN:
892		ireq->i_val = rsn->rsn_ucastkeylen;
893		break;
894	case IEEE80211_IOC_KEYMGTALGS:
895		ireq->i_val = rsn->rsn_keymgmtset;
896		break;
897	case IEEE80211_IOC_RSNCAPS:
898		ireq->i_val = rsn->rsn_caps;
899		break;
900	case IEEE80211_IOC_WPA:
901		switch (ic->ic_flags & IEEE80211_F_WPA) {
902		case IEEE80211_F_WPA1:
903			ireq->i_val = 1;
904			break;
905		case IEEE80211_F_WPA2:
906			ireq->i_val = 2;
907			break;
908		case IEEE80211_F_WPA1 | IEEE80211_F_WPA2:
909			ireq->i_val = 3;
910			break;
911		default:
912			ireq->i_val = 0;
913			break;
914		}
915		break;
916	case IEEE80211_IOC_CHANLIST:
917		error = ieee80211_ioctl_getchanlist(ic, ireq);
918		break;
919	case IEEE80211_IOC_ROAMING:
920		ireq->i_val = ic->ic_roaming;
921		break;
922	case IEEE80211_IOC_PRIVACY:
923		ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0;
924		break;
925	case IEEE80211_IOC_DROPUNENCRYPTED:
926		ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0;
927		break;
928	case IEEE80211_IOC_COUNTERMEASURES:
929		ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0;
930		break;
931	case IEEE80211_IOC_DRIVER_CAPS:
932		ireq->i_val = ic->ic_caps>>16;
933		ireq->i_len = ic->ic_caps&0xffff;
934		break;
935	case IEEE80211_IOC_WME:
936		ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0;
937		break;
938	case IEEE80211_IOC_HIDESSID:
939		ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0;
940		break;
941	case IEEE80211_IOC_APBRIDGE:
942		ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0;
943		break;
944	case IEEE80211_IOC_OPTIE:
945		if (ic->ic_opt_ie == NULL)
946			return EINVAL;
947		/* NB: truncate, caller can check length */
948		if (ireq->i_len > ic->ic_opt_ie_len)
949			ireq->i_len = ic->ic_opt_ie_len;
950		error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len);
951		break;
952	case IEEE80211_IOC_WPAKEY:
953		error = ieee80211_ioctl_getkey(ic, ireq);
954		break;
955	case IEEE80211_IOC_CHANINFO:
956		error = ieee80211_ioctl_getchaninfo(ic, ireq);
957		break;
958	case IEEE80211_IOC_BSSID:
959		if (ireq->i_len != IEEE80211_ADDR_LEN)
960			return EINVAL;
961		error = copyout(ic->ic_state == IEEE80211_S_RUN ?
962					ic->ic_bss->ni_bssid :
963					ic->ic_des_bssid,
964				ireq->i_data, ireq->i_len);
965		break;
966	case IEEE80211_IOC_WPAIE:
967		error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
968		break;
969	case IEEE80211_IOC_WPAIE2:
970		error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
971		break;
972#ifdef COMPAT_FREEBSD6
973	case IEEE80211_IOC_SCAN_RESULTS_OLD:
974		error = old_getscanresults(ic, ireq);
975		break;
976#endif
977	case IEEE80211_IOC_SCAN_RESULTS:
978		error = ieee80211_ioctl_getscanresults(ic, ireq);
979		break;
980	case IEEE80211_IOC_STA_STATS:
981		error = ieee80211_ioctl_getstastats(ic, ireq);
982		break;
983	case IEEE80211_IOC_TXPOWMAX:
984		ireq->i_val = ic->ic_bss->ni_txpower;
985		break;
986	case IEEE80211_IOC_STA_TXPOW:
987		error = ieee80211_ioctl_getstatxpow(ic, ireq);
988		break;
989#ifdef COMPAT_FREEBSD6
990	case IEEE80211_IOC_STA_INFO_OLD:
991		error = old_getstainfo(ic, ireq);
992		break;
993#endif
994	case IEEE80211_IOC_STA_INFO:
995		error = ieee80211_ioctl_getstainfo(ic, ireq);
996		break;
997	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
998	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
999	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
1000	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
1001	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
1002	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (bss only) */
1003		error = ieee80211_ioctl_getwmeparam(ic, ireq);
1004		break;
1005	case IEEE80211_IOC_DTIM_PERIOD:
1006		ireq->i_val = ic->ic_dtim_period;
1007		break;
1008	case IEEE80211_IOC_BEACON_INTERVAL:
1009		/* NB: get from ic_bss for station mode */
1010		ireq->i_val = ic->ic_bss->ni_intval;
1011		break;
1012	case IEEE80211_IOC_PUREG:
1013		ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0;
1014		break;
1015	case IEEE80211_IOC_FF:
1016		ireq->i_val = getathcap(ic, IEEE80211_F_FF);
1017		break;
1018	case IEEE80211_IOC_TURBOP:
1019		ireq->i_val = getathcap(ic, IEEE80211_F_TURBOP);
1020		break;
1021	case IEEE80211_IOC_BGSCAN:
1022		ireq->i_val = (ic->ic_flags & IEEE80211_F_BGSCAN) != 0;
1023		break;
1024	case IEEE80211_IOC_BGSCAN_IDLE:
1025		ireq->i_val = ic->ic_bgscanidle*hz/1000;	/* ms */
1026		break;
1027	case IEEE80211_IOC_BGSCAN_INTERVAL:
1028		ireq->i_val = ic->ic_bgscanintvl/hz;		/* seconds */
1029		break;
1030	case IEEE80211_IOC_SCANVALID:
1031		ireq->i_val = ic->ic_scanvalid/hz;		/* seconds */
1032		break;
1033	case IEEE80211_IOC_ROAM_RSSI_11A:
1034		ireq->i_val = ic->ic_roam.rssi11a;
1035		break;
1036	case IEEE80211_IOC_ROAM_RSSI_11B:
1037		ireq->i_val = ic->ic_roam.rssi11bOnly;
1038		break;
1039	case IEEE80211_IOC_ROAM_RSSI_11G:
1040		ireq->i_val = ic->ic_roam.rssi11b;
1041		break;
1042	case IEEE80211_IOC_ROAM_RATE_11A:
1043		ireq->i_val = ic->ic_roam.rate11a;
1044		break;
1045	case IEEE80211_IOC_ROAM_RATE_11B:
1046		ireq->i_val = ic->ic_roam.rate11bOnly;
1047		break;
1048	case IEEE80211_IOC_ROAM_RATE_11G:
1049		ireq->i_val = ic->ic_roam.rate11b;
1050		break;
1051	case IEEE80211_IOC_MCAST_RATE:
1052		ireq->i_val = ic->ic_mcast_rate;
1053		break;
1054	case IEEE80211_IOC_FRAGTHRESHOLD:
1055		ireq->i_val = ic->ic_fragthreshold;
1056		break;
1057	case IEEE80211_IOC_MACCMD:
1058		error = ieee80211_ioctl_getmaccmd(ic, ireq);
1059		break;
1060	case IEEE80211_IOC_BURST:
1061		ireq->i_val = (ic->ic_flags & IEEE80211_F_BURST) != 0;
1062		break;
1063	case IEEE80211_IOC_BMISSTHRESHOLD:
1064		ireq->i_val = ic->ic_bmissthreshold;
1065		break;
1066	case IEEE80211_IOC_CURCHAN:
1067		error = ieee80211_ioctl_getcurchan(ic, ireq);
1068		break;
1069	case IEEE80211_IOC_SHORTGI:
1070		ireq->i_val = 0;
1071		if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
1072			ireq->i_val |= IEEE80211_HTCAP_SHORTGI20;
1073		if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
1074			ireq->i_val |= IEEE80211_HTCAP_SHORTGI40;
1075		break;
1076	case IEEE80211_IOC_AMPDU:
1077		ireq->i_val = 0;
1078		if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)
1079			ireq->i_val |= 1;
1080		if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)
1081			ireq->i_val |= 2;
1082		break;
1083	case IEEE80211_IOC_AMPDU_LIMIT:
1084		ireq->i_val = ic->ic_ampdu_limit;	/* XXX truncation? */
1085		break;
1086	case IEEE80211_IOC_AMPDU_DENSITY:
1087		ireq->i_val = ic->ic_ampdu_density;
1088		break;
1089	case IEEE80211_IOC_AMSDU:
1090		ireq->i_val = 0;
1091		if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_TX)
1092			ireq->i_val |= 1;
1093		if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_RX)
1094			ireq->i_val |= 2;
1095		break;
1096	case IEEE80211_IOC_AMSDU_LIMIT:
1097		ireq->i_val = ic->ic_amsdu_limit;	/* XXX truncation? */
1098		break;
1099	case IEEE80211_IOC_PUREN:
1100		ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) != 0;
1101		break;
1102	case IEEE80211_IOC_DOTH:
1103		ireq->i_val = (ic->ic_flags & IEEE80211_F_DOTH) != 0;
1104		break;
1105	case IEEE80211_IOC_HTCOMPAT:
1106		ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0;
1107		break;
1108	case IEEE80211_IOC_INACTIVITY:
1109		ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_INACT) != 0;
1110		break;
1111	case IEEE80211_IOC_HTPROTMODE:
1112		ireq->i_val = ic->ic_htprotmode;
1113		break;
1114	case IEEE80211_IOC_HTCONF:
1115		if (ic->ic_flags_ext & IEEE80211_FEXT_HT) {
1116			ireq->i_val = 1;
1117			if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
1118				ireq->i_val |= 2;
1119		} else
1120			ireq->i_val = 0;
1121		break;
1122	default:
1123		error = EINVAL;
1124		break;
1125	}
1126	return error;
1127}
1128
1129static int
1130ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq)
1131{
1132	int error;
1133	void *ie, *oie;
1134
1135	/*
1136	 * NB: Doing this for ap operation could be useful (e.g. for
1137	 *     WPA and/or WME) except that it typically is worthless
1138	 *     without being able to intervene when processing
1139	 *     association response frames--so disallow it for now.
1140	 */
1141	if (ic->ic_opmode != IEEE80211_M_STA)
1142		return EINVAL;
1143	if (ireq->i_len > IEEE80211_MAX_OPT_IE)
1144		return EINVAL;
1145	/* NB: data.length is validated by the wireless extensions code */
1146	/* XXX M_WAITOK after driver lock released */
1147	if (ireq->i_len > 0) {
1148		MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_NOWAIT);
1149		if (ie == NULL)
1150			return ENOMEM;
1151		error = copyin(ireq->i_data, ie, ireq->i_len);
1152		if (error) {
1153			FREE(ie, M_DEVBUF);
1154			return error;
1155		}
1156	} else {
1157		ie = NULL;
1158		ireq->i_len = 0;
1159	}
1160	/* XXX sanity check data? */
1161	oie = ic->ic_opt_ie;
1162	ic->ic_opt_ie = ie;
1163	ic->ic_opt_ie_len = ireq->i_len;
1164	if (oie != NULL)
1165		FREE(oie, M_DEVBUF);
1166	return 0;
1167}
1168
1169static int
1170ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
1171{
1172	struct ieee80211req_key ik;
1173	struct ieee80211_node *ni;
1174	struct ieee80211_key *wk;
1175	uint16_t kid;
1176	int error;
1177
1178	if (ireq->i_len != sizeof(ik))
1179		return EINVAL;
1180	error = copyin(ireq->i_data, &ik, sizeof(ik));
1181	if (error)
1182		return error;
1183	/* NB: cipher support is verified by ieee80211_crypt_newkey */
1184	/* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */
1185	if (ik.ik_keylen > sizeof(ik.ik_keydata))
1186		return E2BIG;
1187	kid = ik.ik_keyix;
1188	if (kid == IEEE80211_KEYIX_NONE) {
1189		/* XXX unicast keys currently must be tx/rx */
1190		if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
1191			return EINVAL;
1192		if (ic->ic_opmode == IEEE80211_M_STA) {
1193			ni = ieee80211_ref_node(ic->ic_bss);
1194			if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) {
1195				ieee80211_free_node(ni);
1196				return EADDRNOTAVAIL;
1197			}
1198		} else {
1199			ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
1200			if (ni == NULL)
1201				return ENOENT;
1202		}
1203		wk = &ni->ni_ucastkey;
1204	} else {
1205		if (kid >= IEEE80211_WEP_NKID)
1206			return EINVAL;
1207		wk = &ic->ic_nw_keys[kid];
1208		/*
1209		 * Global slots start off w/o any assigned key index.
1210		 * Force one here for consistency with IEEE80211_IOC_WEPKEY.
1211		 */
1212		if (wk->wk_keyix == IEEE80211_KEYIX_NONE)
1213			wk->wk_keyix = kid;
1214		ni = NULL;
1215	}
1216	error = 0;
1217	ieee80211_key_update_begin(ic);
1218	if (ieee80211_crypto_newkey(ic, ik.ik_type, ik.ik_flags, wk)) {
1219		wk->wk_keylen = ik.ik_keylen;
1220		/* NB: MIC presence is implied by cipher type */
1221		if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
1222			wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
1223		wk->wk_keyrsc = ik.ik_keyrsc;
1224		wk->wk_keytsc = 0;			/* new key, reset */
1225		memset(wk->wk_key, 0, sizeof(wk->wk_key));
1226		memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen);
1227		if (!ieee80211_crypto_setkey(ic, wk,
1228		    ni != NULL ? ni->ni_macaddr : ik.ik_macaddr))
1229			error = EIO;
1230		else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT))
1231			ic->ic_def_txkey = kid;
1232	} else
1233		error = ENXIO;
1234	ieee80211_key_update_end(ic);
1235	if (ni != NULL)
1236		ieee80211_free_node(ni);
1237	return error;
1238}
1239
1240static int
1241ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
1242{
1243	struct ieee80211req_del_key dk;
1244	int kid, error;
1245
1246	if (ireq->i_len != sizeof(dk))
1247		return EINVAL;
1248	error = copyin(ireq->i_data, &dk, sizeof(dk));
1249	if (error)
1250		return error;
1251	kid = dk.idk_keyix;
1252	/* XXX uint8_t -> uint16_t */
1253	if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) {
1254		struct ieee80211_node *ni;
1255
1256		if (ic->ic_opmode == IEEE80211_M_STA) {
1257			ni = ieee80211_ref_node(ic->ic_bss);
1258			if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) {
1259				ieee80211_free_node(ni);
1260				return EADDRNOTAVAIL;
1261			}
1262		} else {
1263			ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr);
1264			if (ni == NULL)
1265				return ENOENT;
1266		}
1267		/* XXX error return */
1268		ieee80211_node_delucastkey(ni);
1269		ieee80211_free_node(ni);
1270	} else {
1271		if (kid >= IEEE80211_WEP_NKID)
1272			return EINVAL;
1273		/* XXX error return */
1274		ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]);
1275	}
1276	return 0;
1277}
1278
1279static void
1280domlme(void *arg, struct ieee80211_node *ni)
1281{
1282	struct ieee80211com *ic = ni->ni_ic;
1283	struct ieee80211req_mlme *mlme = arg;
1284
1285	if (ni->ni_associd != 0) {
1286		IEEE80211_SEND_MGMT(ic, ni,
1287			mlme->im_op == IEEE80211_MLME_DEAUTH ?
1288				IEEE80211_FC0_SUBTYPE_DEAUTH :
1289				IEEE80211_FC0_SUBTYPE_DISASSOC,
1290			mlme->im_reason);
1291	}
1292	ieee80211_node_leave(ic, ni);
1293}
1294
1295struct scanlookup {
1296	const uint8_t *mac;
1297	int esslen;
1298	const uint8_t *essid;
1299	const struct ieee80211_scan_entry *se;
1300};
1301
1302/*
1303 * Match mac address and any ssid.
1304 */
1305static void
1306mlmelookup(void *arg, const struct ieee80211_scan_entry *se)
1307{
1308	struct scanlookup *look = arg;
1309
1310	if (!IEEE80211_ADDR_EQ(look->mac, se->se_macaddr))
1311		return;
1312	if (look->esslen != 0) {
1313		if (se->se_ssid[1] != look->esslen)
1314			return;
1315		if (memcmp(look->essid, se->se_ssid+2, look->esslen))
1316			return;
1317	}
1318	look->se = se;
1319}
1320
1321static int
1322ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
1323{
1324	struct ieee80211req_mlme mlme;
1325	struct ieee80211_node *ni;
1326	int error;
1327
1328	if (ireq->i_len != sizeof(mlme))
1329		return EINVAL;
1330	error = copyin(ireq->i_data, &mlme, sizeof(mlme));
1331	if (error)
1332		return error;
1333	switch (mlme.im_op) {
1334	case IEEE80211_MLME_ASSOC:
1335		/* XXX ibss/ahdemo */
1336		if (ic->ic_opmode == IEEE80211_M_STA) {
1337			struct scanlookup lookup;
1338
1339			lookup.se = NULL;
1340			lookup.mac = mlme.im_macaddr;
1341			/* XXX use revised api w/ explicit ssid */
1342			lookup.esslen = ic->ic_des_ssid[0].len;
1343			lookup.essid = ic->ic_des_ssid[0].ssid;
1344			ieee80211_scan_iterate(ic, mlmelookup, &lookup);
1345			if (lookup.se != NULL &&
1346			    ieee80211_sta_join(ic, lookup.se))
1347				return 0;
1348		}
1349		return EINVAL;
1350	case IEEE80211_MLME_DISASSOC:
1351	case IEEE80211_MLME_DEAUTH:
1352		switch (ic->ic_opmode) {
1353		case IEEE80211_M_STA:
1354			/* XXX not quite right */
1355			ieee80211_new_state(ic, IEEE80211_S_INIT,
1356				mlme.im_reason);
1357			break;
1358		case IEEE80211_M_HOSTAP:
1359			/* NB: the broadcast address means do 'em all */
1360			if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) {
1361				if ((ni = ieee80211_find_node(&ic->ic_sta,
1362						mlme.im_macaddr)) == NULL)
1363					return EINVAL;
1364				domlme(&mlme, ni);
1365				ieee80211_free_node(ni);
1366			} else {
1367				ieee80211_iterate_nodes(&ic->ic_sta,
1368						domlme, &mlme);
1369			}
1370			break;
1371		default:
1372			return EINVAL;
1373		}
1374		break;
1375	case IEEE80211_MLME_AUTHORIZE:
1376	case IEEE80211_MLME_UNAUTHORIZE:
1377		if (ic->ic_opmode != IEEE80211_M_HOSTAP)
1378			return EINVAL;
1379		ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr);
1380		if (ni == NULL)
1381			return EINVAL;
1382		if (mlme.im_op == IEEE80211_MLME_AUTHORIZE)
1383			ieee80211_node_authorize(ni);
1384		else
1385			ieee80211_node_unauthorize(ni);
1386		ieee80211_free_node(ni);
1387		break;
1388	default:
1389		return EINVAL;
1390	}
1391	return 0;
1392}
1393
1394static int
1395ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
1396{
1397	uint8_t mac[IEEE80211_ADDR_LEN];
1398	const struct ieee80211_aclator *acl = ic->ic_acl;
1399	int error;
1400
1401	if (ireq->i_len != sizeof(mac))
1402		return EINVAL;
1403	error = copyin(ireq->i_data, mac, ireq->i_len);
1404	if (error)
1405		return error;
1406	if (acl == NULL) {
1407		acl = ieee80211_aclator_get("mac");
1408		if (acl == NULL || !acl->iac_attach(ic))
1409			return EINVAL;
1410		ic->ic_acl = acl;
1411	}
1412	if (ireq->i_type == IEEE80211_IOC_ADDMAC)
1413		acl->iac_add(ic, mac);
1414	else
1415		acl->iac_remove(ic, mac);
1416	return 0;
1417}
1418
1419static int
1420ieee80211_ioctl_setmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
1421{
1422	const struct ieee80211_aclator *acl = ic->ic_acl;
1423
1424	switch (ireq->i_val) {
1425	case IEEE80211_MACCMD_POLICY_OPEN:
1426	case IEEE80211_MACCMD_POLICY_ALLOW:
1427	case IEEE80211_MACCMD_POLICY_DENY:
1428		if (acl == NULL) {
1429			acl = ieee80211_aclator_get("mac");
1430			if (acl == NULL || !acl->iac_attach(ic))
1431				return EINVAL;
1432			ic->ic_acl = acl;
1433		}
1434		acl->iac_setpolicy(ic, ireq->i_val);
1435		break;
1436	case IEEE80211_MACCMD_FLUSH:
1437		if (acl != NULL)
1438			acl->iac_flush(ic);
1439		/* NB: silently ignore when not in use */
1440		break;
1441	case IEEE80211_MACCMD_DETACH:
1442		if (acl != NULL) {
1443			ic->ic_acl = NULL;
1444			acl->iac_detach(ic);
1445		}
1446		break;
1447	default:
1448		if (acl == NULL)
1449			return EINVAL;
1450		else
1451			return acl->iac_setioctl(ic, ireq);
1452	}
1453	return 0;
1454}
1455
1456static int
1457ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
1458{
1459	struct ieee80211req_chanlist list;
1460	u_char chanlist[IEEE80211_CHAN_BYTES];
1461	int i, j, nchan, error;
1462
1463	if (ireq->i_len != sizeof(list))
1464		return EINVAL;
1465	error = copyin(ireq->i_data, &list, sizeof(list));
1466	if (error)
1467		return error;
1468	memset(chanlist, 0, sizeof(chanlist));
1469	/*
1470	 * Since channel 0 is not available for DS, channel 1
1471	 * is assigned to LSB on WaveLAN.
1472	 */
1473	if (ic->ic_phytype == IEEE80211_T_DS)
1474		i = 1;
1475	else
1476		i = 0;
1477	nchan = 0;
1478	for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
1479		/*
1480		 * NB: silently discard unavailable channels so users
1481		 *     can specify 1-255 to get all available channels.
1482		 */
1483		if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) {
1484			setbit(chanlist, i);
1485			nchan++;
1486		}
1487	}
1488	if (nchan == 0)
1489		return EINVAL;
1490	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&	/* XXX */
1491	    isclr(chanlist, ic->ic_bsschan->ic_ieee))
1492		ic->ic_bsschan = IEEE80211_CHAN_ANYC;
1493	memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
1494	return IS_UP_AUTO(ic) ? ieee80211_init(ic, RESCAN) : 0;
1495}
1496
1497static int
1498ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
1499{
1500	struct ieee80211_node *ni;
1501	uint8_t macaddr[IEEE80211_ADDR_LEN];
1502	int error;
1503
1504	/*
1505	 * NB: we could copyin ieee80211req_sta_stats so apps
1506	 *     could make selective changes but that's overkill;
1507	 *     just clear all stats for now.
1508	 */
1509	if (ireq->i_len < IEEE80211_ADDR_LEN)
1510		return EINVAL;
1511	error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
1512	if (error != 0)
1513		return error;
1514	ni = ieee80211_find_node(&ic->ic_sta, macaddr);
1515	if (ni == NULL)
1516		return EINVAL;		/* XXX */
1517	memset(&ni->ni_stats, 0, sizeof(ni->ni_stats));
1518	ieee80211_free_node(ni);
1519	return 0;
1520}
1521
1522static int
1523ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
1524{
1525	struct ieee80211_node *ni;
1526	struct ieee80211req_sta_txpow txpow;
1527	int error;
1528
1529	if (ireq->i_len != sizeof(txpow))
1530		return EINVAL;
1531	error = copyin(ireq->i_data, &txpow, sizeof(txpow));
1532	if (error != 0)
1533		return error;
1534	ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
1535	if (ni == NULL)
1536		return EINVAL;		/* XXX */
1537	ni->ni_txpower = txpow.it_txpow;
1538	ieee80211_free_node(ni);
1539	return error;
1540}
1541
1542static int
1543ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
1544{
1545	struct ieee80211_wme_state *wme = &ic->ic_wme;
1546	struct wmeParams *wmep, *chanp;
1547	int isbss, ac;
1548
1549	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
1550		return EINVAL;
1551
1552	isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS);
1553	ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
1554	if (ac >= WME_NUM_AC)
1555		ac = WME_AC_BE;
1556	if (isbss) {
1557		chanp = &wme->wme_bssChanParams.cap_wmeParams[ac];
1558		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
1559	} else {
1560		chanp = &wme->wme_chanParams.cap_wmeParams[ac];
1561		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
1562	}
1563	switch (ireq->i_type) {
1564	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
1565		if (isbss) {
1566			wmep->wmep_logcwmin = ireq->i_val;
1567			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
1568				chanp->wmep_logcwmin = ireq->i_val;
1569		} else {
1570			wmep->wmep_logcwmin = chanp->wmep_logcwmin =
1571				ireq->i_val;
1572		}
1573		break;
1574	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
1575		if (isbss) {
1576			wmep->wmep_logcwmax = ireq->i_val;
1577			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
1578				chanp->wmep_logcwmax = ireq->i_val;
1579		} else {
1580			wmep->wmep_logcwmax = chanp->wmep_logcwmax =
1581				ireq->i_val;
1582		}
1583		break;
1584	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
1585		if (isbss) {
1586			wmep->wmep_aifsn = ireq->i_val;
1587			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
1588				chanp->wmep_aifsn = ireq->i_val;
1589		} else {
1590			wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val;
1591		}
1592		break;
1593	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
1594		if (isbss) {
1595			wmep->wmep_txopLimit = ireq->i_val;
1596			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
1597				chanp->wmep_txopLimit = ireq->i_val;
1598		} else {
1599			wmep->wmep_txopLimit = chanp->wmep_txopLimit =
1600				ireq->i_val;
1601		}
1602		break;
1603	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
1604		wmep->wmep_acm = ireq->i_val;
1605		if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
1606			chanp->wmep_acm = ireq->i_val;
1607		break;
1608	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (!bss only)*/
1609		wmep->wmep_noackPolicy = chanp->wmep_noackPolicy =
1610			(ireq->i_val) == 0;
1611		break;
1612	}
1613	ieee80211_wme_updateparams(ic);
1614	return 0;
1615}
1616
1617static int
1618cipher2cap(int cipher)
1619{
1620	switch (cipher) {
1621	case IEEE80211_CIPHER_WEP:	return IEEE80211_C_WEP;
1622	case IEEE80211_CIPHER_AES_OCB:	return IEEE80211_C_AES;
1623	case IEEE80211_CIPHER_AES_CCM:	return IEEE80211_C_AES_CCM;
1624	case IEEE80211_CIPHER_CKIP:	return IEEE80211_C_CKIP;
1625	case IEEE80211_CIPHER_TKIP:	return IEEE80211_C_TKIP;
1626	}
1627	return 0;
1628}
1629
1630static int
1631find11gchannel(struct ieee80211com *ic, int start, int freq)
1632{
1633	const struct ieee80211_channel *c;
1634	int i;
1635
1636	for (i = start+1; i < ic->ic_nchans; i++) {
1637		c = &ic->ic_channels[i];
1638		if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
1639			return 1;
1640	}
1641	/* NB: should not be needed but in case things are mis-sorted */
1642	for (i = 0; i < start; i++) {
1643		c = &ic->ic_channels[i];
1644		if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
1645			return 1;
1646	}
1647	return 0;
1648}
1649
1650static struct ieee80211_channel *
1651findchannel(struct ieee80211com *ic, int ieee, int mode)
1652{
1653	static const u_int chanflags[IEEE80211_MODE_MAX] = {
1654		0,			/* IEEE80211_MODE_AUTO */
1655		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11A */
1656		IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
1657		IEEE80211_CHAN_G,	/* IEEE80211_MODE_11G */
1658		IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
1659		IEEE80211_CHAN_108A,	/* IEEE80211_MODE_TURBO_A */
1660		IEEE80211_CHAN_108G,	/* IEEE80211_MODE_TURBO_G */
1661		IEEE80211_CHAN_STURBO,	/* IEEE80211_MODE_STURBO_A */
1662		/* NB: handled specially below */
1663		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11NA */
1664		IEEE80211_CHAN_G,	/* IEEE80211_MODE_11NG */
1665	};
1666	u_int modeflags;
1667	int i;
1668
1669	KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
1670	modeflags = chanflags[mode];
1671	KASSERT(modeflags != 0 || mode == IEEE80211_MODE_AUTO,
1672	    ("no chanflags for mode %u", mode));
1673	for (i = 0; i < ic->ic_nchans; i++) {
1674		struct ieee80211_channel *c = &ic->ic_channels[i];
1675
1676		if (c->ic_ieee != ieee)
1677			continue;
1678		if (mode == IEEE80211_MODE_AUTO) {
1679			/* ignore turbo channels for autoselect */
1680			if (IEEE80211_IS_CHAN_TURBO(c))
1681				continue;
1682			/*
1683			 * XXX special-case 11b/g channels so we
1684			 *     always select the g channel if both
1685			 *     are present.
1686			 * XXX prefer HT to non-HT?
1687			 */
1688			if (!IEEE80211_IS_CHAN_B(c) ||
1689			    !find11gchannel(ic, i, c->ic_freq))
1690				return c;
1691		} else {
1692			/* must check HT specially */
1693			if ((mode == IEEE80211_MODE_11NA ||
1694			    mode == IEEE80211_MODE_11NG) &&
1695			    !IEEE80211_IS_CHAN_HT(c))
1696				continue;
1697			if ((c->ic_flags & modeflags) == modeflags)
1698				return c;
1699		}
1700	}
1701	return NULL;
1702}
1703
1704/*
1705 * Check the specified against any desired mode (aka netband).
1706 * This is only used (presently) when operating in hostap mode
1707 * to enforce consistency.
1708 */
1709static int
1710check_mode_consistency(const struct ieee80211_channel *c, int mode)
1711{
1712	KASSERT(c != IEEE80211_CHAN_ANYC, ("oops, no channel"));
1713
1714	switch (mode) {
1715	case IEEE80211_MODE_11B:
1716		return (IEEE80211_IS_CHAN_B(c));
1717	case IEEE80211_MODE_11G:
1718		return (IEEE80211_IS_CHAN_ANYG(c) && !IEEE80211_IS_CHAN_HT(c));
1719	case IEEE80211_MODE_11A:
1720		return (IEEE80211_IS_CHAN_A(c) && !IEEE80211_IS_CHAN_HT(c));
1721	case IEEE80211_MODE_STURBO_A:
1722		return (IEEE80211_IS_CHAN_STURBO(c));
1723	case IEEE80211_MODE_11NA:
1724		return (IEEE80211_IS_CHAN_HTA(c));
1725	case IEEE80211_MODE_11NG:
1726		return (IEEE80211_IS_CHAN_HTG(c));
1727	}
1728	return 1;
1729
1730}
1731
1732/*
1733 * Common code to set the current channel.  If the device
1734 * is up and running this may result in an immediate channel
1735 * change or a kick of the state machine.
1736 */
1737static int
1738setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
1739{
1740	int error;
1741
1742	if (c != IEEE80211_CHAN_ANYC) {
1743		if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
1744		    !check_mode_consistency(c, ic->ic_des_mode))
1745			return EINVAL;
1746		if (ic->ic_state == IEEE80211_S_RUN && c == ic->ic_curchan)
1747			return 0;	/* NB: nothing to do */
1748	}
1749	ic->ic_des_chan = c;
1750
1751	error = 0;
1752	if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
1753	    ic->ic_opmode == IEEE80211_M_WDS) &&
1754	    ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
1755		/*
1756		 * Monitor and wds modes can switch directly.
1757		 */
1758		ic->ic_curchan = ic->ic_des_chan;
1759		if (ic->ic_state == IEEE80211_S_RUN)
1760			ic->ic_set_channel(ic);
1761	} else {
1762		/*
1763		 * Need to go through the state machine in case we
1764		 * need to reassociate or the like.  The state machine
1765		 * will pickup the desired channel and avoid scanning.
1766		 */
1767		if (IS_UP_AUTO(ic))
1768			error = ieee80211_init(ic, RESCAN);
1769		else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
1770			/*
1771			 * When not up+running and a real channel has
1772			 * been specified fix the current channel so
1773			 * there is immediate feedback; e.g. via ifconfig.
1774			 */
1775			ic->ic_curchan = ic->ic_des_chan;
1776		}
1777	}
1778	return error;
1779}
1780
1781/*
1782 * Old api for setting the current channel; this is
1783 * deprecated because channel numbers are ambiguous.
1784 */
1785static int
1786ieee80211_ioctl_setchannel(struct ieee80211com *ic,
1787	const struct ieee80211req *ireq)
1788{
1789	struct ieee80211_channel *c;
1790
1791	/* XXX 0xffff overflows 16-bit signed */
1792	if (ireq->i_val == 0 ||
1793	    ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) {
1794		c = IEEE80211_CHAN_ANYC;
1795	} else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX) {
1796		return EINVAL;
1797	} else {
1798		struct ieee80211_channel *c2;
1799
1800		c = findchannel(ic, ireq->i_val, ic->ic_des_mode);
1801		if (c == NULL) {
1802			c = findchannel(ic, ireq->i_val,
1803				IEEE80211_MODE_AUTO);
1804			if (c == NULL)
1805				return EINVAL;
1806		}
1807		/*
1808		 * Fine tune channel selection based on desired mode:
1809		 *   if 11b is requested, find the 11b version of any
1810		 *      11g channel returned,
1811		 *   if static turbo, find the turbo version of any
1812		 *	11a channel return,
1813		 *   if 11na is requested, find the ht version of any
1814		 *      11a channel returned,
1815		 *   if 11ng is requested, find the ht version of any
1816		 *      11g channel returned,
1817		 *   otherwise we should be ok with what we've got.
1818		 */
1819		switch (ic->ic_des_mode) {
1820		case IEEE80211_MODE_11B:
1821			if (IEEE80211_IS_CHAN_ANYG(c)) {
1822				c2 = findchannel(ic, ireq->i_val,
1823					IEEE80211_MODE_11B);
1824				/* NB: should not happen, =>'s 11g w/o 11b */
1825				if (c2 != NULL)
1826					c = c2;
1827			}
1828			break;
1829		case IEEE80211_MODE_TURBO_A:
1830			if (IEEE80211_IS_CHAN_A(c)) {
1831				c2 = findchannel(ic, ireq->i_val,
1832					IEEE80211_MODE_TURBO_A);
1833				if (c2 != NULL)
1834					c = c2;
1835			}
1836			break;
1837		case IEEE80211_MODE_11NA:
1838			if (IEEE80211_IS_CHAN_A(c)) {
1839				c2 = findchannel(ic, ireq->i_val,
1840					IEEE80211_MODE_11NA);
1841				if (c2 != NULL)
1842					c = c2;
1843			}
1844			break;
1845		case IEEE80211_MODE_11NG:
1846			if (IEEE80211_IS_CHAN_ANYG(c)) {
1847				c2 = findchannel(ic, ireq->i_val,
1848					IEEE80211_MODE_11NG);
1849				if (c2 != NULL)
1850					c = c2;
1851			}
1852			break;
1853		default:		/* NB: no static turboG */
1854			break;
1855		}
1856	}
1857	return setcurchan(ic, c);
1858}
1859
1860/*
1861 * New/current api for setting the current channel; a complete
1862 * channel description is provide so there is no ambiguity in
1863 * identifying the channel.
1864 */
1865static int
1866ieee80211_ioctl_setcurchan(struct ieee80211com *ic,
1867	const struct ieee80211req *ireq)
1868{
1869	struct ieee80211_channel chan, *c;
1870	int error;
1871
1872	if (ireq->i_len != sizeof(chan))
1873		return EINVAL;
1874	error = copyin(ireq->i_data, &chan, sizeof(chan));
1875	if (error != 0)
1876		return error;
1877	/* XXX 0xffff overflows 16-bit signed */
1878	if (chan.ic_freq == 0 || chan.ic_freq == IEEE80211_CHAN_ANY) {
1879		c = IEEE80211_CHAN_ANYC;
1880	} else {
1881		c = ieee80211_find_channel(ic, chan.ic_freq, chan.ic_flags);
1882		if (c == NULL)
1883			return EINVAL;
1884	}
1885	return setcurchan(ic, c);
1886}
1887
1888static int
1889ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
1890{
1891	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
1892	struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
1893	int error;
1894	const struct ieee80211_authenticator *auth;
1895	uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
1896	char tmpssid[IEEE80211_NWID_LEN];
1897	uint8_t tmpbssid[IEEE80211_ADDR_LEN];
1898	struct ieee80211_key *k;
1899	int j, caps;
1900	u_int kid;
1901
1902	error = 0;
1903	switch (ireq->i_type) {
1904	case IEEE80211_IOC_SSID:
1905		if (ireq->i_val != 0 ||
1906		    ireq->i_len > IEEE80211_NWID_LEN)
1907			return EINVAL;
1908		error = copyin(ireq->i_data, tmpssid, ireq->i_len);
1909		if (error)
1910			break;
1911		memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
1912		ic->ic_des_ssid[0].len = ireq->i_len;
1913		memcpy(ic->ic_des_ssid[0].ssid, tmpssid, ireq->i_len);
1914		ic->ic_des_nssid = (ireq->i_len > 0);
1915		if (IS_UP_AUTO(ic))
1916			error = ieee80211_init(ic, RESCAN);
1917		break;
1918	case IEEE80211_IOC_WEP:
1919		switch (ireq->i_val) {
1920		case IEEE80211_WEP_OFF:
1921			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
1922			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
1923			break;
1924		case IEEE80211_WEP_ON:
1925			ic->ic_flags |= IEEE80211_F_PRIVACY;
1926			ic->ic_flags |= IEEE80211_F_DROPUNENC;
1927			break;
1928		case IEEE80211_WEP_MIXED:
1929			ic->ic_flags |= IEEE80211_F_PRIVACY;
1930			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
1931			break;
1932		}
1933		if (IS_UP_AUTO(ic))
1934			error = ieee80211_init(ic, RESCAN);
1935		break;
1936	case IEEE80211_IOC_WEPKEY:
1937		kid = (u_int) ireq->i_val;
1938		if (kid >= IEEE80211_WEP_NKID)
1939			return EINVAL;
1940		k = &ic->ic_nw_keys[kid];
1941		if (ireq->i_len == 0) {
1942			/* zero-len =>'s delete any existing key */
1943			(void) ieee80211_crypto_delkey(ic, k);
1944			break;
1945		}
1946		if (ireq->i_len > sizeof(tmpkey))
1947			return EINVAL;
1948		memset(tmpkey, 0, sizeof(tmpkey));
1949		error = copyin(ireq->i_data, tmpkey, ireq->i_len);
1950		if (error)
1951			break;
1952		ieee80211_key_update_begin(ic);
1953		k->wk_keyix = kid;	/* NB: force fixed key id */
1954		if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP,
1955		    IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) {
1956			k->wk_keylen = ireq->i_len;
1957			memcpy(k->wk_key, tmpkey, sizeof(tmpkey));
1958			if  (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr))
1959				error = EINVAL;
1960		} else
1961			error = EINVAL;
1962		ieee80211_key_update_end(ic);
1963		break;
1964	case IEEE80211_IOC_WEPTXKEY:
1965		kid = (u_int) ireq->i_val;
1966		if (kid >= IEEE80211_WEP_NKID &&
1967		    (uint16_t) kid != IEEE80211_KEYIX_NONE)
1968			return EINVAL;
1969		ic->ic_def_txkey = kid;
1970		break;
1971	case IEEE80211_IOC_AUTHMODE:
1972		switch (ireq->i_val) {
1973		case IEEE80211_AUTH_WPA:
1974		case IEEE80211_AUTH_8021X:	/* 802.1x */
1975		case IEEE80211_AUTH_OPEN:	/* open */
1976		case IEEE80211_AUTH_SHARED:	/* shared-key */
1977		case IEEE80211_AUTH_AUTO:	/* auto */
1978			auth = ieee80211_authenticator_get(ireq->i_val);
1979			if (auth == NULL)
1980				return EINVAL;
1981			break;
1982		default:
1983			return EINVAL;
1984		}
1985		switch (ireq->i_val) {
1986		case IEEE80211_AUTH_WPA:	/* WPA w/ 802.1x */
1987			ic->ic_flags |= IEEE80211_F_PRIVACY;
1988			ireq->i_val = IEEE80211_AUTH_8021X;
1989			break;
1990		case IEEE80211_AUTH_OPEN:	/* open */
1991			ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
1992			break;
1993		case IEEE80211_AUTH_SHARED:	/* shared-key */
1994		case IEEE80211_AUTH_8021X:	/* 802.1x */
1995			ic->ic_flags &= ~IEEE80211_F_WPA;
1996			/* both require a key so mark the PRIVACY capability */
1997			ic->ic_flags |= IEEE80211_F_PRIVACY;
1998			break;
1999		case IEEE80211_AUTH_AUTO:	/* auto */
2000			ic->ic_flags &= ~IEEE80211_F_WPA;
2001			/* XXX PRIVACY handling? */
2002			/* XXX what's the right way to do this? */
2003			break;
2004		}
2005		/* NB: authenticator attach/detach happens on state change */
2006		ic->ic_bss->ni_authmode = ireq->i_val;
2007		/* XXX mixed/mode/usage? */
2008		ic->ic_auth = auth;
2009		if (IS_UP_AUTO(ic))
2010			error = ieee80211_init(ic, RESCAN);
2011		break;
2012	case IEEE80211_IOC_CHANNEL:
2013		error = ieee80211_ioctl_setchannel(ic, ireq);
2014		break;
2015	case IEEE80211_IOC_POWERSAVE:
2016		switch (ireq->i_val) {
2017		case IEEE80211_POWERSAVE_OFF:
2018			if (ic->ic_flags & IEEE80211_F_PMGTON) {
2019				ic->ic_flags &= ~IEEE80211_F_PMGTON;
2020				error = ENETRESET;
2021			}
2022			break;
2023		case IEEE80211_POWERSAVE_ON:
2024			if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
2025				error = EINVAL;
2026			else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
2027				ic->ic_flags |= IEEE80211_F_PMGTON;
2028				error = ENETRESET;
2029			}
2030			break;
2031		default:
2032			error = EINVAL;
2033			break;
2034		}
2035		if (error == ENETRESET) {
2036			/*
2037			 * Switching in+out of power save mode
2038			 * should not require a state change.
2039			 */
2040			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2041		}
2042		break;
2043	case IEEE80211_IOC_POWERSAVESLEEP:
2044		if (ireq->i_val < 0)
2045			return EINVAL;
2046		ic->ic_lintval = ireq->i_val;
2047		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2048		break;
2049	case IEEE80211_IOC_RTSTHRESHOLD:
2050		if (!(IEEE80211_RTS_MIN <= ireq->i_val &&
2051		      ireq->i_val <= IEEE80211_RTS_MAX))
2052			return EINVAL;
2053		ic->ic_rtsthreshold = ireq->i_val;
2054		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2055		break;
2056	case IEEE80211_IOC_PROTMODE:
2057		if (ireq->i_val > IEEE80211_PROT_RTSCTS)
2058			return EINVAL;
2059		ic->ic_protmode = ireq->i_val;
2060		/* NB: if not operating in 11g this can wait */
2061		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
2062		    IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
2063			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2064		break;
2065	case IEEE80211_IOC_TXPOWER:
2066		if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
2067			return EINVAL;
2068		if (!(IEEE80211_TXPOWER_MIN <= ireq->i_val &&
2069		      ireq->i_val <= IEEE80211_TXPOWER_MAX))
2070			return EINVAL;
2071		ic->ic_txpowlimit = ireq->i_val;
2072		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2073		break;
2074	case IEEE80211_IOC_ROAMING:
2075		if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val &&
2076		    ireq->i_val <= IEEE80211_ROAMING_MANUAL))
2077			return EINVAL;
2078		ic->ic_roaming = ireq->i_val;
2079		/* XXXX reset? */
2080		break;
2081	case IEEE80211_IOC_PRIVACY:
2082		if (ireq->i_val) {
2083			/* XXX check for key state? */
2084			ic->ic_flags |= IEEE80211_F_PRIVACY;
2085		} else
2086			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
2087		break;
2088	case IEEE80211_IOC_DROPUNENCRYPTED:
2089		if (ireq->i_val)
2090			ic->ic_flags |= IEEE80211_F_DROPUNENC;
2091		else
2092			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
2093		break;
2094	case IEEE80211_IOC_WPAKEY:
2095		error = ieee80211_ioctl_setkey(ic, ireq);
2096		break;
2097	case IEEE80211_IOC_DELKEY:
2098		error = ieee80211_ioctl_delkey(ic, ireq);
2099		break;
2100	case IEEE80211_IOC_MLME:
2101		error = ieee80211_ioctl_setmlme(ic, ireq);
2102		break;
2103	case IEEE80211_IOC_OPTIE:
2104		error = ieee80211_ioctl_setoptie(ic, ireq);
2105		break;
2106	case IEEE80211_IOC_COUNTERMEASURES:
2107		if (ireq->i_val) {
2108			if ((ic->ic_flags & IEEE80211_F_WPA) == 0)
2109				return EINVAL;
2110			ic->ic_flags |= IEEE80211_F_COUNTERM;
2111		} else
2112			ic->ic_flags &= ~IEEE80211_F_COUNTERM;
2113		break;
2114	case IEEE80211_IOC_WPA:
2115		if (ireq->i_val > 3)
2116			return EINVAL;
2117		/* XXX verify ciphers available */
2118		ic->ic_flags &= ~IEEE80211_F_WPA;
2119		switch (ireq->i_val) {
2120		case 1:
2121			ic->ic_flags |= IEEE80211_F_WPA1;
2122			break;
2123		case 2:
2124			ic->ic_flags |= IEEE80211_F_WPA2;
2125			break;
2126		case 3:
2127			ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
2128			break;
2129		}
2130		error = ENETRESET;
2131		break;
2132	case IEEE80211_IOC_WME:
2133		if (ireq->i_val) {
2134			if ((ic->ic_caps & IEEE80211_C_WME) == 0)
2135				return EINVAL;
2136			ic->ic_flags |= IEEE80211_F_WME;
2137		} else
2138			ic->ic_flags &= ~IEEE80211_F_WME;
2139		if (IS_UP_AUTO(ic))
2140			error = ieee80211_init(ic, 0);
2141		break;
2142	case IEEE80211_IOC_HIDESSID:
2143		if (ireq->i_val)
2144			ic->ic_flags |= IEEE80211_F_HIDESSID;
2145		else
2146			ic->ic_flags &= ~IEEE80211_F_HIDESSID;
2147		error = ENETRESET;
2148		break;
2149	case IEEE80211_IOC_APBRIDGE:
2150		if (ireq->i_val == 0)
2151			ic->ic_flags |= IEEE80211_F_NOBRIDGE;
2152		else
2153			ic->ic_flags &= ~IEEE80211_F_NOBRIDGE;
2154		break;
2155	case IEEE80211_IOC_MCASTCIPHER:
2156		if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 &&
2157		    !ieee80211_crypto_available(ireq->i_val))
2158			return EINVAL;
2159		rsn->rsn_mcastcipher = ireq->i_val;
2160		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
2161		break;
2162	case IEEE80211_IOC_MCASTKEYLEN:
2163		if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
2164			return EINVAL;
2165		/* XXX no way to verify driver capability */
2166		rsn->rsn_mcastkeylen = ireq->i_val;
2167		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
2168		break;
2169	case IEEE80211_IOC_UCASTCIPHERS:
2170		/*
2171		 * Convert user-specified cipher set to the set
2172		 * we can support (via hardware or software).
2173		 * NB: this logic intentionally ignores unknown and
2174		 * unsupported ciphers so folks can specify 0xff or
2175		 * similar and get all available ciphers.
2176		 */
2177		caps = 0;
2178		for (j = 1; j < 32; j++)	/* NB: skip WEP */
2179			if ((ireq->i_val & (1<<j)) &&
2180			    ((ic->ic_caps & cipher2cap(j)) ||
2181			     ieee80211_crypto_available(j)))
2182				caps |= 1<<j;
2183		if (caps == 0)			/* nothing available */
2184			return EINVAL;
2185		/* XXX verify ciphers ok for unicast use? */
2186		/* XXX disallow if running as it'll have no effect */
2187		rsn->rsn_ucastcipherset = caps;
2188		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
2189		break;
2190	case IEEE80211_IOC_UCASTCIPHER:
2191		if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0)
2192			return EINVAL;
2193		rsn->rsn_ucastcipher = ireq->i_val;
2194		break;
2195	case IEEE80211_IOC_UCASTKEYLEN:
2196		if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
2197			return EINVAL;
2198		/* XXX no way to verify driver capability */
2199		rsn->rsn_ucastkeylen = ireq->i_val;
2200		break;
2201	case IEEE80211_IOC_DRIVER_CAPS:
2202		/* NB: for testing */
2203		ic->ic_caps = (((uint16_t) ireq->i_val) << 16) |
2204			       ((uint16_t) ireq->i_len);
2205		break;
2206	case IEEE80211_IOC_KEYMGTALGS:
2207		/* XXX check */
2208		rsn->rsn_keymgmtset = ireq->i_val;
2209		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
2210		break;
2211	case IEEE80211_IOC_RSNCAPS:
2212		/* XXX check */
2213		rsn->rsn_caps = ireq->i_val;
2214		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
2215		break;
2216	case IEEE80211_IOC_BSSID:
2217		if (ireq->i_len != sizeof(tmpbssid))
2218			return EINVAL;
2219		error = copyin(ireq->i_data, tmpbssid, ireq->i_len);
2220		if (error)
2221			break;
2222		IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid);
2223		if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid))
2224			ic->ic_flags &= ~IEEE80211_F_DESBSSID;
2225		else
2226			ic->ic_flags |= IEEE80211_F_DESBSSID;
2227		if (IS_UP_AUTO(ic))
2228			error = ieee80211_init(ic, RESCAN);
2229		break;
2230	case IEEE80211_IOC_CHANLIST:
2231		error = ieee80211_ioctl_setchanlist(ic, ireq);
2232		break;
2233	case IEEE80211_IOC_SCAN_REQ:
2234		if (!IS_UP(ic))
2235			return EINVAL;
2236		(void) ieee80211_start_scan(ic,
2237			IEEE80211_SCAN_ACTIVE |
2238			IEEE80211_SCAN_NOPICK |
2239			IEEE80211_SCAN_ONCE, IEEE80211_SCAN_FOREVER,
2240			/* XXX use ioctl params */
2241			ic->ic_des_nssid, ic->ic_des_ssid);
2242		break;
2243	case IEEE80211_IOC_ADDMAC:
2244	case IEEE80211_IOC_DELMAC:
2245		error = ieee80211_ioctl_macmac(ic, ireq);
2246		break;
2247	case IEEE80211_IOC_MACCMD:
2248		error = ieee80211_ioctl_setmaccmd(ic, ireq);
2249		break;
2250	case IEEE80211_IOC_STA_STATS:
2251		error = ieee80211_ioctl_setstastats(ic, ireq);
2252		break;
2253	case IEEE80211_IOC_STA_TXPOW:
2254		error = ieee80211_ioctl_setstatxpow(ic, ireq);
2255		break;
2256	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
2257	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
2258	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
2259	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
2260	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
2261	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (bss only) */
2262		error = ieee80211_ioctl_setwmeparam(ic, ireq);
2263		break;
2264	case IEEE80211_IOC_DTIM_PERIOD:
2265		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
2266		    ic->ic_opmode != IEEE80211_M_IBSS)
2267			return EINVAL;
2268		if (IEEE80211_DTIM_MIN <= ireq->i_val &&
2269		    ireq->i_val <= IEEE80211_DTIM_MAX) {
2270			ic->ic_dtim_period = ireq->i_val;
2271			error = ENETRESET;		/* requires restart */
2272		} else
2273			error = EINVAL;
2274		break;
2275	case IEEE80211_IOC_BEACON_INTERVAL:
2276		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
2277		    ic->ic_opmode != IEEE80211_M_IBSS)
2278			return EINVAL;
2279		if (IEEE80211_BINTVAL_MIN <= ireq->i_val &&
2280		    ireq->i_val <= IEEE80211_BINTVAL_MAX) {
2281			ic->ic_bintval = ireq->i_val;
2282			error = ENETRESET;		/* requires restart */
2283		} else
2284			error = EINVAL;
2285		break;
2286	case IEEE80211_IOC_PUREG:
2287		if (ireq->i_val)
2288			ic->ic_flags |= IEEE80211_F_PUREG;
2289		else
2290			ic->ic_flags &= ~IEEE80211_F_PUREG;
2291		/* NB: reset only if we're operating on an 11g channel */
2292		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
2293		    IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
2294			error = ENETRESET;
2295		break;
2296	case IEEE80211_IOC_FF:
2297		if (ireq->i_val) {
2298			if ((ic->ic_caps & IEEE80211_C_FF) == 0)
2299				return EINVAL;
2300			ic->ic_flags |= IEEE80211_F_FF;
2301		} else
2302			ic->ic_flags &= ~IEEE80211_F_FF;
2303		error = ENETRESET;
2304		break;
2305	case IEEE80211_IOC_TURBOP:
2306		if (ireq->i_val) {
2307			if ((ic->ic_caps & IEEE80211_C_TURBOP) == 0)
2308				return EINVAL;
2309			ic->ic_flags |= IEEE80211_F_TURBOP;
2310		} else
2311			ic->ic_flags &= ~IEEE80211_F_TURBOP;
2312		error = ENETRESET;
2313		break;
2314	case IEEE80211_IOC_BGSCAN:
2315		if (ireq->i_val) {
2316			if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0)
2317				return EINVAL;
2318			ic->ic_flags |= IEEE80211_F_BGSCAN;
2319		} else
2320			ic->ic_flags &= ~IEEE80211_F_BGSCAN;
2321		break;
2322	case IEEE80211_IOC_BGSCAN_IDLE:
2323		if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN)
2324			ic->ic_bgscanidle = ireq->i_val*hz/1000;
2325		else
2326			error = EINVAL;
2327		break;
2328	case IEEE80211_IOC_BGSCAN_INTERVAL:
2329		if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN)
2330			ic->ic_bgscanintvl = ireq->i_val*hz;
2331		else
2332			error = EINVAL;
2333		break;
2334	case IEEE80211_IOC_SCANVALID:
2335		if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN)
2336			ic->ic_scanvalid = ireq->i_val*hz;
2337		else
2338			error = EINVAL;
2339		break;
2340	case IEEE80211_IOC_ROAM_RSSI_11A:
2341		ic->ic_roam.rssi11a = ireq->i_val;
2342		break;
2343	case IEEE80211_IOC_ROAM_RSSI_11B:
2344		ic->ic_roam.rssi11bOnly = ireq->i_val;
2345		break;
2346	case IEEE80211_IOC_ROAM_RSSI_11G:
2347		ic->ic_roam.rssi11b = ireq->i_val;
2348		break;
2349	case IEEE80211_IOC_ROAM_RATE_11A:
2350		ic->ic_roam.rate11a = ireq->i_val & IEEE80211_RATE_VAL;
2351		break;
2352	case IEEE80211_IOC_ROAM_RATE_11B:
2353		ic->ic_roam.rate11bOnly = ireq->i_val & IEEE80211_RATE_VAL;
2354		break;
2355	case IEEE80211_IOC_ROAM_RATE_11G:
2356		ic->ic_roam.rate11b = ireq->i_val & IEEE80211_RATE_VAL;
2357		break;
2358	case IEEE80211_IOC_MCAST_RATE:
2359		ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL;
2360		break;
2361	case IEEE80211_IOC_FRAGTHRESHOLD:
2362		if ((ic->ic_caps & IEEE80211_C_TXFRAG) == 0 &&
2363		    ireq->i_val != IEEE80211_FRAG_MAX)
2364			return EINVAL;
2365		if (!(IEEE80211_FRAG_MIN <= ireq->i_val &&
2366		      ireq->i_val <= IEEE80211_FRAG_MAX))
2367			return EINVAL;
2368		ic->ic_fragthreshold = ireq->i_val;
2369		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2370		break;
2371	case IEEE80211_IOC_BURST:
2372		if (ireq->i_val) {
2373			if ((ic->ic_caps & IEEE80211_C_BURST) == 0)
2374				return EINVAL;
2375			ic->ic_flags |= IEEE80211_F_BURST;
2376		} else
2377			ic->ic_flags &= ~IEEE80211_F_BURST;
2378		error = ENETRESET;		/* XXX maybe not for station? */
2379		break;
2380	case IEEE80211_IOC_BMISSTHRESHOLD:
2381		if (!(IEEE80211_HWBMISS_MIN <= ireq->i_val &&
2382		      ireq->i_val <= IEEE80211_HWBMISS_MAX))
2383			return EINVAL;
2384		ic->ic_bmissthreshold = ireq->i_val;
2385		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2386		break;
2387	case IEEE80211_IOC_CURCHAN:
2388		error = ieee80211_ioctl_setcurchan(ic, ireq);
2389		break;
2390	case IEEE80211_IOC_SHORTGI:
2391		if (ireq->i_val) {
2392#define	IEEE80211_HTCAP_SHORTGI \
2393	(IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40)
2394			if (((ireq->i_val ^ ic->ic_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0)
2395				return EINVAL;
2396			if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20)
2397				ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
2398			if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40)
2399				ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
2400#undef IEEE80211_HTCAP_SHORTGI
2401		} else
2402			ic->ic_flags_ext &=
2403			    ~(IEEE80211_FEXT_SHORTGI20 | IEEE80211_FEXT_SHORTGI40);
2404		/* XXX kick state machine? */
2405		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2406		break;
2407	case IEEE80211_IOC_AMPDU:
2408		if (ireq->i_val) {
2409			if ((ic->ic_htcaps & IEEE80211_HTC_AMPDU) == 0)
2410				return EINVAL;
2411			if (ireq->i_val & 1)
2412				ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
2413			if (ireq->i_val & 2)
2414				ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
2415		} else
2416			ic->ic_flags_ext &=
2417			    ~(IEEE80211_FEXT_AMPDU_TX|IEEE80211_FEXT_AMPDU_RX);
2418		/* NB: reset only if we're operating on an 11n channel */
2419		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
2420		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
2421			error = ENETRESET;
2422		break;
2423	case IEEE80211_IOC_AMPDU_LIMIT:
2424		/* XXX validate */
2425		ic->ic_ampdu_limit = ireq->i_val;
2426		break;
2427	case IEEE80211_IOC_AMPDU_DENSITY:
2428		/* XXX validate */
2429		ic->ic_ampdu_density = ireq->i_val;
2430		break;
2431	case IEEE80211_IOC_AMSDU:
2432		if (ireq->i_val) {
2433			if ((ic->ic_htcaps & IEEE80211_HTC_AMSDU) == 0)
2434				return EINVAL;
2435			if (ireq->i_val & 1)
2436				ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
2437			if (ireq->i_val & 2)
2438				ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
2439		} else
2440			ic->ic_flags_ext &=
2441			    ~(IEEE80211_FEXT_AMSDU_TX|IEEE80211_FEXT_AMSDU_RX);
2442		/* NB: reset only if we're operating on an 11n channel */
2443		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
2444		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
2445			error = ENETRESET;
2446		break;
2447	case IEEE80211_IOC_AMSDU_LIMIT:
2448		/* XXX validate */
2449		ic->ic_amsdu_limit = ireq->i_val;	/* XXX truncation? */
2450		break;
2451	case IEEE80211_IOC_PUREN:
2452		if (ireq->i_val) {
2453			if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
2454				return EINVAL;
2455			ic->ic_flags_ext |= IEEE80211_FEXT_PUREN;
2456		} else
2457			ic->ic_flags_ext &= ~IEEE80211_FEXT_PUREN;
2458		/* NB: reset only if we're operating on an 11n channel */
2459		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
2460		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
2461			error = ENETRESET;
2462		break;
2463	case IEEE80211_IOC_DOTH:
2464		if (ireq->i_val) {
2465#if 0
2466			/* XXX no capability */
2467			if ((ic->ic_caps & IEEE80211_C_DOTH) == 0)
2468				return EINVAL;
2469#endif
2470			ic->ic_flags |= IEEE80211_F_DOTH;
2471		} else
2472			ic->ic_flags &= ~IEEE80211_F_DOTH;
2473		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2474		break;
2475	case IEEE80211_IOC_HTCOMPAT:
2476		if (ireq->i_val) {
2477			if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
2478				return EINVAL;
2479			ic->ic_flags_ext |= IEEE80211_FEXT_HTCOMPAT;
2480		} else
2481			ic->ic_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT;
2482		/* NB: reset only if we're operating on an 11n channel */
2483		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
2484		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
2485			error = ENETRESET;
2486		break;
2487	case IEEE80211_IOC_INACTIVITY:
2488		if (ireq->i_val)
2489			ic->ic_flags_ext |= IEEE80211_FEXT_INACT;
2490		else
2491			ic->ic_flags_ext &= ~IEEE80211_FEXT_INACT;
2492		break;
2493	case IEEE80211_IOC_HTPROTMODE:
2494		if (ireq->i_val > IEEE80211_PROT_RTSCTS)
2495			return EINVAL;
2496		ic->ic_htprotmode = ireq->i_val ?
2497		    IEEE80211_PROT_RTSCTS : IEEE80211_PROT_NONE;
2498		/* NB: if not operating in 11n this can wait */
2499		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
2500		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
2501			error = ERESTART;
2502		break;
2503	case IEEE80211_IOC_HTCONF:
2504		if (ireq->i_val & 1)
2505			ic->ic_flags_ext |= IEEE80211_FEXT_HT;
2506		else
2507			ic->ic_flags_ext &= ~IEEE80211_FEXT_HT;
2508		if (ireq->i_val & 2)
2509			ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
2510		else
2511			ic->ic_flags_ext &= ~IEEE80211_FEXT_USEHT40;
2512		error = ENETRESET;
2513		break;
2514	default:
2515		error = EINVAL;
2516		break;
2517	}
2518	if (error == ENETRESET)
2519		error = IS_UP_AUTO(ic) ? ieee80211_init(ic, 0) : 0;
2520	return error;
2521}
2522
2523int
2524ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data)
2525{
2526	struct ifnet *ifp = ic->ic_ifp;
2527	int error = 0;
2528	struct ifreq *ifr;
2529	struct ifaddr *ifa;			/* XXX */
2530
2531	switch (cmd) {
2532	case SIOCSIFMEDIA:
2533	case SIOCGIFMEDIA:
2534		error = ifmedia_ioctl(ifp, (struct ifreq *) data,
2535				&ic->ic_media, cmd);
2536		break;
2537	case SIOCG80211:
2538		error = ieee80211_ioctl_get80211(ic, cmd,
2539				(struct ieee80211req *) data);
2540		break;
2541	case SIOCS80211:
2542		error = priv_check(curthread, PRIV_NET80211_MANAGE);
2543		if (error == 0)
2544			error = ieee80211_ioctl_set80211(ic, cmd,
2545					(struct ieee80211req *) data);
2546		break;
2547	case SIOCG80211STATS:
2548		ifr = (struct ifreq *)data;
2549		copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats));
2550		break;
2551	case SIOCSIFMTU:
2552		ifr = (struct ifreq *)data;
2553		if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu &&
2554		    ifr->ifr_mtu <= IEEE80211_MTU_MAX))
2555			error = EINVAL;
2556		else
2557			ifp->if_mtu = ifr->ifr_mtu;
2558		break;
2559	case SIOCSIFADDR:
2560		/*
2561		 * XXX Handle this directly so we can supress if_init calls.
2562		 * XXX This should be done in ether_ioctl but for the moment
2563		 * XXX there are too many other parts of the system that
2564		 * XXX set IFF_UP and so supress if_init being called when
2565		 * XXX it should be.
2566		 */
2567		ifa = (struct ifaddr *) data;
2568		switch (ifa->ifa_addr->sa_family) {
2569#ifdef INET
2570		case AF_INET:
2571			if ((ifp->if_flags & IFF_UP) == 0) {
2572				ifp->if_flags |= IFF_UP;
2573				ifp->if_init(ifp->if_softc);
2574			}
2575			arp_ifinit(ifp, ifa);
2576			break;
2577#endif
2578#ifdef IPX
2579		/*
2580		 * XXX - This code is probably wrong,
2581		 *	 but has been copied many times.
2582		 */
2583		case AF_IPX: {
2584			struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr);
2585
2586			if (ipx_nullhost(*ina))
2587				ina->x_host = *(union ipx_host *)
2588				    IF_LLADDR(ifp);
2589			else
2590				bcopy((caddr_t) ina->x_host.c_host,
2591				      (caddr_t) IF_LLADDR(ifp),
2592				      ETHER_ADDR_LEN);
2593			/* fall thru... */
2594		}
2595#endif
2596		default:
2597			if ((ifp->if_flags & IFF_UP) == 0) {
2598				ifp->if_flags |= IFF_UP;
2599				ifp->if_init(ifp->if_softc);
2600			}
2601			break;
2602		}
2603		break;
2604	default:
2605		error = ether_ioctl(ifp, cmd, data);
2606		break;
2607	}
2608	return error;
2609}
2610