xref: /freebsd/sys/net80211/ieee80211_ioctl.c (revision 1b6167d23905ef392a200c3cf17c0b59d5461bb9)
11a1e1d21SSam Leffler /*-
27535e66aSSam Leffler  * Copyright (c) 2001 Atsushi Onoe
3ae8b7333SSam Leffler  * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
41a1e1d21SSam Leffler  * All rights reserved.
51a1e1d21SSam Leffler  *
61a1e1d21SSam Leffler  * Redistribution and use in source and binary forms, with or without
71a1e1d21SSam Leffler  * modification, are permitted provided that the following conditions
81a1e1d21SSam Leffler  * are met:
91a1e1d21SSam Leffler  * 1. Redistributions of source code must retain the above copyright
107535e66aSSam Leffler  *    notice, this list of conditions and the following disclaimer.
117535e66aSSam Leffler  * 2. Redistributions in binary form must reproduce the above copyright
127535e66aSSam Leffler  *    notice, this list of conditions and the following disclaimer in the
137535e66aSSam Leffler  *    documentation and/or other materials provided with the distribution.
141a1e1d21SSam Leffler  *
157535e66aSSam Leffler  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
167535e66aSSam Leffler  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
177535e66aSSam Leffler  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
187535e66aSSam Leffler  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
197535e66aSSam Leffler  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
207535e66aSSam Leffler  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
217535e66aSSam Leffler  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
227535e66aSSam Leffler  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
237535e66aSSam Leffler  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
247535e66aSSam Leffler  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
251a1e1d21SSam Leffler  */
261a1e1d21SSam Leffler 
271a1e1d21SSam Leffler #include <sys/cdefs.h>
281a1e1d21SSam Leffler __FBSDID("$FreeBSD$");
291a1e1d21SSam Leffler 
30f88b3f22SRuslan Ermilov #include "opt_compat.h"
31f88b3f22SRuslan Ermilov 
321a1e1d21SSam Leffler /*
331a1e1d21SSam Leffler  * IEEE 802.11 ioctl support (FreeBSD-specific)
341a1e1d21SSam Leffler  */
351a1e1d21SSam Leffler 
36b2e95691SSam Leffler #include "opt_inet.h"
37b2e95691SSam Leffler #include "opt_ipx.h"
38b2e95691SSam Leffler 
391a1e1d21SSam Leffler #include <sys/endian.h>
401a1e1d21SSam Leffler #include <sys/param.h>
411a1e1d21SSam Leffler #include <sys/kernel.h>
42acd3428bSRobert Watson #include <sys/priv.h>
431a1e1d21SSam Leffler #include <sys/socket.h>
441a1e1d21SSam Leffler #include <sys/sockio.h>
451a1e1d21SSam Leffler #include <sys/systm.h>
461a1e1d21SSam Leffler 
471a1e1d21SSam Leffler #include <net/if.h>
484a0d6638SRuslan Ermilov #include <net/if_dl.h>
491a1e1d21SSam Leffler #include <net/if_media.h>
501a1e1d21SSam Leffler #include <net/ethernet.h>
511a1e1d21SSam Leffler 
52b2e95691SSam Leffler #ifdef INET
53b2e95691SSam Leffler #include <netinet/in.h>
54b2e95691SSam Leffler #include <netinet/if_ether.h>
55b2e95691SSam Leffler #endif
56b2e95691SSam Leffler 
57b2e95691SSam Leffler #ifdef IPX
58b2e95691SSam Leffler #include <netipx/ipx.h>
59b2e95691SSam Leffler #include <netipx/ipx_if.h>
60b2e95691SSam Leffler #endif
61b2e95691SSam Leffler 
621a1e1d21SSam Leffler #include <net80211/ieee80211_var.h>
631a1e1d21SSam Leffler #include <net80211/ieee80211_ioctl.h>
641a1e1d21SSam Leffler 
658a1b9b6aSSam Leffler #define	IS_UP(_ic) \
6613f4c340SRobert Watson 	(((_ic)->ic_ifp->if_flags & IFF_UP) &&			\
6713f4c340SRobert Watson 	    ((_ic)->ic_ifp->if_drv_flags & IFF_DRV_RUNNING))
688a1b9b6aSSam Leffler #define	IS_UP_AUTO(_ic) \
698a1b9b6aSSam Leffler 	(IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO)
7068e8e04eSSam Leffler #define	RESCAN	1
718a1b9b6aSSam Leffler 
7268e8e04eSSam Leffler static struct ieee80211_channel *findchannel(struct ieee80211com *,
7368e8e04eSSam Leffler 		int ieee, int mode);
741a1e1d21SSam Leffler 
758a1b9b6aSSam Leffler static int
768a1b9b6aSSam Leffler cap2cipher(int flag)
778a1b9b6aSSam Leffler {
788a1b9b6aSSam Leffler 	switch (flag) {
798a1b9b6aSSam Leffler 	case IEEE80211_C_WEP:		return IEEE80211_CIPHER_WEP;
808a1b9b6aSSam Leffler 	case IEEE80211_C_AES:		return IEEE80211_CIPHER_AES_OCB;
818a1b9b6aSSam Leffler 	case IEEE80211_C_AES_CCM:	return IEEE80211_CIPHER_AES_CCM;
828a1b9b6aSSam Leffler 	case IEEE80211_C_CKIP:		return IEEE80211_CIPHER_CKIP;
838a1b9b6aSSam Leffler 	case IEEE80211_C_TKIP:		return IEEE80211_CIPHER_TKIP;
848a1b9b6aSSam Leffler 	}
858a1b9b6aSSam Leffler 	return -1;
868a1b9b6aSSam Leffler }
878a1b9b6aSSam Leffler 
888a1b9b6aSSam Leffler static int
898a1b9b6aSSam Leffler ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq)
908a1b9b6aSSam Leffler {
918a1b9b6aSSam Leffler 	struct ieee80211_node *ni;
928a1b9b6aSSam Leffler 	struct ieee80211req_key ik;
938a1b9b6aSSam Leffler 	struct ieee80211_key *wk;
948a1b9b6aSSam Leffler 	const struct ieee80211_cipher *cip;
958a1b9b6aSSam Leffler 	u_int kid;
968a1b9b6aSSam Leffler 	int error;
978a1b9b6aSSam Leffler 
988a1b9b6aSSam Leffler 	if (ireq->i_len != sizeof(ik))
998a1b9b6aSSam Leffler 		return EINVAL;
1008a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, &ik, sizeof(ik));
1018a1b9b6aSSam Leffler 	if (error)
1028a1b9b6aSSam Leffler 		return error;
1038a1b9b6aSSam Leffler 	kid = ik.ik_keyix;
1048a1b9b6aSSam Leffler 	if (kid == IEEE80211_KEYIX_NONE) {
105acc4f7f5SSam Leffler 		ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
1068a1b9b6aSSam Leffler 		if (ni == NULL)
1078a1b9b6aSSam Leffler 			return EINVAL;		/* XXX */
1088a1b9b6aSSam Leffler 		wk = &ni->ni_ucastkey;
1098a1b9b6aSSam Leffler 	} else {
1108a1b9b6aSSam Leffler 		if (kid >= IEEE80211_WEP_NKID)
1118a1b9b6aSSam Leffler 			return EINVAL;
1128a1b9b6aSSam Leffler 		wk = &ic->ic_nw_keys[kid];
1138a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr);
1148a1b9b6aSSam Leffler 		ni = NULL;
1158a1b9b6aSSam Leffler 	}
1168a1b9b6aSSam Leffler 	cip = wk->wk_cipher;
1178a1b9b6aSSam Leffler 	ik.ik_type = cip->ic_cipher;
1188a1b9b6aSSam Leffler 	ik.ik_keylen = wk->wk_keylen;
1198a1b9b6aSSam Leffler 	ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
1208a1b9b6aSSam Leffler 	if (wk->wk_keyix == ic->ic_def_txkey)
1218a1b9b6aSSam Leffler 		ik.ik_flags |= IEEE80211_KEY_DEFAULT;
122acd3428bSRobert Watson 	if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
1238a1b9b6aSSam Leffler 		/* NB: only root can read key data */
1248a1b9b6aSSam Leffler 		ik.ik_keyrsc = wk->wk_keyrsc;
1258a1b9b6aSSam Leffler 		ik.ik_keytsc = wk->wk_keytsc;
1268a1b9b6aSSam Leffler 		memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen);
1278a1b9b6aSSam Leffler 		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) {
1288a1b9b6aSSam Leffler 			memcpy(ik.ik_keydata+wk->wk_keylen,
1298a1b9b6aSSam Leffler 				wk->wk_key + IEEE80211_KEYBUF_SIZE,
1308a1b9b6aSSam Leffler 				IEEE80211_MICBUF_SIZE);
1318a1b9b6aSSam Leffler 			ik.ik_keylen += IEEE80211_MICBUF_SIZE;
1328a1b9b6aSSam Leffler 		}
1338a1b9b6aSSam Leffler 	} else {
1348a1b9b6aSSam Leffler 		ik.ik_keyrsc = 0;
1358a1b9b6aSSam Leffler 		ik.ik_keytsc = 0;
1368a1b9b6aSSam Leffler 		memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata));
1378a1b9b6aSSam Leffler 	}
1388a1b9b6aSSam Leffler 	if (ni != NULL)
1398a1b9b6aSSam Leffler 		ieee80211_free_node(ni);
1408a1b9b6aSSam Leffler 	return copyout(&ik, ireq->i_data, sizeof(ik));
1418a1b9b6aSSam Leffler }
1428a1b9b6aSSam Leffler 
1438a1b9b6aSSam Leffler static int
1448a1b9b6aSSam Leffler ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
1458a1b9b6aSSam Leffler {
1468a1b9b6aSSam Leffler 
147e55e5e42SSam Leffler 	if (sizeof(ic->ic_chan_active) < ireq->i_len)
1488a1b9b6aSSam Leffler 		ireq->i_len = sizeof(ic->ic_chan_active);
1498a1b9b6aSSam Leffler 	return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len);
1508a1b9b6aSSam Leffler }
1518a1b9b6aSSam Leffler 
1528a1b9b6aSSam Leffler static int
1538a1b9b6aSSam Leffler ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq)
1548a1b9b6aSSam Leffler {
15568e8e04eSSam Leffler 	int space;
1568a1b9b6aSSam Leffler 
1578a1b9b6aSSam Leffler 	space = __offsetof(struct ieee80211req_chaninfo,
15868e8e04eSSam Leffler 			ic_chans[ic->ic_nchans]);
1598a1b9b6aSSam Leffler 	if (space > ireq->i_len)
1608a1b9b6aSSam Leffler 		space = ireq->i_len;
16168e8e04eSSam Leffler 	/* XXX assumes compatible layout */
16268e8e04eSSam Leffler 	return copyout(&ic->ic_nchans, ireq->i_data, space);
1638a1b9b6aSSam Leffler }
1648a1b9b6aSSam Leffler 
1658a1b9b6aSSam Leffler static int
16668e8e04eSSam Leffler ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int req)
1678a1b9b6aSSam Leffler {
1688a1b9b6aSSam Leffler 	struct ieee80211_node *ni;
16968e8e04eSSam Leffler 	struct ieee80211req_wpaie2 wpaie;
1708a1b9b6aSSam Leffler 	int error;
1718a1b9b6aSSam Leffler 
1728a1b9b6aSSam Leffler 	if (ireq->i_len < IEEE80211_ADDR_LEN)
1738a1b9b6aSSam Leffler 		return EINVAL;
1748a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN);
1758a1b9b6aSSam Leffler 	if (error != 0)
1768a1b9b6aSSam Leffler 		return error;
177acc4f7f5SSam Leffler 	ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr);
1788a1b9b6aSSam Leffler 	if (ni == NULL)
17968e8e04eSSam Leffler 		return ENOENT;		/* XXX */
1808a1b9b6aSSam Leffler 	memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie));
1818a1b9b6aSSam Leffler 	if (ni->ni_wpa_ie != NULL) {
1828a1b9b6aSSam Leffler 		int ielen = ni->ni_wpa_ie[1] + 2;
1838a1b9b6aSSam Leffler 		if (ielen > sizeof(wpaie.wpa_ie))
1848a1b9b6aSSam Leffler 			ielen = sizeof(wpaie.wpa_ie);
1858a1b9b6aSSam Leffler 		memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen);
1868a1b9b6aSSam Leffler 	}
18768e8e04eSSam Leffler 	if (req == IEEE80211_IOC_WPAIE2) {
18868e8e04eSSam Leffler 		memset(wpaie.rsn_ie, 0, sizeof(wpaie.rsn_ie));
18968e8e04eSSam Leffler 		if (ni->ni_rsn_ie != NULL) {
19068e8e04eSSam Leffler 			int ielen = ni->ni_rsn_ie[1] + 2;
19168e8e04eSSam Leffler 			if (ielen > sizeof(wpaie.rsn_ie))
19268e8e04eSSam Leffler 				ielen = sizeof(wpaie.rsn_ie);
19368e8e04eSSam Leffler 			memcpy(wpaie.rsn_ie, ni->ni_rsn_ie, ielen);
19468e8e04eSSam Leffler 		}
19568e8e04eSSam Leffler 		if (ireq->i_len > sizeof(struct ieee80211req_wpaie2))
19668e8e04eSSam Leffler 			ireq->i_len = sizeof(struct ieee80211req_wpaie2);
19768e8e04eSSam Leffler 	} else {
19868e8e04eSSam Leffler 		/* compatibility op, may overwrite wpa ie */
19968e8e04eSSam Leffler 		/* XXX check ic_flags? */
20068e8e04eSSam Leffler 		if (ni->ni_rsn_ie != NULL) {
20168e8e04eSSam Leffler 			int ielen = ni->ni_rsn_ie[1] + 2;
20268e8e04eSSam Leffler 			if (ielen > sizeof(wpaie.wpa_ie))
20368e8e04eSSam Leffler 				ielen = sizeof(wpaie.wpa_ie);
20468e8e04eSSam Leffler 			memcpy(wpaie.wpa_ie, ni->ni_rsn_ie, ielen);
20568e8e04eSSam Leffler 		}
20668e8e04eSSam Leffler 		if (ireq->i_len > sizeof(struct ieee80211req_wpaie))
20768e8e04eSSam Leffler 			ireq->i_len = sizeof(struct ieee80211req_wpaie);
20868e8e04eSSam Leffler 	}
2098a1b9b6aSSam Leffler 	ieee80211_free_node(ni);
2108a1b9b6aSSam Leffler 	return copyout(&wpaie, ireq->i_data, ireq->i_len);
2118a1b9b6aSSam Leffler }
2128a1b9b6aSSam Leffler 
2138a1b9b6aSSam Leffler static int
2148a1b9b6aSSam Leffler ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
2158a1b9b6aSSam Leffler {
2168a1b9b6aSSam Leffler 	struct ieee80211_node *ni;
21768e8e04eSSam Leffler 	uint8_t macaddr[IEEE80211_ADDR_LEN];
2188a1b9b6aSSam Leffler 	const int off = __offsetof(struct ieee80211req_sta_stats, is_stats);
2198a1b9b6aSSam Leffler 	int error;
2208a1b9b6aSSam Leffler 
2218a1b9b6aSSam Leffler 	if (ireq->i_len < off)
2228a1b9b6aSSam Leffler 		return EINVAL;
2238a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
2248a1b9b6aSSam Leffler 	if (error != 0)
2258a1b9b6aSSam Leffler 		return error;
226acc4f7f5SSam Leffler 	ni = ieee80211_find_node(&ic->ic_sta, macaddr);
22768e8e04eSSam Leffler 	if (ni == NULL)
22868e8e04eSSam Leffler 		return EINVAL;
2298a1b9b6aSSam Leffler 	if (ireq->i_len > sizeof(struct ieee80211req_sta_stats))
2308a1b9b6aSSam Leffler 		ireq->i_len = sizeof(struct ieee80211req_sta_stats);
2318a1b9b6aSSam Leffler 	/* NB: copy out only the statistics */
23268e8e04eSSam Leffler 	error = copyout(&ni->ni_stats, (uint8_t *) ireq->i_data + off,
2338a1b9b6aSSam Leffler 			ireq->i_len - off);
2348a1b9b6aSSam Leffler 	ieee80211_free_node(ni);
2358a1b9b6aSSam Leffler 	return error;
2368a1b9b6aSSam Leffler }
2378a1b9b6aSSam Leffler 
23868e8e04eSSam Leffler static __inline uint8_t *
23968e8e04eSSam Leffler copyie(uint8_t *cp, const uint8_t *ie)
24068e8e04eSSam Leffler {
24168e8e04eSSam Leffler 	if (ie != NULL) {
24268e8e04eSSam Leffler 		memcpy(cp, ie, 2+ie[1]);
24368e8e04eSSam Leffler 		cp += 2+ie[1];
24468e8e04eSSam Leffler 	}
24568e8e04eSSam Leffler 	return cp;
24668e8e04eSSam Leffler }
24768e8e04eSSam Leffler 
248239cc3b6SSam Leffler #ifdef COMPAT_FREEBSD6
249239cc3b6SSam Leffler #define	IEEE80211_IOC_SCAN_RESULTS_OLD	24
250239cc3b6SSam Leffler 
251239cc3b6SSam Leffler struct scan_result_old {
25268e8e04eSSam Leffler 	uint16_t	isr_len;		/* length (mult of 4) */
25368e8e04eSSam Leffler 	uint16_t	isr_freq;		/* MHz */
25468e8e04eSSam Leffler 	uint16_t	isr_flags;		/* channel flags */
25568e8e04eSSam Leffler 	uint8_t		isr_noise;
25668e8e04eSSam Leffler 	uint8_t		isr_rssi;
25768e8e04eSSam Leffler 	uint8_t		isr_intval;		/* beacon interval */
25868e8e04eSSam Leffler 	uint8_t		isr_capinfo;		/* capabilities */
25968e8e04eSSam Leffler 	uint8_t		isr_erp;		/* ERP element */
26068e8e04eSSam Leffler 	uint8_t		isr_bssid[IEEE80211_ADDR_LEN];
26168e8e04eSSam Leffler 	uint8_t		isr_nrates;
26268e8e04eSSam Leffler 	uint8_t		isr_rates[IEEE80211_RATE_MAXSIZE];
26368e8e04eSSam Leffler 	uint8_t		isr_ssid_len;		/* SSID length */
26468e8e04eSSam Leffler 	uint8_t		isr_ie_len;		/* IE length */
26568e8e04eSSam Leffler 	uint8_t		isr_pad[5];
266239cc3b6SSam Leffler 	/* variable length SSID followed by IE data */
267239cc3b6SSam Leffler };
268239cc3b6SSam Leffler 
26968e8e04eSSam Leffler struct oscanreq {
27068e8e04eSSam Leffler 	struct scan_result_old *sr;
271239cc3b6SSam Leffler 	size_t space;
272239cc3b6SSam Leffler };
273239cc3b6SSam Leffler 
274239cc3b6SSam Leffler static size_t
27568e8e04eSSam Leffler old_scan_space(const struct ieee80211_scan_entry *se, int *ielen)
276239cc3b6SSam Leffler {
277239cc3b6SSam Leffler 	size_t len;
278239cc3b6SSam Leffler 
279239cc3b6SSam Leffler 	*ielen = 0;
28068e8e04eSSam Leffler 	if (se->se_wpa_ie != NULL)
28168e8e04eSSam Leffler 		*ielen += 2+se->se_wpa_ie[1];
28268e8e04eSSam Leffler 	if (se->se_wme_ie != NULL)
28368e8e04eSSam Leffler 		*ielen += 2+se->se_wme_ie[1];
284239cc3b6SSam Leffler 	/*
285239cc3b6SSam Leffler 	 * NB: ie's can be no more than 255 bytes and the max 802.11
286239cc3b6SSam Leffler 	 * packet is <3Kbytes so we are sure this doesn't overflow
287239cc3b6SSam Leffler 	 * 16-bits; if this is a concern we can drop the ie's.
288239cc3b6SSam Leffler 	 */
28968e8e04eSSam Leffler 	len = sizeof(struct scan_result_old) + se->se_ssid[1] + *ielen;
29068e8e04eSSam Leffler 	return roundup(len, sizeof(uint32_t));
291239cc3b6SSam Leffler }
292239cc3b6SSam Leffler 
293239cc3b6SSam Leffler static void
29468e8e04eSSam Leffler old_get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
295239cc3b6SSam Leffler {
29668e8e04eSSam Leffler 	struct oscanreq *req = arg;
29768e8e04eSSam Leffler 	int ielen;
298239cc3b6SSam Leffler 
29968e8e04eSSam Leffler 	req->space += old_scan_space(se, &ielen);
300239cc3b6SSam Leffler }
301239cc3b6SSam Leffler 
302239cc3b6SSam Leffler static void
30368e8e04eSSam Leffler old_get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
304239cc3b6SSam Leffler {
30568e8e04eSSam Leffler 	struct oscanreq *req = arg;
30668e8e04eSSam Leffler 	struct scan_result_old *sr;
30768e8e04eSSam Leffler 	int ielen, len, nr, nxr;
30868e8e04eSSam Leffler 	uint8_t *cp;
309239cc3b6SSam Leffler 
31068e8e04eSSam Leffler 	len = old_scan_space(se, &ielen);
311239cc3b6SSam Leffler 	if (len > req->space)
312239cc3b6SSam Leffler 		return;
31368e8e04eSSam Leffler 
314239cc3b6SSam Leffler 	sr = req->sr;
31568e8e04eSSam Leffler 	memset(sr, 0, sizeof(*sr));
31668e8e04eSSam Leffler 	sr->isr_ssid_len = se->se_ssid[1];
31768e8e04eSSam Leffler 	/* NB: beware of overflow, isr_ie_len is 8 bits */
31868e8e04eSSam Leffler 	sr->isr_ie_len = (ielen > 255 ? 0 : ielen);
319239cc3b6SSam Leffler 	sr->isr_len = len;
32068e8e04eSSam Leffler 	sr->isr_freq = se->se_chan->ic_freq;
32168e8e04eSSam Leffler 	sr->isr_flags = se->se_chan->ic_flags;
32268e8e04eSSam Leffler 	sr->isr_rssi = se->se_rssi;
32368e8e04eSSam Leffler 	sr->isr_noise = se->se_noise;
32468e8e04eSSam Leffler 	sr->isr_intval = se->se_intval;
32568e8e04eSSam Leffler 	sr->isr_capinfo = se->se_capinfo;
32668e8e04eSSam Leffler 	sr->isr_erp = se->se_erp;
32768e8e04eSSam Leffler 	IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid);
32868e8e04eSSam Leffler 	nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE);
32968e8e04eSSam Leffler 	memcpy(sr->isr_rates, se->se_rates+2, nr);
33068e8e04eSSam Leffler 	nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr);
33168e8e04eSSam Leffler 	memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
33268e8e04eSSam Leffler 	sr->isr_nrates = nr + nxr;
33368e8e04eSSam Leffler 
33468e8e04eSSam Leffler 	cp = (uint8_t *)(sr+1);
33568e8e04eSSam Leffler 	memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
33668e8e04eSSam Leffler 	cp += sr->isr_ssid_len;
337239cc3b6SSam Leffler 	if (sr->isr_ie_len) {
33868e8e04eSSam Leffler 		cp = copyie(cp, se->se_wpa_ie);
33968e8e04eSSam Leffler 		cp = copyie(cp, se->se_wme_ie);
340239cc3b6SSam Leffler 	}
341239cc3b6SSam Leffler 
342239cc3b6SSam Leffler 	req->space -= len;
34368e8e04eSSam Leffler 	req->sr = (struct scan_result_old *)(((uint8_t *)sr) + len);
344239cc3b6SSam Leffler }
345239cc3b6SSam Leffler 
346239cc3b6SSam Leffler static int
34768e8e04eSSam Leffler old_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
348239cc3b6SSam Leffler {
34968e8e04eSSam Leffler 	struct oscanreq req;
350239cc3b6SSam Leffler 	int error;
351239cc3b6SSam Leffler 
35268e8e04eSSam Leffler 	if (ireq->i_len < sizeof(struct scan_result_old))
353239cc3b6SSam Leffler 		return EFAULT;
354239cc3b6SSam Leffler 
355239cc3b6SSam Leffler 	error = 0;
356239cc3b6SSam Leffler 	req.space = 0;
35768e8e04eSSam Leffler 	ieee80211_scan_iterate(ic, old_get_scan_space, &req);
358239cc3b6SSam Leffler 	if (req.space > ireq->i_len)
359239cc3b6SSam Leffler 		req.space = ireq->i_len;
360239cc3b6SSam Leffler 	if (req.space > 0) {
361239cc3b6SSam Leffler 		size_t space;
362239cc3b6SSam Leffler 		void *p;
363239cc3b6SSam Leffler 
364239cc3b6SSam Leffler 		space = req.space;
365239cc3b6SSam Leffler 		/* XXX M_WAITOK after driver lock released */
366239cc3b6SSam Leffler 		MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
367239cc3b6SSam Leffler 		if (p == NULL)
368239cc3b6SSam Leffler 			return ENOMEM;
369239cc3b6SSam Leffler 		req.sr = p;
37068e8e04eSSam Leffler 		ieee80211_scan_iterate(ic, old_get_scan_result, &req);
37168e8e04eSSam Leffler 		ireq->i_len = space - req.space;
37268e8e04eSSam Leffler 		error = copyout(p, ireq->i_data, ireq->i_len);
37368e8e04eSSam Leffler 		FREE(p, M_TEMP);
37468e8e04eSSam Leffler 	} else
37568e8e04eSSam Leffler 		ireq->i_len = 0;
37668e8e04eSSam Leffler 
37768e8e04eSSam Leffler 	return error;
37868e8e04eSSam Leffler }
37968e8e04eSSam Leffler #endif /* COMPAT_FREEBSD6 */
38068e8e04eSSam Leffler 
38168e8e04eSSam Leffler struct scanreq {
38268e8e04eSSam Leffler 	struct ieee80211req_scan_result *sr;
38368e8e04eSSam Leffler 	size_t space;
38468e8e04eSSam Leffler };
38568e8e04eSSam Leffler 
38668e8e04eSSam Leffler static size_t
38768e8e04eSSam Leffler scan_space(const struct ieee80211_scan_entry *se, int *ielen)
38868e8e04eSSam Leffler {
38968e8e04eSSam Leffler 	size_t len;
39068e8e04eSSam Leffler 
39168e8e04eSSam Leffler 	*ielen = 0;
39268e8e04eSSam Leffler 	if (se->se_wpa_ie != NULL)
39368e8e04eSSam Leffler 		*ielen += 2+se->se_wpa_ie[1];
39468e8e04eSSam Leffler 	if (se->se_rsn_ie != NULL)
39568e8e04eSSam Leffler 		*ielen += 2+se->se_rsn_ie[1];
39668e8e04eSSam Leffler 	if (se->se_wme_ie != NULL)
39768e8e04eSSam Leffler 		*ielen += 2+se->se_wme_ie[1];
39868e8e04eSSam Leffler 	if (se->se_ath_ie != NULL)
39968e8e04eSSam Leffler 		*ielen += 2+se->se_ath_ie[1];
40068e8e04eSSam Leffler 	/*
40168e8e04eSSam Leffler 	 * NB: ie's can be no more than 255 bytes and the max 802.11
40268e8e04eSSam Leffler 	 * packet is <3Kbytes so we are sure this doesn't overflow
40368e8e04eSSam Leffler 	 * 16-bits; if this is a concern we can drop the ie's.
40468e8e04eSSam Leffler 	 */
40568e8e04eSSam Leffler 	len = sizeof(struct ieee80211req_scan_result) + se->se_ssid[1] + *ielen;
40668e8e04eSSam Leffler 	return roundup(len, sizeof(uint32_t));
40768e8e04eSSam Leffler }
40868e8e04eSSam Leffler 
40968e8e04eSSam Leffler static void
41068e8e04eSSam Leffler get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
41168e8e04eSSam Leffler {
41268e8e04eSSam Leffler 	struct scanreq *req = arg;
41368e8e04eSSam Leffler 	int ielen;
41468e8e04eSSam Leffler 
41568e8e04eSSam Leffler 	req->space += scan_space(se, &ielen);
41668e8e04eSSam Leffler }
41768e8e04eSSam Leffler 
41868e8e04eSSam Leffler static void
41968e8e04eSSam Leffler get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
42068e8e04eSSam Leffler {
42168e8e04eSSam Leffler 	struct scanreq *req = arg;
42268e8e04eSSam Leffler 	struct ieee80211req_scan_result *sr;
42368e8e04eSSam Leffler 	int ielen, len, nr, nxr;
42468e8e04eSSam Leffler 	uint8_t *cp;
42568e8e04eSSam Leffler 
42668e8e04eSSam Leffler 	len = scan_space(se, &ielen);
42768e8e04eSSam Leffler 	if (len > req->space)
42868e8e04eSSam Leffler 		return;
42968e8e04eSSam Leffler 
43068e8e04eSSam Leffler 	sr = req->sr;
43168e8e04eSSam Leffler 	KASSERT(len <= 65535 && ielen <= 65535,
43268e8e04eSSam Leffler 	    ("len %u ssid %u ie %u", len, se->se_ssid[1], ielen));
43368e8e04eSSam Leffler 	sr->isr_ie_off = sizeof(struct ieee80211req_scan_result);
43468e8e04eSSam Leffler 	sr->isr_ie_len = ielen;
43568e8e04eSSam Leffler 	sr->isr_len = len;
43668e8e04eSSam Leffler 	sr->isr_freq = se->se_chan->ic_freq;
43768e8e04eSSam Leffler 	sr->isr_flags = se->se_chan->ic_flags;
43868e8e04eSSam Leffler 	sr->isr_rssi = se->se_rssi;
43968e8e04eSSam Leffler 	sr->isr_noise = se->se_noise;
44068e8e04eSSam Leffler 	sr->isr_intval = se->se_intval;
44168e8e04eSSam Leffler 	sr->isr_capinfo = se->se_capinfo;
44268e8e04eSSam Leffler 	sr->isr_erp = se->se_erp;
44368e8e04eSSam Leffler 	IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid);
44468e8e04eSSam Leffler 	nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE);
44568e8e04eSSam Leffler 	memcpy(sr->isr_rates, se->se_rates+2, nr);
44668e8e04eSSam Leffler 	nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr);
44768e8e04eSSam Leffler 	memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
44868e8e04eSSam Leffler 	sr->isr_nrates = nr + nxr;
44968e8e04eSSam Leffler 
45068e8e04eSSam Leffler 	sr->isr_ssid_len = se->se_ssid[1];
45168e8e04eSSam Leffler 	cp = ((uint8_t *)sr) + sr->isr_ie_off;
45268e8e04eSSam Leffler 	memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
45368e8e04eSSam Leffler 
45468e8e04eSSam Leffler 	if (ielen) {
45568e8e04eSSam Leffler 		cp += sr->isr_ssid_len;
45668e8e04eSSam Leffler 		cp = copyie(cp, se->se_wpa_ie);
45768e8e04eSSam Leffler 		cp = copyie(cp, se->se_rsn_ie);
45868e8e04eSSam Leffler 		cp = copyie(cp, se->se_wme_ie);
45968e8e04eSSam Leffler 		cp = copyie(cp, se->se_ath_ie);
46068e8e04eSSam Leffler 		cp = copyie(cp, se->se_htcap_ie);
46168e8e04eSSam Leffler 	}
46268e8e04eSSam Leffler 
46368e8e04eSSam Leffler 	req->space -= len;
46468e8e04eSSam Leffler 	req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len);
46568e8e04eSSam Leffler }
46668e8e04eSSam Leffler 
46768e8e04eSSam Leffler static int
46868e8e04eSSam Leffler ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
46968e8e04eSSam Leffler {
47068e8e04eSSam Leffler 	struct scanreq req;
47168e8e04eSSam Leffler 	int error;
47268e8e04eSSam Leffler 
47368e8e04eSSam Leffler 	if (ireq->i_len < sizeof(struct ieee80211req_scan_result))
47468e8e04eSSam Leffler 		return EFAULT;
47568e8e04eSSam Leffler 
47668e8e04eSSam Leffler 	error = 0;
47768e8e04eSSam Leffler 	req.space = 0;
47868e8e04eSSam Leffler 	ieee80211_scan_iterate(ic, get_scan_space, &req);
47968e8e04eSSam Leffler 	if (req.space > ireq->i_len)
48068e8e04eSSam Leffler 		req.space = ireq->i_len;
48168e8e04eSSam Leffler 	if (req.space > 0) {
48268e8e04eSSam Leffler 		size_t space;
48368e8e04eSSam Leffler 		void *p;
48468e8e04eSSam Leffler 
48568e8e04eSSam Leffler 		space = req.space;
48668e8e04eSSam Leffler 		/* XXX M_WAITOK after driver lock released */
48768e8e04eSSam Leffler 		MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
48868e8e04eSSam Leffler 		if (p == NULL)
48968e8e04eSSam Leffler 			return ENOMEM;
49068e8e04eSSam Leffler 		req.sr = p;
49168e8e04eSSam Leffler 		ieee80211_scan_iterate(ic, get_scan_result, &req);
492239cc3b6SSam Leffler 		ireq->i_len = space - req.space;
493239cc3b6SSam Leffler 		error = copyout(p, ireq->i_data, ireq->i_len);
494239cc3b6SSam Leffler 		FREE(p, M_TEMP);
495239cc3b6SSam Leffler 	} else
496239cc3b6SSam Leffler 		ireq->i_len = 0;
497239cc3b6SSam Leffler 
498239cc3b6SSam Leffler 	return error;
499239cc3b6SSam Leffler }
5008a1b9b6aSSam Leffler 
5012cab1d3dSSam Leffler struct stainforeq {
5022cab1d3dSSam Leffler 	struct ieee80211com *ic;
5032cab1d3dSSam Leffler 	struct ieee80211req_sta_info *si;
5042cab1d3dSSam Leffler 	size_t	space;
5052cab1d3dSSam Leffler };
5068a1b9b6aSSam Leffler 
5072cab1d3dSSam Leffler static size_t
5082cab1d3dSSam Leffler sta_space(const struct ieee80211_node *ni, size_t *ielen)
5092cab1d3dSSam Leffler {
5102cab1d3dSSam Leffler 	*ielen = 0;
5118a1b9b6aSSam Leffler 	if (ni->ni_wpa_ie != NULL)
5122cab1d3dSSam Leffler 		*ielen += 2+ni->ni_wpa_ie[1];
51368e8e04eSSam Leffler 	if (ni->ni_rsn_ie != NULL)
51468e8e04eSSam Leffler 		*ielen += 2+ni->ni_rsn_ie[1];
5158a1b9b6aSSam Leffler 	if (ni->ni_wme_ie != NULL)
5162cab1d3dSSam Leffler 		*ielen += 2+ni->ni_wme_ie[1];
51768e8e04eSSam Leffler 	if (ni->ni_ath_ie != NULL)
51868e8e04eSSam Leffler 		*ielen += 2+ni->ni_ath_ie[1];
5192cab1d3dSSam Leffler 	return roundup(sizeof(struct ieee80211req_sta_info) + *ielen,
52068e8e04eSSam Leffler 		      sizeof(uint32_t));
5212cab1d3dSSam Leffler }
5222cab1d3dSSam Leffler 
5232cab1d3dSSam Leffler static void
5242cab1d3dSSam Leffler get_sta_space(void *arg, struct ieee80211_node *ni)
5252cab1d3dSSam Leffler {
5262cab1d3dSSam Leffler 	struct stainforeq *req = arg;
5272cab1d3dSSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
5282cab1d3dSSam Leffler 	size_t ielen;
5292cab1d3dSSam Leffler 
5302cab1d3dSSam Leffler 	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
5312cab1d3dSSam Leffler 	    ni->ni_associd == 0)	/* only associated stations */
5322cab1d3dSSam Leffler 		return;
5332cab1d3dSSam Leffler 	req->space += sta_space(ni, &ielen);
5342cab1d3dSSam Leffler }
5352cab1d3dSSam Leffler 
5362cab1d3dSSam Leffler static void
5372cab1d3dSSam Leffler get_sta_info(void *arg, struct ieee80211_node *ni)
5382cab1d3dSSam Leffler {
5392cab1d3dSSam Leffler 	struct stainforeq *req = arg;
5402cab1d3dSSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
5412cab1d3dSSam Leffler 	struct ieee80211req_sta_info *si;
5422cab1d3dSSam Leffler 	size_t ielen, len;
54368e8e04eSSam Leffler 	uint8_t *cp;
5442cab1d3dSSam Leffler 
5452cab1d3dSSam Leffler 	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
5462cab1d3dSSam Leffler 	    ni->ni_associd == 0)	/* only associated stations */
5472cab1d3dSSam Leffler 		return;
5482cab1d3dSSam Leffler 	if (ni->ni_chan == IEEE80211_CHAN_ANYC)	/* XXX bogus entry */
5492cab1d3dSSam Leffler 		return;
5502cab1d3dSSam Leffler 	len = sta_space(ni, &ielen);
5512cab1d3dSSam Leffler 	if (len > req->space)
5522cab1d3dSSam Leffler 		return;
5532cab1d3dSSam Leffler 	si = req->si;
5542cab1d3dSSam Leffler 	si->isi_len = len;
55568e8e04eSSam Leffler 	si->isi_ie_off = sizeof(struct ieee80211req_sta_info);
5562cab1d3dSSam Leffler 	si->isi_ie_len = ielen;
5578a1b9b6aSSam Leffler 	si->isi_freq = ni->ni_chan->ic_freq;
5588a1b9b6aSSam Leffler 	si->isi_flags = ni->ni_chan->ic_flags;
5598a1b9b6aSSam Leffler 	si->isi_state = ni->ni_flags;
5608a1b9b6aSSam Leffler 	si->isi_authmode = ni->ni_authmode;
56168e8e04eSSam Leffler 	ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise);
562d1c85daeSSam Leffler 	si->isi_noise = 0;		/* XXX */
5638a1b9b6aSSam Leffler 	si->isi_capinfo = ni->ni_capinfo;
5648a1b9b6aSSam Leffler 	si->isi_erp = ni->ni_erp;
5658a1b9b6aSSam Leffler 	IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr);
5668a1b9b6aSSam Leffler 	si->isi_nrates = ni->ni_rates.rs_nrates;
5678a1b9b6aSSam Leffler 	if (si->isi_nrates > 15)
5688a1b9b6aSSam Leffler 		si->isi_nrates = 15;
5698a1b9b6aSSam Leffler 	memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates);
5708a1b9b6aSSam Leffler 	si->isi_txrate = ni->ni_txrate;
57168e8e04eSSam Leffler 	si->isi_ie_len = ielen;
5728a1b9b6aSSam Leffler 	si->isi_associd = ni->ni_associd;
5738a1b9b6aSSam Leffler 	si->isi_txpower = ni->ni_txpower;
5748a1b9b6aSSam Leffler 	si->isi_vlan = ni->ni_vlan;
5758a1b9b6aSSam Leffler 	if (ni->ni_flags & IEEE80211_NODE_QOS) {
5768a1b9b6aSSam Leffler 		memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs));
5778a1b9b6aSSam Leffler 		memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs));
5788a1b9b6aSSam Leffler 	} else {
579801df4a5SSam Leffler 		si->isi_txseqs[0] = ni->ni_txseqs[IEEE80211_NONQOS_TID];
580801df4a5SSam Leffler 		si->isi_rxseqs[0] = ni->ni_rxseqs[IEEE80211_NONQOS_TID];
5818a1b9b6aSSam Leffler 	}
5822cab1d3dSSam Leffler 	/* NB: leave all cases in case we relax ni_associd == 0 check */
5832cab1d3dSSam Leffler 	if (ieee80211_node_is_authorized(ni))
5848a1b9b6aSSam Leffler 		si->isi_inact = ic->ic_inact_run;
5852cab1d3dSSam Leffler 	else if (ni->ni_associd != 0)
5868a1b9b6aSSam Leffler 		si->isi_inact = ic->ic_inact_auth;
5878a1b9b6aSSam Leffler 	else
5888a1b9b6aSSam Leffler 		si->isi_inact = ic->ic_inact_init;
5898a1b9b6aSSam Leffler 	si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
5908a1b9b6aSSam Leffler 
59168e8e04eSSam Leffler 	if (ielen) {
59268e8e04eSSam Leffler 		cp = ((uint8_t *)si) + si->isi_ie_off;
59368e8e04eSSam Leffler 		cp = copyie(cp, ni->ni_wpa_ie);
59468e8e04eSSam Leffler 		cp = copyie(cp, ni->ni_rsn_ie);
59568e8e04eSSam Leffler 		cp = copyie(cp, ni->ni_wme_ie);
59668e8e04eSSam Leffler 		cp = copyie(cp, ni->ni_ath_ie);
5978a1b9b6aSSam Leffler 	}
5982cab1d3dSSam Leffler 
59968e8e04eSSam Leffler 	req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len);
6002cab1d3dSSam Leffler 	req->space -= len;
6018a1b9b6aSSam Leffler }
6022cab1d3dSSam Leffler 
6032cab1d3dSSam Leffler static int
604d1c85daeSSam Leffler getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq,
605d1c85daeSSam Leffler 	struct ieee80211_node *ni, int off)
6062cab1d3dSSam Leffler {
6072cab1d3dSSam Leffler 	struct stainforeq req;
608d1c85daeSSam Leffler 	size_t space;
609d1c85daeSSam Leffler 	void *p;
6102cab1d3dSSam Leffler 	int error;
6112cab1d3dSSam Leffler 
6122cab1d3dSSam Leffler 	error = 0;
6132cab1d3dSSam Leffler 	req.space = 0;
614d1c85daeSSam Leffler 	if (ni == NULL)
6152cab1d3dSSam Leffler 		ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req);
616d1c85daeSSam Leffler 	else
617d1c85daeSSam Leffler 		get_sta_space(&req, ni);
6182cab1d3dSSam Leffler 	if (req.space > ireq->i_len)
6192cab1d3dSSam Leffler 		req.space = ireq->i_len;
6202cab1d3dSSam Leffler 	if (req.space > 0) {
6212cab1d3dSSam Leffler 		space = req.space;
6222cab1d3dSSam Leffler 		/* XXX M_WAITOK after driver lock released */
6232cab1d3dSSam Leffler 		MALLOC(p, void *, space, M_TEMP, M_NOWAIT);
624d1c85daeSSam Leffler 		if (p == NULL) {
625d1c85daeSSam Leffler 			error = ENOMEM;
626d1c85daeSSam Leffler 			goto bad;
627d1c85daeSSam Leffler 		}
6282cab1d3dSSam Leffler 		req.si = p;
629d1c85daeSSam Leffler 		if (ni == NULL)
6302cab1d3dSSam Leffler 			ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req);
631d1c85daeSSam Leffler 		else
632d1c85daeSSam Leffler 			get_sta_info(&req, ni);
6332cab1d3dSSam Leffler 		ireq->i_len = space - req.space;
63468e8e04eSSam Leffler 		error = copyout(p, (uint8_t *) ireq->i_data+off, ireq->i_len);
6352cab1d3dSSam Leffler 		FREE(p, M_TEMP);
6362cab1d3dSSam Leffler 	} else
6372cab1d3dSSam Leffler 		ireq->i_len = 0;
638d1c85daeSSam Leffler bad:
639d1c85daeSSam Leffler 	if (ni != NULL)
640d1c85daeSSam Leffler 		ieee80211_free_node(ni);
6418a1b9b6aSSam Leffler 	return error;
6428a1b9b6aSSam Leffler }
6438a1b9b6aSSam Leffler 
6448a1b9b6aSSam Leffler static int
645d1c85daeSSam Leffler ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
646d1c85daeSSam Leffler {
64768e8e04eSSam Leffler 	uint8_t macaddr[IEEE80211_ADDR_LEN];
648d1c85daeSSam Leffler 	const int off = __offsetof(struct ieee80211req_sta_req, info);
649d1c85daeSSam Leffler 	struct ieee80211_node *ni;
650d1c85daeSSam Leffler 	int error;
651d1c85daeSSam Leffler 
652d1c85daeSSam Leffler 	if (ireq->i_len < sizeof(struct ieee80211req_sta_req))
653d1c85daeSSam Leffler 		return EFAULT;
654d1c85daeSSam Leffler 	error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
655d1c85daeSSam Leffler 	if (error != 0)
656d1c85daeSSam Leffler 		return error;
657d1c85daeSSam Leffler 	if (IEEE80211_ADDR_EQ(macaddr, ic->ic_ifp->if_broadcastaddr)) {
658d1c85daeSSam Leffler 		ni = NULL;
659d1c85daeSSam Leffler 	} else {
660d1c85daeSSam Leffler 		ni = ieee80211_find_node(&ic->ic_sta, macaddr);
66168e8e04eSSam Leffler 		if (ni == NULL)
66268e8e04eSSam Leffler 			return EINVAL;
663d1c85daeSSam Leffler 	}
664d1c85daeSSam Leffler 	return getstainfo_common(ic, ireq, ni, off);
665d1c85daeSSam Leffler }
666d1c85daeSSam Leffler 
667d1c85daeSSam Leffler #ifdef COMPAT_FREEBSD6
668d1c85daeSSam Leffler #define	IEEE80211_IOC_STA_INFO_OLD	45
669d1c85daeSSam Leffler 
670d1c85daeSSam Leffler static int
671d1c85daeSSam Leffler old_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
672d1c85daeSSam Leffler {
673d1c85daeSSam Leffler 	if (ireq->i_len < sizeof(struct ieee80211req_sta_info))
674d1c85daeSSam Leffler 		return EFAULT;
675d1c85daeSSam Leffler 	return getstainfo_common(ic, ireq, NULL, 0);
676d1c85daeSSam Leffler }
677d1c85daeSSam Leffler #endif /* COMPAT_FREEBSD6 */
678d1c85daeSSam Leffler 
679d1c85daeSSam Leffler static int
6808a1b9b6aSSam Leffler ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
6818a1b9b6aSSam Leffler {
6828a1b9b6aSSam Leffler 	struct ieee80211_node *ni;
6838a1b9b6aSSam Leffler 	struct ieee80211req_sta_txpow txpow;
6848a1b9b6aSSam Leffler 	int error;
6858a1b9b6aSSam Leffler 
6868a1b9b6aSSam Leffler 	if (ireq->i_len != sizeof(txpow))
6878a1b9b6aSSam Leffler 		return EINVAL;
6888a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, &txpow, sizeof(txpow));
6898a1b9b6aSSam Leffler 	if (error != 0)
6908a1b9b6aSSam Leffler 		return error;
691acc4f7f5SSam Leffler 	ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
6928a1b9b6aSSam Leffler 	if (ni == NULL)
6938a1b9b6aSSam Leffler 		return EINVAL;		/* XXX */
6948a1b9b6aSSam Leffler 	txpow.it_txpow = ni->ni_txpower;
6958a1b9b6aSSam Leffler 	error = copyout(&txpow, ireq->i_data, sizeof(txpow));
6968a1b9b6aSSam Leffler 	ieee80211_free_node(ni);
6978a1b9b6aSSam Leffler 	return error;
6988a1b9b6aSSam Leffler }
6998a1b9b6aSSam Leffler 
7008a1b9b6aSSam Leffler static int
7018a1b9b6aSSam Leffler ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
7028a1b9b6aSSam Leffler {
7038a1b9b6aSSam Leffler 	struct ieee80211_wme_state *wme = &ic->ic_wme;
7048a1b9b6aSSam Leffler 	struct wmeParams *wmep;
7058a1b9b6aSSam Leffler 	int ac;
7068a1b9b6aSSam Leffler 
7078a1b9b6aSSam Leffler 	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
7088a1b9b6aSSam Leffler 		return EINVAL;
7098a1b9b6aSSam Leffler 
7108a1b9b6aSSam Leffler 	ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
7118a1b9b6aSSam Leffler 	if (ac >= WME_NUM_AC)
7128a1b9b6aSSam Leffler 		ac = WME_AC_BE;
7138a1b9b6aSSam Leffler 	if (ireq->i_len & IEEE80211_WMEPARAM_BSS)
7148a1b9b6aSSam Leffler 		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
7158a1b9b6aSSam Leffler 	else
7168a1b9b6aSSam Leffler 		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
7178a1b9b6aSSam Leffler 	switch (ireq->i_type) {
7188a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
7198a1b9b6aSSam Leffler 		ireq->i_val = wmep->wmep_logcwmin;
7208a1b9b6aSSam Leffler 		break;
7218a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
7228a1b9b6aSSam Leffler 		ireq->i_val = wmep->wmep_logcwmax;
7238a1b9b6aSSam Leffler 		break;
7248a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
7258a1b9b6aSSam Leffler 		ireq->i_val = wmep->wmep_aifsn;
7268a1b9b6aSSam Leffler 		break;
7278a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
7288a1b9b6aSSam Leffler 		ireq->i_val = wmep->wmep_txopLimit;
7298a1b9b6aSSam Leffler 		break;
7308a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
7318a1b9b6aSSam Leffler 		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
7328a1b9b6aSSam Leffler 		ireq->i_val = wmep->wmep_acm;
7338a1b9b6aSSam Leffler 		break;
7348a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (!bss only)*/
7358a1b9b6aSSam Leffler 		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
7368a1b9b6aSSam Leffler 		ireq->i_val = !wmep->wmep_noackPolicy;
7378a1b9b6aSSam Leffler 		break;
7388a1b9b6aSSam Leffler 	}
7398a1b9b6aSSam Leffler 	return 0;
7408a1b9b6aSSam Leffler }
7418a1b9b6aSSam Leffler 
742188757f5SSam Leffler static int
743188757f5SSam Leffler ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
744188757f5SSam Leffler {
745188757f5SSam Leffler 	const struct ieee80211_aclator *acl = ic->ic_acl;
746188757f5SSam Leffler 
747188757f5SSam Leffler 	return (acl == NULL ? EINVAL : acl->iac_getioctl(ic, ireq));
748188757f5SSam Leffler }
749188757f5SSam Leffler 
750c788ca3eSBill Paul /*
75168e8e04eSSam Leffler  * Return the current ``state'' of an Atheros capbility.
75268e8e04eSSam Leffler  * If associated in station mode report the negotiated
75368e8e04eSSam Leffler  * setting. Otherwise report the current setting.
75468e8e04eSSam Leffler  */
75568e8e04eSSam Leffler static int
75668e8e04eSSam Leffler getathcap(struct ieee80211com *ic, int cap)
75768e8e04eSSam Leffler {
75868e8e04eSSam Leffler 	if (ic->ic_opmode == IEEE80211_M_STA && ic->ic_state == IEEE80211_S_RUN)
75968e8e04eSSam Leffler 		return IEEE80211_ATH_CAP(ic, ic->ic_bss, cap) != 0;
76068e8e04eSSam Leffler 	else
76168e8e04eSSam Leffler 		return (ic->ic_flags & cap) != 0;
76268e8e04eSSam Leffler }
76368e8e04eSSam Leffler 
76468e8e04eSSam Leffler static int
76568e8e04eSSam Leffler ieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq)
76668e8e04eSSam Leffler {
76768e8e04eSSam Leffler 	if (ireq->i_len != sizeof(struct ieee80211_channel))
76868e8e04eSSam Leffler 		return EINVAL;
76968e8e04eSSam Leffler 	return copyout(ic->ic_curchan, ireq->i_data, sizeof(*ic->ic_curchan));
77068e8e04eSSam Leffler }
77168e8e04eSSam Leffler 
77268e8e04eSSam Leffler /*
773c788ca3eSBill Paul  * When building the kernel with -O2 on the i386 architecture, gcc
774c788ca3eSBill Paul  * seems to want to inline this function into ieee80211_ioctl()
775c788ca3eSBill Paul  * (which is the only routine that calls it). When this happens,
776c788ca3eSBill Paul  * ieee80211_ioctl() ends up consuming an additional 2K of stack
777c788ca3eSBill Paul  * space. (Exactly why it needs so much is unclear.) The problem
778c788ca3eSBill Paul  * is that it's possible for ieee80211_ioctl() to invoke other
779c788ca3eSBill Paul  * routines (including driver init functions) which could then find
780c788ca3eSBill Paul  * themselves perilously close to exhausting the stack.
781c788ca3eSBill Paul  *
782c788ca3eSBill Paul  * To avoid this, we deliberately prevent gcc from inlining this
783c788ca3eSBill Paul  * routine. Another way to avoid this is to use less agressive
784c788ca3eSBill Paul  * optimization when compiling this file (i.e. -O instead of -O2)
785c788ca3eSBill Paul  * but special-casing the compilation of this one module in the
786c788ca3eSBill Paul  * build system would be awkward.
787c788ca3eSBill Paul  */
788c788ca3eSBill Paul #ifdef __GNUC__
789c788ca3eSBill Paul __attribute__ ((noinline))
790c788ca3eSBill Paul #endif
7918a1b9b6aSSam Leffler static int
7928a1b9b6aSSam Leffler ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
7938a1b9b6aSSam Leffler {
7948a1b9b6aSSam Leffler 	const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
7951a1e1d21SSam Leffler 	int error = 0;
7968a1b9b6aSSam Leffler 	u_int kid, len, m;
79768e8e04eSSam Leffler 	uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
7981a1e1d21SSam Leffler 	char tmpssid[IEEE80211_NWID_LEN];
7991a1e1d21SSam Leffler 
8001a1e1d21SSam Leffler 	switch (ireq->i_type) {
8011a1e1d21SSam Leffler 	case IEEE80211_IOC_SSID:
8021a1e1d21SSam Leffler 		switch (ic->ic_state) {
8031a1e1d21SSam Leffler 		case IEEE80211_S_INIT:
8041a1e1d21SSam Leffler 		case IEEE80211_S_SCAN:
80568e8e04eSSam Leffler 			ireq->i_len = ic->ic_des_ssid[0].len;
80668e8e04eSSam Leffler 			memcpy(tmpssid, ic->ic_des_ssid[0].ssid, ireq->i_len);
8071a1e1d21SSam Leffler 			break;
8081a1e1d21SSam Leffler 		default:
8091a1e1d21SSam Leffler 			ireq->i_len = ic->ic_bss->ni_esslen;
8101a1e1d21SSam Leffler 			memcpy(tmpssid, ic->ic_bss->ni_essid,
8111a1e1d21SSam Leffler 				ireq->i_len);
8121a1e1d21SSam Leffler 			break;
8131a1e1d21SSam Leffler 		}
8141a1e1d21SSam Leffler 		error = copyout(tmpssid, ireq->i_data, ireq->i_len);
8151a1e1d21SSam Leffler 		break;
8161a1e1d21SSam Leffler 	case IEEE80211_IOC_NUMSSIDS:
8171a1e1d21SSam Leffler 		ireq->i_val = 1;
8181a1e1d21SSam Leffler 		break;
8191a1e1d21SSam Leffler 	case IEEE80211_IOC_WEP:
8208a1b9b6aSSam Leffler 		if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
8218a1b9b6aSSam Leffler 			ireq->i_val = IEEE80211_WEP_OFF;
8228a1b9b6aSSam Leffler 		else if (ic->ic_flags & IEEE80211_F_DROPUNENC)
8238a1b9b6aSSam Leffler 			ireq->i_val = IEEE80211_WEP_ON;
8248a1b9b6aSSam Leffler 		else
8258a1b9b6aSSam Leffler 			ireq->i_val = IEEE80211_WEP_MIXED;
8261a1e1d21SSam Leffler 		break;
8271a1e1d21SSam Leffler 	case IEEE80211_IOC_WEPKEY:
8281a1e1d21SSam Leffler 		kid = (u_int) ireq->i_val;
8298a1b9b6aSSam Leffler 		if (kid >= IEEE80211_WEP_NKID)
8308a1b9b6aSSam Leffler 			return EINVAL;
8318a1b9b6aSSam Leffler 		len = (u_int) ic->ic_nw_keys[kid].wk_keylen;
8321a1e1d21SSam Leffler 		/* NB: only root can read WEP keys */
833acd3428bSRobert Watson 		if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
8341a1e1d21SSam Leffler 			bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
8351a1e1d21SSam Leffler 		} else {
8361a1e1d21SSam Leffler 			bzero(tmpkey, len);
8371a1e1d21SSam Leffler 		}
8381a1e1d21SSam Leffler 		ireq->i_len = len;
8391a1e1d21SSam Leffler 		error = copyout(tmpkey, ireq->i_data, len);
8401a1e1d21SSam Leffler 		break;
8411a1e1d21SSam Leffler 	case IEEE80211_IOC_NUMWEPKEYS:
8421a1e1d21SSam Leffler 		ireq->i_val = IEEE80211_WEP_NKID;
8431a1e1d21SSam Leffler 		break;
8441a1e1d21SSam Leffler 	case IEEE80211_IOC_WEPTXKEY:
8458a1b9b6aSSam Leffler 		ireq->i_val = ic->ic_def_txkey;
8461a1e1d21SSam Leffler 		break;
8471a1e1d21SSam Leffler 	case IEEE80211_IOC_AUTHMODE:
8488a1b9b6aSSam Leffler 		if (ic->ic_flags & IEEE80211_F_WPA)
8498a1b9b6aSSam Leffler 			ireq->i_val = IEEE80211_AUTH_WPA;
8508a1b9b6aSSam Leffler 		else
8518a1b9b6aSSam Leffler 			ireq->i_val = ic->ic_bss->ni_authmode;
8521a1e1d21SSam Leffler 		break;
8531a1e1d21SSam Leffler 	case IEEE80211_IOC_CHANNEL:
854b5c99415SSam Leffler 		ireq->i_val = ieee80211_chan2ieee(ic, ic->ic_curchan);
8551a1e1d21SSam Leffler 		break;
8561a1e1d21SSam Leffler 	case IEEE80211_IOC_POWERSAVE:
8571a1e1d21SSam Leffler 		if (ic->ic_flags & IEEE80211_F_PMGTON)
8581a1e1d21SSam Leffler 			ireq->i_val = IEEE80211_POWERSAVE_ON;
8591a1e1d21SSam Leffler 		else
8601a1e1d21SSam Leffler 			ireq->i_val = IEEE80211_POWERSAVE_OFF;
8611a1e1d21SSam Leffler 		break;
8621a1e1d21SSam Leffler 	case IEEE80211_IOC_POWERSAVESLEEP:
8631a1e1d21SSam Leffler 		ireq->i_val = ic->ic_lintval;
8641a1e1d21SSam Leffler 		break;
86513604e6bSSam Leffler 	case IEEE80211_IOC_RTSTHRESHOLD:
8661a1e1d21SSam Leffler 		ireq->i_val = ic->ic_rtsthreshold;
8671a1e1d21SSam Leffler 		break;
8682e79ca97SSam Leffler 	case IEEE80211_IOC_PROTMODE:
8692e79ca97SSam Leffler 		ireq->i_val = ic->ic_protmode;
8702e79ca97SSam Leffler 		break;
8712e79ca97SSam Leffler 	case IEEE80211_IOC_TXPOWER:
8722e79ca97SSam Leffler 		if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
8738a1b9b6aSSam Leffler 			return EINVAL;
8748a1b9b6aSSam Leffler 		ireq->i_val = ic->ic_txpowlimit;
8758a1b9b6aSSam Leffler 		break;
8768a1b9b6aSSam Leffler 	case IEEE80211_IOC_MCASTCIPHER:
8778a1b9b6aSSam Leffler 		ireq->i_val = rsn->rsn_mcastcipher;
8788a1b9b6aSSam Leffler 		break;
8798a1b9b6aSSam Leffler 	case IEEE80211_IOC_MCASTKEYLEN:
8808a1b9b6aSSam Leffler 		ireq->i_val = rsn->rsn_mcastkeylen;
8818a1b9b6aSSam Leffler 		break;
8828a1b9b6aSSam Leffler 	case IEEE80211_IOC_UCASTCIPHERS:
8838a1b9b6aSSam Leffler 		ireq->i_val = 0;
8848a1b9b6aSSam Leffler 		for (m = 0x1; m != 0; m <<= 1)
8858a1b9b6aSSam Leffler 			if (rsn->rsn_ucastcipherset & m)
8868a1b9b6aSSam Leffler 				ireq->i_val |= 1<<cap2cipher(m);
8878a1b9b6aSSam Leffler 		break;
8888a1b9b6aSSam Leffler 	case IEEE80211_IOC_UCASTCIPHER:
8898a1b9b6aSSam Leffler 		ireq->i_val = rsn->rsn_ucastcipher;
8908a1b9b6aSSam Leffler 		break;
8918a1b9b6aSSam Leffler 	case IEEE80211_IOC_UCASTKEYLEN:
8928a1b9b6aSSam Leffler 		ireq->i_val = rsn->rsn_ucastkeylen;
8938a1b9b6aSSam Leffler 		break;
8948a1b9b6aSSam Leffler 	case IEEE80211_IOC_KEYMGTALGS:
8958a1b9b6aSSam Leffler 		ireq->i_val = rsn->rsn_keymgmtset;
8968a1b9b6aSSam Leffler 		break;
8978a1b9b6aSSam Leffler 	case IEEE80211_IOC_RSNCAPS:
8988a1b9b6aSSam Leffler 		ireq->i_val = rsn->rsn_caps;
8998a1b9b6aSSam Leffler 		break;
9008a1b9b6aSSam Leffler 	case IEEE80211_IOC_WPA:
9018a1b9b6aSSam Leffler 		switch (ic->ic_flags & IEEE80211_F_WPA) {
9028a1b9b6aSSam Leffler 		case IEEE80211_F_WPA1:
9038a1b9b6aSSam Leffler 			ireq->i_val = 1;
9048a1b9b6aSSam Leffler 			break;
9058a1b9b6aSSam Leffler 		case IEEE80211_F_WPA2:
9068a1b9b6aSSam Leffler 			ireq->i_val = 2;
9078a1b9b6aSSam Leffler 			break;
9088a1b9b6aSSam Leffler 		case IEEE80211_F_WPA1 | IEEE80211_F_WPA2:
9098a1b9b6aSSam Leffler 			ireq->i_val = 3;
9108a1b9b6aSSam Leffler 			break;
9118a1b9b6aSSam Leffler 		default:
9128a1b9b6aSSam Leffler 			ireq->i_val = 0;
9138a1b9b6aSSam Leffler 			break;
9148a1b9b6aSSam Leffler 		}
9158a1b9b6aSSam Leffler 		break;
9168a1b9b6aSSam Leffler 	case IEEE80211_IOC_CHANLIST:
9178a1b9b6aSSam Leffler 		error = ieee80211_ioctl_getchanlist(ic, ireq);
9188a1b9b6aSSam Leffler 		break;
9198a1b9b6aSSam Leffler 	case IEEE80211_IOC_ROAMING:
9208a1b9b6aSSam Leffler 		ireq->i_val = ic->ic_roaming;
9218a1b9b6aSSam Leffler 		break;
9228a1b9b6aSSam Leffler 	case IEEE80211_IOC_PRIVACY:
9238a1b9b6aSSam Leffler 		ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0;
9248a1b9b6aSSam Leffler 		break;
9258a1b9b6aSSam Leffler 	case IEEE80211_IOC_DROPUNENCRYPTED:
9268a1b9b6aSSam Leffler 		ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0;
9278a1b9b6aSSam Leffler 		break;
9288a1b9b6aSSam Leffler 	case IEEE80211_IOC_COUNTERMEASURES:
9298a1b9b6aSSam Leffler 		ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0;
9308a1b9b6aSSam Leffler 		break;
9318a1b9b6aSSam Leffler 	case IEEE80211_IOC_DRIVER_CAPS:
9328a1b9b6aSSam Leffler 		ireq->i_val = ic->ic_caps>>16;
9338a1b9b6aSSam Leffler 		ireq->i_len = ic->ic_caps&0xffff;
9348a1b9b6aSSam Leffler 		break;
9358a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME:
9368a1b9b6aSSam Leffler 		ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0;
9378a1b9b6aSSam Leffler 		break;
9388a1b9b6aSSam Leffler 	case IEEE80211_IOC_HIDESSID:
9398a1b9b6aSSam Leffler 		ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0;
9408a1b9b6aSSam Leffler 		break;
9418a1b9b6aSSam Leffler 	case IEEE80211_IOC_APBRIDGE:
9428a1b9b6aSSam Leffler 		ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0;
9438a1b9b6aSSam Leffler 		break;
9448a1b9b6aSSam Leffler 	case IEEE80211_IOC_OPTIE:
9458a1b9b6aSSam Leffler 		if (ic->ic_opt_ie == NULL)
9468a1b9b6aSSam Leffler 			return EINVAL;
9478a1b9b6aSSam Leffler 		/* NB: truncate, caller can check length */
9488a1b9b6aSSam Leffler 		if (ireq->i_len > ic->ic_opt_ie_len)
9498a1b9b6aSSam Leffler 			ireq->i_len = ic->ic_opt_ie_len;
9508a1b9b6aSSam Leffler 		error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len);
9518a1b9b6aSSam Leffler 		break;
9528a1b9b6aSSam Leffler 	case IEEE80211_IOC_WPAKEY:
9538a1b9b6aSSam Leffler 		error = ieee80211_ioctl_getkey(ic, ireq);
9548a1b9b6aSSam Leffler 		break;
9558a1b9b6aSSam Leffler 	case IEEE80211_IOC_CHANINFO:
9568a1b9b6aSSam Leffler 		error = ieee80211_ioctl_getchaninfo(ic, ireq);
9578a1b9b6aSSam Leffler 		break;
9588a1b9b6aSSam Leffler 	case IEEE80211_IOC_BSSID:
9598a1b9b6aSSam Leffler 		if (ireq->i_len != IEEE80211_ADDR_LEN)
9608a1b9b6aSSam Leffler 			return EINVAL;
9618a1b9b6aSSam Leffler 		error = copyout(ic->ic_state == IEEE80211_S_RUN ?
9628a1b9b6aSSam Leffler 					ic->ic_bss->ni_bssid :
9638a1b9b6aSSam Leffler 					ic->ic_des_bssid,
9648a1b9b6aSSam Leffler 				ireq->i_data, ireq->i_len);
9658a1b9b6aSSam Leffler 		break;
9668a1b9b6aSSam Leffler 	case IEEE80211_IOC_WPAIE:
96768e8e04eSSam Leffler 		error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
96868e8e04eSSam Leffler 		break;
96968e8e04eSSam Leffler 	case IEEE80211_IOC_WPAIE2:
97068e8e04eSSam Leffler 		error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
9718a1b9b6aSSam Leffler 		break;
972239cc3b6SSam Leffler #ifdef COMPAT_FREEBSD6
973239cc3b6SSam Leffler 	case IEEE80211_IOC_SCAN_RESULTS_OLD:
974239cc3b6SSam Leffler 		error = old_getscanresults(ic, ireq);
975239cc3b6SSam Leffler 		break;
976239cc3b6SSam Leffler #endif
9778a1b9b6aSSam Leffler 	case IEEE80211_IOC_SCAN_RESULTS:
9788a1b9b6aSSam Leffler 		error = ieee80211_ioctl_getscanresults(ic, ireq);
9798a1b9b6aSSam Leffler 		break;
9808a1b9b6aSSam Leffler 	case IEEE80211_IOC_STA_STATS:
9818a1b9b6aSSam Leffler 		error = ieee80211_ioctl_getstastats(ic, ireq);
9828a1b9b6aSSam Leffler 		break;
9838a1b9b6aSSam Leffler 	case IEEE80211_IOC_TXPOWMAX:
9848a1b9b6aSSam Leffler 		ireq->i_val = ic->ic_bss->ni_txpower;
9858a1b9b6aSSam Leffler 		break;
9868a1b9b6aSSam Leffler 	case IEEE80211_IOC_STA_TXPOW:
9878a1b9b6aSSam Leffler 		error = ieee80211_ioctl_getstatxpow(ic, ireq);
9888a1b9b6aSSam Leffler 		break;
989d1c85daeSSam Leffler #ifdef COMPAT_FREEBSD6
990d1c85daeSSam Leffler 	case IEEE80211_IOC_STA_INFO_OLD:
991d1c85daeSSam Leffler 		error = old_getstainfo(ic, ireq);
992d1c85daeSSam Leffler 		break;
993d1c85daeSSam Leffler #endif
9948a1b9b6aSSam Leffler 	case IEEE80211_IOC_STA_INFO:
9958a1b9b6aSSam Leffler 		error = ieee80211_ioctl_getstainfo(ic, ireq);
9968a1b9b6aSSam Leffler 		break;
9978a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
9988a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
9998a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
10008a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
10018a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
10028a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (bss only) */
10038a1b9b6aSSam Leffler 		error = ieee80211_ioctl_getwmeparam(ic, ireq);
10048a1b9b6aSSam Leffler 		break;
10058a1b9b6aSSam Leffler 	case IEEE80211_IOC_DTIM_PERIOD:
10068a1b9b6aSSam Leffler 		ireq->i_val = ic->ic_dtim_period;
10078a1b9b6aSSam Leffler 		break;
10088a1b9b6aSSam Leffler 	case IEEE80211_IOC_BEACON_INTERVAL:
10098a1b9b6aSSam Leffler 		/* NB: get from ic_bss for station mode */
10108a1b9b6aSSam Leffler 		ireq->i_val = ic->ic_bss->ni_intval;
10112e79ca97SSam Leffler 		break;
1012c4f040c3SSam Leffler 	case IEEE80211_IOC_PUREG:
1013c4f040c3SSam Leffler 		ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0;
1014c4f040c3SSam Leffler 		break;
101568e8e04eSSam Leffler 	case IEEE80211_IOC_FF:
101668e8e04eSSam Leffler 		ireq->i_val = getathcap(ic, IEEE80211_F_FF);
101768e8e04eSSam Leffler 		break;
101868e8e04eSSam Leffler 	case IEEE80211_IOC_TURBOP:
101968e8e04eSSam Leffler 		ireq->i_val = getathcap(ic, IEEE80211_F_TURBOP);
102068e8e04eSSam Leffler 		break;
102168e8e04eSSam Leffler 	case IEEE80211_IOC_BGSCAN:
102268e8e04eSSam Leffler 		ireq->i_val = (ic->ic_flags & IEEE80211_F_BGSCAN) != 0;
102368e8e04eSSam Leffler 		break;
102468e8e04eSSam Leffler 	case IEEE80211_IOC_BGSCAN_IDLE:
102568e8e04eSSam Leffler 		ireq->i_val = ic->ic_bgscanidle*hz/1000;	/* ms */
102668e8e04eSSam Leffler 		break;
102768e8e04eSSam Leffler 	case IEEE80211_IOC_BGSCAN_INTERVAL:
102868e8e04eSSam Leffler 		ireq->i_val = ic->ic_bgscanintvl/hz;		/* seconds */
102968e8e04eSSam Leffler 		break;
103068e8e04eSSam Leffler 	case IEEE80211_IOC_SCANVALID:
103168e8e04eSSam Leffler 		ireq->i_val = ic->ic_scanvalid/hz;		/* seconds */
103268e8e04eSSam Leffler 		break;
103368e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RSSI_11A:
103468e8e04eSSam Leffler 		ireq->i_val = ic->ic_roam.rssi11a;
103568e8e04eSSam Leffler 		break;
103668e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RSSI_11B:
103768e8e04eSSam Leffler 		ireq->i_val = ic->ic_roam.rssi11bOnly;
103868e8e04eSSam Leffler 		break;
103968e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RSSI_11G:
104068e8e04eSSam Leffler 		ireq->i_val = ic->ic_roam.rssi11b;
104168e8e04eSSam Leffler 		break;
104268e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RATE_11A:
104368e8e04eSSam Leffler 		ireq->i_val = ic->ic_roam.rate11a;
104468e8e04eSSam Leffler 		break;
104568e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RATE_11B:
104668e8e04eSSam Leffler 		ireq->i_val = ic->ic_roam.rate11bOnly;
104768e8e04eSSam Leffler 		break;
104868e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RATE_11G:
104968e8e04eSSam Leffler 		ireq->i_val = ic->ic_roam.rate11b;
105068e8e04eSSam Leffler 		break;
105164353cb0SSam Leffler 	case IEEE80211_IOC_MCAST_RATE:
105264353cb0SSam Leffler 		ireq->i_val = ic->ic_mcast_rate;
105364353cb0SSam Leffler 		break;
105470231e3dSSam Leffler 	case IEEE80211_IOC_FRAGTHRESHOLD:
105570231e3dSSam Leffler 		ireq->i_val = ic->ic_fragthreshold;
105670231e3dSSam Leffler 		break;
1057188757f5SSam Leffler 	case IEEE80211_IOC_MACCMD:
1058188757f5SSam Leffler 		error = ieee80211_ioctl_getmaccmd(ic, ireq);
1059188757f5SSam Leffler 		break;
1060c27e4e31SSam Leffler 	case IEEE80211_IOC_BURST:
1061c27e4e31SSam Leffler 		ireq->i_val = (ic->ic_flags & IEEE80211_F_BURST) != 0;
1062c27e4e31SSam Leffler 		break;
1063546786c9SSam Leffler 	case IEEE80211_IOC_BMISSTHRESHOLD:
1064546786c9SSam Leffler 		ireq->i_val = ic->ic_bmissthreshold;
1065546786c9SSam Leffler 		break;
106668e8e04eSSam Leffler 	case IEEE80211_IOC_CURCHAN:
106768e8e04eSSam Leffler 		error = ieee80211_ioctl_getcurchan(ic, ireq);
106868e8e04eSSam Leffler 		break;
106968e8e04eSSam Leffler 	case IEEE80211_IOC_SHORTGI:
107068e8e04eSSam Leffler 		ireq->i_val = 0;
107168e8e04eSSam Leffler 		if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
107268e8e04eSSam Leffler 			ireq->i_val |= IEEE80211_HTCAP_SHORTGI20;
107368e8e04eSSam Leffler 		if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
107468e8e04eSSam Leffler 			ireq->i_val |= IEEE80211_HTCAP_SHORTGI40;
107568e8e04eSSam Leffler 		break;
107668e8e04eSSam Leffler 	case IEEE80211_IOC_AMPDU:
107768e8e04eSSam Leffler 		ireq->i_val = 0;
107868e8e04eSSam Leffler 		if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)
107968e8e04eSSam Leffler 			ireq->i_val |= 1;
108068e8e04eSSam Leffler 		if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)
108168e8e04eSSam Leffler 			ireq->i_val |= 2;
108268e8e04eSSam Leffler 		break;
108368e8e04eSSam Leffler 	case IEEE80211_IOC_AMPDU_LIMIT:
108468e8e04eSSam Leffler 		ireq->i_val = ic->ic_ampdu_limit;	/* XXX truncation? */
108568e8e04eSSam Leffler 		break;
108668e8e04eSSam Leffler 	case IEEE80211_IOC_AMPDU_DENSITY:
108768e8e04eSSam Leffler 		ireq->i_val = ic->ic_ampdu_density;
108868e8e04eSSam Leffler 		break;
108968e8e04eSSam Leffler 	case IEEE80211_IOC_AMSDU:
109068e8e04eSSam Leffler 		ireq->i_val = 0;
109168e8e04eSSam Leffler 		if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_TX)
109268e8e04eSSam Leffler 			ireq->i_val |= 1;
109368e8e04eSSam Leffler 		if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_RX)
109468e8e04eSSam Leffler 			ireq->i_val |= 2;
109568e8e04eSSam Leffler 		break;
109668e8e04eSSam Leffler 	case IEEE80211_IOC_AMSDU_LIMIT:
109768e8e04eSSam Leffler 		ireq->i_val = ic->ic_amsdu_limit;	/* XXX truncation? */
109868e8e04eSSam Leffler 		break;
109968e8e04eSSam Leffler 	case IEEE80211_IOC_PUREN:
110068e8e04eSSam Leffler 		ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) != 0;
110168e8e04eSSam Leffler 		break;
110268e8e04eSSam Leffler 	case IEEE80211_IOC_DOTH:
110368e8e04eSSam Leffler 		ireq->i_val = (ic->ic_flags & IEEE80211_F_DOTH) != 0;
110468e8e04eSSam Leffler 		break;
110568e8e04eSSam Leffler 	case IEEE80211_IOC_HTCOMPAT:
110668e8e04eSSam Leffler 		ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0;
110768e8e04eSSam Leffler 		break;
1108c066143cSSam Leffler 	case IEEE80211_IOC_INACTIVITY:
1109c066143cSSam Leffler 		ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_INACT) != 0;
1110c066143cSSam Leffler 		break;
11111b6167d2SSam Leffler 	case IEEE80211_IOC_HTPROTMODE:
11121b6167d2SSam Leffler 		ireq->i_val = ic->ic_htprotmode;
11131b6167d2SSam Leffler 		break;
11141b6167d2SSam Leffler 	case IEEE80211_IOC_HTCONF:
11151b6167d2SSam Leffler 		if (ic->ic_flags_ext & IEEE80211_FEXT_HT) {
11161b6167d2SSam Leffler 			ireq->i_val = 1;
11171b6167d2SSam Leffler 			if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
11181b6167d2SSam Leffler 				ireq->i_val |= 2;
11191b6167d2SSam Leffler 		} else
11201b6167d2SSam Leffler 			ireq->i_val = 0;
11211b6167d2SSam Leffler 		break;
11221a1e1d21SSam Leffler 	default:
11231a1e1d21SSam Leffler 		error = EINVAL;
1124b2e95691SSam Leffler 		break;
11251a1e1d21SSam Leffler 	}
11268a1b9b6aSSam Leffler 	return error;
11278a1b9b6aSSam Leffler }
11288a1b9b6aSSam Leffler 
11298a1b9b6aSSam Leffler static int
11308a1b9b6aSSam Leffler ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq)
11318a1b9b6aSSam Leffler {
11328a1b9b6aSSam Leffler 	int error;
1133d3b3a464SSam Leffler 	void *ie, *oie;
11348a1b9b6aSSam Leffler 
11358a1b9b6aSSam Leffler 	/*
11368a1b9b6aSSam Leffler 	 * NB: Doing this for ap operation could be useful (e.g. for
11378a1b9b6aSSam Leffler 	 *     WPA and/or WME) except that it typically is worthless
11388a1b9b6aSSam Leffler 	 *     without being able to intervene when processing
11398a1b9b6aSSam Leffler 	 *     association response frames--so disallow it for now.
11408a1b9b6aSSam Leffler 	 */
11418a1b9b6aSSam Leffler 	if (ic->ic_opmode != IEEE80211_M_STA)
11428a1b9b6aSSam Leffler 		return EINVAL;
11438a1b9b6aSSam Leffler 	if (ireq->i_len > IEEE80211_MAX_OPT_IE)
11448a1b9b6aSSam Leffler 		return EINVAL;
114568e8e04eSSam Leffler 	/* NB: data.length is validated by the wireless extensions code */
114668e8e04eSSam Leffler 	/* XXX M_WAITOK after driver lock released */
1147d3b3a464SSam Leffler 	if (ireq->i_len > 0) {
1148c2544286SSam Leffler 		MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_NOWAIT);
11498a1b9b6aSSam Leffler 		if (ie == NULL)
11508a1b9b6aSSam Leffler 			return ENOMEM;
11518a1b9b6aSSam Leffler 		error = copyin(ireq->i_data, ie, ireq->i_len);
1152d3b3a464SSam Leffler 		if (error) {
1153d3b3a464SSam Leffler 			FREE(ie, M_DEVBUF);
1154d3b3a464SSam Leffler 			return error;
1155d3b3a464SSam Leffler 		}
1156d3b3a464SSam Leffler 	} else {
1157d3b3a464SSam Leffler 		ie = NULL;
1158d3b3a464SSam Leffler 		ireq->i_len = 0;
1159d3b3a464SSam Leffler 	}
11608a1b9b6aSSam Leffler 	/* XXX sanity check data? */
1161d3b3a464SSam Leffler 	oie = ic->ic_opt_ie;
11628a1b9b6aSSam Leffler 	ic->ic_opt_ie = ie;
11638a1b9b6aSSam Leffler 	ic->ic_opt_ie_len = ireq->i_len;
1164d3b3a464SSam Leffler 	if (oie != NULL)
1165d3b3a464SSam Leffler 		FREE(oie, M_DEVBUF);
11668a1b9b6aSSam Leffler 	return 0;
11678a1b9b6aSSam Leffler }
11688a1b9b6aSSam Leffler 
11698a1b9b6aSSam Leffler static int
11708a1b9b6aSSam Leffler ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
11718a1b9b6aSSam Leffler {
11728a1b9b6aSSam Leffler 	struct ieee80211req_key ik;
11738a1b9b6aSSam Leffler 	struct ieee80211_node *ni;
11748a1b9b6aSSam Leffler 	struct ieee80211_key *wk;
117568e8e04eSSam Leffler 	uint16_t kid;
11768a1b9b6aSSam Leffler 	int error;
11778a1b9b6aSSam Leffler 
11788a1b9b6aSSam Leffler 	if (ireq->i_len != sizeof(ik))
11798a1b9b6aSSam Leffler 		return EINVAL;
11808a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, &ik, sizeof(ik));
11811a1e1d21SSam Leffler 	if (error)
11828a1b9b6aSSam Leffler 		return error;
11838a1b9b6aSSam Leffler 	/* NB: cipher support is verified by ieee80211_crypt_newkey */
11848a1b9b6aSSam Leffler 	/* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */
11858a1b9b6aSSam Leffler 	if (ik.ik_keylen > sizeof(ik.ik_keydata))
11868a1b9b6aSSam Leffler 		return E2BIG;
11878a1b9b6aSSam Leffler 	kid = ik.ik_keyix;
11888a1b9b6aSSam Leffler 	if (kid == IEEE80211_KEYIX_NONE) {
11898a1b9b6aSSam Leffler 		/* XXX unicast keys currently must be tx/rx */
11908a1b9b6aSSam Leffler 		if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
11918a1b9b6aSSam Leffler 			return EINVAL;
11928a1b9b6aSSam Leffler 		if (ic->ic_opmode == IEEE80211_M_STA) {
1193b5d4660fSSam Leffler 			ni = ieee80211_ref_node(ic->ic_bss);
1194b5d4660fSSam Leffler 			if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) {
1195b5d4660fSSam Leffler 				ieee80211_free_node(ni);
11968a1b9b6aSSam Leffler 				return EADDRNOTAVAIL;
1197b5d4660fSSam Leffler 			}
11988a1b9b6aSSam Leffler 		} else {
1199acc4f7f5SSam Leffler 			ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
12008a1b9b6aSSam Leffler 			if (ni == NULL)
12018a1b9b6aSSam Leffler 				return ENOENT;
12028a1b9b6aSSam Leffler 		}
12038a1b9b6aSSam Leffler 		wk = &ni->ni_ucastkey;
12048a1b9b6aSSam Leffler 	} else {
12058a1b9b6aSSam Leffler 		if (kid >= IEEE80211_WEP_NKID)
12068a1b9b6aSSam Leffler 			return EINVAL;
12078a1b9b6aSSam Leffler 		wk = &ic->ic_nw_keys[kid];
1208386d84f6SSam Leffler 		/*
1209386d84f6SSam Leffler 		 * Global slots start off w/o any assigned key index.
1210386d84f6SSam Leffler 		 * Force one here for consistency with IEEE80211_IOC_WEPKEY.
1211386d84f6SSam Leffler 		 */
1212386d84f6SSam Leffler 		if (wk->wk_keyix == IEEE80211_KEYIX_NONE)
1213386d84f6SSam Leffler 			wk->wk_keyix = kid;
12148a1b9b6aSSam Leffler 		ni = NULL;
12158a1b9b6aSSam Leffler 	}
12168a1b9b6aSSam Leffler 	error = 0;
12178a1b9b6aSSam Leffler 	ieee80211_key_update_begin(ic);
1218dd70e17bSSam Leffler 	if (ieee80211_crypto_newkey(ic, ik.ik_type, ik.ik_flags, wk)) {
12198a1b9b6aSSam Leffler 		wk->wk_keylen = ik.ik_keylen;
12208a1b9b6aSSam Leffler 		/* NB: MIC presence is implied by cipher type */
12218a1b9b6aSSam Leffler 		if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
12228a1b9b6aSSam Leffler 			wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
12238a1b9b6aSSam Leffler 		wk->wk_keyrsc = ik.ik_keyrsc;
12248a1b9b6aSSam Leffler 		wk->wk_keytsc = 0;			/* new key, reset */
12258a1b9b6aSSam Leffler 		memset(wk->wk_key, 0, sizeof(wk->wk_key));
12268a1b9b6aSSam Leffler 		memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen);
12278a1b9b6aSSam Leffler 		if (!ieee80211_crypto_setkey(ic, wk,
12288a1b9b6aSSam Leffler 		    ni != NULL ? ni->ni_macaddr : ik.ik_macaddr))
12298a1b9b6aSSam Leffler 			error = EIO;
12308a1b9b6aSSam Leffler 		else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT))
12318a1b9b6aSSam Leffler 			ic->ic_def_txkey = kid;
12328a1b9b6aSSam Leffler 	} else
12338a1b9b6aSSam Leffler 		error = ENXIO;
12348a1b9b6aSSam Leffler 	ieee80211_key_update_end(ic);
12358a1b9b6aSSam Leffler 	if (ni != NULL)
12368a1b9b6aSSam Leffler 		ieee80211_free_node(ni);
12378a1b9b6aSSam Leffler 	return error;
12388a1b9b6aSSam Leffler }
12398a1b9b6aSSam Leffler 
12408a1b9b6aSSam Leffler static int
12418a1b9b6aSSam Leffler ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
12428a1b9b6aSSam Leffler {
12438a1b9b6aSSam Leffler 	struct ieee80211req_del_key dk;
12448a1b9b6aSSam Leffler 	int kid, error;
12458a1b9b6aSSam Leffler 
12468a1b9b6aSSam Leffler 	if (ireq->i_len != sizeof(dk))
12478a1b9b6aSSam Leffler 		return EINVAL;
12488a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, &dk, sizeof(dk));
12498a1b9b6aSSam Leffler 	if (error)
12508a1b9b6aSSam Leffler 		return error;
12518a1b9b6aSSam Leffler 	kid = dk.idk_keyix;
125268e8e04eSSam Leffler 	/* XXX uint8_t -> uint16_t */
125368e8e04eSSam Leffler 	if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) {
12548a1b9b6aSSam Leffler 		struct ieee80211_node *ni;
12558a1b9b6aSSam Leffler 
1256b5d4660fSSam Leffler 		if (ic->ic_opmode == IEEE80211_M_STA) {
1257b5d4660fSSam Leffler 			ni = ieee80211_ref_node(ic->ic_bss);
1258b5d4660fSSam Leffler 			if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) {
1259b5d4660fSSam Leffler 				ieee80211_free_node(ni);
1260b5d4660fSSam Leffler 				return EADDRNOTAVAIL;
1261b5d4660fSSam Leffler 			}
1262b5d4660fSSam Leffler 		} else {
1263acc4f7f5SSam Leffler 			ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr);
12648a1b9b6aSSam Leffler 			if (ni == NULL)
1265b5d4660fSSam Leffler 				return ENOENT;
1266b5d4660fSSam Leffler 		}
12678a1b9b6aSSam Leffler 		/* XXX error return */
1268c1225b52SSam Leffler 		ieee80211_node_delucastkey(ni);
12698a1b9b6aSSam Leffler 		ieee80211_free_node(ni);
12708a1b9b6aSSam Leffler 	} else {
12718a1b9b6aSSam Leffler 		if (kid >= IEEE80211_WEP_NKID)
12728a1b9b6aSSam Leffler 			return EINVAL;
12738a1b9b6aSSam Leffler 		/* XXX error return */
12748a1b9b6aSSam Leffler 		ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]);
12758a1b9b6aSSam Leffler 	}
12768a1b9b6aSSam Leffler 	return 0;
12778a1b9b6aSSam Leffler }
12788a1b9b6aSSam Leffler 
12798a1b9b6aSSam Leffler static void
12808a1b9b6aSSam Leffler domlme(void *arg, struct ieee80211_node *ni)
12818a1b9b6aSSam Leffler {
12828a1b9b6aSSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
12838a1b9b6aSSam Leffler 	struct ieee80211req_mlme *mlme = arg;
12848a1b9b6aSSam Leffler 
12858a1b9b6aSSam Leffler 	if (ni->ni_associd != 0) {
12868a1b9b6aSSam Leffler 		IEEE80211_SEND_MGMT(ic, ni,
12878a1b9b6aSSam Leffler 			mlme->im_op == IEEE80211_MLME_DEAUTH ?
12888a1b9b6aSSam Leffler 				IEEE80211_FC0_SUBTYPE_DEAUTH :
12898a1b9b6aSSam Leffler 				IEEE80211_FC0_SUBTYPE_DISASSOC,
12908a1b9b6aSSam Leffler 			mlme->im_reason);
12918a1b9b6aSSam Leffler 	}
12928a1b9b6aSSam Leffler 	ieee80211_node_leave(ic, ni);
12938a1b9b6aSSam Leffler }
12948a1b9b6aSSam Leffler 
129568e8e04eSSam Leffler struct scanlookup {
129668e8e04eSSam Leffler 	const uint8_t *mac;
129768e8e04eSSam Leffler 	int esslen;
129868e8e04eSSam Leffler 	const uint8_t *essid;
129968e8e04eSSam Leffler 	const struct ieee80211_scan_entry *se;
130068e8e04eSSam Leffler };
130168e8e04eSSam Leffler 
130268e8e04eSSam Leffler /*
130368e8e04eSSam Leffler  * Match mac address and any ssid.
130468e8e04eSSam Leffler  */
130568e8e04eSSam Leffler static void
130668e8e04eSSam Leffler mlmelookup(void *arg, const struct ieee80211_scan_entry *se)
130768e8e04eSSam Leffler {
130868e8e04eSSam Leffler 	struct scanlookup *look = arg;
130968e8e04eSSam Leffler 
131068e8e04eSSam Leffler 	if (!IEEE80211_ADDR_EQ(look->mac, se->se_macaddr))
131168e8e04eSSam Leffler 		return;
131268e8e04eSSam Leffler 	if (look->esslen != 0) {
131368e8e04eSSam Leffler 		if (se->se_ssid[1] != look->esslen)
131468e8e04eSSam Leffler 			return;
131568e8e04eSSam Leffler 		if (memcmp(look->essid, se->se_ssid+2, look->esslen))
131668e8e04eSSam Leffler 			return;
131768e8e04eSSam Leffler 	}
131868e8e04eSSam Leffler 	look->se = se;
131968e8e04eSSam Leffler }
132068e8e04eSSam Leffler 
13218a1b9b6aSSam Leffler static int
13228a1b9b6aSSam Leffler ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
13238a1b9b6aSSam Leffler {
13248a1b9b6aSSam Leffler 	struct ieee80211req_mlme mlme;
13258a1b9b6aSSam Leffler 	struct ieee80211_node *ni;
13268a1b9b6aSSam Leffler 	int error;
13278a1b9b6aSSam Leffler 
13288a1b9b6aSSam Leffler 	if (ireq->i_len != sizeof(mlme))
13298a1b9b6aSSam Leffler 		return EINVAL;
13308a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, &mlme, sizeof(mlme));
13318a1b9b6aSSam Leffler 	if (error)
13328a1b9b6aSSam Leffler 		return error;
13338a1b9b6aSSam Leffler 	switch (mlme.im_op) {
13348a1b9b6aSSam Leffler 	case IEEE80211_MLME_ASSOC:
133568e8e04eSSam Leffler 		/* XXX ibss/ahdemo */
133668e8e04eSSam Leffler 		if (ic->ic_opmode == IEEE80211_M_STA) {
133768e8e04eSSam Leffler 			struct scanlookup lookup;
13388a1b9b6aSSam Leffler 
133968e8e04eSSam Leffler 			lookup.se = NULL;
134068e8e04eSSam Leffler 			lookup.mac = mlme.im_macaddr;
134168e8e04eSSam Leffler 			/* XXX use revised api w/ explicit ssid */
134268e8e04eSSam Leffler 			lookup.esslen = ic->ic_des_ssid[0].len;
134368e8e04eSSam Leffler 			lookup.essid = ic->ic_des_ssid[0].ssid;
134468e8e04eSSam Leffler 			ieee80211_scan_iterate(ic, mlmelookup, &lookup);
134568e8e04eSSam Leffler 			if (lookup.se != NULL &&
134668e8e04eSSam Leffler 			    ieee80211_sta_join(ic, lookup.se))
134768e8e04eSSam Leffler 				return 0;
13488a1b9b6aSSam Leffler 		}
13498a1b9b6aSSam Leffler 		return EINVAL;
13508a1b9b6aSSam Leffler 	case IEEE80211_MLME_DISASSOC:
13518a1b9b6aSSam Leffler 	case IEEE80211_MLME_DEAUTH:
13528a1b9b6aSSam Leffler 		switch (ic->ic_opmode) {
13538a1b9b6aSSam Leffler 		case IEEE80211_M_STA:
13548a1b9b6aSSam Leffler 			/* XXX not quite right */
13558a1b9b6aSSam Leffler 			ieee80211_new_state(ic, IEEE80211_S_INIT,
13568a1b9b6aSSam Leffler 				mlme.im_reason);
13578a1b9b6aSSam Leffler 			break;
13588a1b9b6aSSam Leffler 		case IEEE80211_M_HOSTAP:
13598a1b9b6aSSam Leffler 			/* NB: the broadcast address means do 'em all */
13608a1b9b6aSSam Leffler 			if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) {
1361acc4f7f5SSam Leffler 				if ((ni = ieee80211_find_node(&ic->ic_sta,
13628a1b9b6aSSam Leffler 						mlme.im_macaddr)) == NULL)
13638a1b9b6aSSam Leffler 					return EINVAL;
13648a1b9b6aSSam Leffler 				domlme(&mlme, ni);
13658a1b9b6aSSam Leffler 				ieee80211_free_node(ni);
13668a1b9b6aSSam Leffler 			} else {
1367acc4f7f5SSam Leffler 				ieee80211_iterate_nodes(&ic->ic_sta,
13688a1b9b6aSSam Leffler 						domlme, &mlme);
13698a1b9b6aSSam Leffler 			}
13708a1b9b6aSSam Leffler 			break;
13718a1b9b6aSSam Leffler 		default:
13728a1b9b6aSSam Leffler 			return EINVAL;
13738a1b9b6aSSam Leffler 		}
13748a1b9b6aSSam Leffler 		break;
13758a1b9b6aSSam Leffler 	case IEEE80211_MLME_AUTHORIZE:
13768a1b9b6aSSam Leffler 	case IEEE80211_MLME_UNAUTHORIZE:
13778a1b9b6aSSam Leffler 		if (ic->ic_opmode != IEEE80211_M_HOSTAP)
13788a1b9b6aSSam Leffler 			return EINVAL;
1379acc4f7f5SSam Leffler 		ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr);
13808a1b9b6aSSam Leffler 		if (ni == NULL)
13818a1b9b6aSSam Leffler 			return EINVAL;
13828a1b9b6aSSam Leffler 		if (mlme.im_op == IEEE80211_MLME_AUTHORIZE)
1383e4918ecdSSam Leffler 			ieee80211_node_authorize(ni);
13848a1b9b6aSSam Leffler 		else
1385e4918ecdSSam Leffler 			ieee80211_node_unauthorize(ni);
13868a1b9b6aSSam Leffler 		ieee80211_free_node(ni);
13878a1b9b6aSSam Leffler 		break;
13888a1b9b6aSSam Leffler 	default:
13898a1b9b6aSSam Leffler 		return EINVAL;
13908a1b9b6aSSam Leffler 	}
13918a1b9b6aSSam Leffler 	return 0;
13928a1b9b6aSSam Leffler }
13938a1b9b6aSSam Leffler 
13948a1b9b6aSSam Leffler static int
13958a1b9b6aSSam Leffler ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
13968a1b9b6aSSam Leffler {
139768e8e04eSSam Leffler 	uint8_t mac[IEEE80211_ADDR_LEN];
13988a1b9b6aSSam Leffler 	const struct ieee80211_aclator *acl = ic->ic_acl;
13998a1b9b6aSSam Leffler 	int error;
14008a1b9b6aSSam Leffler 
14018a1b9b6aSSam Leffler 	if (ireq->i_len != sizeof(mac))
14028a1b9b6aSSam Leffler 		return EINVAL;
14038a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, mac, ireq->i_len);
14048a1b9b6aSSam Leffler 	if (error)
14058a1b9b6aSSam Leffler 		return error;
14068a1b9b6aSSam Leffler 	if (acl == NULL) {
14078a1b9b6aSSam Leffler 		acl = ieee80211_aclator_get("mac");
14088a1b9b6aSSam Leffler 		if (acl == NULL || !acl->iac_attach(ic))
14098a1b9b6aSSam Leffler 			return EINVAL;
14108a1b9b6aSSam Leffler 		ic->ic_acl = acl;
14118a1b9b6aSSam Leffler 	}
14128a1b9b6aSSam Leffler 	if (ireq->i_type == IEEE80211_IOC_ADDMAC)
14138a1b9b6aSSam Leffler 		acl->iac_add(ic, mac);
14148a1b9b6aSSam Leffler 	else
14158a1b9b6aSSam Leffler 		acl->iac_remove(ic, mac);
14168a1b9b6aSSam Leffler 	return 0;
14178a1b9b6aSSam Leffler }
14188a1b9b6aSSam Leffler 
14198a1b9b6aSSam Leffler static int
1420188757f5SSam Leffler ieee80211_ioctl_setmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
14218a1b9b6aSSam Leffler {
14228a1b9b6aSSam Leffler 	const struct ieee80211_aclator *acl = ic->ic_acl;
14238a1b9b6aSSam Leffler 
14248a1b9b6aSSam Leffler 	switch (ireq->i_val) {
14258a1b9b6aSSam Leffler 	case IEEE80211_MACCMD_POLICY_OPEN:
14268a1b9b6aSSam Leffler 	case IEEE80211_MACCMD_POLICY_ALLOW:
14278a1b9b6aSSam Leffler 	case IEEE80211_MACCMD_POLICY_DENY:
14288a1b9b6aSSam Leffler 		if (acl == NULL) {
14298a1b9b6aSSam Leffler 			acl = ieee80211_aclator_get("mac");
14308a1b9b6aSSam Leffler 			if (acl == NULL || !acl->iac_attach(ic))
14318a1b9b6aSSam Leffler 				return EINVAL;
14328a1b9b6aSSam Leffler 			ic->ic_acl = acl;
14338a1b9b6aSSam Leffler 		}
14348a1b9b6aSSam Leffler 		acl->iac_setpolicy(ic, ireq->i_val);
14358a1b9b6aSSam Leffler 		break;
14368a1b9b6aSSam Leffler 	case IEEE80211_MACCMD_FLUSH:
14378a1b9b6aSSam Leffler 		if (acl != NULL)
14388a1b9b6aSSam Leffler 			acl->iac_flush(ic);
14398a1b9b6aSSam Leffler 		/* NB: silently ignore when not in use */
14408a1b9b6aSSam Leffler 		break;
14418a1b9b6aSSam Leffler 	case IEEE80211_MACCMD_DETACH:
14428a1b9b6aSSam Leffler 		if (acl != NULL) {
14438a1b9b6aSSam Leffler 			ic->ic_acl = NULL;
14448a1b9b6aSSam Leffler 			acl->iac_detach(ic);
14458a1b9b6aSSam Leffler 		}
14468a1b9b6aSSam Leffler 		break;
14478a1b9b6aSSam Leffler 	default:
1448188757f5SSam Leffler 		if (acl == NULL)
14498a1b9b6aSSam Leffler 			return EINVAL;
1450188757f5SSam Leffler 		else
1451188757f5SSam Leffler 			return acl->iac_setioctl(ic, ireq);
14528a1b9b6aSSam Leffler 	}
14538a1b9b6aSSam Leffler 	return 0;
14548a1b9b6aSSam Leffler }
14558a1b9b6aSSam Leffler 
14568a1b9b6aSSam Leffler static int
14578a1b9b6aSSam Leffler ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
14588a1b9b6aSSam Leffler {
14598a1b9b6aSSam Leffler 	struct ieee80211req_chanlist list;
14608a1b9b6aSSam Leffler 	u_char chanlist[IEEE80211_CHAN_BYTES];
146168e8e04eSSam Leffler 	int i, j, nchan, error;
14628a1b9b6aSSam Leffler 
14638a1b9b6aSSam Leffler 	if (ireq->i_len != sizeof(list))
14648a1b9b6aSSam Leffler 		return EINVAL;
14658a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, &list, sizeof(list));
14668a1b9b6aSSam Leffler 	if (error)
14678a1b9b6aSSam Leffler 		return error;
14688a1b9b6aSSam Leffler 	memset(chanlist, 0, sizeof(chanlist));
14698a1b9b6aSSam Leffler 	/*
14708a1b9b6aSSam Leffler 	 * Since channel 0 is not available for DS, channel 1
14718a1b9b6aSSam Leffler 	 * is assigned to LSB on WaveLAN.
14728a1b9b6aSSam Leffler 	 */
14738a1b9b6aSSam Leffler 	if (ic->ic_phytype == IEEE80211_T_DS)
14748a1b9b6aSSam Leffler 		i = 1;
14758a1b9b6aSSam Leffler 	else
14768a1b9b6aSSam Leffler 		i = 0;
147768e8e04eSSam Leffler 	nchan = 0;
14788a1b9b6aSSam Leffler 	for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
14798a1b9b6aSSam Leffler 		/*
14808a1b9b6aSSam Leffler 		 * NB: silently discard unavailable channels so users
14818a1b9b6aSSam Leffler 		 *     can specify 1-255 to get all available channels.
14828a1b9b6aSSam Leffler 		 */
148368e8e04eSSam Leffler 		if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) {
14848a1b9b6aSSam Leffler 			setbit(chanlist, i);
148568e8e04eSSam Leffler 			nchan++;
14868a1b9b6aSSam Leffler 		}
14878a1b9b6aSSam Leffler 	}
148868e8e04eSSam Leffler 	if (nchan == 0)
148968e8e04eSSam Leffler 		return EINVAL;
149068e8e04eSSam Leffler 	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&	/* XXX */
149168e8e04eSSam Leffler 	    isclr(chanlist, ic->ic_bsschan->ic_ieee))
149268e8e04eSSam Leffler 		ic->ic_bsschan = IEEE80211_CHAN_ANYC;
14938a1b9b6aSSam Leffler 	memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
149468e8e04eSSam Leffler 	return IS_UP_AUTO(ic) ? ieee80211_init(ic, RESCAN) : 0;
14958a1b9b6aSSam Leffler }
14968a1b9b6aSSam Leffler 
14978a1b9b6aSSam Leffler static int
149842568791SSam Leffler ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
149942568791SSam Leffler {
150042568791SSam Leffler 	struct ieee80211_node *ni;
150168e8e04eSSam Leffler 	uint8_t macaddr[IEEE80211_ADDR_LEN];
150242568791SSam Leffler 	int error;
150342568791SSam Leffler 
150442568791SSam Leffler 	/*
150542568791SSam Leffler 	 * NB: we could copyin ieee80211req_sta_stats so apps
150642568791SSam Leffler 	 *     could make selective changes but that's overkill;
150742568791SSam Leffler 	 *     just clear all stats for now.
150842568791SSam Leffler 	 */
150942568791SSam Leffler 	if (ireq->i_len < IEEE80211_ADDR_LEN)
151042568791SSam Leffler 		return EINVAL;
151142568791SSam Leffler 	error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
151242568791SSam Leffler 	if (error != 0)
151342568791SSam Leffler 		return error;
151442568791SSam Leffler 	ni = ieee80211_find_node(&ic->ic_sta, macaddr);
151542568791SSam Leffler 	if (ni == NULL)
151642568791SSam Leffler 		return EINVAL;		/* XXX */
151742568791SSam Leffler 	memset(&ni->ni_stats, 0, sizeof(ni->ni_stats));
151842568791SSam Leffler 	ieee80211_free_node(ni);
151942568791SSam Leffler 	return 0;
152042568791SSam Leffler }
152142568791SSam Leffler 
152242568791SSam Leffler static int
15238a1b9b6aSSam Leffler ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
15248a1b9b6aSSam Leffler {
15258a1b9b6aSSam Leffler 	struct ieee80211_node *ni;
15268a1b9b6aSSam Leffler 	struct ieee80211req_sta_txpow txpow;
15278a1b9b6aSSam Leffler 	int error;
15288a1b9b6aSSam Leffler 
15298a1b9b6aSSam Leffler 	if (ireq->i_len != sizeof(txpow))
15308a1b9b6aSSam Leffler 		return EINVAL;
15318a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, &txpow, sizeof(txpow));
15328a1b9b6aSSam Leffler 	if (error != 0)
15338a1b9b6aSSam Leffler 		return error;
1534acc4f7f5SSam Leffler 	ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
15358a1b9b6aSSam Leffler 	if (ni == NULL)
15368a1b9b6aSSam Leffler 		return EINVAL;		/* XXX */
15378a1b9b6aSSam Leffler 	ni->ni_txpower = txpow.it_txpow;
15388a1b9b6aSSam Leffler 	ieee80211_free_node(ni);
15398a1b9b6aSSam Leffler 	return error;
15408a1b9b6aSSam Leffler }
15418a1b9b6aSSam Leffler 
15428a1b9b6aSSam Leffler static int
15438a1b9b6aSSam Leffler ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
15448a1b9b6aSSam Leffler {
15458a1b9b6aSSam Leffler 	struct ieee80211_wme_state *wme = &ic->ic_wme;
15468a1b9b6aSSam Leffler 	struct wmeParams *wmep, *chanp;
15478a1b9b6aSSam Leffler 	int isbss, ac;
15488a1b9b6aSSam Leffler 
15498a1b9b6aSSam Leffler 	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
15508a1b9b6aSSam Leffler 		return EINVAL;
15518a1b9b6aSSam Leffler 
15528a1b9b6aSSam Leffler 	isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS);
15538a1b9b6aSSam Leffler 	ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
15548a1b9b6aSSam Leffler 	if (ac >= WME_NUM_AC)
15558a1b9b6aSSam Leffler 		ac = WME_AC_BE;
15568a1b9b6aSSam Leffler 	if (isbss) {
15578a1b9b6aSSam Leffler 		chanp = &wme->wme_bssChanParams.cap_wmeParams[ac];
15588a1b9b6aSSam Leffler 		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
15598a1b9b6aSSam Leffler 	} else {
15608a1b9b6aSSam Leffler 		chanp = &wme->wme_chanParams.cap_wmeParams[ac];
15618a1b9b6aSSam Leffler 		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
15628a1b9b6aSSam Leffler 	}
15638a1b9b6aSSam Leffler 	switch (ireq->i_type) {
15648a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
15658a1b9b6aSSam Leffler 		if (isbss) {
15668a1b9b6aSSam Leffler 			wmep->wmep_logcwmin = ireq->i_val;
15678a1b9b6aSSam Leffler 			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
15688a1b9b6aSSam Leffler 				chanp->wmep_logcwmin = ireq->i_val;
15698a1b9b6aSSam Leffler 		} else {
15708a1b9b6aSSam Leffler 			wmep->wmep_logcwmin = chanp->wmep_logcwmin =
15718a1b9b6aSSam Leffler 				ireq->i_val;
15728a1b9b6aSSam Leffler 		}
15738a1b9b6aSSam Leffler 		break;
15748a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
15758a1b9b6aSSam Leffler 		if (isbss) {
15768a1b9b6aSSam Leffler 			wmep->wmep_logcwmax = ireq->i_val;
15778a1b9b6aSSam Leffler 			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
15788a1b9b6aSSam Leffler 				chanp->wmep_logcwmax = ireq->i_val;
15798a1b9b6aSSam Leffler 		} else {
15808a1b9b6aSSam Leffler 			wmep->wmep_logcwmax = chanp->wmep_logcwmax =
15818a1b9b6aSSam Leffler 				ireq->i_val;
15828a1b9b6aSSam Leffler 		}
15838a1b9b6aSSam Leffler 		break;
15848a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
15858a1b9b6aSSam Leffler 		if (isbss) {
15868a1b9b6aSSam Leffler 			wmep->wmep_aifsn = ireq->i_val;
15878a1b9b6aSSam Leffler 			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
15888a1b9b6aSSam Leffler 				chanp->wmep_aifsn = ireq->i_val;
15898a1b9b6aSSam Leffler 		} else {
15908a1b9b6aSSam Leffler 			wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val;
15918a1b9b6aSSam Leffler 		}
15928a1b9b6aSSam Leffler 		break;
15938a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
15948a1b9b6aSSam Leffler 		if (isbss) {
15958a1b9b6aSSam Leffler 			wmep->wmep_txopLimit = ireq->i_val;
15968a1b9b6aSSam Leffler 			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
15978a1b9b6aSSam Leffler 				chanp->wmep_txopLimit = ireq->i_val;
15988a1b9b6aSSam Leffler 		} else {
15998a1b9b6aSSam Leffler 			wmep->wmep_txopLimit = chanp->wmep_txopLimit =
16008a1b9b6aSSam Leffler 				ireq->i_val;
16018a1b9b6aSSam Leffler 		}
16028a1b9b6aSSam Leffler 		break;
16038a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
16048a1b9b6aSSam Leffler 		wmep->wmep_acm = ireq->i_val;
16058a1b9b6aSSam Leffler 		if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
16068a1b9b6aSSam Leffler 			chanp->wmep_acm = ireq->i_val;
16078a1b9b6aSSam Leffler 		break;
16088a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (!bss only)*/
16098a1b9b6aSSam Leffler 		wmep->wmep_noackPolicy = chanp->wmep_noackPolicy =
16108a1b9b6aSSam Leffler 			(ireq->i_val) == 0;
16118a1b9b6aSSam Leffler 		break;
16128a1b9b6aSSam Leffler 	}
16138a1b9b6aSSam Leffler 	ieee80211_wme_updateparams(ic);
16148a1b9b6aSSam Leffler 	return 0;
16158a1b9b6aSSam Leffler }
16168a1b9b6aSSam Leffler 
16178a1b9b6aSSam Leffler static int
16188a1b9b6aSSam Leffler cipher2cap(int cipher)
16198a1b9b6aSSam Leffler {
16208a1b9b6aSSam Leffler 	switch (cipher) {
16218a1b9b6aSSam Leffler 	case IEEE80211_CIPHER_WEP:	return IEEE80211_C_WEP;
16228a1b9b6aSSam Leffler 	case IEEE80211_CIPHER_AES_OCB:	return IEEE80211_C_AES;
16238a1b9b6aSSam Leffler 	case IEEE80211_CIPHER_AES_CCM:	return IEEE80211_C_AES_CCM;
16248a1b9b6aSSam Leffler 	case IEEE80211_CIPHER_CKIP:	return IEEE80211_C_CKIP;
16258a1b9b6aSSam Leffler 	case IEEE80211_CIPHER_TKIP:	return IEEE80211_C_TKIP;
16268a1b9b6aSSam Leffler 	}
16278a1b9b6aSSam Leffler 	return 0;
16288a1b9b6aSSam Leffler }
16298a1b9b6aSSam Leffler 
16308a1b9b6aSSam Leffler static int
163168e8e04eSSam Leffler find11gchannel(struct ieee80211com *ic, int start, int freq)
163268e8e04eSSam Leffler {
163368e8e04eSSam Leffler 	const struct ieee80211_channel *c;
163468e8e04eSSam Leffler 	int i;
163568e8e04eSSam Leffler 
163668e8e04eSSam Leffler 	for (i = start+1; i < ic->ic_nchans; i++) {
163768e8e04eSSam Leffler 		c = &ic->ic_channels[i];
163868e8e04eSSam Leffler 		if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
163968e8e04eSSam Leffler 			return 1;
164068e8e04eSSam Leffler 	}
164168e8e04eSSam Leffler 	/* NB: should not be needed but in case things are mis-sorted */
164268e8e04eSSam Leffler 	for (i = 0; i < start; i++) {
164368e8e04eSSam Leffler 		c = &ic->ic_channels[i];
164468e8e04eSSam Leffler 		if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
164568e8e04eSSam Leffler 			return 1;
164668e8e04eSSam Leffler 	}
164768e8e04eSSam Leffler 	return 0;
164868e8e04eSSam Leffler }
164968e8e04eSSam Leffler 
165068e8e04eSSam Leffler static struct ieee80211_channel *
165168e8e04eSSam Leffler findchannel(struct ieee80211com *ic, int ieee, int mode)
165268e8e04eSSam Leffler {
165368e8e04eSSam Leffler 	static const u_int chanflags[IEEE80211_MODE_MAX] = {
165468e8e04eSSam Leffler 		0,			/* IEEE80211_MODE_AUTO */
165568e8e04eSSam Leffler 		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11A */
165668e8e04eSSam Leffler 		IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
165768e8e04eSSam Leffler 		IEEE80211_CHAN_G,	/* IEEE80211_MODE_11G */
165868e8e04eSSam Leffler 		IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
165968e8e04eSSam Leffler 		IEEE80211_CHAN_108A,	/* IEEE80211_MODE_TURBO_A */
166068e8e04eSSam Leffler 		IEEE80211_CHAN_108G,	/* IEEE80211_MODE_TURBO_G */
166168e8e04eSSam Leffler 		IEEE80211_CHAN_STURBO,	/* IEEE80211_MODE_STURBO_A */
166268e8e04eSSam Leffler 		/* NB: handled specially below */
166368e8e04eSSam Leffler 		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11NA */
166468e8e04eSSam Leffler 		IEEE80211_CHAN_G,	/* IEEE80211_MODE_11NG */
166568e8e04eSSam Leffler 	};
166668e8e04eSSam Leffler 	u_int modeflags;
166768e8e04eSSam Leffler 	int i;
166868e8e04eSSam Leffler 
166968e8e04eSSam Leffler 	KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
167068e8e04eSSam Leffler 	modeflags = chanflags[mode];
167168e8e04eSSam Leffler 	KASSERT(modeflags != 0 || mode == IEEE80211_MODE_AUTO,
167268e8e04eSSam Leffler 	    ("no chanflags for mode %u", mode));
167368e8e04eSSam Leffler 	for (i = 0; i < ic->ic_nchans; i++) {
167468e8e04eSSam Leffler 		struct ieee80211_channel *c = &ic->ic_channels[i];
167568e8e04eSSam Leffler 
167668e8e04eSSam Leffler 		if (c->ic_ieee != ieee)
167768e8e04eSSam Leffler 			continue;
167868e8e04eSSam Leffler 		if (mode == IEEE80211_MODE_AUTO) {
167968e8e04eSSam Leffler 			/* ignore turbo channels for autoselect */
168068e8e04eSSam Leffler 			if (IEEE80211_IS_CHAN_TURBO(c))
168168e8e04eSSam Leffler 				continue;
168268e8e04eSSam Leffler 			/*
168368e8e04eSSam Leffler 			 * XXX special-case 11b/g channels so we
168468e8e04eSSam Leffler 			 *     always select the g channel if both
168568e8e04eSSam Leffler 			 *     are present.
168668e8e04eSSam Leffler 			 * XXX prefer HT to non-HT?
168768e8e04eSSam Leffler 			 */
168868e8e04eSSam Leffler 			if (!IEEE80211_IS_CHAN_B(c) ||
168968e8e04eSSam Leffler 			    !find11gchannel(ic, i, c->ic_freq))
169068e8e04eSSam Leffler 				return c;
169168e8e04eSSam Leffler 		} else {
169268e8e04eSSam Leffler 			/* must check HT specially */
169368e8e04eSSam Leffler 			if ((mode == IEEE80211_MODE_11NA ||
169468e8e04eSSam Leffler 			    mode == IEEE80211_MODE_11NG) &&
169568e8e04eSSam Leffler 			    !IEEE80211_IS_CHAN_HT(c))
169668e8e04eSSam Leffler 				continue;
169768e8e04eSSam Leffler 			if ((c->ic_flags & modeflags) == modeflags)
169868e8e04eSSam Leffler 				return c;
169968e8e04eSSam Leffler 		}
170068e8e04eSSam Leffler 	}
170168e8e04eSSam Leffler 	return NULL;
170268e8e04eSSam Leffler }
170368e8e04eSSam Leffler 
170468e8e04eSSam Leffler /*
170568e8e04eSSam Leffler  * Check the specified against any desired mode (aka netband).
170668e8e04eSSam Leffler  * This is only used (presently) when operating in hostap mode
170768e8e04eSSam Leffler  * to enforce consistency.
170868e8e04eSSam Leffler  */
170968e8e04eSSam Leffler static int
171068e8e04eSSam Leffler check_mode_consistency(const struct ieee80211_channel *c, int mode)
171168e8e04eSSam Leffler {
171268e8e04eSSam Leffler 	KASSERT(c != IEEE80211_CHAN_ANYC, ("oops, no channel"));
171368e8e04eSSam Leffler 
171468e8e04eSSam Leffler 	switch (mode) {
171568e8e04eSSam Leffler 	case IEEE80211_MODE_11B:
171668e8e04eSSam Leffler 		return (IEEE80211_IS_CHAN_B(c));
171768e8e04eSSam Leffler 	case IEEE80211_MODE_11G:
171868e8e04eSSam Leffler 		return (IEEE80211_IS_CHAN_ANYG(c) && !IEEE80211_IS_CHAN_HT(c));
171968e8e04eSSam Leffler 	case IEEE80211_MODE_11A:
172068e8e04eSSam Leffler 		return (IEEE80211_IS_CHAN_A(c) && !IEEE80211_IS_CHAN_HT(c));
172168e8e04eSSam Leffler 	case IEEE80211_MODE_STURBO_A:
172268e8e04eSSam Leffler 		return (IEEE80211_IS_CHAN_STURBO(c));
172368e8e04eSSam Leffler 	case IEEE80211_MODE_11NA:
172468e8e04eSSam Leffler 		return (IEEE80211_IS_CHAN_HTA(c));
172568e8e04eSSam Leffler 	case IEEE80211_MODE_11NG:
172668e8e04eSSam Leffler 		return (IEEE80211_IS_CHAN_HTG(c));
172768e8e04eSSam Leffler 	}
172868e8e04eSSam Leffler 	return 1;
172968e8e04eSSam Leffler 
173068e8e04eSSam Leffler }
173168e8e04eSSam Leffler 
173268e8e04eSSam Leffler /*
173368e8e04eSSam Leffler  * Common code to set the current channel.  If the device
173468e8e04eSSam Leffler  * is up and running this may result in an immediate channel
173568e8e04eSSam Leffler  * change or a kick of the state machine.
173668e8e04eSSam Leffler  */
173768e8e04eSSam Leffler static int
173868e8e04eSSam Leffler setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
173968e8e04eSSam Leffler {
174068e8e04eSSam Leffler 	int error;
174168e8e04eSSam Leffler 
174268e8e04eSSam Leffler 	if (c != IEEE80211_CHAN_ANYC) {
174368e8e04eSSam Leffler 		if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
174468e8e04eSSam Leffler 		    !check_mode_consistency(c, ic->ic_des_mode))
174568e8e04eSSam Leffler 			return EINVAL;
174668e8e04eSSam Leffler 		if (ic->ic_state == IEEE80211_S_RUN && c == ic->ic_curchan)
174768e8e04eSSam Leffler 			return 0;	/* NB: nothing to do */
174868e8e04eSSam Leffler 	}
174968e8e04eSSam Leffler 	ic->ic_des_chan = c;
175068e8e04eSSam Leffler 
175168e8e04eSSam Leffler 	error = 0;
175268e8e04eSSam Leffler 	if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
175368e8e04eSSam Leffler 	    ic->ic_opmode == IEEE80211_M_WDS) &&
175468e8e04eSSam Leffler 	    ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
175568e8e04eSSam Leffler 		/*
175668e8e04eSSam Leffler 		 * Monitor and wds modes can switch directly.
175768e8e04eSSam Leffler 		 */
175868e8e04eSSam Leffler 		ic->ic_curchan = ic->ic_des_chan;
175968e8e04eSSam Leffler 		if (ic->ic_state == IEEE80211_S_RUN)
176068e8e04eSSam Leffler 			ic->ic_set_channel(ic);
176168e8e04eSSam Leffler 	} else {
176268e8e04eSSam Leffler 		/*
176368e8e04eSSam Leffler 		 * Need to go through the state machine in case we
176468e8e04eSSam Leffler 		 * need to reassociate or the like.  The state machine
176568e8e04eSSam Leffler 		 * will pickup the desired channel and avoid scanning.
176668e8e04eSSam Leffler 		 */
176768e8e04eSSam Leffler 		if (IS_UP_AUTO(ic))
176868e8e04eSSam Leffler 			error = ieee80211_init(ic, RESCAN);
176968e8e04eSSam Leffler 		else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
177068e8e04eSSam Leffler 			/*
177168e8e04eSSam Leffler 			 * When not up+running and a real channel has
177268e8e04eSSam Leffler 			 * been specified fix the current channel so
177368e8e04eSSam Leffler 			 * there is immediate feedback; e.g. via ifconfig.
177468e8e04eSSam Leffler 			 */
177568e8e04eSSam Leffler 			ic->ic_curchan = ic->ic_des_chan;
177668e8e04eSSam Leffler 		}
177768e8e04eSSam Leffler 	}
177868e8e04eSSam Leffler 	return error;
177968e8e04eSSam Leffler }
178068e8e04eSSam Leffler 
178168e8e04eSSam Leffler /*
178268e8e04eSSam Leffler  * Old api for setting the current channel; this is
178368e8e04eSSam Leffler  * deprecated because channel numbers are ambiguous.
178468e8e04eSSam Leffler  */
178568e8e04eSSam Leffler static int
178668e8e04eSSam Leffler ieee80211_ioctl_setchannel(struct ieee80211com *ic,
178768e8e04eSSam Leffler 	const struct ieee80211req *ireq)
178868e8e04eSSam Leffler {
178968e8e04eSSam Leffler 	struct ieee80211_channel *c;
179068e8e04eSSam Leffler 
179168e8e04eSSam Leffler 	/* XXX 0xffff overflows 16-bit signed */
179268e8e04eSSam Leffler 	if (ireq->i_val == 0 ||
179368e8e04eSSam Leffler 	    ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) {
179468e8e04eSSam Leffler 		c = IEEE80211_CHAN_ANYC;
179568e8e04eSSam Leffler 	} else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX) {
179668e8e04eSSam Leffler 		return EINVAL;
179768e8e04eSSam Leffler 	} else {
179868e8e04eSSam Leffler 		struct ieee80211_channel *c2;
179968e8e04eSSam Leffler 
180068e8e04eSSam Leffler 		c = findchannel(ic, ireq->i_val, ic->ic_des_mode);
180168e8e04eSSam Leffler 		if (c == NULL) {
180268e8e04eSSam Leffler 			c = findchannel(ic, ireq->i_val,
180368e8e04eSSam Leffler 				IEEE80211_MODE_AUTO);
180468e8e04eSSam Leffler 			if (c == NULL)
180568e8e04eSSam Leffler 				return EINVAL;
180668e8e04eSSam Leffler 		}
180768e8e04eSSam Leffler 		/*
180868e8e04eSSam Leffler 		 * Fine tune channel selection based on desired mode:
180968e8e04eSSam Leffler 		 *   if 11b is requested, find the 11b version of any
181068e8e04eSSam Leffler 		 *      11g channel returned,
181168e8e04eSSam Leffler 		 *   if static turbo, find the turbo version of any
181268e8e04eSSam Leffler 		 *	11a channel return,
181368e8e04eSSam Leffler 		 *   if 11na is requested, find the ht version of any
181468e8e04eSSam Leffler 		 *      11a channel returned,
181568e8e04eSSam Leffler 		 *   if 11ng is requested, find the ht version of any
181668e8e04eSSam Leffler 		 *      11g channel returned,
181768e8e04eSSam Leffler 		 *   otherwise we should be ok with what we've got.
181868e8e04eSSam Leffler 		 */
181968e8e04eSSam Leffler 		switch (ic->ic_des_mode) {
182068e8e04eSSam Leffler 		case IEEE80211_MODE_11B:
182168e8e04eSSam Leffler 			if (IEEE80211_IS_CHAN_ANYG(c)) {
182268e8e04eSSam Leffler 				c2 = findchannel(ic, ireq->i_val,
182368e8e04eSSam Leffler 					IEEE80211_MODE_11B);
182468e8e04eSSam Leffler 				/* NB: should not happen, =>'s 11g w/o 11b */
182568e8e04eSSam Leffler 				if (c2 != NULL)
182668e8e04eSSam Leffler 					c = c2;
182768e8e04eSSam Leffler 			}
182868e8e04eSSam Leffler 			break;
182968e8e04eSSam Leffler 		case IEEE80211_MODE_TURBO_A:
183068e8e04eSSam Leffler 			if (IEEE80211_IS_CHAN_A(c)) {
183168e8e04eSSam Leffler 				c2 = findchannel(ic, ireq->i_val,
183268e8e04eSSam Leffler 					IEEE80211_MODE_TURBO_A);
183368e8e04eSSam Leffler 				if (c2 != NULL)
183468e8e04eSSam Leffler 					c = c2;
183568e8e04eSSam Leffler 			}
183668e8e04eSSam Leffler 			break;
183768e8e04eSSam Leffler 		case IEEE80211_MODE_11NA:
183868e8e04eSSam Leffler 			if (IEEE80211_IS_CHAN_A(c)) {
183968e8e04eSSam Leffler 				c2 = findchannel(ic, ireq->i_val,
184068e8e04eSSam Leffler 					IEEE80211_MODE_11NA);
184168e8e04eSSam Leffler 				if (c2 != NULL)
184268e8e04eSSam Leffler 					c = c2;
184368e8e04eSSam Leffler 			}
184468e8e04eSSam Leffler 			break;
184568e8e04eSSam Leffler 		case IEEE80211_MODE_11NG:
184668e8e04eSSam Leffler 			if (IEEE80211_IS_CHAN_ANYG(c)) {
184768e8e04eSSam Leffler 				c2 = findchannel(ic, ireq->i_val,
184868e8e04eSSam Leffler 					IEEE80211_MODE_11NG);
184968e8e04eSSam Leffler 				if (c2 != NULL)
185068e8e04eSSam Leffler 					c = c2;
185168e8e04eSSam Leffler 			}
185268e8e04eSSam Leffler 			break;
185368e8e04eSSam Leffler 		default:		/* NB: no static turboG */
185468e8e04eSSam Leffler 			break;
185568e8e04eSSam Leffler 		}
185668e8e04eSSam Leffler 	}
185768e8e04eSSam Leffler 	return setcurchan(ic, c);
185868e8e04eSSam Leffler }
185968e8e04eSSam Leffler 
186068e8e04eSSam Leffler /*
186168e8e04eSSam Leffler  * New/current api for setting the current channel; a complete
186268e8e04eSSam Leffler  * channel description is provide so there is no ambiguity in
186368e8e04eSSam Leffler  * identifying the channel.
186468e8e04eSSam Leffler  */
186568e8e04eSSam Leffler static int
186668e8e04eSSam Leffler ieee80211_ioctl_setcurchan(struct ieee80211com *ic,
186768e8e04eSSam Leffler 	const struct ieee80211req *ireq)
186868e8e04eSSam Leffler {
186968e8e04eSSam Leffler 	struct ieee80211_channel chan, *c;
187068e8e04eSSam Leffler 	int error;
187168e8e04eSSam Leffler 
187268e8e04eSSam Leffler 	if (ireq->i_len != sizeof(chan))
187368e8e04eSSam Leffler 		return EINVAL;
187468e8e04eSSam Leffler 	error = copyin(ireq->i_data, &chan, sizeof(chan));
187568e8e04eSSam Leffler 	if (error != 0)
187668e8e04eSSam Leffler 		return error;
187768e8e04eSSam Leffler 	/* XXX 0xffff overflows 16-bit signed */
187868e8e04eSSam Leffler 	if (chan.ic_freq == 0 || chan.ic_freq == IEEE80211_CHAN_ANY) {
187968e8e04eSSam Leffler 		c = IEEE80211_CHAN_ANYC;
188068e8e04eSSam Leffler 	} else {
188168e8e04eSSam Leffler 		c = ieee80211_find_channel(ic, chan.ic_freq, chan.ic_flags);
188268e8e04eSSam Leffler 		if (c == NULL)
188368e8e04eSSam Leffler 			return EINVAL;
188468e8e04eSSam Leffler 	}
188568e8e04eSSam Leffler 	return setcurchan(ic, c);
188668e8e04eSSam Leffler }
188768e8e04eSSam Leffler 
188868e8e04eSSam Leffler static int
18898a1b9b6aSSam Leffler ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
18908a1b9b6aSSam Leffler {
189168e8e04eSSam Leffler 	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
18928a1b9b6aSSam Leffler 	struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
18938a1b9b6aSSam Leffler 	int error;
18948a1b9b6aSSam Leffler 	const struct ieee80211_authenticator *auth;
189568e8e04eSSam Leffler 	uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
18968a1b9b6aSSam Leffler 	char tmpssid[IEEE80211_NWID_LEN];
189768e8e04eSSam Leffler 	uint8_t tmpbssid[IEEE80211_ADDR_LEN];
18988a1b9b6aSSam Leffler 	struct ieee80211_key *k;
18998a1b9b6aSSam Leffler 	int j, caps;
19008a1b9b6aSSam Leffler 	u_int kid;
19018a1b9b6aSSam Leffler 
19028a1b9b6aSSam Leffler 	error = 0;
19031a1e1d21SSam Leffler 	switch (ireq->i_type) {
19041a1e1d21SSam Leffler 	case IEEE80211_IOC_SSID:
19051a1e1d21SSam Leffler 		if (ireq->i_val != 0 ||
19068a1b9b6aSSam Leffler 		    ireq->i_len > IEEE80211_NWID_LEN)
19078a1b9b6aSSam Leffler 			return EINVAL;
19081a1e1d21SSam Leffler 		error = copyin(ireq->i_data, tmpssid, ireq->i_len);
19091a1e1d21SSam Leffler 		if (error)
19101a1e1d21SSam Leffler 			break;
191168e8e04eSSam Leffler 		memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
191268e8e04eSSam Leffler 		ic->ic_des_ssid[0].len = ireq->i_len;
191368e8e04eSSam Leffler 		memcpy(ic->ic_des_ssid[0].ssid, tmpssid, ireq->i_len);
191468e8e04eSSam Leffler 		ic->ic_des_nssid = (ireq->i_len > 0);
191568e8e04eSSam Leffler 		if (IS_UP_AUTO(ic))
191668e8e04eSSam Leffler 			error = ieee80211_init(ic, RESCAN);
19171a1e1d21SSam Leffler 		break;
19181a1e1d21SSam Leffler 	case IEEE80211_IOC_WEP:
19198a1b9b6aSSam Leffler 		switch (ireq->i_val) {
19208a1b9b6aSSam Leffler 		case IEEE80211_WEP_OFF:
19218a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
19228a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
19238a1b9b6aSSam Leffler 			break;
19248a1b9b6aSSam Leffler 		case IEEE80211_WEP_ON:
19258a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_PRIVACY;
19268a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_DROPUNENC;
19278a1b9b6aSSam Leffler 			break;
19288a1b9b6aSSam Leffler 		case IEEE80211_WEP_MIXED:
19298a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_PRIVACY;
19308a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
19318a1b9b6aSSam Leffler 			break;
19321a1e1d21SSam Leffler 		}
193368e8e04eSSam Leffler 		if (IS_UP_AUTO(ic))
193468e8e04eSSam Leffler 			error = ieee80211_init(ic, RESCAN);
19351a1e1d21SSam Leffler 		break;
19361a1e1d21SSam Leffler 	case IEEE80211_IOC_WEPKEY:
19371a1e1d21SSam Leffler 		kid = (u_int) ireq->i_val;
19388a1b9b6aSSam Leffler 		if (kid >= IEEE80211_WEP_NKID)
19398a1b9b6aSSam Leffler 			return EINVAL;
19408a1b9b6aSSam Leffler 		k = &ic->ic_nw_keys[kid];
19418a1b9b6aSSam Leffler 		if (ireq->i_len == 0) {
19428a1b9b6aSSam Leffler 			/* zero-len =>'s delete any existing key */
19438a1b9b6aSSam Leffler 			(void) ieee80211_crypto_delkey(ic, k);
19441a1e1d21SSam Leffler 			break;
19451a1e1d21SSam Leffler 		}
19468a1b9b6aSSam Leffler 		if (ireq->i_len > sizeof(tmpkey))
19478a1b9b6aSSam Leffler 			return EINVAL;
19481a1e1d21SSam Leffler 		memset(tmpkey, 0, sizeof(tmpkey));
19491a1e1d21SSam Leffler 		error = copyin(ireq->i_data, tmpkey, ireq->i_len);
19501a1e1d21SSam Leffler 		if (error)
19511a1e1d21SSam Leffler 			break;
19528a1b9b6aSSam Leffler 		ieee80211_key_update_begin(ic);
1953dd70e17bSSam Leffler 		k->wk_keyix = kid;	/* NB: force fixed key id */
1954dd70e17bSSam Leffler 		if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP,
1955dd70e17bSSam Leffler 		    IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) {
19568a1b9b6aSSam Leffler 			k->wk_keylen = ireq->i_len;
19578a1b9b6aSSam Leffler 			memcpy(k->wk_key, tmpkey, sizeof(tmpkey));
19588a1b9b6aSSam Leffler 			if  (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr))
19598a1b9b6aSSam Leffler 				error = EINVAL;
19608a1b9b6aSSam Leffler 		} else
19618a1b9b6aSSam Leffler 			error = EINVAL;
19628a1b9b6aSSam Leffler 		ieee80211_key_update_end(ic);
19631a1e1d21SSam Leffler 		break;
19641a1e1d21SSam Leffler 	case IEEE80211_IOC_WEPTXKEY:
19651a1e1d21SSam Leffler 		kid = (u_int) ireq->i_val;
1966380c5fa9SSam Leffler 		if (kid >= IEEE80211_WEP_NKID &&
196768e8e04eSSam Leffler 		    (uint16_t) kid != IEEE80211_KEYIX_NONE)
19688a1b9b6aSSam Leffler 			return EINVAL;
19698a1b9b6aSSam Leffler 		ic->ic_def_txkey = kid;
19708a1b9b6aSSam Leffler 		break;
19718a1b9b6aSSam Leffler 	case IEEE80211_IOC_AUTHMODE:
19728a1b9b6aSSam Leffler 		switch (ireq->i_val) {
19738a1b9b6aSSam Leffler 		case IEEE80211_AUTH_WPA:
19748a1b9b6aSSam Leffler 		case IEEE80211_AUTH_8021X:	/* 802.1x */
19758a1b9b6aSSam Leffler 		case IEEE80211_AUTH_OPEN:	/* open */
19768a1b9b6aSSam Leffler 		case IEEE80211_AUTH_SHARED:	/* shared-key */
19778a1b9b6aSSam Leffler 		case IEEE80211_AUTH_AUTO:	/* auto */
19788a1b9b6aSSam Leffler 			auth = ieee80211_authenticator_get(ireq->i_val);
19798a1b9b6aSSam Leffler 			if (auth == NULL)
19808a1b9b6aSSam Leffler 				return EINVAL;
19818a1b9b6aSSam Leffler 			break;
19828a1b9b6aSSam Leffler 		default:
19838a1b9b6aSSam Leffler 			return EINVAL;
19848a1b9b6aSSam Leffler 		}
19858a1b9b6aSSam Leffler 		switch (ireq->i_val) {
19868a1b9b6aSSam Leffler 		case IEEE80211_AUTH_WPA:	/* WPA w/ 802.1x */
19878a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_PRIVACY;
19888a1b9b6aSSam Leffler 			ireq->i_val = IEEE80211_AUTH_8021X;
19898a1b9b6aSSam Leffler 			break;
19908a1b9b6aSSam Leffler 		case IEEE80211_AUTH_OPEN:	/* open */
19918a1b9b6aSSam Leffler 			ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
19928a1b9b6aSSam Leffler 			break;
19938a1b9b6aSSam Leffler 		case IEEE80211_AUTH_SHARED:	/* shared-key */
19948a1b9b6aSSam Leffler 		case IEEE80211_AUTH_8021X:	/* 802.1x */
19958a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_WPA;
19968a1b9b6aSSam Leffler 			/* both require a key so mark the PRIVACY capability */
19978a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_PRIVACY;
19988a1b9b6aSSam Leffler 			break;
19998a1b9b6aSSam Leffler 		case IEEE80211_AUTH_AUTO:	/* auto */
20008a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_WPA;
20018a1b9b6aSSam Leffler 			/* XXX PRIVACY handling? */
20028a1b9b6aSSam Leffler 			/* XXX what's the right way to do this? */
20031a1e1d21SSam Leffler 			break;
20041a1e1d21SSam Leffler 		}
20058a1b9b6aSSam Leffler 		/* NB: authenticator attach/detach happens on state change */
20068a1b9b6aSSam Leffler 		ic->ic_bss->ni_authmode = ireq->i_val;
20078a1b9b6aSSam Leffler 		/* XXX mixed/mode/usage? */
20088a1b9b6aSSam Leffler 		ic->ic_auth = auth;
200968e8e04eSSam Leffler 		if (IS_UP_AUTO(ic))
201068e8e04eSSam Leffler 			error = ieee80211_init(ic, RESCAN);
20111a1e1d21SSam Leffler 		break;
20121a1e1d21SSam Leffler 	case IEEE80211_IOC_CHANNEL:
201368e8e04eSSam Leffler 		error = ieee80211_ioctl_setchannel(ic, ireq);
20141a1e1d21SSam Leffler 		break;
20151a1e1d21SSam Leffler 	case IEEE80211_IOC_POWERSAVE:
20161a1e1d21SSam Leffler 		switch (ireq->i_val) {
20171a1e1d21SSam Leffler 		case IEEE80211_POWERSAVE_OFF:
20181a1e1d21SSam Leffler 			if (ic->ic_flags & IEEE80211_F_PMGTON) {
20191a1e1d21SSam Leffler 				ic->ic_flags &= ~IEEE80211_F_PMGTON;
20201a1e1d21SSam Leffler 				error = ENETRESET;
20211a1e1d21SSam Leffler 			}
20221a1e1d21SSam Leffler 			break;
20231a1e1d21SSam Leffler 		case IEEE80211_POWERSAVE_ON:
20241a1e1d21SSam Leffler 			if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
20251a1e1d21SSam Leffler 				error = EINVAL;
20261a1e1d21SSam Leffler 			else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
20271a1e1d21SSam Leffler 				ic->ic_flags |= IEEE80211_F_PMGTON;
20281a1e1d21SSam Leffler 				error = ENETRESET;
20291a1e1d21SSam Leffler 			}
20301a1e1d21SSam Leffler 			break;
20311a1e1d21SSam Leffler 		default:
20321a1e1d21SSam Leffler 			error = EINVAL;
20331a1e1d21SSam Leffler 			break;
20341a1e1d21SSam Leffler 		}
20350eda166bSSam Leffler 		if (error == ENETRESET) {
20360eda166bSSam Leffler 			/*
20370eda166bSSam Leffler 			 * Switching in+out of power save mode
20380eda166bSSam Leffler 			 * should not require a state change.
20390eda166bSSam Leffler 			 */
20400eda166bSSam Leffler 			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
20410eda166bSSam Leffler 		}
20421a1e1d21SSam Leffler 		break;
20431a1e1d21SSam Leffler 	case IEEE80211_IOC_POWERSAVESLEEP:
20448a1b9b6aSSam Leffler 		if (ireq->i_val < 0)
20458a1b9b6aSSam Leffler 			return EINVAL;
20461a1e1d21SSam Leffler 		ic->ic_lintval = ireq->i_val;
20478a1b9b6aSSam Leffler 		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
20481a1e1d21SSam Leffler 		break;
204913604e6bSSam Leffler 	case IEEE80211_IOC_RTSTHRESHOLD:
205070231e3dSSam Leffler 		if (!(IEEE80211_RTS_MIN <= ireq->i_val &&
205170231e3dSSam Leffler 		      ireq->i_val <= IEEE80211_RTS_MAX))
20528a1b9b6aSSam Leffler 			return EINVAL;
20531a1e1d21SSam Leffler 		ic->ic_rtsthreshold = ireq->i_val;
20548a1b9b6aSSam Leffler 		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
20551a1e1d21SSam Leffler 		break;
20562e79ca97SSam Leffler 	case IEEE80211_IOC_PROTMODE:
20578a1b9b6aSSam Leffler 		if (ireq->i_val > IEEE80211_PROT_RTSCTS)
20588a1b9b6aSSam Leffler 			return EINVAL;
20592e79ca97SSam Leffler 		ic->ic_protmode = ireq->i_val;
20602e79ca97SSam Leffler 		/* NB: if not operating in 11g this can wait */
206168e8e04eSSam Leffler 		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
206268e8e04eSSam Leffler 		    IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
20638a1b9b6aSSam Leffler 			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
20642e79ca97SSam Leffler 		break;
20652e79ca97SSam Leffler 	case IEEE80211_IOC_TXPOWER:
20668a1b9b6aSSam Leffler 		if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
20678a1b9b6aSSam Leffler 			return EINVAL;
206868e8e04eSSam Leffler 		if (!(IEEE80211_TXPOWER_MIN <= ireq->i_val &&
206968e8e04eSSam Leffler 		      ireq->i_val <= IEEE80211_TXPOWER_MAX))
20708a1b9b6aSSam Leffler 			return EINVAL;
20718a1b9b6aSSam Leffler 		ic->ic_txpowlimit = ireq->i_val;
20728a1b9b6aSSam Leffler 		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
20738a1b9b6aSSam Leffler 		break;
20748a1b9b6aSSam Leffler 	case IEEE80211_IOC_ROAMING:
20758a1b9b6aSSam Leffler 		if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val &&
20768a1b9b6aSSam Leffler 		    ireq->i_val <= IEEE80211_ROAMING_MANUAL))
20778a1b9b6aSSam Leffler 			return EINVAL;
20788a1b9b6aSSam Leffler 		ic->ic_roaming = ireq->i_val;
20798a1b9b6aSSam Leffler 		/* XXXX reset? */
20808a1b9b6aSSam Leffler 		break;
20818a1b9b6aSSam Leffler 	case IEEE80211_IOC_PRIVACY:
20828a1b9b6aSSam Leffler 		if (ireq->i_val) {
20838a1b9b6aSSam Leffler 			/* XXX check for key state? */
20848a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_PRIVACY;
20858a1b9b6aSSam Leffler 		} else
20868a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
20878a1b9b6aSSam Leffler 		break;
20888a1b9b6aSSam Leffler 	case IEEE80211_IOC_DROPUNENCRYPTED:
20898a1b9b6aSSam Leffler 		if (ireq->i_val)
20908a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_DROPUNENC;
20918a1b9b6aSSam Leffler 		else
20928a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
20938a1b9b6aSSam Leffler 		break;
20948a1b9b6aSSam Leffler 	case IEEE80211_IOC_WPAKEY:
20958a1b9b6aSSam Leffler 		error = ieee80211_ioctl_setkey(ic, ireq);
20968a1b9b6aSSam Leffler 		break;
20978a1b9b6aSSam Leffler 	case IEEE80211_IOC_DELKEY:
20988a1b9b6aSSam Leffler 		error = ieee80211_ioctl_delkey(ic, ireq);
20998a1b9b6aSSam Leffler 		break;
21008a1b9b6aSSam Leffler 	case IEEE80211_IOC_MLME:
21018a1b9b6aSSam Leffler 		error = ieee80211_ioctl_setmlme(ic, ireq);
21028a1b9b6aSSam Leffler 		break;
21038a1b9b6aSSam Leffler 	case IEEE80211_IOC_OPTIE:
21048a1b9b6aSSam Leffler 		error = ieee80211_ioctl_setoptie(ic, ireq);
21058a1b9b6aSSam Leffler 		break;
21068a1b9b6aSSam Leffler 	case IEEE80211_IOC_COUNTERMEASURES:
21078a1b9b6aSSam Leffler 		if (ireq->i_val) {
21088a1b9b6aSSam Leffler 			if ((ic->ic_flags & IEEE80211_F_WPA) == 0)
21098a1b9b6aSSam Leffler 				return EINVAL;
21108a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_COUNTERM;
21118a1b9b6aSSam Leffler 		} else
21128a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_COUNTERM;
21138a1b9b6aSSam Leffler 		break;
21148a1b9b6aSSam Leffler 	case IEEE80211_IOC_WPA:
21158a1b9b6aSSam Leffler 		if (ireq->i_val > 3)
21168a1b9b6aSSam Leffler 			return EINVAL;
21178a1b9b6aSSam Leffler 		/* XXX verify ciphers available */
21188a1b9b6aSSam Leffler 		ic->ic_flags &= ~IEEE80211_F_WPA;
21198a1b9b6aSSam Leffler 		switch (ireq->i_val) {
21208a1b9b6aSSam Leffler 		case 1:
21218a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_WPA1;
21228a1b9b6aSSam Leffler 			break;
21238a1b9b6aSSam Leffler 		case 2:
21248a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_WPA2;
21258a1b9b6aSSam Leffler 			break;
21268a1b9b6aSSam Leffler 		case 3:
21278a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
21282e79ca97SSam Leffler 			break;
21292e79ca97SSam Leffler 		}
213068e8e04eSSam Leffler 		error = ENETRESET;
21318a1b9b6aSSam Leffler 		break;
21328a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME:
21338a1b9b6aSSam Leffler 		if (ireq->i_val) {
21348a1b9b6aSSam Leffler 			if ((ic->ic_caps & IEEE80211_C_WME) == 0)
21358a1b9b6aSSam Leffler 				return EINVAL;
21368a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_WME;
21378a1b9b6aSSam Leffler 		} else
21388a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_WME;
213968e8e04eSSam Leffler 		if (IS_UP_AUTO(ic))
214068e8e04eSSam Leffler 			error = ieee80211_init(ic, 0);
21418a1b9b6aSSam Leffler 		break;
21428a1b9b6aSSam Leffler 	case IEEE80211_IOC_HIDESSID:
21438a1b9b6aSSam Leffler 		if (ireq->i_val)
21448a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_HIDESSID;
21458a1b9b6aSSam Leffler 		else
21468a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_HIDESSID;
21472e79ca97SSam Leffler 		error = ENETRESET;
21482e79ca97SSam Leffler 		break;
21498a1b9b6aSSam Leffler 	case IEEE80211_IOC_APBRIDGE:
21508a1b9b6aSSam Leffler 		if (ireq->i_val == 0)
21518a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_NOBRIDGE;
21528a1b9b6aSSam Leffler 		else
21538a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_NOBRIDGE;
21548a1b9b6aSSam Leffler 		break;
21558a1b9b6aSSam Leffler 	case IEEE80211_IOC_MCASTCIPHER:
21568a1b9b6aSSam Leffler 		if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 &&
21578a1b9b6aSSam Leffler 		    !ieee80211_crypto_available(ireq->i_val))
21588a1b9b6aSSam Leffler 			return EINVAL;
21598a1b9b6aSSam Leffler 		rsn->rsn_mcastcipher = ireq->i_val;
21608a1b9b6aSSam Leffler 		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
21618a1b9b6aSSam Leffler 		break;
21628a1b9b6aSSam Leffler 	case IEEE80211_IOC_MCASTKEYLEN:
21638a1b9b6aSSam Leffler 		if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
21648a1b9b6aSSam Leffler 			return EINVAL;
21658a1b9b6aSSam Leffler 		/* XXX no way to verify driver capability */
21668a1b9b6aSSam Leffler 		rsn->rsn_mcastkeylen = ireq->i_val;
21678a1b9b6aSSam Leffler 		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
21688a1b9b6aSSam Leffler 		break;
21698a1b9b6aSSam Leffler 	case IEEE80211_IOC_UCASTCIPHERS:
21708a1b9b6aSSam Leffler 		/*
21718a1b9b6aSSam Leffler 		 * Convert user-specified cipher set to the set
21728a1b9b6aSSam Leffler 		 * we can support (via hardware or software).
21738a1b9b6aSSam Leffler 		 * NB: this logic intentionally ignores unknown and
21748a1b9b6aSSam Leffler 		 * unsupported ciphers so folks can specify 0xff or
21758a1b9b6aSSam Leffler 		 * similar and get all available ciphers.
21768a1b9b6aSSam Leffler 		 */
21778a1b9b6aSSam Leffler 		caps = 0;
21788a1b9b6aSSam Leffler 		for (j = 1; j < 32; j++)	/* NB: skip WEP */
21798a1b9b6aSSam Leffler 			if ((ireq->i_val & (1<<j)) &&
21808a1b9b6aSSam Leffler 			    ((ic->ic_caps & cipher2cap(j)) ||
21818a1b9b6aSSam Leffler 			     ieee80211_crypto_available(j)))
21828a1b9b6aSSam Leffler 				caps |= 1<<j;
21838a1b9b6aSSam Leffler 		if (caps == 0)			/* nothing available */
21848a1b9b6aSSam Leffler 			return EINVAL;
21858a1b9b6aSSam Leffler 		/* XXX verify ciphers ok for unicast use? */
21868a1b9b6aSSam Leffler 		/* XXX disallow if running as it'll have no effect */
21878a1b9b6aSSam Leffler 		rsn->rsn_ucastcipherset = caps;
21888a1b9b6aSSam Leffler 		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
21898a1b9b6aSSam Leffler 		break;
21908a1b9b6aSSam Leffler 	case IEEE80211_IOC_UCASTCIPHER:
21918a1b9b6aSSam Leffler 		if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0)
21928a1b9b6aSSam Leffler 			return EINVAL;
21938a1b9b6aSSam Leffler 		rsn->rsn_ucastcipher = ireq->i_val;
21948a1b9b6aSSam Leffler 		break;
21958a1b9b6aSSam Leffler 	case IEEE80211_IOC_UCASTKEYLEN:
21968a1b9b6aSSam Leffler 		if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
21978a1b9b6aSSam Leffler 			return EINVAL;
21988a1b9b6aSSam Leffler 		/* XXX no way to verify driver capability */
21998a1b9b6aSSam Leffler 		rsn->rsn_ucastkeylen = ireq->i_val;
22008a1b9b6aSSam Leffler 		break;
22018a1b9b6aSSam Leffler 	case IEEE80211_IOC_DRIVER_CAPS:
22028a1b9b6aSSam Leffler 		/* NB: for testing */
220368e8e04eSSam Leffler 		ic->ic_caps = (((uint16_t) ireq->i_val) << 16) |
220468e8e04eSSam Leffler 			       ((uint16_t) ireq->i_len);
22058a1b9b6aSSam Leffler 		break;
22068a1b9b6aSSam Leffler 	case IEEE80211_IOC_KEYMGTALGS:
22078a1b9b6aSSam Leffler 		/* XXX check */
22088a1b9b6aSSam Leffler 		rsn->rsn_keymgmtset = ireq->i_val;
22098a1b9b6aSSam Leffler 		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
22108a1b9b6aSSam Leffler 		break;
22118a1b9b6aSSam Leffler 	case IEEE80211_IOC_RSNCAPS:
22128a1b9b6aSSam Leffler 		/* XXX check */
22138a1b9b6aSSam Leffler 		rsn->rsn_caps = ireq->i_val;
22148a1b9b6aSSam Leffler 		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
22158a1b9b6aSSam Leffler 		break;
22168a1b9b6aSSam Leffler 	case IEEE80211_IOC_BSSID:
22178a1b9b6aSSam Leffler 		if (ireq->i_len != sizeof(tmpbssid))
22188a1b9b6aSSam Leffler 			return EINVAL;
22198a1b9b6aSSam Leffler 		error = copyin(ireq->i_data, tmpbssid, ireq->i_len);
22208a1b9b6aSSam Leffler 		if (error)
22218a1b9b6aSSam Leffler 			break;
22228a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid);
22238a1b9b6aSSam Leffler 		if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid))
22248a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_DESBSSID;
22258a1b9b6aSSam Leffler 		else
22268a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_DESBSSID;
222768e8e04eSSam Leffler 		if (IS_UP_AUTO(ic))
222868e8e04eSSam Leffler 			error = ieee80211_init(ic, RESCAN);
22298a1b9b6aSSam Leffler 		break;
22308a1b9b6aSSam Leffler 	case IEEE80211_IOC_CHANLIST:
22318a1b9b6aSSam Leffler 		error = ieee80211_ioctl_setchanlist(ic, ireq);
22328a1b9b6aSSam Leffler 		break;
22338a1b9b6aSSam Leffler 	case IEEE80211_IOC_SCAN_REQ:
223468e8e04eSSam Leffler 		if (!IS_UP(ic))
223568e8e04eSSam Leffler 			return EINVAL;
223668e8e04eSSam Leffler 		(void) ieee80211_start_scan(ic,
223768e8e04eSSam Leffler 			IEEE80211_SCAN_ACTIVE |
223868e8e04eSSam Leffler 			IEEE80211_SCAN_NOPICK |
223968e8e04eSSam Leffler 			IEEE80211_SCAN_ONCE, IEEE80211_SCAN_FOREVER,
224068e8e04eSSam Leffler 			/* XXX use ioctl params */
224168e8e04eSSam Leffler 			ic->ic_des_nssid, ic->ic_des_ssid);
22428a1b9b6aSSam Leffler 		break;
22438a1b9b6aSSam Leffler 	case IEEE80211_IOC_ADDMAC:
22448a1b9b6aSSam Leffler 	case IEEE80211_IOC_DELMAC:
22458a1b9b6aSSam Leffler 		error = ieee80211_ioctl_macmac(ic, ireq);
22468a1b9b6aSSam Leffler 		break;
22478a1b9b6aSSam Leffler 	case IEEE80211_IOC_MACCMD:
2248188757f5SSam Leffler 		error = ieee80211_ioctl_setmaccmd(ic, ireq);
22498a1b9b6aSSam Leffler 		break;
225042568791SSam Leffler 	case IEEE80211_IOC_STA_STATS:
225142568791SSam Leffler 		error = ieee80211_ioctl_setstastats(ic, ireq);
225242568791SSam Leffler 		break;
22538a1b9b6aSSam Leffler 	case IEEE80211_IOC_STA_TXPOW:
22548a1b9b6aSSam Leffler 		error = ieee80211_ioctl_setstatxpow(ic, ireq);
22558a1b9b6aSSam Leffler 		break;
22568a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
22578a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
22588a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
22598a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
22608a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
22618a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (bss only) */
22628a1b9b6aSSam Leffler 		error = ieee80211_ioctl_setwmeparam(ic, ireq);
22638a1b9b6aSSam Leffler 		break;
22648a1b9b6aSSam Leffler 	case IEEE80211_IOC_DTIM_PERIOD:
22658a1b9b6aSSam Leffler 		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
22668a1b9b6aSSam Leffler 		    ic->ic_opmode != IEEE80211_M_IBSS)
22678a1b9b6aSSam Leffler 			return EINVAL;
22688a1b9b6aSSam Leffler 		if (IEEE80211_DTIM_MIN <= ireq->i_val &&
22698a1b9b6aSSam Leffler 		    ireq->i_val <= IEEE80211_DTIM_MAX) {
22708a1b9b6aSSam Leffler 			ic->ic_dtim_period = ireq->i_val;
22718a1b9b6aSSam Leffler 			error = ENETRESET;		/* requires restart */
22728a1b9b6aSSam Leffler 		} else
22738a1b9b6aSSam Leffler 			error = EINVAL;
22748a1b9b6aSSam Leffler 		break;
22758a1b9b6aSSam Leffler 	case IEEE80211_IOC_BEACON_INTERVAL:
22768a1b9b6aSSam Leffler 		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
22778a1b9b6aSSam Leffler 		    ic->ic_opmode != IEEE80211_M_IBSS)
22788a1b9b6aSSam Leffler 			return EINVAL;
22798a1b9b6aSSam Leffler 		if (IEEE80211_BINTVAL_MIN <= ireq->i_val &&
22808a1b9b6aSSam Leffler 		    ireq->i_val <= IEEE80211_BINTVAL_MAX) {
2281d365f9c7SSam Leffler 			ic->ic_bintval = ireq->i_val;
22828a1b9b6aSSam Leffler 			error = ENETRESET;		/* requires restart */
22838a1b9b6aSSam Leffler 		} else
22848a1b9b6aSSam Leffler 			error = EINVAL;
22858a1b9b6aSSam Leffler 		break;
2286c4f040c3SSam Leffler 	case IEEE80211_IOC_PUREG:
2287c4f040c3SSam Leffler 		if (ireq->i_val)
2288c4f040c3SSam Leffler 			ic->ic_flags |= IEEE80211_F_PUREG;
2289c4f040c3SSam Leffler 		else
2290c4f040c3SSam Leffler 			ic->ic_flags &= ~IEEE80211_F_PUREG;
2291c4f040c3SSam Leffler 		/* NB: reset only if we're operating on an 11g channel */
229268e8e04eSSam Leffler 		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
229368e8e04eSSam Leffler 		    IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
2294c4f040c3SSam Leffler 			error = ENETRESET;
2295c4f040c3SSam Leffler 		break;
229668e8e04eSSam Leffler 	case IEEE80211_IOC_FF:
229768e8e04eSSam Leffler 		if (ireq->i_val) {
229868e8e04eSSam Leffler 			if ((ic->ic_caps & IEEE80211_C_FF) == 0)
229968e8e04eSSam Leffler 				return EINVAL;
230068e8e04eSSam Leffler 			ic->ic_flags |= IEEE80211_F_FF;
230168e8e04eSSam Leffler 		} else
230268e8e04eSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_FF;
230368e8e04eSSam Leffler 		error = ENETRESET;
230468e8e04eSSam Leffler 		break;
230568e8e04eSSam Leffler 	case IEEE80211_IOC_TURBOP:
230668e8e04eSSam Leffler 		if (ireq->i_val) {
230768e8e04eSSam Leffler 			if ((ic->ic_caps & IEEE80211_C_TURBOP) == 0)
230868e8e04eSSam Leffler 				return EINVAL;
230968e8e04eSSam Leffler 			ic->ic_flags |= IEEE80211_F_TURBOP;
231068e8e04eSSam Leffler 		} else
231168e8e04eSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_TURBOP;
231268e8e04eSSam Leffler 		error = ENETRESET;
231368e8e04eSSam Leffler 		break;
231468e8e04eSSam Leffler 	case IEEE80211_IOC_BGSCAN:
231568e8e04eSSam Leffler 		if (ireq->i_val) {
231668e8e04eSSam Leffler 			if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0)
231768e8e04eSSam Leffler 				return EINVAL;
231868e8e04eSSam Leffler 			ic->ic_flags |= IEEE80211_F_BGSCAN;
231968e8e04eSSam Leffler 		} else
232068e8e04eSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_BGSCAN;
232168e8e04eSSam Leffler 		break;
232268e8e04eSSam Leffler 	case IEEE80211_IOC_BGSCAN_IDLE:
232368e8e04eSSam Leffler 		if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN)
232468e8e04eSSam Leffler 			ic->ic_bgscanidle = ireq->i_val*hz/1000;
232568e8e04eSSam Leffler 		else
232668e8e04eSSam Leffler 			error = EINVAL;
232768e8e04eSSam Leffler 		break;
232868e8e04eSSam Leffler 	case IEEE80211_IOC_BGSCAN_INTERVAL:
232968e8e04eSSam Leffler 		if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN)
233068e8e04eSSam Leffler 			ic->ic_bgscanintvl = ireq->i_val*hz;
233168e8e04eSSam Leffler 		else
233268e8e04eSSam Leffler 			error = EINVAL;
233368e8e04eSSam Leffler 		break;
233468e8e04eSSam Leffler 	case IEEE80211_IOC_SCANVALID:
233568e8e04eSSam Leffler 		if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN)
233668e8e04eSSam Leffler 			ic->ic_scanvalid = ireq->i_val*hz;
233768e8e04eSSam Leffler 		else
233868e8e04eSSam Leffler 			error = EINVAL;
233968e8e04eSSam Leffler 		break;
234068e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RSSI_11A:
234168e8e04eSSam Leffler 		ic->ic_roam.rssi11a = ireq->i_val;
234268e8e04eSSam Leffler 		break;
234368e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RSSI_11B:
234468e8e04eSSam Leffler 		ic->ic_roam.rssi11bOnly = ireq->i_val;
234568e8e04eSSam Leffler 		break;
234668e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RSSI_11G:
234768e8e04eSSam Leffler 		ic->ic_roam.rssi11b = ireq->i_val;
234868e8e04eSSam Leffler 		break;
234968e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RATE_11A:
235068e8e04eSSam Leffler 		ic->ic_roam.rate11a = ireq->i_val & IEEE80211_RATE_VAL;
235168e8e04eSSam Leffler 		break;
235268e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RATE_11B:
235368e8e04eSSam Leffler 		ic->ic_roam.rate11bOnly = ireq->i_val & IEEE80211_RATE_VAL;
235468e8e04eSSam Leffler 		break;
235568e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RATE_11G:
235668e8e04eSSam Leffler 		ic->ic_roam.rate11b = ireq->i_val & IEEE80211_RATE_VAL;
235768e8e04eSSam Leffler 		break;
235864353cb0SSam Leffler 	case IEEE80211_IOC_MCAST_RATE:
235964353cb0SSam Leffler 		ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL;
236064353cb0SSam Leffler 		break;
236170231e3dSSam Leffler 	case IEEE80211_IOC_FRAGTHRESHOLD:
236270231e3dSSam Leffler 		if ((ic->ic_caps & IEEE80211_C_TXFRAG) == 0 &&
236370231e3dSSam Leffler 		    ireq->i_val != IEEE80211_FRAG_MAX)
236470231e3dSSam Leffler 			return EINVAL;
236570231e3dSSam Leffler 		if (!(IEEE80211_FRAG_MIN <= ireq->i_val &&
236670231e3dSSam Leffler 		      ireq->i_val <= IEEE80211_FRAG_MAX))
236770231e3dSSam Leffler 			return EINVAL;
236870231e3dSSam Leffler 		ic->ic_fragthreshold = ireq->i_val;
236970231e3dSSam Leffler 		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
237070231e3dSSam Leffler 		break;
2371c27e4e31SSam Leffler 	case IEEE80211_IOC_BURST:
2372c27e4e31SSam Leffler 		if (ireq->i_val) {
2373c27e4e31SSam Leffler 			if ((ic->ic_caps & IEEE80211_C_BURST) == 0)
2374c27e4e31SSam Leffler 				return EINVAL;
2375c27e4e31SSam Leffler 			ic->ic_flags |= IEEE80211_F_BURST;
2376c27e4e31SSam Leffler 		} else
2377c27e4e31SSam Leffler 			ic->ic_flags &= ~IEEE80211_F_BURST;
2378c27e4e31SSam Leffler 		error = ENETRESET;		/* XXX maybe not for station? */
2379c27e4e31SSam Leffler 		break;
2380546786c9SSam Leffler 	case IEEE80211_IOC_BMISSTHRESHOLD:
2381546786c9SSam Leffler 		if (!(IEEE80211_HWBMISS_MIN <= ireq->i_val &&
2382546786c9SSam Leffler 		      ireq->i_val <= IEEE80211_HWBMISS_MAX))
2383546786c9SSam Leffler 			return EINVAL;
2384546786c9SSam Leffler 		ic->ic_bmissthreshold = ireq->i_val;
2385546786c9SSam Leffler 		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2386546786c9SSam Leffler 		break;
238768e8e04eSSam Leffler 	case IEEE80211_IOC_CURCHAN:
238868e8e04eSSam Leffler 		error = ieee80211_ioctl_setcurchan(ic, ireq);
238968e8e04eSSam Leffler 		break;
239068e8e04eSSam Leffler 	case IEEE80211_IOC_SHORTGI:
239168e8e04eSSam Leffler 		if (ireq->i_val) {
239268e8e04eSSam Leffler #define	IEEE80211_HTCAP_SHORTGI \
239368e8e04eSSam Leffler 	(IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40)
239468e8e04eSSam Leffler 			if (((ireq->i_val ^ ic->ic_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0)
239568e8e04eSSam Leffler 				return EINVAL;
239668e8e04eSSam Leffler 			if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20)
239768e8e04eSSam Leffler 				ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
239868e8e04eSSam Leffler 			if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40)
239968e8e04eSSam Leffler 				ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
240068e8e04eSSam Leffler #undef IEEE80211_HTCAP_SHORTGI
240168e8e04eSSam Leffler 		} else
240268e8e04eSSam Leffler 			ic->ic_flags_ext &=
240368e8e04eSSam Leffler 			    ~(IEEE80211_FEXT_SHORTGI20 | IEEE80211_FEXT_SHORTGI40);
240468e8e04eSSam Leffler 		/* XXX kick state machine? */
240568e8e04eSSam Leffler 		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
240668e8e04eSSam Leffler 		break;
240768e8e04eSSam Leffler 	case IEEE80211_IOC_AMPDU:
240868e8e04eSSam Leffler 		if (ireq->i_val) {
240968e8e04eSSam Leffler 			if ((ic->ic_htcaps & IEEE80211_HTC_AMPDU) == 0)
241068e8e04eSSam Leffler 				return EINVAL;
241168e8e04eSSam Leffler 			if (ireq->i_val & 1)
241268e8e04eSSam Leffler 				ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
241368e8e04eSSam Leffler 			if (ireq->i_val & 2)
241468e8e04eSSam Leffler 				ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
241568e8e04eSSam Leffler 		} else
241668e8e04eSSam Leffler 			ic->ic_flags_ext &=
241768e8e04eSSam Leffler 			    ~(IEEE80211_FEXT_AMPDU_TX|IEEE80211_FEXT_AMPDU_RX);
241868e8e04eSSam Leffler 		/* NB: reset only if we're operating on an 11n channel */
241968e8e04eSSam Leffler 		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
242068e8e04eSSam Leffler 		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
242168e8e04eSSam Leffler 			error = ENETRESET;
242268e8e04eSSam Leffler 		break;
242368e8e04eSSam Leffler 	case IEEE80211_IOC_AMPDU_LIMIT:
242468e8e04eSSam Leffler 		/* XXX validate */
242568e8e04eSSam Leffler 		ic->ic_ampdu_limit = ireq->i_val;
242668e8e04eSSam Leffler 		break;
242768e8e04eSSam Leffler 	case IEEE80211_IOC_AMPDU_DENSITY:
242868e8e04eSSam Leffler 		/* XXX validate */
242968e8e04eSSam Leffler 		ic->ic_ampdu_density = ireq->i_val;
243068e8e04eSSam Leffler 		break;
243168e8e04eSSam Leffler 	case IEEE80211_IOC_AMSDU:
243268e8e04eSSam Leffler 		if (ireq->i_val) {
243368e8e04eSSam Leffler 			if ((ic->ic_htcaps & IEEE80211_HTC_AMSDU) == 0)
243468e8e04eSSam Leffler 				return EINVAL;
243568e8e04eSSam Leffler 			if (ireq->i_val & 1)
243668e8e04eSSam Leffler 				ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
243768e8e04eSSam Leffler 			if (ireq->i_val & 2)
243868e8e04eSSam Leffler 				ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
243968e8e04eSSam Leffler 		} else
244068e8e04eSSam Leffler 			ic->ic_flags_ext &=
244168e8e04eSSam Leffler 			    ~(IEEE80211_FEXT_AMSDU_TX|IEEE80211_FEXT_AMSDU_RX);
244268e8e04eSSam Leffler 		/* NB: reset only if we're operating on an 11n channel */
244368e8e04eSSam Leffler 		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
244468e8e04eSSam Leffler 		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
244568e8e04eSSam Leffler 			error = ENETRESET;
244668e8e04eSSam Leffler 		break;
244768e8e04eSSam Leffler 	case IEEE80211_IOC_AMSDU_LIMIT:
244868e8e04eSSam Leffler 		/* XXX validate */
244968e8e04eSSam Leffler 		ic->ic_amsdu_limit = ireq->i_val;	/* XXX truncation? */
245068e8e04eSSam Leffler 		break;
245168e8e04eSSam Leffler 	case IEEE80211_IOC_PUREN:
245268e8e04eSSam Leffler 		if (ireq->i_val) {
245368e8e04eSSam Leffler 			if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
245468e8e04eSSam Leffler 				return EINVAL;
245568e8e04eSSam Leffler 			ic->ic_flags_ext |= IEEE80211_FEXT_PUREN;
245668e8e04eSSam Leffler 		} else
245768e8e04eSSam Leffler 			ic->ic_flags_ext &= ~IEEE80211_FEXT_PUREN;
245868e8e04eSSam Leffler 		/* NB: reset only if we're operating on an 11n channel */
245968e8e04eSSam Leffler 		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
246068e8e04eSSam Leffler 		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
246168e8e04eSSam Leffler 			error = ENETRESET;
246268e8e04eSSam Leffler 		break;
246368e8e04eSSam Leffler 	case IEEE80211_IOC_DOTH:
246468e8e04eSSam Leffler 		if (ireq->i_val) {
246568e8e04eSSam Leffler #if 0
246668e8e04eSSam Leffler 			/* XXX no capability */
246768e8e04eSSam Leffler 			if ((ic->ic_caps & IEEE80211_C_DOTH) == 0)
246868e8e04eSSam Leffler 				return EINVAL;
246968e8e04eSSam Leffler #endif
247068e8e04eSSam Leffler 			ic->ic_flags |= IEEE80211_F_DOTH;
247168e8e04eSSam Leffler 		} else
247268e8e04eSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_DOTH;
247368e8e04eSSam Leffler 		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
247468e8e04eSSam Leffler 		break;
247568e8e04eSSam Leffler 	case IEEE80211_IOC_HTCOMPAT:
247668e8e04eSSam Leffler 		if (ireq->i_val) {
247768e8e04eSSam Leffler 			if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
247868e8e04eSSam Leffler 				return EINVAL;
247968e8e04eSSam Leffler 			ic->ic_flags_ext |= IEEE80211_FEXT_HTCOMPAT;
248068e8e04eSSam Leffler 		} else
248168e8e04eSSam Leffler 			ic->ic_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT;
248268e8e04eSSam Leffler 		/* NB: reset only if we're operating on an 11n channel */
248368e8e04eSSam Leffler 		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
248468e8e04eSSam Leffler 		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
248568e8e04eSSam Leffler 			error = ENETRESET;
248668e8e04eSSam Leffler 		break;
2487c066143cSSam Leffler 	case IEEE80211_IOC_INACTIVITY:
2488c066143cSSam Leffler 		if (ireq->i_val)
2489c066143cSSam Leffler 			ic->ic_flags_ext |= IEEE80211_FEXT_INACT;
2490c066143cSSam Leffler 		else
2491c066143cSSam Leffler 			ic->ic_flags_ext &= ~IEEE80211_FEXT_INACT;
2492c066143cSSam Leffler 		break;
24931b6167d2SSam Leffler 	case IEEE80211_IOC_HTPROTMODE:
24941b6167d2SSam Leffler 		if (ireq->i_val > IEEE80211_PROT_RTSCTS)
24951b6167d2SSam Leffler 			return EINVAL;
24961b6167d2SSam Leffler 		ic->ic_htprotmode = ireq->i_val ?
24971b6167d2SSam Leffler 		    IEEE80211_PROT_RTSCTS : IEEE80211_PROT_NONE;
24981b6167d2SSam Leffler 		/* NB: if not operating in 11n this can wait */
24991b6167d2SSam Leffler 		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
25001b6167d2SSam Leffler 		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
25011b6167d2SSam Leffler 			error = ERESTART;
25021b6167d2SSam Leffler 		break;
25031b6167d2SSam Leffler 	case IEEE80211_IOC_HTCONF:
25041b6167d2SSam Leffler 		if (ireq->i_val & 1)
25051b6167d2SSam Leffler 			ic->ic_flags_ext |= IEEE80211_FEXT_HT;
25061b6167d2SSam Leffler 		else
25071b6167d2SSam Leffler 			ic->ic_flags_ext &= ~IEEE80211_FEXT_HT;
25081b6167d2SSam Leffler 		if (ireq->i_val & 2)
25091b6167d2SSam Leffler 			ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
25101b6167d2SSam Leffler 		else
25111b6167d2SSam Leffler 			ic->ic_flags_ext &= ~IEEE80211_FEXT_USEHT40;
25121b6167d2SSam Leffler 		error = ENETRESET;
25131b6167d2SSam Leffler 		break;
25141a1e1d21SSam Leffler 	default:
25151a1e1d21SSam Leffler 		error = EINVAL;
25161a1e1d21SSam Leffler 		break;
25171a1e1d21SSam Leffler 	}
251868e8e04eSSam Leffler 	if (error == ENETRESET)
251968e8e04eSSam Leffler 		error = IS_UP_AUTO(ic) ? ieee80211_init(ic, 0) : 0;
25208a1b9b6aSSam Leffler 	return error;
25218a1b9b6aSSam Leffler }
25228a1b9b6aSSam Leffler 
25238a1b9b6aSSam Leffler int
25248a1b9b6aSSam Leffler ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data)
25258a1b9b6aSSam Leffler {
25268a1b9b6aSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
25278a1b9b6aSSam Leffler 	int error = 0;
25288a1b9b6aSSam Leffler 	struct ifreq *ifr;
25298a1b9b6aSSam Leffler 	struct ifaddr *ifa;			/* XXX */
25308a1b9b6aSSam Leffler 
25318a1b9b6aSSam Leffler 	switch (cmd) {
25328a1b9b6aSSam Leffler 	case SIOCSIFMEDIA:
25338a1b9b6aSSam Leffler 	case SIOCGIFMEDIA:
25348a1b9b6aSSam Leffler 		error = ifmedia_ioctl(ifp, (struct ifreq *) data,
25358a1b9b6aSSam Leffler 				&ic->ic_media, cmd);
25368a1b9b6aSSam Leffler 		break;
25378a1b9b6aSSam Leffler 	case SIOCG80211:
25388a1b9b6aSSam Leffler 		error = ieee80211_ioctl_get80211(ic, cmd,
25398a1b9b6aSSam Leffler 				(struct ieee80211req *) data);
25408a1b9b6aSSam Leffler 		break;
25418a1b9b6aSSam Leffler 	case SIOCS80211:
2542acd3428bSRobert Watson 		error = priv_check(curthread, PRIV_NET80211_MANAGE);
25438a1b9b6aSSam Leffler 		if (error == 0)
25448a1b9b6aSSam Leffler 			error = ieee80211_ioctl_set80211(ic, cmd,
25458a1b9b6aSSam Leffler 					(struct ieee80211req *) data);
25461a1e1d21SSam Leffler 		break;
25471be50176SSam Leffler 	case SIOCG80211STATS:
25481be50176SSam Leffler 		ifr = (struct ifreq *)data;
25491be50176SSam Leffler 		copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats));
25501be50176SSam Leffler 		break;
25516f161f03SSam Leffler 	case SIOCSIFMTU:
25526f161f03SSam Leffler 		ifr = (struct ifreq *)data;
25536f161f03SSam Leffler 		if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu &&
25546f161f03SSam Leffler 		    ifr->ifr_mtu <= IEEE80211_MTU_MAX))
25556f161f03SSam Leffler 			error = EINVAL;
25566f161f03SSam Leffler 		else
25576f161f03SSam Leffler 			ifp->if_mtu = ifr->ifr_mtu;
25586f161f03SSam Leffler 		break;
2559b2e95691SSam Leffler 	case SIOCSIFADDR:
2560b2e95691SSam Leffler 		/*
2561b2e95691SSam Leffler 		 * XXX Handle this directly so we can supress if_init calls.
2562b2e95691SSam Leffler 		 * XXX This should be done in ether_ioctl but for the moment
2563b2e95691SSam Leffler 		 * XXX there are too many other parts of the system that
2564b2e95691SSam Leffler 		 * XXX set IFF_UP and so supress if_init being called when
2565b2e95691SSam Leffler 		 * XXX it should be.
2566b2e95691SSam Leffler 		 */
2567b2e95691SSam Leffler 		ifa = (struct ifaddr *) data;
2568b2e95691SSam Leffler 		switch (ifa->ifa_addr->sa_family) {
2569b2e95691SSam Leffler #ifdef INET
2570b2e95691SSam Leffler 		case AF_INET:
2571b2e95691SSam Leffler 			if ((ifp->if_flags & IFF_UP) == 0) {
2572b2e95691SSam Leffler 				ifp->if_flags |= IFF_UP;
2573b2e95691SSam Leffler 				ifp->if_init(ifp->if_softc);
2574b2e95691SSam Leffler 			}
2575b2e95691SSam Leffler 			arp_ifinit(ifp, ifa);
2576b2e95691SSam Leffler 			break;
2577b2e95691SSam Leffler #endif
2578b2e95691SSam Leffler #ifdef IPX
2579b2e95691SSam Leffler 		/*
2580b2e95691SSam Leffler 		 * XXX - This code is probably wrong,
2581b2e95691SSam Leffler 		 *	 but has been copied many times.
2582b2e95691SSam Leffler 		 */
2583b2e95691SSam Leffler 		case AF_IPX: {
2584b2e95691SSam Leffler 			struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr);
2585b2e95691SSam Leffler 
2586b2e95691SSam Leffler 			if (ipx_nullhost(*ina))
2587fc74a9f9SBrooks Davis 				ina->x_host = *(union ipx_host *)
25884a0d6638SRuslan Ermilov 				    IF_LLADDR(ifp);
2589b2e95691SSam Leffler 			else
2590b2e95691SSam Leffler 				bcopy((caddr_t) ina->x_host.c_host,
25914a0d6638SRuslan Ermilov 				      (caddr_t) IF_LLADDR(ifp),
2592fc74a9f9SBrooks Davis 				      ETHER_ADDR_LEN);
2593b2e95691SSam Leffler 			/* fall thru... */
2594b2e95691SSam Leffler 		}
2595b2e95691SSam Leffler #endif
2596b2e95691SSam Leffler 		default:
2597b2e95691SSam Leffler 			if ((ifp->if_flags & IFF_UP) == 0) {
2598b2e95691SSam Leffler 				ifp->if_flags |= IFF_UP;
2599b2e95691SSam Leffler 				ifp->if_init(ifp->if_softc);
2600b2e95691SSam Leffler 			}
2601b2e95691SSam Leffler 			break;
2602b2e95691SSam Leffler 		}
2603b2e95691SSam Leffler 		break;
26041a1e1d21SSam Leffler 	default:
26051a1e1d21SSam Leffler 		error = ether_ioctl(ifp, cmd, data);
26061a1e1d21SSam Leffler 		break;
26071a1e1d21SSam Leffler 	}
26081a1e1d21SSam Leffler 	return error;
26091a1e1d21SSam Leffler }
2610