ieee80211_ioctl.c revision 170530
1116742Ssam/*-
2116904Ssam * Copyright (c) 2001 Atsushi Onoe
3170360Ssam * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
4116742Ssam * All rights reserved.
5116742Ssam *
6116742Ssam * Redistribution and use in source and binary forms, with or without
7116742Ssam * modification, are permitted provided that the following conditions
8116742Ssam * are met:
9116742Ssam * 1. Redistributions of source code must retain the above copyright
10116904Ssam *    notice, this list of conditions and the following disclaimer.
11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright
12116904Ssam *    notice, this list of conditions and the following disclaimer in the
13116904Ssam *    documentation and/or other materials provided with the distribution.
14116742Ssam *
15116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25116742Ssam */
26116742Ssam
27116742Ssam#include <sys/cdefs.h>
28116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ioctl.c 170530 2007-06-11 03:36:55Z sam $");
29116742Ssam
30162659Sru#include "opt_compat.h"
31162659Sru
32116742Ssam/*
33116742Ssam * IEEE 802.11 ioctl support (FreeBSD-specific)
34116742Ssam */
35116742Ssam
36127646Ssam#include "opt_inet.h"
37127646Ssam#include "opt_ipx.h"
38127646Ssam
39116742Ssam#include <sys/endian.h>
40116742Ssam#include <sys/param.h>
41116742Ssam#include <sys/kernel.h>
42164033Srwatson#include <sys/priv.h>
43116742Ssam#include <sys/socket.h>
44116742Ssam#include <sys/sockio.h>
45116742Ssam#include <sys/systm.h>
46116742Ssam
47116742Ssam#include <net/if.h>
48152315Sru#include <net/if_dl.h>
49116742Ssam#include <net/if_media.h>
50116742Ssam#include <net/ethernet.h>
51116742Ssam
52127646Ssam#ifdef INET
53127646Ssam#include <netinet/in.h>
54127646Ssam#include <netinet/if_ether.h>
55127646Ssam#endif
56127646Ssam
57127646Ssam#ifdef IPX
58127646Ssam#include <netipx/ipx.h>
59127646Ssam#include <netipx/ipx_if.h>
60127646Ssam#endif
61127646Ssam
62116742Ssam#include <net80211/ieee80211_var.h>
63116742Ssam#include <net80211/ieee80211_ioctl.h>
64116742Ssam
65138568Ssam#define	IS_UP(_ic) \
66148887Srwatson	(((_ic)->ic_ifp->if_flags & IFF_UP) &&			\
67148887Srwatson	    ((_ic)->ic_ifp->if_drv_flags & IFF_DRV_RUNNING))
68138568Ssam#define	IS_UP_AUTO(_ic) \
69138568Ssam	(IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO)
70170530Ssam#define	RESCAN	1
71138568Ssam
72170530Ssamstatic struct ieee80211_channel *findchannel(struct ieee80211com *,
73170530Ssam		int ieee, int mode);
74116742Ssam
75116742Ssamstatic int
76138568Ssamcap2cipher(int flag)
77138568Ssam{
78138568Ssam	switch (flag) {
79138568Ssam	case IEEE80211_C_WEP:		return IEEE80211_CIPHER_WEP;
80138568Ssam	case IEEE80211_C_AES:		return IEEE80211_CIPHER_AES_OCB;
81138568Ssam	case IEEE80211_C_AES_CCM:	return IEEE80211_CIPHER_AES_CCM;
82138568Ssam	case IEEE80211_C_CKIP:		return IEEE80211_CIPHER_CKIP;
83138568Ssam	case IEEE80211_C_TKIP:		return IEEE80211_CIPHER_TKIP;
84138568Ssam	}
85138568Ssam	return -1;
86138568Ssam}
87138568Ssam
88138568Ssamstatic int
89138568Ssamieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq)
90138568Ssam{
91138568Ssam	struct ieee80211_node *ni;
92138568Ssam	struct ieee80211req_key ik;
93138568Ssam	struct ieee80211_key *wk;
94138568Ssam	const struct ieee80211_cipher *cip;
95138568Ssam	u_int kid;
96138568Ssam	int error;
97138568Ssam
98138568Ssam	if (ireq->i_len != sizeof(ik))
99138568Ssam		return EINVAL;
100138568Ssam	error = copyin(ireq->i_data, &ik, sizeof(ik));
101138568Ssam	if (error)
102138568Ssam		return error;
103138568Ssam	kid = ik.ik_keyix;
104138568Ssam	if (kid == IEEE80211_KEYIX_NONE) {
105140753Ssam		ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
106138568Ssam		if (ni == NULL)
107138568Ssam			return EINVAL;		/* XXX */
108138568Ssam		wk = &ni->ni_ucastkey;
109138568Ssam	} else {
110138568Ssam		if (kid >= IEEE80211_WEP_NKID)
111138568Ssam			return EINVAL;
112138568Ssam		wk = &ic->ic_nw_keys[kid];
113138568Ssam		IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr);
114138568Ssam		ni = NULL;
115138568Ssam	}
116138568Ssam	cip = wk->wk_cipher;
117138568Ssam	ik.ik_type = cip->ic_cipher;
118138568Ssam	ik.ik_keylen = wk->wk_keylen;
119138568Ssam	ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
120138568Ssam	if (wk->wk_keyix == ic->ic_def_txkey)
121138568Ssam		ik.ik_flags |= IEEE80211_KEY_DEFAULT;
122164033Srwatson	if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
123138568Ssam		/* NB: only root can read key data */
124138568Ssam		ik.ik_keyrsc = wk->wk_keyrsc;
125138568Ssam		ik.ik_keytsc = wk->wk_keytsc;
126138568Ssam		memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen);
127138568Ssam		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) {
128138568Ssam			memcpy(ik.ik_keydata+wk->wk_keylen,
129138568Ssam				wk->wk_key + IEEE80211_KEYBUF_SIZE,
130138568Ssam				IEEE80211_MICBUF_SIZE);
131138568Ssam			ik.ik_keylen += IEEE80211_MICBUF_SIZE;
132138568Ssam		}
133138568Ssam	} else {
134138568Ssam		ik.ik_keyrsc = 0;
135138568Ssam		ik.ik_keytsc = 0;
136138568Ssam		memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata));
137138568Ssam	}
138138568Ssam	if (ni != NULL)
139138568Ssam		ieee80211_free_node(ni);
140138568Ssam	return copyout(&ik, ireq->i_data, sizeof(ik));
141138568Ssam}
142138568Ssam
143138568Ssamstatic int
144138568Ssamieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
145138568Ssam{
146138568Ssam
147153344Ssam	if (sizeof(ic->ic_chan_active) < ireq->i_len)
148138568Ssam		ireq->i_len = sizeof(ic->ic_chan_active);
149138568Ssam	return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len);
150138568Ssam}
151138568Ssam
152138568Ssamstatic int
153138568Ssamieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq)
154138568Ssam{
155170530Ssam	int space;
156138568Ssam
157138568Ssam	space = __offsetof(struct ieee80211req_chaninfo,
158170530Ssam			ic_chans[ic->ic_nchans]);
159138568Ssam	if (space > ireq->i_len)
160138568Ssam		space = ireq->i_len;
161170530Ssam	/* XXX assumes compatible layout */
162170530Ssam	return copyout(&ic->ic_nchans, ireq->i_data, space);
163138568Ssam}
164138568Ssam
165138568Ssamstatic int
166170530Ssamieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int req)
167138568Ssam{
168138568Ssam	struct ieee80211_node *ni;
169170530Ssam	struct ieee80211req_wpaie2 wpaie;
170138568Ssam	int error;
171138568Ssam
172138568Ssam	if (ireq->i_len < IEEE80211_ADDR_LEN)
173138568Ssam		return EINVAL;
174138568Ssam	error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN);
175138568Ssam	if (error != 0)
176138568Ssam		return error;
177140753Ssam	ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr);
178138568Ssam	if (ni == NULL)
179170530Ssam		return ENOENT;		/* XXX */
180138568Ssam	memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie));
181138568Ssam	if (ni->ni_wpa_ie != NULL) {
182138568Ssam		int ielen = ni->ni_wpa_ie[1] + 2;
183138568Ssam		if (ielen > sizeof(wpaie.wpa_ie))
184138568Ssam			ielen = sizeof(wpaie.wpa_ie);
185138568Ssam		memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen);
186138568Ssam	}
187170530Ssam	if (req == IEEE80211_IOC_WPAIE2) {
188170530Ssam		memset(wpaie.rsn_ie, 0, sizeof(wpaie.rsn_ie));
189170530Ssam		if (ni->ni_rsn_ie != NULL) {
190170530Ssam			int ielen = ni->ni_rsn_ie[1] + 2;
191170530Ssam			if (ielen > sizeof(wpaie.rsn_ie))
192170530Ssam				ielen = sizeof(wpaie.rsn_ie);
193170530Ssam			memcpy(wpaie.rsn_ie, ni->ni_rsn_ie, ielen);
194170530Ssam		}
195170530Ssam		if (ireq->i_len > sizeof(struct ieee80211req_wpaie2))
196170530Ssam			ireq->i_len = sizeof(struct ieee80211req_wpaie2);
197170530Ssam	} else {
198170530Ssam		/* compatibility op, may overwrite wpa ie */
199170530Ssam		/* XXX check ic_flags? */
200170530Ssam		if (ni->ni_rsn_ie != NULL) {
201170530Ssam			int ielen = ni->ni_rsn_ie[1] + 2;
202170530Ssam			if (ielen > sizeof(wpaie.wpa_ie))
203170530Ssam				ielen = sizeof(wpaie.wpa_ie);
204170530Ssam			memcpy(wpaie.wpa_ie, ni->ni_rsn_ie, ielen);
205170530Ssam		}
206170530Ssam		if (ireq->i_len > sizeof(struct ieee80211req_wpaie))
207170530Ssam			ireq->i_len = sizeof(struct ieee80211req_wpaie);
208170530Ssam	}
209138568Ssam	ieee80211_free_node(ni);
210138568Ssam	return copyout(&wpaie, ireq->i_data, ireq->i_len);
211138568Ssam}
212138568Ssam
213138568Ssamstatic int
214138568Ssamieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
215138568Ssam{
216138568Ssam	struct ieee80211_node *ni;
217170530Ssam	uint8_t macaddr[IEEE80211_ADDR_LEN];
218138568Ssam	const int off = __offsetof(struct ieee80211req_sta_stats, is_stats);
219138568Ssam	int error;
220138568Ssam
221138568Ssam	if (ireq->i_len < off)
222138568Ssam		return EINVAL;
223138568Ssam	error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
224138568Ssam	if (error != 0)
225138568Ssam		return error;
226140753Ssam	ni = ieee80211_find_node(&ic->ic_sta, macaddr);
227170530Ssam	if (ni == NULL)
228170530Ssam		return EINVAL;
229138568Ssam	if (ireq->i_len > sizeof(struct ieee80211req_sta_stats))
230138568Ssam		ireq->i_len = sizeof(struct ieee80211req_sta_stats);
231138568Ssam	/* NB: copy out only the statistics */
232170530Ssam	error = copyout(&ni->ni_stats, (uint8_t *) ireq->i_data + off,
233138568Ssam			ireq->i_len - off);
234138568Ssam	ieee80211_free_node(ni);
235138568Ssam	return error;
236138568Ssam}
237138568Ssam
238170530Ssamstatic __inline uint8_t *
239170530Ssamcopyie(uint8_t *cp, const uint8_t *ie)
240170530Ssam{
241170530Ssam	if (ie != NULL) {
242170530Ssam		memcpy(cp, ie, 2+ie[1]);
243170530Ssam		cp += 2+ie[1];
244170530Ssam	}
245170530Ssam	return cp;
246170530Ssam}
247170530Ssam
248154541Ssam#ifdef COMPAT_FREEBSD6
249154541Ssam#define	IEEE80211_IOC_SCAN_RESULTS_OLD	24
250154541Ssam
251154541Ssamstruct scan_result_old {
252170530Ssam	uint16_t	isr_len;		/* length (mult of 4) */
253170530Ssam	uint16_t	isr_freq;		/* MHz */
254170530Ssam	uint16_t	isr_flags;		/* channel flags */
255170530Ssam	uint8_t		isr_noise;
256170530Ssam	uint8_t		isr_rssi;
257170530Ssam	uint8_t		isr_intval;		/* beacon interval */
258170530Ssam	uint8_t		isr_capinfo;		/* capabilities */
259170530Ssam	uint8_t		isr_erp;		/* ERP element */
260170530Ssam	uint8_t		isr_bssid[IEEE80211_ADDR_LEN];
261170530Ssam	uint8_t		isr_nrates;
262170530Ssam	uint8_t		isr_rates[IEEE80211_RATE_MAXSIZE];
263170530Ssam	uint8_t		isr_ssid_len;		/* SSID length */
264170530Ssam	uint8_t		isr_ie_len;		/* IE length */
265170530Ssam	uint8_t		isr_pad[5];
266154541Ssam	/* variable length SSID followed by IE data */
267154541Ssam};
268154541Ssam
269170530Ssamstruct oscanreq {
270170530Ssam	struct scan_result_old *sr;
271170530Ssam	size_t space;
272170530Ssam};
273170530Ssam
274170530Ssamstatic size_t
275170530Ssamold_scan_space(const struct ieee80211_scan_entry *se, int *ielen)
276170530Ssam{
277170530Ssam	size_t len;
278170530Ssam
279170530Ssam	*ielen = 0;
280170530Ssam	if (se->se_wpa_ie != NULL)
281170530Ssam		*ielen += 2+se->se_wpa_ie[1];
282170530Ssam	if (se->se_wme_ie != NULL)
283170530Ssam		*ielen += 2+se->se_wme_ie[1];
284170530Ssam	/*
285170530Ssam	 * NB: ie's can be no more than 255 bytes and the max 802.11
286170530Ssam	 * packet is <3Kbytes so we are sure this doesn't overflow
287170530Ssam	 * 16-bits; if this is a concern we can drop the ie's.
288170530Ssam	 */
289170530Ssam	len = sizeof(struct scan_result_old) + se->se_ssid[1] + *ielen;
290170530Ssam	return roundup(len, sizeof(uint32_t));
291170530Ssam}
292170530Ssam
293138568Ssamstatic void
294170530Ssamold_get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
295138568Ssam{
296170530Ssam	struct oscanreq *req = arg;
297170530Ssam	int ielen;
298138568Ssam
299170530Ssam	req->space += old_scan_space(se, &ielen);
300170530Ssam}
301170530Ssam
302170530Ssamstatic void
303170530Ssamold_get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
304170530Ssam{
305170530Ssam	struct oscanreq *req = arg;
306170530Ssam	struct scan_result_old *sr;
307170530Ssam	int ielen, len, nr, nxr;
308170530Ssam	uint8_t *cp;
309170530Ssam
310170530Ssam	len = old_scan_space(se, &ielen);
311170530Ssam	if (len > req->space)
312170530Ssam		return;
313170530Ssam
314170530Ssam	sr = req->sr;
315138568Ssam	memset(sr, 0, sizeof(*sr));
316170530Ssam	sr->isr_ssid_len = se->se_ssid[1];
317154541Ssam	/* NB: beware of overflow, isr_ie_len is 8 bits */
318154541Ssam	sr->isr_ie_len = (ielen > 255 ? 0 : ielen);
319170530Ssam	sr->isr_len = len;
320170530Ssam	sr->isr_freq = se->se_chan->ic_freq;
321170530Ssam	sr->isr_flags = se->se_chan->ic_flags;
322170530Ssam	sr->isr_rssi = se->se_rssi;
323170530Ssam	sr->isr_noise = se->se_noise;
324170530Ssam	sr->isr_intval = se->se_intval;
325170530Ssam	sr->isr_capinfo = se->se_capinfo;
326170530Ssam	sr->isr_erp = se->se_erp;
327170530Ssam	IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid);
328170530Ssam	nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE);
329170530Ssam	memcpy(sr->isr_rates, se->se_rates+2, nr);
330170530Ssam	nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr);
331170530Ssam	memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
332170530Ssam	sr->isr_nrates = nr + nxr;
333170530Ssam
334170530Ssam	cp = (uint8_t *)(sr+1);
335170530Ssam	memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
336170530Ssam	cp += sr->isr_ssid_len;
337170530Ssam	if (sr->isr_ie_len) {
338170530Ssam		cp = copyie(cp, se->se_wpa_ie);
339170530Ssam		cp = copyie(cp, se->se_wme_ie);
340138568Ssam	}
341170530Ssam
342170530Ssam	req->space -= len;
343170530Ssam	req->sr = (struct scan_result_old *)(((uint8_t *)sr) + len);
344138568Ssam}
345138568Ssam
346138568Ssamstatic int
347154541Ssamold_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
348138568Ssam{
349170530Ssam	struct oscanreq req;
350170530Ssam	int error;
351138568Ssam
352170530Ssam	if (ireq->i_len < sizeof(struct scan_result_old))
353170530Ssam		return EFAULT;
354170530Ssam
355138568Ssam	error = 0;
356170530Ssam	req.space = 0;
357170530Ssam	ieee80211_scan_iterate(ic, old_get_scan_space, &req);
358170530Ssam	if (req.space > ireq->i_len)
359170530Ssam		req.space = ireq->i_len;
360170530Ssam	if (req.space > 0) {
361170530Ssam		size_t space;
362170530Ssam		void *p;
363170530Ssam
364170530Ssam		space = req.space;
365170530Ssam		/* XXX M_WAITOK after driver lock released */
366170530Ssam		MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
367170530Ssam		if (p == NULL)
368170530Ssam			return ENOMEM;
369170530Ssam		req.sr = p;
370170530Ssam		ieee80211_scan_iterate(ic, old_get_scan_result, &req);
371170530Ssam		ireq->i_len = space - req.space;
372170530Ssam		error = copyout(p, ireq->i_data, ireq->i_len);
373170530Ssam		FREE(p, M_TEMP);
374170530Ssam	} else
375170530Ssam		ireq->i_len = 0;
376170530Ssam
377138568Ssam	return error;
378138568Ssam}
379154541Ssam#endif /* COMPAT_FREEBSD6 */
380138568Ssam
381170530Ssamstruct scanreq {
382154541Ssam	struct ieee80211req_scan_result *sr;
383170530Ssam	size_t space;
384154541Ssam};
385154541Ssam
386154541Ssamstatic size_t
387170530Ssamscan_space(const struct ieee80211_scan_entry *se, int *ielen)
388154541Ssam{
389154541Ssam	size_t len;
390154541Ssam
391154541Ssam	*ielen = 0;
392170530Ssam	if (se->se_wpa_ie != NULL)
393170530Ssam		*ielen += 2+se->se_wpa_ie[1];
394170530Ssam	if (se->se_rsn_ie != NULL)
395170530Ssam		*ielen += 2+se->se_rsn_ie[1];
396170530Ssam	if (se->se_wme_ie != NULL)
397170530Ssam		*ielen += 2+se->se_wme_ie[1];
398170530Ssam	if (se->se_ath_ie != NULL)
399170530Ssam		*ielen += 2+se->se_ath_ie[1];
400154541Ssam	/*
401154541Ssam	 * NB: ie's can be no more than 255 bytes and the max 802.11
402154541Ssam	 * packet is <3Kbytes so we are sure this doesn't overflow
403154541Ssam	 * 16-bits; if this is a concern we can drop the ie's.
404154541Ssam	 */
405170530Ssam	len = sizeof(struct ieee80211req_scan_result) + se->se_ssid[1] + *ielen;
406170530Ssam	return roundup(len, sizeof(uint32_t));
407154541Ssam}
408154541Ssam
409154541Ssamstatic void
410170530Ssamget_scan_space(void *arg, const struct ieee80211_scan_entry *se)
411154541Ssam{
412170530Ssam	struct scanreq *req = arg;
413170530Ssam	int ielen;
414154541Ssam
415170530Ssam	req->space += scan_space(se, &ielen);
416154541Ssam}
417154541Ssam
418154541Ssamstatic void
419170530Ssamget_scan_result(void *arg, const struct ieee80211_scan_entry *se)
420154541Ssam{
421170530Ssam	struct scanreq *req = arg;
422154541Ssam	struct ieee80211req_scan_result *sr;
423170530Ssam	int ielen, len, nr, nxr;
424170530Ssam	uint8_t *cp;
425154541Ssam
426170530Ssam	len = scan_space(se, &ielen);
427154541Ssam	if (len > req->space)
428154541Ssam		return;
429170530Ssam
430154541Ssam	sr = req->sr;
431154541Ssam	KASSERT(len <= 65535 && ielen <= 65535,
432170530Ssam	    ("len %u ssid %u ie %u", len, se->se_ssid[1], ielen));
433170530Ssam	sr->isr_ie_off = sizeof(struct ieee80211req_scan_result);
434170530Ssam	sr->isr_ie_len = ielen;
435154541Ssam	sr->isr_len = len;
436170530Ssam	sr->isr_freq = se->se_chan->ic_freq;
437170530Ssam	sr->isr_flags = se->se_chan->ic_flags;
438170530Ssam	sr->isr_rssi = se->se_rssi;
439170530Ssam	sr->isr_noise = se->se_noise;
440170530Ssam	sr->isr_intval = se->se_intval;
441170530Ssam	sr->isr_capinfo = se->se_capinfo;
442170530Ssam	sr->isr_erp = se->se_erp;
443170530Ssam	IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid);
444170530Ssam	nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE);
445170530Ssam	memcpy(sr->isr_rates, se->se_rates+2, nr);
446170530Ssam	nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr);
447170530Ssam	memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
448170530Ssam	sr->isr_nrates = nr + nxr;
449170530Ssam
450170530Ssam	sr->isr_ssid_len = se->se_ssid[1];
451170530Ssam	cp = ((uint8_t *)sr) + sr->isr_ie_off;
452170530Ssam	memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
453170530Ssam
454170530Ssam	if (ielen) {
455170530Ssam		cp += sr->isr_ssid_len;
456170530Ssam		cp = copyie(cp, se->se_wpa_ie);
457170530Ssam		cp = copyie(cp, se->se_rsn_ie);
458170530Ssam		cp = copyie(cp, se->se_wme_ie);
459170530Ssam		cp = copyie(cp, se->se_ath_ie);
460170530Ssam		cp = copyie(cp, se->se_htcap_ie);
461154541Ssam	}
462154541Ssam
463154541Ssam	req->space -= len;
464170530Ssam	req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len);
465154541Ssam}
466154541Ssam
467154541Ssamstatic int
468154541Ssamieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
469154541Ssam{
470170530Ssam	struct scanreq req;
471154541Ssam	int error;
472154541Ssam
473167245Ssam	if (ireq->i_len < sizeof(struct ieee80211req_scan_result))
474154541Ssam		return EFAULT;
475154541Ssam
476154541Ssam	error = 0;
477154541Ssam	req.space = 0;
478170530Ssam	ieee80211_scan_iterate(ic, get_scan_space, &req);
479154541Ssam	if (req.space > ireq->i_len)
480154541Ssam		req.space = ireq->i_len;
481154541Ssam	if (req.space > 0) {
482154541Ssam		size_t space;
483154541Ssam		void *p;
484154541Ssam
485154541Ssam		space = req.space;
486154541Ssam		/* XXX M_WAITOK after driver lock released */
487154541Ssam		MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
488154541Ssam		if (p == NULL)
489154541Ssam			return ENOMEM;
490154541Ssam		req.sr = p;
491170530Ssam		ieee80211_scan_iterate(ic, get_scan_result, &req);
492154541Ssam		ireq->i_len = space - req.space;
493154541Ssam		error = copyout(p, ireq->i_data, ireq->i_len);
494154541Ssam		FREE(p, M_TEMP);
495154541Ssam	} else
496154541Ssam		ireq->i_len = 0;
497154541Ssam
498154541Ssam	return error;
499154541Ssam}
500154541Ssam
501148845Ssamstruct stainforeq {
502148845Ssam	struct ieee80211com *ic;
503148845Ssam	struct ieee80211req_sta_info *si;
504148845Ssam	size_t	space;
505148845Ssam};
506148845Ssam
507148845Ssamstatic size_t
508148845Ssamsta_space(const struct ieee80211_node *ni, size_t *ielen)
509148845Ssam{
510148845Ssam	*ielen = 0;
511148845Ssam	if (ni->ni_wpa_ie != NULL)
512148845Ssam		*ielen += 2+ni->ni_wpa_ie[1];
513170530Ssam	if (ni->ni_rsn_ie != NULL)
514170530Ssam		*ielen += 2+ni->ni_rsn_ie[1];
515148845Ssam	if (ni->ni_wme_ie != NULL)
516148845Ssam		*ielen += 2+ni->ni_wme_ie[1];
517170530Ssam	if (ni->ni_ath_ie != NULL)
518170530Ssam		*ielen += 2+ni->ni_ath_ie[1];
519148845Ssam	return roundup(sizeof(struct ieee80211req_sta_info) + *ielen,
520170530Ssam		      sizeof(uint32_t));
521148845Ssam}
522148845Ssam
523138568Ssamstatic void
524148845Ssamget_sta_space(void *arg, struct ieee80211_node *ni)
525138568Ssam{
526148845Ssam	struct stainforeq *req = arg;
527138568Ssam	struct ieee80211com *ic = ni->ni_ic;
528148845Ssam	size_t ielen;
529138568Ssam
530148845Ssam	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
531148845Ssam	    ni->ni_associd == 0)	/* only associated stations */
532148845Ssam		return;
533148845Ssam	req->space += sta_space(ni, &ielen);
534148845Ssam}
535148845Ssam
536148845Ssamstatic void
537148845Ssamget_sta_info(void *arg, struct ieee80211_node *ni)
538148845Ssam{
539148845Ssam	struct stainforeq *req = arg;
540148845Ssam	struct ieee80211com *ic = ni->ni_ic;
541148845Ssam	struct ieee80211req_sta_info *si;
542148845Ssam	size_t ielen, len;
543170530Ssam	uint8_t *cp;
544148845Ssam
545148845Ssam	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
546148845Ssam	    ni->ni_associd == 0)	/* only associated stations */
547148845Ssam		return;
548148845Ssam	if (ni->ni_chan == IEEE80211_CHAN_ANYC)	/* XXX bogus entry */
549148845Ssam		return;
550148845Ssam	len = sta_space(ni, &ielen);
551148845Ssam	if (len > req->space)
552148845Ssam		return;
553148845Ssam	si = req->si;
554148845Ssam	si->isi_len = len;
555170530Ssam	si->isi_ie_off = sizeof(struct ieee80211req_sta_info);
556148845Ssam	si->isi_ie_len = ielen;
557138568Ssam	si->isi_freq = ni->ni_chan->ic_freq;
558138568Ssam	si->isi_flags = ni->ni_chan->ic_flags;
559138568Ssam	si->isi_state = ni->ni_flags;
560138568Ssam	si->isi_authmode = ni->ni_authmode;
561170530Ssam	ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise);
562161146Ssam	si->isi_noise = 0;		/* XXX */
563138568Ssam	si->isi_capinfo = ni->ni_capinfo;
564138568Ssam	si->isi_erp = ni->ni_erp;
565138568Ssam	IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr);
566138568Ssam	si->isi_nrates = ni->ni_rates.rs_nrates;
567138568Ssam	if (si->isi_nrates > 15)
568138568Ssam		si->isi_nrates = 15;
569138568Ssam	memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates);
570138568Ssam	si->isi_txrate = ni->ni_txrate;
571170530Ssam	si->isi_ie_len = ielen;
572138568Ssam	si->isi_associd = ni->ni_associd;
573138568Ssam	si->isi_txpower = ni->ni_txpower;
574138568Ssam	si->isi_vlan = ni->ni_vlan;
575138568Ssam	if (ni->ni_flags & IEEE80211_NODE_QOS) {
576138568Ssam		memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs));
577138568Ssam		memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs));
578138568Ssam	} else {
579167439Ssam		si->isi_txseqs[0] = ni->ni_txseqs[IEEE80211_NONQOS_TID];
580167439Ssam		si->isi_rxseqs[0] = ni->ni_rxseqs[IEEE80211_NONQOS_TID];
581138568Ssam	}
582148845Ssam	/* NB: leave all cases in case we relax ni_associd == 0 check */
583148845Ssam	if (ieee80211_node_is_authorized(ni))
584138568Ssam		si->isi_inact = ic->ic_inact_run;
585148845Ssam	else if (ni->ni_associd != 0)
586138568Ssam		si->isi_inact = ic->ic_inact_auth;
587138568Ssam	else
588138568Ssam		si->isi_inact = ic->ic_inact_init;
589138568Ssam	si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
590148845Ssam
591170530Ssam	if (ielen) {
592170530Ssam		cp = ((uint8_t *)si) + si->isi_ie_off;
593170530Ssam		cp = copyie(cp, ni->ni_wpa_ie);
594170530Ssam		cp = copyie(cp, ni->ni_rsn_ie);
595170530Ssam		cp = copyie(cp, ni->ni_wme_ie);
596170530Ssam		cp = copyie(cp, ni->ni_ath_ie);
597148845Ssam	}
598148845Ssam
599170530Ssam	req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len);
600148845Ssam	req->space -= len;
601138568Ssam}
602138568Ssam
603138568Ssamstatic int
604161146Ssamgetstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq,
605161146Ssam	struct ieee80211_node *ni, int off)
606138568Ssam{
607148845Ssam	struct stainforeq req;
608161146Ssam	size_t space;
609161146Ssam	void *p;
610148845Ssam	int error;
611138568Ssam
612138568Ssam	error = 0;
613148845Ssam	req.space = 0;
614161146Ssam	if (ni == NULL)
615161146Ssam		ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req);
616161146Ssam	else
617161146Ssam		get_sta_space(&req, ni);
618148845Ssam	if (req.space > ireq->i_len)
619148845Ssam		req.space = ireq->i_len;
620148845Ssam	if (req.space > 0) {
621148845Ssam		space = req.space;
622148845Ssam		/* XXX M_WAITOK after driver lock released */
623148845Ssam		MALLOC(p, void *, space, M_TEMP, M_NOWAIT);
624161146Ssam		if (p == NULL) {
625161146Ssam			error = ENOMEM;
626161146Ssam			goto bad;
627161146Ssam		}
628148845Ssam		req.si = p;
629161146Ssam		if (ni == NULL)
630161146Ssam			ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req);
631161146Ssam		else
632161146Ssam			get_sta_info(&req, ni);
633148845Ssam		ireq->i_len = space - req.space;
634170530Ssam		error = copyout(p, (uint8_t *) ireq->i_data+off, ireq->i_len);
635148845Ssam		FREE(p, M_TEMP);
636148845Ssam	} else
637148845Ssam		ireq->i_len = 0;
638161146Ssambad:
639161146Ssam	if (ni != NULL)
640161146Ssam		ieee80211_free_node(ni);
641138568Ssam	return error;
642138568Ssam}
643138568Ssam
644138568Ssamstatic int
645161146Ssamieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
646161146Ssam{
647170530Ssam	uint8_t macaddr[IEEE80211_ADDR_LEN];
648161146Ssam	const int off = __offsetof(struct ieee80211req_sta_req, info);
649161146Ssam	struct ieee80211_node *ni;
650161146Ssam	int error;
651161146Ssam
652161146Ssam	if (ireq->i_len < sizeof(struct ieee80211req_sta_req))
653161146Ssam		return EFAULT;
654161146Ssam	error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
655161146Ssam	if (error != 0)
656161146Ssam		return error;
657161146Ssam	if (IEEE80211_ADDR_EQ(macaddr, ic->ic_ifp->if_broadcastaddr)) {
658161146Ssam		ni = NULL;
659161146Ssam	} else {
660161146Ssam		ni = ieee80211_find_node(&ic->ic_sta, macaddr);
661170530Ssam		if (ni == NULL)
662170530Ssam			return EINVAL;
663161146Ssam	}
664161146Ssam	return getstainfo_common(ic, ireq, ni, off);
665161146Ssam}
666161146Ssam
667161146Ssam#ifdef COMPAT_FREEBSD6
668161146Ssam#define	IEEE80211_IOC_STA_INFO_OLD	45
669161146Ssam
670161146Ssamstatic int
671161146Ssamold_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
672161146Ssam{
673161146Ssam	if (ireq->i_len < sizeof(struct ieee80211req_sta_info))
674161146Ssam		return EFAULT;
675161146Ssam	return getstainfo_common(ic, ireq, NULL, 0);
676161146Ssam}
677161146Ssam#endif /* COMPAT_FREEBSD6 */
678161146Ssam
679161146Ssamstatic int
680138568Ssamieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
681138568Ssam{
682138568Ssam	struct ieee80211_node *ni;
683138568Ssam	struct ieee80211req_sta_txpow txpow;
684138568Ssam	int error;
685138568Ssam
686138568Ssam	if (ireq->i_len != sizeof(txpow))
687138568Ssam		return EINVAL;
688138568Ssam	error = copyin(ireq->i_data, &txpow, sizeof(txpow));
689138568Ssam	if (error != 0)
690138568Ssam		return error;
691140753Ssam	ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
692138568Ssam	if (ni == NULL)
693138568Ssam		return EINVAL;		/* XXX */
694138568Ssam	txpow.it_txpow = ni->ni_txpower;
695138568Ssam	error = copyout(&txpow, ireq->i_data, sizeof(txpow));
696138568Ssam	ieee80211_free_node(ni);
697138568Ssam	return error;
698138568Ssam}
699138568Ssam
700138568Ssamstatic int
701138568Ssamieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
702138568Ssam{
703138568Ssam	struct ieee80211_wme_state *wme = &ic->ic_wme;
704138568Ssam	struct wmeParams *wmep;
705138568Ssam	int ac;
706138568Ssam
707138568Ssam	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
708138568Ssam		return EINVAL;
709138568Ssam
710138568Ssam	ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
711138568Ssam	if (ac >= WME_NUM_AC)
712138568Ssam		ac = WME_AC_BE;
713138568Ssam	if (ireq->i_len & IEEE80211_WMEPARAM_BSS)
714138568Ssam		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
715138568Ssam	else
716138568Ssam		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
717138568Ssam	switch (ireq->i_type) {
718138568Ssam	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
719138568Ssam		ireq->i_val = wmep->wmep_logcwmin;
720138568Ssam		break;
721138568Ssam	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
722138568Ssam		ireq->i_val = wmep->wmep_logcwmax;
723138568Ssam		break;
724138568Ssam	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
725138568Ssam		ireq->i_val = wmep->wmep_aifsn;
726138568Ssam		break;
727138568Ssam	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
728138568Ssam		ireq->i_val = wmep->wmep_txopLimit;
729138568Ssam		break;
730138568Ssam	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
731138568Ssam		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
732138568Ssam		ireq->i_val = wmep->wmep_acm;
733138568Ssam		break;
734138568Ssam	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (!bss only)*/
735138568Ssam		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
736138568Ssam		ireq->i_val = !wmep->wmep_noackPolicy;
737138568Ssam		break;
738138568Ssam	}
739138568Ssam	return 0;
740138568Ssam}
741138568Ssam
742149028Ssamstatic int
743149028Ssamieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
744149028Ssam{
745149028Ssam	const struct ieee80211_aclator *acl = ic->ic_acl;
746149028Ssam
747149028Ssam	return (acl == NULL ? EINVAL : acl->iac_getioctl(ic, ireq));
748149028Ssam}
749149028Ssam
750143110Swpaul/*
751170530Ssam * Return the current ``state'' of an Atheros capbility.
752170530Ssam * If associated in station mode report the negotiated
753170530Ssam * setting. Otherwise report the current setting.
754170530Ssam */
755170530Ssamstatic int
756170530Ssamgetathcap(struct ieee80211com *ic, int cap)
757170530Ssam{
758170530Ssam	if (ic->ic_opmode == IEEE80211_M_STA && ic->ic_state == IEEE80211_S_RUN)
759170530Ssam		return IEEE80211_ATH_CAP(ic, ic->ic_bss, cap) != 0;
760170530Ssam	else
761170530Ssam		return (ic->ic_flags & cap) != 0;
762170530Ssam}
763170530Ssam
764170530Ssamstatic int
765170530Ssamieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq)
766170530Ssam{
767170530Ssam	if (ireq->i_len != sizeof(struct ieee80211_channel))
768170530Ssam		return EINVAL;
769170530Ssam	return copyout(ic->ic_curchan, ireq->i_data, sizeof(*ic->ic_curchan));
770170530Ssam}
771170530Ssam
772170530Ssam/*
773143110Swpaul * When building the kernel with -O2 on the i386 architecture, gcc
774143110Swpaul * seems to want to inline this function into ieee80211_ioctl()
775143110Swpaul * (which is the only routine that calls it). When this happens,
776143110Swpaul * ieee80211_ioctl() ends up consuming an additional 2K of stack
777143110Swpaul * space. (Exactly why it needs so much is unclear.) The problem
778143110Swpaul * is that it's possible for ieee80211_ioctl() to invoke other
779143110Swpaul * routines (including driver init functions) which could then find
780143110Swpaul * themselves perilously close to exhausting the stack.
781143110Swpaul *
782143110Swpaul * To avoid this, we deliberately prevent gcc from inlining this
783143110Swpaul * routine. Another way to avoid this is to use less agressive
784143110Swpaul * optimization when compiling this file (i.e. -O instead of -O2)
785143110Swpaul * but special-casing the compilation of this one module in the
786143110Swpaul * build system would be awkward.
787143110Swpaul */
788143110Swpaul#ifdef __GNUC__
789143110Swpaul__attribute__ ((noinline))
790143110Swpaul#endif
791138568Ssamstatic int
792138568Ssamieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
793138568Ssam{
794138568Ssam	const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
795116742Ssam	int error = 0;
796138568Ssam	u_int kid, len, m;
797170530Ssam	uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
798116742Ssam	char tmpssid[IEEE80211_NWID_LEN];
799116742Ssam
800138568Ssam	switch (ireq->i_type) {
801138568Ssam	case IEEE80211_IOC_SSID:
802138568Ssam		switch (ic->ic_state) {
803138568Ssam		case IEEE80211_S_INIT:
804138568Ssam		case IEEE80211_S_SCAN:
805170530Ssam			ireq->i_len = ic->ic_des_ssid[0].len;
806170530Ssam			memcpy(tmpssid, ic->ic_des_ssid[0].ssid, ireq->i_len);
807138568Ssam			break;
808138568Ssam		default:
809138568Ssam			ireq->i_len = ic->ic_bss->ni_esslen;
810138568Ssam			memcpy(tmpssid, ic->ic_bss->ni_essid,
811138568Ssam				ireq->i_len);
812138568Ssam			break;
813138568Ssam		}
814138568Ssam		error = copyout(tmpssid, ireq->i_data, ireq->i_len);
815116742Ssam		break;
816138568Ssam	case IEEE80211_IOC_NUMSSIDS:
817138568Ssam		ireq->i_val = 1;
818138568Ssam		break;
819138568Ssam	case IEEE80211_IOC_WEP:
820138568Ssam		if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
821138568Ssam			ireq->i_val = IEEE80211_WEP_OFF;
822138568Ssam		else if (ic->ic_flags & IEEE80211_F_DROPUNENC)
823138568Ssam			ireq->i_val = IEEE80211_WEP_ON;
824138568Ssam		else
825138568Ssam			ireq->i_val = IEEE80211_WEP_MIXED;
826138568Ssam		break;
827138568Ssam	case IEEE80211_IOC_WEPKEY:
828138568Ssam		kid = (u_int) ireq->i_val;
829138568Ssam		if (kid >= IEEE80211_WEP_NKID)
830138568Ssam			return EINVAL;
831138568Ssam		len = (u_int) ic->ic_nw_keys[kid].wk_keylen;
832138568Ssam		/* NB: only root can read WEP keys */
833164033Srwatson		if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
834138568Ssam			bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
835138568Ssam		} else {
836138568Ssam			bzero(tmpkey, len);
837138568Ssam		}
838138568Ssam		ireq->i_len = len;
839138568Ssam		error = copyout(tmpkey, ireq->i_data, len);
840138568Ssam		break;
841138568Ssam	case IEEE80211_IOC_NUMWEPKEYS:
842138568Ssam		ireq->i_val = IEEE80211_WEP_NKID;
843138568Ssam		break;
844138568Ssam	case IEEE80211_IOC_WEPTXKEY:
845138568Ssam		ireq->i_val = ic->ic_def_txkey;
846138568Ssam		break;
847138568Ssam	case IEEE80211_IOC_AUTHMODE:
848138568Ssam		if (ic->ic_flags & IEEE80211_F_WPA)
849138568Ssam			ireq->i_val = IEEE80211_AUTH_WPA;
850138568Ssam		else
851138568Ssam			ireq->i_val = ic->ic_bss->ni_authmode;
852138568Ssam		break;
853138568Ssam	case IEEE80211_IOC_CHANNEL:
854148936Ssam		ireq->i_val = ieee80211_chan2ieee(ic, ic->ic_curchan);
855138568Ssam		break;
856138568Ssam	case IEEE80211_IOC_POWERSAVE:
857138568Ssam		if (ic->ic_flags & IEEE80211_F_PMGTON)
858138568Ssam			ireq->i_val = IEEE80211_POWERSAVE_ON;
859138568Ssam		else
860138568Ssam			ireq->i_val = IEEE80211_POWERSAVE_OFF;
861138568Ssam		break;
862138568Ssam	case IEEE80211_IOC_POWERSAVESLEEP:
863138568Ssam		ireq->i_val = ic->ic_lintval;
864138568Ssam		break;
865138568Ssam	case IEEE80211_IOC_RTSTHRESHOLD:
866138568Ssam		ireq->i_val = ic->ic_rtsthreshold;
867138568Ssam		break;
868138568Ssam	case IEEE80211_IOC_PROTMODE:
869138568Ssam		ireq->i_val = ic->ic_protmode;
870138568Ssam		break;
871138568Ssam	case IEEE80211_IOC_TXPOWER:
872138568Ssam		if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
873138568Ssam			return EINVAL;
874138568Ssam		ireq->i_val = ic->ic_txpowlimit;
875138568Ssam		break;
876138568Ssam	case IEEE80211_IOC_MCASTCIPHER:
877138568Ssam		ireq->i_val = rsn->rsn_mcastcipher;
878138568Ssam		break;
879138568Ssam	case IEEE80211_IOC_MCASTKEYLEN:
880138568Ssam		ireq->i_val = rsn->rsn_mcastkeylen;
881138568Ssam		break;
882138568Ssam	case IEEE80211_IOC_UCASTCIPHERS:
883138568Ssam		ireq->i_val = 0;
884138568Ssam		for (m = 0x1; m != 0; m <<= 1)
885138568Ssam			if (rsn->rsn_ucastcipherset & m)
886138568Ssam				ireq->i_val |= 1<<cap2cipher(m);
887138568Ssam		break;
888138568Ssam	case IEEE80211_IOC_UCASTCIPHER:
889138568Ssam		ireq->i_val = rsn->rsn_ucastcipher;
890138568Ssam		break;
891138568Ssam	case IEEE80211_IOC_UCASTKEYLEN:
892138568Ssam		ireq->i_val = rsn->rsn_ucastkeylen;
893138568Ssam		break;
894138568Ssam	case IEEE80211_IOC_KEYMGTALGS:
895138568Ssam		ireq->i_val = rsn->rsn_keymgmtset;
896138568Ssam		break;
897138568Ssam	case IEEE80211_IOC_RSNCAPS:
898138568Ssam		ireq->i_val = rsn->rsn_caps;
899138568Ssam		break;
900138568Ssam	case IEEE80211_IOC_WPA:
901138568Ssam		switch (ic->ic_flags & IEEE80211_F_WPA) {
902138568Ssam		case IEEE80211_F_WPA1:
903116742Ssam			ireq->i_val = 1;
904116742Ssam			break;
905138568Ssam		case IEEE80211_F_WPA2:
906138568Ssam			ireq->i_val = 2;
907116742Ssam			break;
908138568Ssam		case IEEE80211_F_WPA1 | IEEE80211_F_WPA2:
909138568Ssam			ireq->i_val = 3;
910116742Ssam			break;
911138568Ssam		default:
912138568Ssam			ireq->i_val = 0;
913116742Ssam			break;
914138568Ssam		}
915138568Ssam		break;
916138568Ssam	case IEEE80211_IOC_CHANLIST:
917138568Ssam		error = ieee80211_ioctl_getchanlist(ic, ireq);
918138568Ssam		break;
919138568Ssam	case IEEE80211_IOC_ROAMING:
920138568Ssam		ireq->i_val = ic->ic_roaming;
921138568Ssam		break;
922138568Ssam	case IEEE80211_IOC_PRIVACY:
923138568Ssam		ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0;
924138568Ssam		break;
925138568Ssam	case IEEE80211_IOC_DROPUNENCRYPTED:
926138568Ssam		ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0;
927138568Ssam		break;
928138568Ssam	case IEEE80211_IOC_COUNTERMEASURES:
929138568Ssam		ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0;
930138568Ssam		break;
931138568Ssam	case IEEE80211_IOC_DRIVER_CAPS:
932138568Ssam		ireq->i_val = ic->ic_caps>>16;
933138568Ssam		ireq->i_len = ic->ic_caps&0xffff;
934138568Ssam		break;
935138568Ssam	case IEEE80211_IOC_WME:
936138568Ssam		ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0;
937138568Ssam		break;
938138568Ssam	case IEEE80211_IOC_HIDESSID:
939138568Ssam		ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0;
940138568Ssam		break;
941138568Ssam	case IEEE80211_IOC_APBRIDGE:
942138568Ssam		ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0;
943138568Ssam		break;
944138568Ssam	case IEEE80211_IOC_OPTIE:
945138568Ssam		if (ic->ic_opt_ie == NULL)
946138568Ssam			return EINVAL;
947138568Ssam		/* NB: truncate, caller can check length */
948138568Ssam		if (ireq->i_len > ic->ic_opt_ie_len)
949138568Ssam			ireq->i_len = ic->ic_opt_ie_len;
950138568Ssam		error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len);
951138568Ssam		break;
952138568Ssam	case IEEE80211_IOC_WPAKEY:
953138568Ssam		error = ieee80211_ioctl_getkey(ic, ireq);
954138568Ssam		break;
955138568Ssam	case IEEE80211_IOC_CHANINFO:
956138568Ssam		error = ieee80211_ioctl_getchaninfo(ic, ireq);
957138568Ssam		break;
958138568Ssam	case IEEE80211_IOC_BSSID:
959138568Ssam		if (ireq->i_len != IEEE80211_ADDR_LEN)
960138568Ssam			return EINVAL;
961138568Ssam		error = copyout(ic->ic_state == IEEE80211_S_RUN ?
962138568Ssam					ic->ic_bss->ni_bssid :
963138568Ssam					ic->ic_des_bssid,
964138568Ssam				ireq->i_data, ireq->i_len);
965138568Ssam		break;
966138568Ssam	case IEEE80211_IOC_WPAIE:
967170530Ssam		error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
968138568Ssam		break;
969170530Ssam	case IEEE80211_IOC_WPAIE2:
970170530Ssam		error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
971170530Ssam		break;
972154541Ssam#ifdef COMPAT_FREEBSD6
973154541Ssam	case IEEE80211_IOC_SCAN_RESULTS_OLD:
974154541Ssam		error = old_getscanresults(ic, ireq);
975154541Ssam		break;
976154541Ssam#endif
977138568Ssam	case IEEE80211_IOC_SCAN_RESULTS:
978138568Ssam		error = ieee80211_ioctl_getscanresults(ic, ireq);
979138568Ssam		break;
980138568Ssam	case IEEE80211_IOC_STA_STATS:
981138568Ssam		error = ieee80211_ioctl_getstastats(ic, ireq);
982138568Ssam		break;
983138568Ssam	case IEEE80211_IOC_TXPOWMAX:
984138568Ssam		ireq->i_val = ic->ic_bss->ni_txpower;
985138568Ssam		break;
986138568Ssam	case IEEE80211_IOC_STA_TXPOW:
987138568Ssam		error = ieee80211_ioctl_getstatxpow(ic, ireq);
988138568Ssam		break;
989161146Ssam#ifdef COMPAT_FREEBSD6
990161146Ssam	case IEEE80211_IOC_STA_INFO_OLD:
991161146Ssam		error = old_getstainfo(ic, ireq);
992161146Ssam		break;
993161146Ssam#endif
994138568Ssam	case IEEE80211_IOC_STA_INFO:
995138568Ssam		error = ieee80211_ioctl_getstainfo(ic, ireq);
996138568Ssam		break;
997138568Ssam	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
998138568Ssam	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
999138568Ssam	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
1000138568Ssam	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
1001138568Ssam	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
1002138568Ssam	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (bss only) */
1003138568Ssam		error = ieee80211_ioctl_getwmeparam(ic, ireq);
1004138568Ssam		break;
1005138568Ssam	case IEEE80211_IOC_DTIM_PERIOD:
1006138568Ssam		ireq->i_val = ic->ic_dtim_period;
1007138568Ssam		break;
1008138568Ssam	case IEEE80211_IOC_BEACON_INTERVAL:
1009138568Ssam		/* NB: get from ic_bss for station mode */
1010138568Ssam		ireq->i_val = ic->ic_bss->ni_intval;
1011138568Ssam		break;
1012147794Ssam	case IEEE80211_IOC_PUREG:
1013147794Ssam		ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0;
1014147794Ssam		break;
1015170530Ssam	case IEEE80211_IOC_FF:
1016170530Ssam		ireq->i_val = getathcap(ic, IEEE80211_F_FF);
1017170530Ssam		break;
1018170530Ssam	case IEEE80211_IOC_TURBOP:
1019170530Ssam		ireq->i_val = getathcap(ic, IEEE80211_F_TURBOP);
1020170530Ssam		break;
1021170530Ssam	case IEEE80211_IOC_BGSCAN:
1022170530Ssam		ireq->i_val = (ic->ic_flags & IEEE80211_F_BGSCAN) != 0;
1023170530Ssam		break;
1024170530Ssam	case IEEE80211_IOC_BGSCAN_IDLE:
1025170530Ssam		ireq->i_val = ic->ic_bgscanidle*hz/1000;	/* ms */
1026170530Ssam		break;
1027170530Ssam	case IEEE80211_IOC_BGSCAN_INTERVAL:
1028170530Ssam		ireq->i_val = ic->ic_bgscanintvl/hz;		/* seconds */
1029170530Ssam		break;
1030170530Ssam	case IEEE80211_IOC_SCANVALID:
1031170530Ssam		ireq->i_val = ic->ic_scanvalid/hz;		/* seconds */
1032170530Ssam		break;
1033170530Ssam	case IEEE80211_IOC_ROAM_RSSI_11A:
1034170530Ssam		ireq->i_val = ic->ic_roam.rssi11a;
1035170530Ssam		break;
1036170530Ssam	case IEEE80211_IOC_ROAM_RSSI_11B:
1037170530Ssam		ireq->i_val = ic->ic_roam.rssi11bOnly;
1038170530Ssam		break;
1039170530Ssam	case IEEE80211_IOC_ROAM_RSSI_11G:
1040170530Ssam		ireq->i_val = ic->ic_roam.rssi11b;
1041170530Ssam		break;
1042170530Ssam	case IEEE80211_IOC_ROAM_RATE_11A:
1043170530Ssam		ireq->i_val = ic->ic_roam.rate11a;
1044170530Ssam		break;
1045170530Ssam	case IEEE80211_IOC_ROAM_RATE_11B:
1046170530Ssam		ireq->i_val = ic->ic_roam.rate11bOnly;
1047170530Ssam		break;
1048170530Ssam	case IEEE80211_IOC_ROAM_RATE_11G:
1049170530Ssam		ireq->i_val = ic->ic_roam.rate11b;
1050170530Ssam		break;
1051153346Ssam	case IEEE80211_IOC_MCAST_RATE:
1052153346Ssam		ireq->i_val = ic->ic_mcast_rate;
1053153346Ssam		break;
1054148292Ssam	case IEEE80211_IOC_FRAGTHRESHOLD:
1055148292Ssam		ireq->i_val = ic->ic_fragthreshold;
1056148292Ssam		break;
1057149028Ssam	case IEEE80211_IOC_MACCMD:
1058149028Ssam		error = ieee80211_ioctl_getmaccmd(ic, ireq);
1059149028Ssam		break;
1060153421Ssam	case IEEE80211_IOC_BURST:
1061153421Ssam		ireq->i_val = (ic->ic_flags & IEEE80211_F_BURST) != 0;
1062153421Ssam		break;
1063160686Ssam	case IEEE80211_IOC_BMISSTHRESHOLD:
1064160686Ssam		ireq->i_val = ic->ic_bmissthreshold;
1065160686Ssam		break;
1066170530Ssam	case IEEE80211_IOC_CURCHAN:
1067170530Ssam		error = ieee80211_ioctl_getcurchan(ic, ireq);
1068170530Ssam		break;
1069170530Ssam	case IEEE80211_IOC_SHORTGI:
1070170530Ssam		ireq->i_val = 0;
1071170530Ssam		if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
1072170530Ssam			ireq->i_val |= IEEE80211_HTCAP_SHORTGI20;
1073170530Ssam		if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
1074170530Ssam			ireq->i_val |= IEEE80211_HTCAP_SHORTGI40;
1075170530Ssam		break;
1076170530Ssam	case IEEE80211_IOC_AMPDU:
1077170530Ssam		ireq->i_val = 0;
1078170530Ssam		if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)
1079170530Ssam			ireq->i_val |= 1;
1080170530Ssam		if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)
1081170530Ssam			ireq->i_val |= 2;
1082170530Ssam		break;
1083170530Ssam	case IEEE80211_IOC_AMPDU_LIMIT:
1084170530Ssam		ireq->i_val = ic->ic_ampdu_limit;	/* XXX truncation? */
1085170530Ssam		break;
1086170530Ssam	case IEEE80211_IOC_AMPDU_DENSITY:
1087170530Ssam		ireq->i_val = ic->ic_ampdu_density;
1088170530Ssam		break;
1089170530Ssam	case IEEE80211_IOC_AMSDU:
1090170530Ssam		ireq->i_val = 0;
1091170530Ssam		if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_TX)
1092170530Ssam			ireq->i_val |= 1;
1093170530Ssam		if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_RX)
1094170530Ssam			ireq->i_val |= 2;
1095170530Ssam		break;
1096170530Ssam	case IEEE80211_IOC_AMSDU_LIMIT:
1097170530Ssam		ireq->i_val = ic->ic_amsdu_limit;	/* XXX truncation? */
1098170530Ssam		break;
1099170530Ssam	case IEEE80211_IOC_PUREN:
1100170530Ssam		ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) != 0;
1101170530Ssam		break;
1102170530Ssam	case IEEE80211_IOC_DOTH:
1103170530Ssam		ireq->i_val = (ic->ic_flags & IEEE80211_F_DOTH) != 0;
1104170530Ssam		break;
1105170530Ssam	case IEEE80211_IOC_HTCOMPAT:
1106170530Ssam		ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0;
1107170530Ssam		break;
1108138568Ssam	default:
1109138568Ssam		error = EINVAL;
1110138568Ssam		break;
1111138568Ssam	}
1112138568Ssam	return error;
1113138568Ssam}
1114138568Ssam
1115138568Ssamstatic int
1116138568Ssamieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq)
1117138568Ssam{
1118138568Ssam	int error;
1119155862Ssam	void *ie, *oie;
1120138568Ssam
1121138568Ssam	/*
1122138568Ssam	 * NB: Doing this for ap operation could be useful (e.g. for
1123138568Ssam	 *     WPA and/or WME) except that it typically is worthless
1124138568Ssam	 *     without being able to intervene when processing
1125138568Ssam	 *     association response frames--so disallow it for now.
1126138568Ssam	 */
1127138568Ssam	if (ic->ic_opmode != IEEE80211_M_STA)
1128138568Ssam		return EINVAL;
1129138568Ssam	if (ireq->i_len > IEEE80211_MAX_OPT_IE)
1130138568Ssam		return EINVAL;
1131170530Ssam	/* NB: data.length is validated by the wireless extensions code */
1132170530Ssam	/* XXX M_WAITOK after driver lock released */
1133155862Ssam	if (ireq->i_len > 0) {
1134155862Ssam		MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_NOWAIT);
1135155862Ssam		if (ie == NULL)
1136155862Ssam			return ENOMEM;
1137155862Ssam		error = copyin(ireq->i_data, ie, ireq->i_len);
1138155862Ssam		if (error) {
1139155862Ssam			FREE(ie, M_DEVBUF);
1140155862Ssam			return error;
1141155862Ssam		}
1142155862Ssam	} else {
1143155862Ssam		ie = NULL;
1144155862Ssam		ireq->i_len = 0;
1145155862Ssam	}
1146138568Ssam	/* XXX sanity check data? */
1147155862Ssam	oie = ic->ic_opt_ie;
1148138568Ssam	ic->ic_opt_ie = ie;
1149138568Ssam	ic->ic_opt_ie_len = ireq->i_len;
1150155862Ssam	if (oie != NULL)
1151155862Ssam		FREE(oie, M_DEVBUF);
1152138568Ssam	return 0;
1153138568Ssam}
1154138568Ssam
1155138568Ssamstatic int
1156138568Ssamieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
1157138568Ssam{
1158138568Ssam	struct ieee80211req_key ik;
1159138568Ssam	struct ieee80211_node *ni;
1160138568Ssam	struct ieee80211_key *wk;
1161170530Ssam	uint16_t kid;
1162138568Ssam	int error;
1163138568Ssam
1164138568Ssam	if (ireq->i_len != sizeof(ik))
1165138568Ssam		return EINVAL;
1166138568Ssam	error = copyin(ireq->i_data, &ik, sizeof(ik));
1167138568Ssam	if (error)
1168138568Ssam		return error;
1169138568Ssam	/* NB: cipher support is verified by ieee80211_crypt_newkey */
1170138568Ssam	/* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */
1171138568Ssam	if (ik.ik_keylen > sizeof(ik.ik_keydata))
1172138568Ssam		return E2BIG;
1173138568Ssam	kid = ik.ik_keyix;
1174138568Ssam	if (kid == IEEE80211_KEYIX_NONE) {
1175138568Ssam		/* XXX unicast keys currently must be tx/rx */
1176138568Ssam		if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
1177138568Ssam			return EINVAL;
1178138568Ssam		if (ic->ic_opmode == IEEE80211_M_STA) {
1179147775Ssam			ni = ieee80211_ref_node(ic->ic_bss);
1180147775Ssam			if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) {
1181147775Ssam				ieee80211_free_node(ni);
1182138568Ssam				return EADDRNOTAVAIL;
1183147775Ssam			}
1184138568Ssam		} else {
1185140753Ssam			ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
1186138568Ssam			if (ni == NULL)
1187138568Ssam				return ENOENT;
1188138568Ssam		}
1189138568Ssam		wk = &ni->ni_ucastkey;
1190138568Ssam	} else {
1191138568Ssam		if (kid >= IEEE80211_WEP_NKID)
1192138568Ssam			return EINVAL;
1193138568Ssam		wk = &ic->ic_nw_keys[kid];
1194155885Ssam		/*
1195155885Ssam		 * Global slots start off w/o any assigned key index.
1196155885Ssam		 * Force one here for consistency with IEEE80211_IOC_WEPKEY.
1197155885Ssam		 */
1198155885Ssam		if (wk->wk_keyix == IEEE80211_KEYIX_NONE)
1199155885Ssam			wk->wk_keyix = kid;
1200138568Ssam		ni = NULL;
1201138568Ssam	}
1202138568Ssam	error = 0;
1203138568Ssam	ieee80211_key_update_begin(ic);
1204144960Ssam	if (ieee80211_crypto_newkey(ic, ik.ik_type, ik.ik_flags, wk)) {
1205138568Ssam		wk->wk_keylen = ik.ik_keylen;
1206138568Ssam		/* NB: MIC presence is implied by cipher type */
1207138568Ssam		if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
1208138568Ssam			wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
1209138568Ssam		wk->wk_keyrsc = ik.ik_keyrsc;
1210138568Ssam		wk->wk_keytsc = 0;			/* new key, reset */
1211138568Ssam		memset(wk->wk_key, 0, sizeof(wk->wk_key));
1212138568Ssam		memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen);
1213138568Ssam		if (!ieee80211_crypto_setkey(ic, wk,
1214138568Ssam		    ni != NULL ? ni->ni_macaddr : ik.ik_macaddr))
1215138568Ssam			error = EIO;
1216138568Ssam		else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT))
1217138568Ssam			ic->ic_def_txkey = kid;
1218138568Ssam	} else
1219138568Ssam		error = ENXIO;
1220138568Ssam	ieee80211_key_update_end(ic);
1221138568Ssam	if (ni != NULL)
1222138568Ssam		ieee80211_free_node(ni);
1223138568Ssam	return error;
1224138568Ssam}
1225138568Ssam
1226138568Ssamstatic int
1227138568Ssamieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
1228138568Ssam{
1229138568Ssam	struct ieee80211req_del_key dk;
1230138568Ssam	int kid, error;
1231138568Ssam
1232138568Ssam	if (ireq->i_len != sizeof(dk))
1233138568Ssam		return EINVAL;
1234138568Ssam	error = copyin(ireq->i_data, &dk, sizeof(dk));
1235138568Ssam	if (error)
1236138568Ssam		return error;
1237138568Ssam	kid = dk.idk_keyix;
1238170530Ssam	/* XXX uint8_t -> uint16_t */
1239170530Ssam	if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) {
1240138568Ssam		struct ieee80211_node *ni;
1241138568Ssam
1242147775Ssam		if (ic->ic_opmode == IEEE80211_M_STA) {
1243147775Ssam			ni = ieee80211_ref_node(ic->ic_bss);
1244147775Ssam			if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) {
1245147775Ssam				ieee80211_free_node(ni);
1246147775Ssam				return EADDRNOTAVAIL;
1247147775Ssam			}
1248147775Ssam		} else {
1249147775Ssam			ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr);
1250147775Ssam			if (ni == NULL)
1251147775Ssam				return ENOENT;
1252147775Ssam		}
1253138568Ssam		/* XXX error return */
1254148863Ssam		ieee80211_node_delucastkey(ni);
1255138568Ssam		ieee80211_free_node(ni);
1256138568Ssam	} else {
1257138568Ssam		if (kid >= IEEE80211_WEP_NKID)
1258138568Ssam			return EINVAL;
1259138568Ssam		/* XXX error return */
1260138568Ssam		ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]);
1261138568Ssam	}
1262138568Ssam	return 0;
1263138568Ssam}
1264138568Ssam
1265138568Ssamstatic void
1266138568Ssamdomlme(void *arg, struct ieee80211_node *ni)
1267138568Ssam{
1268138568Ssam	struct ieee80211com *ic = ni->ni_ic;
1269138568Ssam	struct ieee80211req_mlme *mlme = arg;
1270138568Ssam
1271138568Ssam	if (ni->ni_associd != 0) {
1272138568Ssam		IEEE80211_SEND_MGMT(ic, ni,
1273138568Ssam			mlme->im_op == IEEE80211_MLME_DEAUTH ?
1274138568Ssam				IEEE80211_FC0_SUBTYPE_DEAUTH :
1275138568Ssam				IEEE80211_FC0_SUBTYPE_DISASSOC,
1276138568Ssam			mlme->im_reason);
1277138568Ssam	}
1278138568Ssam	ieee80211_node_leave(ic, ni);
1279138568Ssam}
1280138568Ssam
1281170530Ssamstruct scanlookup {
1282170530Ssam	const uint8_t *mac;
1283170530Ssam	int esslen;
1284170530Ssam	const uint8_t *essid;
1285170530Ssam	const struct ieee80211_scan_entry *se;
1286170530Ssam};
1287170530Ssam
1288170530Ssam/*
1289170530Ssam * Match mac address and any ssid.
1290170530Ssam */
1291170530Ssamstatic void
1292170530Ssammlmelookup(void *arg, const struct ieee80211_scan_entry *se)
1293170530Ssam{
1294170530Ssam	struct scanlookup *look = arg;
1295170530Ssam
1296170530Ssam	if (!IEEE80211_ADDR_EQ(look->mac, se->se_macaddr))
1297170530Ssam		return;
1298170530Ssam	if (look->esslen != 0) {
1299170530Ssam		if (se->se_ssid[1] != look->esslen)
1300170530Ssam			return;
1301170530Ssam		if (memcmp(look->essid, se->se_ssid+2, look->esslen))
1302170530Ssam			return;
1303170530Ssam	}
1304170530Ssam	look->se = se;
1305170530Ssam}
1306170530Ssam
1307138568Ssamstatic int
1308138568Ssamieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
1309138568Ssam{
1310138568Ssam	struct ieee80211req_mlme mlme;
1311138568Ssam	struct ieee80211_node *ni;
1312138568Ssam	int error;
1313138568Ssam
1314138568Ssam	if (ireq->i_len != sizeof(mlme))
1315138568Ssam		return EINVAL;
1316138568Ssam	error = copyin(ireq->i_data, &mlme, sizeof(mlme));
1317138568Ssam	if (error)
1318138568Ssam		return error;
1319138568Ssam	switch (mlme.im_op) {
1320138568Ssam	case IEEE80211_MLME_ASSOC:
1321170530Ssam		/* XXX ibss/ahdemo */
1322170530Ssam		if (ic->ic_opmode == IEEE80211_M_STA) {
1323170530Ssam			struct scanlookup lookup;
1324138568Ssam
1325170530Ssam			lookup.se = NULL;
1326170530Ssam			lookup.mac = mlme.im_macaddr;
1327170530Ssam			/* XXX use revised api w/ explicit ssid */
1328170530Ssam			lookup.esslen = ic->ic_des_ssid[0].len;
1329170530Ssam			lookup.essid = ic->ic_des_ssid[0].ssid;
1330170530Ssam			ieee80211_scan_iterate(ic, mlmelookup, &lookup);
1331170530Ssam			if (lookup.se != NULL &&
1332170530Ssam			    ieee80211_sta_join(ic, lookup.se))
1333170530Ssam				return 0;
1334138568Ssam		}
1335170530Ssam		return EINVAL;
1336138568Ssam	case IEEE80211_MLME_DISASSOC:
1337138568Ssam	case IEEE80211_MLME_DEAUTH:
1338138568Ssam		switch (ic->ic_opmode) {
1339138568Ssam		case IEEE80211_M_STA:
1340138568Ssam			/* XXX not quite right */
1341138568Ssam			ieee80211_new_state(ic, IEEE80211_S_INIT,
1342138568Ssam				mlme.im_reason);
1343116742Ssam			break;
1344138568Ssam		case IEEE80211_M_HOSTAP:
1345138568Ssam			/* NB: the broadcast address means do 'em all */
1346138568Ssam			if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) {
1347140753Ssam				if ((ni = ieee80211_find_node(&ic->ic_sta,
1348138568Ssam						mlme.im_macaddr)) == NULL)
1349138568Ssam					return EINVAL;
1350138568Ssam				domlme(&mlme, ni);
1351138568Ssam				ieee80211_free_node(ni);
1352138568Ssam			} else {
1353140753Ssam				ieee80211_iterate_nodes(&ic->ic_sta,
1354138568Ssam						domlme, &mlme);
1355138568Ssam			}
1356116742Ssam			break;
1357138568Ssam		default:
1358138568Ssam			return EINVAL;
1359138568Ssam		}
1360138568Ssam		break;
1361138568Ssam	case IEEE80211_MLME_AUTHORIZE:
1362138568Ssam	case IEEE80211_MLME_UNAUTHORIZE:
1363138568Ssam		if (ic->ic_opmode != IEEE80211_M_HOSTAP)
1364138568Ssam			return EINVAL;
1365140753Ssam		ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr);
1366138568Ssam		if (ni == NULL)
1367138568Ssam			return EINVAL;
1368138568Ssam		if (mlme.im_op == IEEE80211_MLME_AUTHORIZE)
1369148302Ssam			ieee80211_node_authorize(ni);
1370138568Ssam		else
1371148302Ssam			ieee80211_node_unauthorize(ni);
1372138568Ssam		ieee80211_free_node(ni);
1373138568Ssam		break;
1374138568Ssam	default:
1375138568Ssam		return EINVAL;
1376138568Ssam	}
1377138568Ssam	return 0;
1378138568Ssam}
1379138568Ssam
1380138568Ssamstatic int
1381138568Ssamieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
1382138568Ssam{
1383170530Ssam	uint8_t mac[IEEE80211_ADDR_LEN];
1384138568Ssam	const struct ieee80211_aclator *acl = ic->ic_acl;
1385138568Ssam	int error;
1386138568Ssam
1387138568Ssam	if (ireq->i_len != sizeof(mac))
1388138568Ssam		return EINVAL;
1389138568Ssam	error = copyin(ireq->i_data, mac, ireq->i_len);
1390138568Ssam	if (error)
1391138568Ssam		return error;
1392138568Ssam	if (acl == NULL) {
1393138568Ssam		acl = ieee80211_aclator_get("mac");
1394138568Ssam		if (acl == NULL || !acl->iac_attach(ic))
1395138568Ssam			return EINVAL;
1396138568Ssam		ic->ic_acl = acl;
1397138568Ssam	}
1398138568Ssam	if (ireq->i_type == IEEE80211_IOC_ADDMAC)
1399138568Ssam		acl->iac_add(ic, mac);
1400138568Ssam	else
1401138568Ssam		acl->iac_remove(ic, mac);
1402138568Ssam	return 0;
1403138568Ssam}
1404138568Ssam
1405138568Ssamstatic int
1406149028Ssamieee80211_ioctl_setmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
1407138568Ssam{
1408138568Ssam	const struct ieee80211_aclator *acl = ic->ic_acl;
1409138568Ssam
1410138568Ssam	switch (ireq->i_val) {
1411138568Ssam	case IEEE80211_MACCMD_POLICY_OPEN:
1412138568Ssam	case IEEE80211_MACCMD_POLICY_ALLOW:
1413138568Ssam	case IEEE80211_MACCMD_POLICY_DENY:
1414138568Ssam		if (acl == NULL) {
1415138568Ssam			acl = ieee80211_aclator_get("mac");
1416138568Ssam			if (acl == NULL || !acl->iac_attach(ic))
1417138568Ssam				return EINVAL;
1418138568Ssam			ic->ic_acl = acl;
1419138568Ssam		}
1420138568Ssam		acl->iac_setpolicy(ic, ireq->i_val);
1421138568Ssam		break;
1422138568Ssam	case IEEE80211_MACCMD_FLUSH:
1423138568Ssam		if (acl != NULL)
1424138568Ssam			acl->iac_flush(ic);
1425138568Ssam		/* NB: silently ignore when not in use */
1426138568Ssam		break;
1427138568Ssam	case IEEE80211_MACCMD_DETACH:
1428138568Ssam		if (acl != NULL) {
1429138568Ssam			ic->ic_acl = NULL;
1430138568Ssam			acl->iac_detach(ic);
1431138568Ssam		}
1432138568Ssam		break;
1433138568Ssam	default:
1434149028Ssam		if (acl == NULL)
1435149028Ssam			return EINVAL;
1436149028Ssam		else
1437149028Ssam			return acl->iac_setioctl(ic, ireq);
1438138568Ssam	}
1439138568Ssam	return 0;
1440138568Ssam}
1441138568Ssam
1442138568Ssamstatic int
1443138568Ssamieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
1444138568Ssam{
1445138568Ssam	struct ieee80211req_chanlist list;
1446138568Ssam	u_char chanlist[IEEE80211_CHAN_BYTES];
1447170530Ssam	int i, j, nchan, error;
1448138568Ssam
1449138568Ssam	if (ireq->i_len != sizeof(list))
1450138568Ssam		return EINVAL;
1451138568Ssam	error = copyin(ireq->i_data, &list, sizeof(list));
1452138568Ssam	if (error)
1453138568Ssam		return error;
1454138568Ssam	memset(chanlist, 0, sizeof(chanlist));
1455138568Ssam	/*
1456138568Ssam	 * Since channel 0 is not available for DS, channel 1
1457138568Ssam	 * is assigned to LSB on WaveLAN.
1458138568Ssam	 */
1459138568Ssam	if (ic->ic_phytype == IEEE80211_T_DS)
1460138568Ssam		i = 1;
1461138568Ssam	else
1462138568Ssam		i = 0;
1463170530Ssam	nchan = 0;
1464138568Ssam	for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
1465138568Ssam		/*
1466138568Ssam		 * NB: silently discard unavailable channels so users
1467138568Ssam		 *     can specify 1-255 to get all available channels.
1468138568Ssam		 */
1469170530Ssam		if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) {
1470138568Ssam			setbit(chanlist, i);
1471170530Ssam			nchan++;
1472170530Ssam		}
1473138568Ssam	}
1474170530Ssam	if (nchan == 0)
1475170530Ssam		return EINVAL;
1476170530Ssam	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&	/* XXX */
1477170530Ssam	    isclr(chanlist, ic->ic_bsschan->ic_ieee))
1478170530Ssam		ic->ic_bsschan = IEEE80211_CHAN_ANYC;
1479138568Ssam	memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
1480170530Ssam	return IS_UP_AUTO(ic) ? ieee80211_init(ic, RESCAN) : 0;
1481138568Ssam}
1482138568Ssam
1483138568Ssamstatic int
1484157172Ssamieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
1485157172Ssam{
1486157172Ssam	struct ieee80211_node *ni;
1487170530Ssam	uint8_t macaddr[IEEE80211_ADDR_LEN];
1488157172Ssam	int error;
1489157172Ssam
1490157172Ssam	/*
1491157172Ssam	 * NB: we could copyin ieee80211req_sta_stats so apps
1492157172Ssam	 *     could make selective changes but that's overkill;
1493157172Ssam	 *     just clear all stats for now.
1494157172Ssam	 */
1495157172Ssam	if (ireq->i_len < IEEE80211_ADDR_LEN)
1496157172Ssam		return EINVAL;
1497157172Ssam	error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
1498157172Ssam	if (error != 0)
1499157172Ssam		return error;
1500157172Ssam	ni = ieee80211_find_node(&ic->ic_sta, macaddr);
1501157172Ssam	if (ni == NULL)
1502157172Ssam		return EINVAL;		/* XXX */
1503157172Ssam	memset(&ni->ni_stats, 0, sizeof(ni->ni_stats));
1504157172Ssam	ieee80211_free_node(ni);
1505157172Ssam	return 0;
1506157172Ssam}
1507157172Ssam
1508157172Ssamstatic int
1509138568Ssamieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
1510138568Ssam{
1511138568Ssam	struct ieee80211_node *ni;
1512138568Ssam	struct ieee80211req_sta_txpow txpow;
1513138568Ssam	int error;
1514138568Ssam
1515138568Ssam	if (ireq->i_len != sizeof(txpow))
1516138568Ssam		return EINVAL;
1517138568Ssam	error = copyin(ireq->i_data, &txpow, sizeof(txpow));
1518138568Ssam	if (error != 0)
1519138568Ssam		return error;
1520140753Ssam	ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
1521138568Ssam	if (ni == NULL)
1522138568Ssam		return EINVAL;		/* XXX */
1523138568Ssam	ni->ni_txpower = txpow.it_txpow;
1524138568Ssam	ieee80211_free_node(ni);
1525138568Ssam	return error;
1526138568Ssam}
1527138568Ssam
1528138568Ssamstatic int
1529138568Ssamieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
1530138568Ssam{
1531138568Ssam	struct ieee80211_wme_state *wme = &ic->ic_wme;
1532138568Ssam	struct wmeParams *wmep, *chanp;
1533138568Ssam	int isbss, ac;
1534138568Ssam
1535138568Ssam	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
1536138568Ssam		return EINVAL;
1537138568Ssam
1538138568Ssam	isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS);
1539138568Ssam	ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
1540138568Ssam	if (ac >= WME_NUM_AC)
1541138568Ssam		ac = WME_AC_BE;
1542138568Ssam	if (isbss) {
1543138568Ssam		chanp = &wme->wme_bssChanParams.cap_wmeParams[ac];
1544138568Ssam		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
1545138568Ssam	} else {
1546138568Ssam		chanp = &wme->wme_chanParams.cap_wmeParams[ac];
1547138568Ssam		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
1548138568Ssam	}
1549138568Ssam	switch (ireq->i_type) {
1550138568Ssam	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
1551138568Ssam		if (isbss) {
1552138568Ssam			wmep->wmep_logcwmin = ireq->i_val;
1553138568Ssam			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
1554138568Ssam				chanp->wmep_logcwmin = ireq->i_val;
1555138568Ssam		} else {
1556138568Ssam			wmep->wmep_logcwmin = chanp->wmep_logcwmin =
1557138568Ssam				ireq->i_val;
1558138568Ssam		}
1559138568Ssam		break;
1560138568Ssam	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
1561138568Ssam		if (isbss) {
1562138568Ssam			wmep->wmep_logcwmax = ireq->i_val;
1563138568Ssam			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
1564138568Ssam				chanp->wmep_logcwmax = ireq->i_val;
1565138568Ssam		} else {
1566138568Ssam			wmep->wmep_logcwmax = chanp->wmep_logcwmax =
1567138568Ssam				ireq->i_val;
1568138568Ssam		}
1569138568Ssam		break;
1570138568Ssam	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
1571138568Ssam		if (isbss) {
1572138568Ssam			wmep->wmep_aifsn = ireq->i_val;
1573138568Ssam			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
1574138568Ssam				chanp->wmep_aifsn = ireq->i_val;
1575138568Ssam		} else {
1576138568Ssam			wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val;
1577138568Ssam		}
1578138568Ssam		break;
1579138568Ssam	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
1580138568Ssam		if (isbss) {
1581138568Ssam			wmep->wmep_txopLimit = ireq->i_val;
1582138568Ssam			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
1583138568Ssam				chanp->wmep_txopLimit = ireq->i_val;
1584138568Ssam		} else {
1585138568Ssam			wmep->wmep_txopLimit = chanp->wmep_txopLimit =
1586138568Ssam				ireq->i_val;
1587138568Ssam		}
1588138568Ssam		break;
1589138568Ssam	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
1590138568Ssam		wmep->wmep_acm = ireq->i_val;
1591138568Ssam		if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
1592138568Ssam			chanp->wmep_acm = ireq->i_val;
1593138568Ssam		break;
1594138568Ssam	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (!bss only)*/
1595138568Ssam		wmep->wmep_noackPolicy = chanp->wmep_noackPolicy =
1596138568Ssam			(ireq->i_val) == 0;
1597138568Ssam		break;
1598138568Ssam	}
1599138568Ssam	ieee80211_wme_updateparams(ic);
1600138568Ssam	return 0;
1601138568Ssam}
1602138568Ssam
1603138568Ssamstatic int
1604138568Ssamcipher2cap(int cipher)
1605138568Ssam{
1606138568Ssam	switch (cipher) {
1607138568Ssam	case IEEE80211_CIPHER_WEP:	return IEEE80211_C_WEP;
1608138568Ssam	case IEEE80211_CIPHER_AES_OCB:	return IEEE80211_C_AES;
1609138568Ssam	case IEEE80211_CIPHER_AES_CCM:	return IEEE80211_C_AES_CCM;
1610138568Ssam	case IEEE80211_CIPHER_CKIP:	return IEEE80211_C_CKIP;
1611138568Ssam	case IEEE80211_CIPHER_TKIP:	return IEEE80211_C_TKIP;
1612138568Ssam	}
1613138568Ssam	return 0;
1614138568Ssam}
1615138568Ssam
1616138568Ssamstatic int
1617170530Ssamfind11gchannel(struct ieee80211com *ic, int start, int freq)
1618170530Ssam{
1619170530Ssam	const struct ieee80211_channel *c;
1620170530Ssam	int i;
1621170530Ssam
1622170530Ssam	for (i = start+1; i < ic->ic_nchans; i++) {
1623170530Ssam		c = &ic->ic_channels[i];
1624170530Ssam		if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
1625170530Ssam			return 1;
1626170530Ssam	}
1627170530Ssam	/* NB: should not be needed but in case things are mis-sorted */
1628170530Ssam	for (i = 0; i < start; i++) {
1629170530Ssam		c = &ic->ic_channels[i];
1630170530Ssam		if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
1631170530Ssam			return 1;
1632170530Ssam	}
1633170530Ssam	return 0;
1634170530Ssam}
1635170530Ssam
1636170530Ssamstatic struct ieee80211_channel *
1637170530Ssamfindchannel(struct ieee80211com *ic, int ieee, int mode)
1638170530Ssam{
1639170530Ssam	static const u_int chanflags[IEEE80211_MODE_MAX] = {
1640170530Ssam		0,			/* IEEE80211_MODE_AUTO */
1641170530Ssam		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11A */
1642170530Ssam		IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
1643170530Ssam		IEEE80211_CHAN_G,	/* IEEE80211_MODE_11G */
1644170530Ssam		IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
1645170530Ssam		IEEE80211_CHAN_108A,	/* IEEE80211_MODE_TURBO_A */
1646170530Ssam		IEEE80211_CHAN_108G,	/* IEEE80211_MODE_TURBO_G */
1647170530Ssam		IEEE80211_CHAN_STURBO,	/* IEEE80211_MODE_STURBO_A */
1648170530Ssam		/* NB: handled specially below */
1649170530Ssam		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11NA */
1650170530Ssam		IEEE80211_CHAN_G,	/* IEEE80211_MODE_11NG */
1651170530Ssam	};
1652170530Ssam	u_int modeflags;
1653170530Ssam	int i;
1654170530Ssam
1655170530Ssam	KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
1656170530Ssam	modeflags = chanflags[mode];
1657170530Ssam	KASSERT(modeflags != 0 || mode == IEEE80211_MODE_AUTO,
1658170530Ssam	    ("no chanflags for mode %u", mode));
1659170530Ssam	for (i = 0; i < ic->ic_nchans; i++) {
1660170530Ssam		struct ieee80211_channel *c = &ic->ic_channels[i];
1661170530Ssam
1662170530Ssam		if (c->ic_ieee != ieee)
1663170530Ssam			continue;
1664170530Ssam		if (mode == IEEE80211_MODE_AUTO) {
1665170530Ssam			/* ignore turbo channels for autoselect */
1666170530Ssam			if (IEEE80211_IS_CHAN_TURBO(c))
1667170530Ssam				continue;
1668170530Ssam			/*
1669170530Ssam			 * XXX special-case 11b/g channels so we
1670170530Ssam			 *     always select the g channel if both
1671170530Ssam			 *     are present.
1672170530Ssam			 * XXX prefer HT to non-HT?
1673170530Ssam			 */
1674170530Ssam			if (!IEEE80211_IS_CHAN_B(c) ||
1675170530Ssam			    !find11gchannel(ic, i, c->ic_freq))
1676170530Ssam				return c;
1677170530Ssam		} else {
1678170530Ssam			/* must check HT specially */
1679170530Ssam			if ((mode == IEEE80211_MODE_11NA ||
1680170530Ssam			    mode == IEEE80211_MODE_11NG) &&
1681170530Ssam			    !IEEE80211_IS_CHAN_HT(c))
1682170530Ssam				continue;
1683170530Ssam			if ((c->ic_flags & modeflags) == modeflags)
1684170530Ssam				return c;
1685170530Ssam		}
1686170530Ssam	}
1687170530Ssam	return NULL;
1688170530Ssam}
1689170530Ssam
1690170530Ssam/*
1691170530Ssam * Check the specified against any desired mode (aka netband).
1692170530Ssam * This is only used (presently) when operating in hostap mode
1693170530Ssam * to enforce consistency.
1694170530Ssam */
1695170530Ssamstatic int
1696170530Ssamcheck_mode_consistency(const struct ieee80211_channel *c, int mode)
1697170530Ssam{
1698170530Ssam	KASSERT(c != IEEE80211_CHAN_ANYC, ("oops, no channel"));
1699170530Ssam
1700170530Ssam	switch (mode) {
1701170530Ssam	case IEEE80211_MODE_11B:
1702170530Ssam		return (IEEE80211_IS_CHAN_B(c));
1703170530Ssam	case IEEE80211_MODE_11G:
1704170530Ssam		return (IEEE80211_IS_CHAN_ANYG(c) && !IEEE80211_IS_CHAN_HT(c));
1705170530Ssam	case IEEE80211_MODE_11A:
1706170530Ssam		return (IEEE80211_IS_CHAN_A(c) && !IEEE80211_IS_CHAN_HT(c));
1707170530Ssam	case IEEE80211_MODE_STURBO_A:
1708170530Ssam		return (IEEE80211_IS_CHAN_STURBO(c));
1709170530Ssam	case IEEE80211_MODE_11NA:
1710170530Ssam		return (IEEE80211_IS_CHAN_HTA(c));
1711170530Ssam	case IEEE80211_MODE_11NG:
1712170530Ssam		return (IEEE80211_IS_CHAN_HTG(c));
1713170530Ssam	}
1714170530Ssam	return 1;
1715170530Ssam
1716170530Ssam}
1717170530Ssam
1718170530Ssam/*
1719170530Ssam * Common code to set the current channel.  If the device
1720170530Ssam * is up and running this may result in an immediate channel
1721170530Ssam * change or a kick of the state machine.
1722170530Ssam */
1723170530Ssamstatic int
1724170530Ssamsetcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
1725170530Ssam{
1726170530Ssam	int error;
1727170530Ssam
1728170530Ssam	if (c != IEEE80211_CHAN_ANYC) {
1729170530Ssam		if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
1730170530Ssam		    !check_mode_consistency(c, ic->ic_des_mode))
1731170530Ssam			return EINVAL;
1732170530Ssam		if (ic->ic_state == IEEE80211_S_RUN && c == ic->ic_curchan)
1733170530Ssam			return 0;	/* NB: nothing to do */
1734170530Ssam	}
1735170530Ssam	ic->ic_des_chan = c;
1736170530Ssam
1737170530Ssam	error = 0;
1738170530Ssam	if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
1739170530Ssam	    ic->ic_opmode == IEEE80211_M_WDS) &&
1740170530Ssam	    ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
1741170530Ssam		/*
1742170530Ssam		 * Monitor and wds modes can switch directly.
1743170530Ssam		 */
1744170530Ssam		ic->ic_curchan = ic->ic_des_chan;
1745170530Ssam		if (ic->ic_state == IEEE80211_S_RUN)
1746170530Ssam			ic->ic_set_channel(ic);
1747170530Ssam	} else {
1748170530Ssam		/*
1749170530Ssam		 * Need to go through the state machine in case we
1750170530Ssam		 * need to reassociate or the like.  The state machine
1751170530Ssam		 * will pickup the desired channel and avoid scanning.
1752170530Ssam		 */
1753170530Ssam		if (IS_UP_AUTO(ic))
1754170530Ssam			error = ieee80211_init(ic, RESCAN);
1755170530Ssam		else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
1756170530Ssam			/*
1757170530Ssam			 * When not up+running and a real channel has
1758170530Ssam			 * been specified fix the current channel so
1759170530Ssam			 * there is immediate feedback; e.g. via ifconfig.
1760170530Ssam			 */
1761170530Ssam			ic->ic_curchan = ic->ic_des_chan;
1762170530Ssam		}
1763170530Ssam	}
1764170530Ssam	return error;
1765170530Ssam}
1766170530Ssam
1767170530Ssam/*
1768170530Ssam * Old api for setting the current channel; this is
1769170530Ssam * deprecated because channel numbers are ambiguous.
1770170530Ssam */
1771170530Ssamstatic int
1772170530Ssamieee80211_ioctl_setchannel(struct ieee80211com *ic,
1773170530Ssam	const struct ieee80211req *ireq)
1774170530Ssam{
1775170530Ssam	struct ieee80211_channel *c;
1776170530Ssam
1777170530Ssam	/* XXX 0xffff overflows 16-bit signed */
1778170530Ssam	if (ireq->i_val == 0 ||
1779170530Ssam	    ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) {
1780170530Ssam		c = IEEE80211_CHAN_ANYC;
1781170530Ssam	} else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX) {
1782170530Ssam		return EINVAL;
1783170530Ssam	} else {
1784170530Ssam		struct ieee80211_channel *c2;
1785170530Ssam
1786170530Ssam		c = findchannel(ic, ireq->i_val, ic->ic_des_mode);
1787170530Ssam		if (c == NULL) {
1788170530Ssam			c = findchannel(ic, ireq->i_val,
1789170530Ssam				IEEE80211_MODE_AUTO);
1790170530Ssam			if (c == NULL)
1791170530Ssam				return EINVAL;
1792170530Ssam		}
1793170530Ssam		/*
1794170530Ssam		 * Fine tune channel selection based on desired mode:
1795170530Ssam		 *   if 11b is requested, find the 11b version of any
1796170530Ssam		 *      11g channel returned,
1797170530Ssam		 *   if static turbo, find the turbo version of any
1798170530Ssam		 *	11a channel return,
1799170530Ssam		 *   if 11na is requested, find the ht version of any
1800170530Ssam		 *      11a channel returned,
1801170530Ssam		 *   if 11ng is requested, find the ht version of any
1802170530Ssam		 *      11g channel returned,
1803170530Ssam		 *   otherwise we should be ok with what we've got.
1804170530Ssam		 */
1805170530Ssam		switch (ic->ic_des_mode) {
1806170530Ssam		case IEEE80211_MODE_11B:
1807170530Ssam			if (IEEE80211_IS_CHAN_ANYG(c)) {
1808170530Ssam				c2 = findchannel(ic, ireq->i_val,
1809170530Ssam					IEEE80211_MODE_11B);
1810170530Ssam				/* NB: should not happen, =>'s 11g w/o 11b */
1811170530Ssam				if (c2 != NULL)
1812170530Ssam					c = c2;
1813170530Ssam			}
1814170530Ssam			break;
1815170530Ssam		case IEEE80211_MODE_TURBO_A:
1816170530Ssam			if (IEEE80211_IS_CHAN_A(c)) {
1817170530Ssam				c2 = findchannel(ic, ireq->i_val,
1818170530Ssam					IEEE80211_MODE_TURBO_A);
1819170530Ssam				if (c2 != NULL)
1820170530Ssam					c = c2;
1821170530Ssam			}
1822170530Ssam			break;
1823170530Ssam		case IEEE80211_MODE_11NA:
1824170530Ssam			if (IEEE80211_IS_CHAN_A(c)) {
1825170530Ssam				c2 = findchannel(ic, ireq->i_val,
1826170530Ssam					IEEE80211_MODE_11NA);
1827170530Ssam				if (c2 != NULL)
1828170530Ssam					c = c2;
1829170530Ssam			}
1830170530Ssam			break;
1831170530Ssam		case IEEE80211_MODE_11NG:
1832170530Ssam			if (IEEE80211_IS_CHAN_ANYG(c)) {
1833170530Ssam				c2 = findchannel(ic, ireq->i_val,
1834170530Ssam					IEEE80211_MODE_11NG);
1835170530Ssam				if (c2 != NULL)
1836170530Ssam					c = c2;
1837170530Ssam			}
1838170530Ssam			break;
1839170530Ssam		default:		/* NB: no static turboG */
1840170530Ssam			break;
1841170530Ssam		}
1842170530Ssam	}
1843170530Ssam	return setcurchan(ic, c);
1844170530Ssam}
1845170530Ssam
1846170530Ssam/*
1847170530Ssam * New/current api for setting the current channel; a complete
1848170530Ssam * channel description is provide so there is no ambiguity in
1849170530Ssam * identifying the channel.
1850170530Ssam */
1851170530Ssamstatic int
1852170530Ssamieee80211_ioctl_setcurchan(struct ieee80211com *ic,
1853170530Ssam	const struct ieee80211req *ireq)
1854170530Ssam{
1855170530Ssam	struct ieee80211_channel chan, *c;
1856170530Ssam	int error;
1857170530Ssam
1858170530Ssam	if (ireq->i_len != sizeof(chan))
1859170530Ssam		return EINVAL;
1860170530Ssam	error = copyin(ireq->i_data, &chan, sizeof(chan));
1861170530Ssam	if (error != 0)
1862170530Ssam		return error;
1863170530Ssam	/* XXX 0xffff overflows 16-bit signed */
1864170530Ssam	if (chan.ic_freq == 0 || chan.ic_freq == IEEE80211_CHAN_ANY) {
1865170530Ssam		c = IEEE80211_CHAN_ANYC;
1866170530Ssam	} else {
1867170530Ssam		c = ieee80211_find_channel(ic, chan.ic_freq, chan.ic_flags);
1868170530Ssam		if (c == NULL)
1869170530Ssam			return EINVAL;
1870170530Ssam	}
1871170530Ssam	return setcurchan(ic, c);
1872170530Ssam}
1873170530Ssam
1874170530Ssamstatic int
1875138568Ssamieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
1876138568Ssam{
1877170530Ssam	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
1878138568Ssam	struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
1879138568Ssam	int error;
1880138568Ssam	const struct ieee80211_authenticator *auth;
1881170530Ssam	uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
1882138568Ssam	char tmpssid[IEEE80211_NWID_LEN];
1883170530Ssam	uint8_t tmpbssid[IEEE80211_ADDR_LEN];
1884138568Ssam	struct ieee80211_key *k;
1885138568Ssam	int j, caps;
1886138568Ssam	u_int kid;
1887138568Ssam
1888138568Ssam	error = 0;
1889138568Ssam	switch (ireq->i_type) {
1890138568Ssam	case IEEE80211_IOC_SSID:
1891138568Ssam		if (ireq->i_val != 0 ||
1892138568Ssam		    ireq->i_len > IEEE80211_NWID_LEN)
1893138568Ssam			return EINVAL;
1894138568Ssam		error = copyin(ireq->i_data, tmpssid, ireq->i_len);
1895138568Ssam		if (error)
1896116742Ssam			break;
1897170530Ssam		memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
1898170530Ssam		ic->ic_des_ssid[0].len = ireq->i_len;
1899170530Ssam		memcpy(ic->ic_des_ssid[0].ssid, tmpssid, ireq->i_len);
1900170530Ssam		ic->ic_des_nssid = (ireq->i_len > 0);
1901170530Ssam		if (IS_UP_AUTO(ic))
1902170530Ssam			error = ieee80211_init(ic, RESCAN);
1903138568Ssam		break;
1904138568Ssam	case IEEE80211_IOC_WEP:
1905138568Ssam		switch (ireq->i_val) {
1906138568Ssam		case IEEE80211_WEP_OFF:
1907138568Ssam			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
1908138568Ssam			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
1909116742Ssam			break;
1910138568Ssam		case IEEE80211_WEP_ON:
1911138568Ssam			ic->ic_flags |= IEEE80211_F_PRIVACY;
1912138568Ssam			ic->ic_flags |= IEEE80211_F_DROPUNENC;
1913116742Ssam			break;
1914138568Ssam		case IEEE80211_WEP_MIXED:
1915138568Ssam			ic->ic_flags |= IEEE80211_F_PRIVACY;
1916138568Ssam			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
1917116742Ssam			break;
1918138568Ssam		}
1919170530Ssam		if (IS_UP_AUTO(ic))
1920170530Ssam			error = ieee80211_init(ic, RESCAN);
1921138568Ssam		break;
1922138568Ssam	case IEEE80211_IOC_WEPKEY:
1923138568Ssam		kid = (u_int) ireq->i_val;
1924138568Ssam		if (kid >= IEEE80211_WEP_NKID)
1925138568Ssam			return EINVAL;
1926138568Ssam		k = &ic->ic_nw_keys[kid];
1927138568Ssam		if (ireq->i_len == 0) {
1928138568Ssam			/* zero-len =>'s delete any existing key */
1929138568Ssam			(void) ieee80211_crypto_delkey(ic, k);
1930127648Ssam			break;
1931138568Ssam		}
1932138568Ssam		if (ireq->i_len > sizeof(tmpkey))
1933138568Ssam			return EINVAL;
1934138568Ssam		memset(tmpkey, 0, sizeof(tmpkey));
1935138568Ssam		error = copyin(ireq->i_data, tmpkey, ireq->i_len);
1936138568Ssam		if (error)
1937138568Ssam			break;
1938138568Ssam		ieee80211_key_update_begin(ic);
1939144960Ssam		k->wk_keyix = kid;	/* NB: force fixed key id */
1940144960Ssam		if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP,
1941144960Ssam		    IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) {
1942138568Ssam			k->wk_keylen = ireq->i_len;
1943138568Ssam			memcpy(k->wk_key, tmpkey, sizeof(tmpkey));
1944138568Ssam			if  (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr))
1945127648Ssam				error = EINVAL;
1946138568Ssam		} else
1947138568Ssam			error = EINVAL;
1948138568Ssam		ieee80211_key_update_end(ic);
1949138568Ssam		break;
1950138568Ssam	case IEEE80211_IOC_WEPTXKEY:
1951138568Ssam		kid = (u_int) ireq->i_val;
1952139519Ssam		if (kid >= IEEE80211_WEP_NKID &&
1953170530Ssam		    (uint16_t) kid != IEEE80211_KEYIX_NONE)
1954138568Ssam			return EINVAL;
1955138568Ssam		ic->ic_def_txkey = kid;
1956138568Ssam		break;
1957138568Ssam	case IEEE80211_IOC_AUTHMODE:
1958138568Ssam		switch (ireq->i_val) {
1959138568Ssam		case IEEE80211_AUTH_WPA:
1960138568Ssam		case IEEE80211_AUTH_8021X:	/* 802.1x */
1961138568Ssam		case IEEE80211_AUTH_OPEN:	/* open */
1962138568Ssam		case IEEE80211_AUTH_SHARED:	/* shared-key */
1963138568Ssam		case IEEE80211_AUTH_AUTO:	/* auto */
1964138568Ssam			auth = ieee80211_authenticator_get(ireq->i_val);
1965138568Ssam			if (auth == NULL)
1966138568Ssam				return EINVAL;
1967127648Ssam			break;
1968116742Ssam		default:
1969138568Ssam			return EINVAL;
1970138568Ssam		}
1971138568Ssam		switch (ireq->i_val) {
1972138568Ssam		case IEEE80211_AUTH_WPA:	/* WPA w/ 802.1x */
1973138568Ssam			ic->ic_flags |= IEEE80211_F_PRIVACY;
1974138568Ssam			ireq->i_val = IEEE80211_AUTH_8021X;
1975127646Ssam			break;
1976138568Ssam		case IEEE80211_AUTH_OPEN:	/* open */
1977138568Ssam			ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
1978138568Ssam			break;
1979138568Ssam		case IEEE80211_AUTH_SHARED:	/* shared-key */
1980138568Ssam		case IEEE80211_AUTH_8021X:	/* 802.1x */
1981138568Ssam			ic->ic_flags &= ~IEEE80211_F_WPA;
1982138568Ssam			/* both require a key so mark the PRIVACY capability */
1983138568Ssam			ic->ic_flags |= IEEE80211_F_PRIVACY;
1984138568Ssam			break;
1985138568Ssam		case IEEE80211_AUTH_AUTO:	/* auto */
1986138568Ssam			ic->ic_flags &= ~IEEE80211_F_WPA;
1987138568Ssam			/* XXX PRIVACY handling? */
1988138568Ssam			/* XXX what's the right way to do this? */
1989138568Ssam			break;
1990116742Ssam		}
1991138568Ssam		/* NB: authenticator attach/detach happens on state change */
1992138568Ssam		ic->ic_bss->ni_authmode = ireq->i_val;
1993138568Ssam		/* XXX mixed/mode/usage? */
1994138568Ssam		ic->ic_auth = auth;
1995170530Ssam		if (IS_UP_AUTO(ic))
1996170530Ssam			error = ieee80211_init(ic, RESCAN);
1997116742Ssam		break;
1998138568Ssam	case IEEE80211_IOC_CHANNEL:
1999170530Ssam		error = ieee80211_ioctl_setchannel(ic, ireq);
2000138568Ssam		break;
2001138568Ssam	case IEEE80211_IOC_POWERSAVE:
2002138568Ssam		switch (ireq->i_val) {
2003138568Ssam		case IEEE80211_POWERSAVE_OFF:
2004138568Ssam			if (ic->ic_flags & IEEE80211_F_PMGTON) {
2005138568Ssam				ic->ic_flags &= ~IEEE80211_F_PMGTON;
2006138568Ssam				error = ENETRESET;
2007116742Ssam			}
2008116742Ssam			break;
2009138568Ssam		case IEEE80211_POWERSAVE_ON:
2010138568Ssam			if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
2011116742Ssam				error = EINVAL;
2012138568Ssam			else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
2013138568Ssam				ic->ic_flags |= IEEE80211_F_PMGTON;
2014116742Ssam				error = ENETRESET;
2015116742Ssam			}
2016116742Ssam			break;
2017138568Ssam		default:
2018138568Ssam			error = EINVAL;
2019116742Ssam			break;
2020138568Ssam		}
2021160407Ssam		if (error == ENETRESET) {
2022160407Ssam			/*
2023160407Ssam			 * Switching in+out of power save mode
2024160407Ssam			 * should not require a state change.
2025160407Ssam			 */
2026160407Ssam			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2027160407Ssam		}
2028138568Ssam		break;
2029138568Ssam	case IEEE80211_IOC_POWERSAVESLEEP:
2030138568Ssam		if (ireq->i_val < 0)
2031138568Ssam			return EINVAL;
2032138568Ssam		ic->ic_lintval = ireq->i_val;
2033138568Ssam		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2034138568Ssam		break;
2035138568Ssam	case IEEE80211_IOC_RTSTHRESHOLD:
2036148292Ssam		if (!(IEEE80211_RTS_MIN <= ireq->i_val &&
2037148292Ssam		      ireq->i_val <= IEEE80211_RTS_MAX))
2038138568Ssam			return EINVAL;
2039138568Ssam		ic->ic_rtsthreshold = ireq->i_val;
2040138568Ssam		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2041138568Ssam		break;
2042138568Ssam	case IEEE80211_IOC_PROTMODE:
2043138568Ssam		if (ireq->i_val > IEEE80211_PROT_RTSCTS)
2044138568Ssam			return EINVAL;
2045138568Ssam		ic->ic_protmode = ireq->i_val;
2046138568Ssam		/* NB: if not operating in 11g this can wait */
2047170530Ssam		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
2048170530Ssam		    IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
2049138568Ssam			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2050138568Ssam		break;
2051138568Ssam	case IEEE80211_IOC_TXPOWER:
2052138568Ssam		if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
2053138568Ssam			return EINVAL;
2054170530Ssam		if (!(IEEE80211_TXPOWER_MIN <= ireq->i_val &&
2055170530Ssam		      ireq->i_val <= IEEE80211_TXPOWER_MAX))
2056138568Ssam			return EINVAL;
2057138568Ssam		ic->ic_txpowlimit = ireq->i_val;
2058138568Ssam		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2059138568Ssam		break;
2060138568Ssam	case IEEE80211_IOC_ROAMING:
2061138568Ssam		if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val &&
2062138568Ssam		    ireq->i_val <= IEEE80211_ROAMING_MANUAL))
2063138568Ssam			return EINVAL;
2064138568Ssam		ic->ic_roaming = ireq->i_val;
2065138568Ssam		/* XXXX reset? */
2066138568Ssam		break;
2067138568Ssam	case IEEE80211_IOC_PRIVACY:
2068138568Ssam		if (ireq->i_val) {
2069138568Ssam			/* XXX check for key state? */
2070138568Ssam			ic->ic_flags |= IEEE80211_F_PRIVACY;
2071138568Ssam		} else
2072138568Ssam			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
2073138568Ssam		break;
2074138568Ssam	case IEEE80211_IOC_DROPUNENCRYPTED:
2075138568Ssam		if (ireq->i_val)
2076138568Ssam			ic->ic_flags |= IEEE80211_F_DROPUNENC;
2077138568Ssam		else
2078138568Ssam			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
2079138568Ssam		break;
2080138568Ssam	case IEEE80211_IOC_WPAKEY:
2081138568Ssam		error = ieee80211_ioctl_setkey(ic, ireq);
2082138568Ssam		break;
2083138568Ssam	case IEEE80211_IOC_DELKEY:
2084138568Ssam		error = ieee80211_ioctl_delkey(ic, ireq);
2085138568Ssam		break;
2086138568Ssam	case IEEE80211_IOC_MLME:
2087138568Ssam		error = ieee80211_ioctl_setmlme(ic, ireq);
2088138568Ssam		break;
2089138568Ssam	case IEEE80211_IOC_OPTIE:
2090138568Ssam		error = ieee80211_ioctl_setoptie(ic, ireq);
2091138568Ssam		break;
2092138568Ssam	case IEEE80211_IOC_COUNTERMEASURES:
2093138568Ssam		if (ireq->i_val) {
2094138568Ssam			if ((ic->ic_flags & IEEE80211_F_WPA) == 0)
2095138568Ssam				return EINVAL;
2096138568Ssam			ic->ic_flags |= IEEE80211_F_COUNTERM;
2097138568Ssam		} else
2098138568Ssam			ic->ic_flags &= ~IEEE80211_F_COUNTERM;
2099138568Ssam		break;
2100138568Ssam	case IEEE80211_IOC_WPA:
2101138568Ssam		if (ireq->i_val > 3)
2102138568Ssam			return EINVAL;
2103138568Ssam		/* XXX verify ciphers available */
2104138568Ssam		ic->ic_flags &= ~IEEE80211_F_WPA;
2105138568Ssam		switch (ireq->i_val) {
2106138568Ssam		case 1:
2107138568Ssam			ic->ic_flags |= IEEE80211_F_WPA1;
2108116742Ssam			break;
2109138568Ssam		case 2:
2110138568Ssam			ic->ic_flags |= IEEE80211_F_WPA2;
2111116742Ssam			break;
2112138568Ssam		case 3:
2113138568Ssam			ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
2114127648Ssam			break;
2115138568Ssam		}
2116170530Ssam		error = ENETRESET;
2117138568Ssam		break;
2118138568Ssam	case IEEE80211_IOC_WME:
2119138568Ssam		if (ireq->i_val) {
2120138568Ssam			if ((ic->ic_caps & IEEE80211_C_WME) == 0)
2121138568Ssam				return EINVAL;
2122138568Ssam			ic->ic_flags |= IEEE80211_F_WME;
2123138568Ssam		} else
2124138568Ssam			ic->ic_flags &= ~IEEE80211_F_WME;
2125170530Ssam		if (IS_UP_AUTO(ic))
2126170530Ssam			error = ieee80211_init(ic, 0);
2127138568Ssam		break;
2128138568Ssam	case IEEE80211_IOC_HIDESSID:
2129138568Ssam		if (ireq->i_val)
2130138568Ssam			ic->ic_flags |= IEEE80211_F_HIDESSID;
2131138568Ssam		else
2132138568Ssam			ic->ic_flags &= ~IEEE80211_F_HIDESSID;
2133138568Ssam		error = ENETRESET;
2134138568Ssam		break;
2135138568Ssam	case IEEE80211_IOC_APBRIDGE:
2136138568Ssam		if (ireq->i_val == 0)
2137138568Ssam			ic->ic_flags |= IEEE80211_F_NOBRIDGE;
2138138568Ssam		else
2139138568Ssam			ic->ic_flags &= ~IEEE80211_F_NOBRIDGE;
2140138568Ssam		break;
2141138568Ssam	case IEEE80211_IOC_MCASTCIPHER:
2142138568Ssam		if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 &&
2143138568Ssam		    !ieee80211_crypto_available(ireq->i_val))
2144138568Ssam			return EINVAL;
2145138568Ssam		rsn->rsn_mcastcipher = ireq->i_val;
2146138568Ssam		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
2147138568Ssam		break;
2148138568Ssam	case IEEE80211_IOC_MCASTKEYLEN:
2149138568Ssam		if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
2150138568Ssam			return EINVAL;
2151138568Ssam		/* XXX no way to verify driver capability */
2152138568Ssam		rsn->rsn_mcastkeylen = ireq->i_val;
2153138568Ssam		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
2154138568Ssam		break;
2155138568Ssam	case IEEE80211_IOC_UCASTCIPHERS:
2156138568Ssam		/*
2157138568Ssam		 * Convert user-specified cipher set to the set
2158138568Ssam		 * we can support (via hardware or software).
2159138568Ssam		 * NB: this logic intentionally ignores unknown and
2160138568Ssam		 * unsupported ciphers so folks can specify 0xff or
2161138568Ssam		 * similar and get all available ciphers.
2162138568Ssam		 */
2163138568Ssam		caps = 0;
2164138568Ssam		for (j = 1; j < 32; j++)	/* NB: skip WEP */
2165138568Ssam			if ((ireq->i_val & (1<<j)) &&
2166138568Ssam			    ((ic->ic_caps & cipher2cap(j)) ||
2167138568Ssam			     ieee80211_crypto_available(j)))
2168138568Ssam				caps |= 1<<j;
2169138568Ssam		if (caps == 0)			/* nothing available */
2170138568Ssam			return EINVAL;
2171138568Ssam		/* XXX verify ciphers ok for unicast use? */
2172138568Ssam		/* XXX disallow if running as it'll have no effect */
2173138568Ssam		rsn->rsn_ucastcipherset = caps;
2174138568Ssam		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
2175138568Ssam		break;
2176138568Ssam	case IEEE80211_IOC_UCASTCIPHER:
2177138568Ssam		if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0)
2178138568Ssam			return EINVAL;
2179138568Ssam		rsn->rsn_ucastcipher = ireq->i_val;
2180138568Ssam		break;
2181138568Ssam	case IEEE80211_IOC_UCASTKEYLEN:
2182138568Ssam		if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
2183138568Ssam			return EINVAL;
2184138568Ssam		/* XXX no way to verify driver capability */
2185138568Ssam		rsn->rsn_ucastkeylen = ireq->i_val;
2186138568Ssam		break;
2187138568Ssam	case IEEE80211_IOC_DRIVER_CAPS:
2188138568Ssam		/* NB: for testing */
2189170530Ssam		ic->ic_caps = (((uint16_t) ireq->i_val) << 16) |
2190170530Ssam			       ((uint16_t) ireq->i_len);
2191138568Ssam		break;
2192138568Ssam	case IEEE80211_IOC_KEYMGTALGS:
2193138568Ssam		/* XXX check */
2194138568Ssam		rsn->rsn_keymgmtset = ireq->i_val;
2195138568Ssam		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
2196138568Ssam		break;
2197138568Ssam	case IEEE80211_IOC_RSNCAPS:
2198138568Ssam		/* XXX check */
2199138568Ssam		rsn->rsn_caps = ireq->i_val;
2200138568Ssam		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
2201138568Ssam		break;
2202138568Ssam	case IEEE80211_IOC_BSSID:
2203138568Ssam		if (ireq->i_len != sizeof(tmpbssid))
2204138568Ssam			return EINVAL;
2205138568Ssam		error = copyin(ireq->i_data, tmpbssid, ireq->i_len);
2206138568Ssam		if (error)
2207127648Ssam			break;
2208138568Ssam		IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid);
2209138568Ssam		if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid))
2210138568Ssam			ic->ic_flags &= ~IEEE80211_F_DESBSSID;
2211138568Ssam		else
2212138568Ssam			ic->ic_flags |= IEEE80211_F_DESBSSID;
2213170530Ssam		if (IS_UP_AUTO(ic))
2214170530Ssam			error = ieee80211_init(ic, RESCAN);
2215138568Ssam		break;
2216138568Ssam	case IEEE80211_IOC_CHANLIST:
2217138568Ssam		error = ieee80211_ioctl_setchanlist(ic, ireq);
2218138568Ssam		break;
2219138568Ssam	case IEEE80211_IOC_SCAN_REQ:
2220170530Ssam		if (!IS_UP(ic))
2221170530Ssam			return EINVAL;
2222170530Ssam		(void) ieee80211_start_scan(ic,
2223170530Ssam			IEEE80211_SCAN_ACTIVE |
2224170530Ssam			IEEE80211_SCAN_NOPICK |
2225170530Ssam			IEEE80211_SCAN_ONCE, IEEE80211_SCAN_FOREVER,
2226170530Ssam			/* XXX use ioctl params */
2227170530Ssam			ic->ic_des_nssid, ic->ic_des_ssid);
2228116742Ssam		break;
2229138568Ssam	case IEEE80211_IOC_ADDMAC:
2230138568Ssam	case IEEE80211_IOC_DELMAC:
2231138568Ssam		error = ieee80211_ioctl_macmac(ic, ireq);
2232138568Ssam		break;
2233138568Ssam	case IEEE80211_IOC_MACCMD:
2234149028Ssam		error = ieee80211_ioctl_setmaccmd(ic, ireq);
2235138568Ssam		break;
2236157172Ssam	case IEEE80211_IOC_STA_STATS:
2237157172Ssam		error = ieee80211_ioctl_setstastats(ic, ireq);
2238157172Ssam		break;
2239138568Ssam	case IEEE80211_IOC_STA_TXPOW:
2240138568Ssam		error = ieee80211_ioctl_setstatxpow(ic, ireq);
2241138568Ssam		break;
2242138568Ssam	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
2243138568Ssam	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
2244138568Ssam	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
2245138568Ssam	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
2246138568Ssam	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
2247138568Ssam	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (bss only) */
2248138568Ssam		error = ieee80211_ioctl_setwmeparam(ic, ireq);
2249138568Ssam		break;
2250138568Ssam	case IEEE80211_IOC_DTIM_PERIOD:
2251138568Ssam		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
2252138568Ssam		    ic->ic_opmode != IEEE80211_M_IBSS)
2253138568Ssam			return EINVAL;
2254138568Ssam		if (IEEE80211_DTIM_MIN <= ireq->i_val &&
2255138568Ssam		    ireq->i_val <= IEEE80211_DTIM_MAX) {
2256138568Ssam			ic->ic_dtim_period = ireq->i_val;
2257138568Ssam			error = ENETRESET;		/* requires restart */
2258138568Ssam		} else
2259138568Ssam			error = EINVAL;
2260138568Ssam		break;
2261138568Ssam	case IEEE80211_IOC_BEACON_INTERVAL:
2262138568Ssam		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
2263138568Ssam		    ic->ic_opmode != IEEE80211_M_IBSS)
2264138568Ssam			return EINVAL;
2265138568Ssam		if (IEEE80211_BINTVAL_MIN <= ireq->i_val &&
2266138568Ssam		    ireq->i_val <= IEEE80211_BINTVAL_MAX) {
2267148843Ssam			ic->ic_bintval = ireq->i_val;
2268138568Ssam			error = ENETRESET;		/* requires restart */
2269138568Ssam		} else
2270138568Ssam			error = EINVAL;
2271138568Ssam		break;
2272147794Ssam	case IEEE80211_IOC_PUREG:
2273147794Ssam		if (ireq->i_val)
2274147794Ssam			ic->ic_flags |= IEEE80211_F_PUREG;
2275147794Ssam		else
2276147794Ssam			ic->ic_flags &= ~IEEE80211_F_PUREG;
2277147794Ssam		/* NB: reset only if we're operating on an 11g channel */
2278170530Ssam		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
2279170530Ssam		    IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
2280147794Ssam			error = ENETRESET;
2281147794Ssam		break;
2282170530Ssam	case IEEE80211_IOC_FF:
2283170530Ssam		if (ireq->i_val) {
2284170530Ssam			if ((ic->ic_caps & IEEE80211_C_FF) == 0)
2285170530Ssam				return EINVAL;
2286170530Ssam			ic->ic_flags |= IEEE80211_F_FF;
2287170530Ssam		} else
2288170530Ssam			ic->ic_flags &= ~IEEE80211_F_FF;
2289170530Ssam		error = ENETRESET;
2290170530Ssam		break;
2291170530Ssam	case IEEE80211_IOC_TURBOP:
2292170530Ssam		if (ireq->i_val) {
2293170530Ssam			if ((ic->ic_caps & IEEE80211_C_TURBOP) == 0)
2294170530Ssam				return EINVAL;
2295170530Ssam			ic->ic_flags |= IEEE80211_F_TURBOP;
2296170530Ssam		} else
2297170530Ssam			ic->ic_flags &= ~IEEE80211_F_TURBOP;
2298170530Ssam		error = ENETRESET;
2299170530Ssam		break;
2300170530Ssam	case IEEE80211_IOC_BGSCAN:
2301170530Ssam		if (ireq->i_val) {
2302170530Ssam			if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0)
2303170530Ssam				return EINVAL;
2304170530Ssam			ic->ic_flags |= IEEE80211_F_BGSCAN;
2305170530Ssam		} else
2306170530Ssam			ic->ic_flags &= ~IEEE80211_F_BGSCAN;
2307170530Ssam		break;
2308170530Ssam	case IEEE80211_IOC_BGSCAN_IDLE:
2309170530Ssam		if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN)
2310170530Ssam			ic->ic_bgscanidle = ireq->i_val*hz/1000;
2311170530Ssam		else
2312170530Ssam			error = EINVAL;
2313170530Ssam		break;
2314170530Ssam	case IEEE80211_IOC_BGSCAN_INTERVAL:
2315170530Ssam		if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN)
2316170530Ssam			ic->ic_bgscanintvl = ireq->i_val*hz;
2317170530Ssam		else
2318170530Ssam			error = EINVAL;
2319170530Ssam		break;
2320170530Ssam	case IEEE80211_IOC_SCANVALID:
2321170530Ssam		if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN)
2322170530Ssam			ic->ic_scanvalid = ireq->i_val*hz;
2323170530Ssam		else
2324170530Ssam			error = EINVAL;
2325170530Ssam		break;
2326170530Ssam	case IEEE80211_IOC_ROAM_RSSI_11A:
2327170530Ssam		ic->ic_roam.rssi11a = ireq->i_val;
2328170530Ssam		break;
2329170530Ssam	case IEEE80211_IOC_ROAM_RSSI_11B:
2330170530Ssam		ic->ic_roam.rssi11bOnly = ireq->i_val;
2331170530Ssam		break;
2332170530Ssam	case IEEE80211_IOC_ROAM_RSSI_11G:
2333170530Ssam		ic->ic_roam.rssi11b = ireq->i_val;
2334170530Ssam		break;
2335170530Ssam	case IEEE80211_IOC_ROAM_RATE_11A:
2336170530Ssam		ic->ic_roam.rate11a = ireq->i_val & IEEE80211_RATE_VAL;
2337170530Ssam		break;
2338170530Ssam	case IEEE80211_IOC_ROAM_RATE_11B:
2339170530Ssam		ic->ic_roam.rate11bOnly = ireq->i_val & IEEE80211_RATE_VAL;
2340170530Ssam		break;
2341170530Ssam	case IEEE80211_IOC_ROAM_RATE_11G:
2342170530Ssam		ic->ic_roam.rate11b = ireq->i_val & IEEE80211_RATE_VAL;
2343170530Ssam		break;
2344153346Ssam	case IEEE80211_IOC_MCAST_RATE:
2345153346Ssam		ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL;
2346153346Ssam		break;
2347148292Ssam	case IEEE80211_IOC_FRAGTHRESHOLD:
2348148292Ssam		if ((ic->ic_caps & IEEE80211_C_TXFRAG) == 0 &&
2349148292Ssam		    ireq->i_val != IEEE80211_FRAG_MAX)
2350148292Ssam			return EINVAL;
2351148292Ssam		if (!(IEEE80211_FRAG_MIN <= ireq->i_val &&
2352148292Ssam		      ireq->i_val <= IEEE80211_FRAG_MAX))
2353148292Ssam			return EINVAL;
2354148292Ssam		ic->ic_fragthreshold = ireq->i_val;
2355148292Ssam		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2356148292Ssam		break;
2357153421Ssam	case IEEE80211_IOC_BURST:
2358153421Ssam		if (ireq->i_val) {
2359153421Ssam			if ((ic->ic_caps & IEEE80211_C_BURST) == 0)
2360153421Ssam				return EINVAL;
2361153421Ssam			ic->ic_flags |= IEEE80211_F_BURST;
2362153421Ssam		} else
2363153421Ssam			ic->ic_flags &= ~IEEE80211_F_BURST;
2364153421Ssam		error = ENETRESET;		/* XXX maybe not for station? */
2365153421Ssam		break;
2366160686Ssam	case IEEE80211_IOC_BMISSTHRESHOLD:
2367160686Ssam		if (!(IEEE80211_HWBMISS_MIN <= ireq->i_val &&
2368160686Ssam		      ireq->i_val <= IEEE80211_HWBMISS_MAX))
2369160686Ssam			return EINVAL;
2370160686Ssam		ic->ic_bmissthreshold = ireq->i_val;
2371160686Ssam		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2372160686Ssam		break;
2373170530Ssam	case IEEE80211_IOC_CURCHAN:
2374170530Ssam		error = ieee80211_ioctl_setcurchan(ic, ireq);
2375170530Ssam		break;
2376170530Ssam	case IEEE80211_IOC_SHORTGI:
2377170530Ssam		if (ireq->i_val) {
2378170530Ssam#define	IEEE80211_HTCAP_SHORTGI \
2379170530Ssam	(IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40)
2380170530Ssam			if (((ireq->i_val ^ ic->ic_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0)
2381170530Ssam				return EINVAL;
2382170530Ssam			if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20)
2383170530Ssam				ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
2384170530Ssam			if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40)
2385170530Ssam				ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
2386170530Ssam#undef IEEE80211_HTCAP_SHORTGI
2387170530Ssam		} else
2388170530Ssam			ic->ic_flags_ext &=
2389170530Ssam			    ~(IEEE80211_FEXT_SHORTGI20 | IEEE80211_FEXT_SHORTGI40);
2390170530Ssam		/* XXX kick state machine? */
2391170530Ssam		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2392170530Ssam		break;
2393170530Ssam	case IEEE80211_IOC_AMPDU:
2394170530Ssam		if (ireq->i_val) {
2395170530Ssam			if ((ic->ic_htcaps & IEEE80211_HTC_AMPDU) == 0)
2396170530Ssam				return EINVAL;
2397170530Ssam			if (ireq->i_val & 1)
2398170530Ssam				ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
2399170530Ssam			if (ireq->i_val & 2)
2400170530Ssam				ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
2401170530Ssam		} else
2402170530Ssam			ic->ic_flags_ext &=
2403170530Ssam			    ~(IEEE80211_FEXT_AMPDU_TX|IEEE80211_FEXT_AMPDU_RX);
2404170530Ssam		/* NB: reset only if we're operating on an 11n channel */
2405170530Ssam		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
2406170530Ssam		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
2407170530Ssam			error = ENETRESET;
2408170530Ssam		break;
2409170530Ssam	case IEEE80211_IOC_AMPDU_LIMIT:
2410170530Ssam		/* XXX validate */
2411170530Ssam		ic->ic_ampdu_limit = ireq->i_val;
2412170530Ssam		break;
2413170530Ssam	case IEEE80211_IOC_AMPDU_DENSITY:
2414170530Ssam		/* XXX validate */
2415170530Ssam		ic->ic_ampdu_density = ireq->i_val;
2416170530Ssam		break;
2417170530Ssam	case IEEE80211_IOC_AMSDU:
2418170530Ssam		if (ireq->i_val) {
2419170530Ssam			if ((ic->ic_htcaps & IEEE80211_HTC_AMSDU) == 0)
2420170530Ssam				return EINVAL;
2421170530Ssam			if (ireq->i_val & 1)
2422170530Ssam				ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
2423170530Ssam			if (ireq->i_val & 2)
2424170530Ssam				ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
2425170530Ssam		} else
2426170530Ssam			ic->ic_flags_ext &=
2427170530Ssam			    ~(IEEE80211_FEXT_AMSDU_TX|IEEE80211_FEXT_AMSDU_RX);
2428170530Ssam		/* NB: reset only if we're operating on an 11n channel */
2429170530Ssam		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
2430170530Ssam		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
2431170530Ssam			error = ENETRESET;
2432170530Ssam		break;
2433170530Ssam	case IEEE80211_IOC_AMSDU_LIMIT:
2434170530Ssam		/* XXX validate */
2435170530Ssam		ic->ic_amsdu_limit = ireq->i_val;	/* XXX truncation? */
2436170530Ssam		break;
2437170530Ssam	case IEEE80211_IOC_PUREN:
2438170530Ssam		if (ireq->i_val) {
2439170530Ssam			if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
2440170530Ssam				return EINVAL;
2441170530Ssam			ic->ic_flags_ext |= IEEE80211_FEXT_PUREN;
2442170530Ssam		} else
2443170530Ssam			ic->ic_flags_ext &= ~IEEE80211_FEXT_PUREN;
2444170530Ssam		/* NB: reset only if we're operating on an 11n channel */
2445170530Ssam		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
2446170530Ssam		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
2447170530Ssam			error = ENETRESET;
2448170530Ssam		break;
2449170530Ssam	case IEEE80211_IOC_DOTH:
2450170530Ssam		if (ireq->i_val) {
2451170530Ssam#if 0
2452170530Ssam			/* XXX no capability */
2453170530Ssam			if ((ic->ic_caps & IEEE80211_C_DOTH) == 0)
2454170530Ssam				return EINVAL;
2455170530Ssam#endif
2456170530Ssam			ic->ic_flags |= IEEE80211_F_DOTH;
2457170530Ssam		} else
2458170530Ssam			ic->ic_flags &= ~IEEE80211_F_DOTH;
2459170530Ssam		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2460170530Ssam		break;
2461170530Ssam	case IEEE80211_IOC_HTCOMPAT:
2462170530Ssam		if (ireq->i_val) {
2463170530Ssam			if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
2464170530Ssam				return EINVAL;
2465170530Ssam			ic->ic_flags_ext |= IEEE80211_FEXT_HTCOMPAT;
2466170530Ssam		} else
2467170530Ssam			ic->ic_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT;
2468170530Ssam		/* NB: reset only if we're operating on an 11n channel */
2469170530Ssam		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
2470170530Ssam		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
2471170530Ssam			error = ENETRESET;
2472170530Ssam		break;
2473138568Ssam	default:
2474138568Ssam		error = EINVAL;
2475138568Ssam		break;
2476138568Ssam	}
2477170530Ssam	if (error == ENETRESET)
2478170530Ssam		error = IS_UP_AUTO(ic) ? ieee80211_init(ic, 0) : 0;
2479138568Ssam	return error;
2480138568Ssam}
2481138568Ssam
2482138568Ssamint
2483138568Ssamieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data)
2484138568Ssam{
2485138568Ssam	struct ifnet *ifp = ic->ic_ifp;
2486138568Ssam	int error = 0;
2487138568Ssam	struct ifreq *ifr;
2488138568Ssam	struct ifaddr *ifa;			/* XXX */
2489138568Ssam
2490138568Ssam	switch (cmd) {
2491138568Ssam	case SIOCSIFMEDIA:
2492138568Ssam	case SIOCGIFMEDIA:
2493138568Ssam		error = ifmedia_ioctl(ifp, (struct ifreq *) data,
2494138568Ssam				&ic->ic_media, cmd);
2495138568Ssam		break;
2496138568Ssam	case SIOCG80211:
2497138568Ssam		error = ieee80211_ioctl_get80211(ic, cmd,
2498138568Ssam				(struct ieee80211req *) data);
2499138568Ssam		break;
2500138568Ssam	case SIOCS80211:
2501164033Srwatson		error = priv_check(curthread, PRIV_NET80211_MANAGE);
2502138568Ssam		if (error == 0)
2503138568Ssam			error = ieee80211_ioctl_set80211(ic, cmd,
2504138568Ssam					(struct ieee80211req *) data);
2505138568Ssam		break;
2506121180Ssam	case SIOCG80211STATS:
2507121180Ssam		ifr = (struct ifreq *)data;
2508121180Ssam		copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats));
2509121180Ssam		break;
2510124457Ssam	case SIOCSIFMTU:
2511124457Ssam		ifr = (struct ifreq *)data;
2512124457Ssam		if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu &&
2513124457Ssam		    ifr->ifr_mtu <= IEEE80211_MTU_MAX))
2514124457Ssam			error = EINVAL;
2515124457Ssam		else
2516124457Ssam			ifp->if_mtu = ifr->ifr_mtu;
2517124457Ssam		break;
2518127646Ssam	case SIOCSIFADDR:
2519127646Ssam		/*
2520127646Ssam		 * XXX Handle this directly so we can supress if_init calls.
2521127646Ssam		 * XXX This should be done in ether_ioctl but for the moment
2522127646Ssam		 * XXX there are too many other parts of the system that
2523127646Ssam		 * XXX set IFF_UP and so supress if_init being called when
2524127646Ssam		 * XXX it should be.
2525127646Ssam		 */
2526127646Ssam		ifa = (struct ifaddr *) data;
2527127646Ssam		switch (ifa->ifa_addr->sa_family) {
2528127646Ssam#ifdef INET
2529127646Ssam		case AF_INET:
2530127646Ssam			if ((ifp->if_flags & IFF_UP) == 0) {
2531127646Ssam				ifp->if_flags |= IFF_UP;
2532127646Ssam				ifp->if_init(ifp->if_softc);
2533127646Ssam			}
2534127646Ssam			arp_ifinit(ifp, ifa);
2535127646Ssam			break;
2536127646Ssam#endif
2537127646Ssam#ifdef IPX
2538127646Ssam		/*
2539127646Ssam		 * XXX - This code is probably wrong,
2540127646Ssam		 *	 but has been copied many times.
2541127646Ssam		 */
2542127646Ssam		case AF_IPX: {
2543127646Ssam			struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr);
2544127646Ssam
2545127646Ssam			if (ipx_nullhost(*ina))
2546147256Sbrooks				ina->x_host = *(union ipx_host *)
2547152315Sru				    IF_LLADDR(ifp);
2548127646Ssam			else
2549127646Ssam				bcopy((caddr_t) ina->x_host.c_host,
2550152315Sru				      (caddr_t) IF_LLADDR(ifp),
2551147256Sbrooks				      ETHER_ADDR_LEN);
2552127646Ssam			/* fall thru... */
2553127646Ssam		}
2554127646Ssam#endif
2555127646Ssam		default:
2556127646Ssam			if ((ifp->if_flags & IFF_UP) == 0) {
2557127646Ssam				ifp->if_flags |= IFF_UP;
2558127646Ssam				ifp->if_init(ifp->if_softc);
2559127646Ssam			}
2560127646Ssam			break;
2561127646Ssam		}
2562127646Ssam		break;
2563116742Ssam	default:
2564116742Ssam		error = ether_ioctl(ifp, cmd, data);
2565116742Ssam		break;
2566116742Ssam	}
2567116742Ssam	return error;
2568116742Ssam}
2569