xref: /freebsd/sys/net80211/ieee80211_ioctl.c (revision c066143c088e6b7f3a0272aeb5d7889a6407ede7)
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;
11111a1e1d21SSam Leffler 	default:
11121a1e1d21SSam Leffler 		error = EINVAL;
1113b2e95691SSam Leffler 		break;
11141a1e1d21SSam Leffler 	}
11158a1b9b6aSSam Leffler 	return error;
11168a1b9b6aSSam Leffler }
11178a1b9b6aSSam Leffler 
11188a1b9b6aSSam Leffler static int
11198a1b9b6aSSam Leffler ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq)
11208a1b9b6aSSam Leffler {
11218a1b9b6aSSam Leffler 	int error;
1122d3b3a464SSam Leffler 	void *ie, *oie;
11238a1b9b6aSSam Leffler 
11248a1b9b6aSSam Leffler 	/*
11258a1b9b6aSSam Leffler 	 * NB: Doing this for ap operation could be useful (e.g. for
11268a1b9b6aSSam Leffler 	 *     WPA and/or WME) except that it typically is worthless
11278a1b9b6aSSam Leffler 	 *     without being able to intervene when processing
11288a1b9b6aSSam Leffler 	 *     association response frames--so disallow it for now.
11298a1b9b6aSSam Leffler 	 */
11308a1b9b6aSSam Leffler 	if (ic->ic_opmode != IEEE80211_M_STA)
11318a1b9b6aSSam Leffler 		return EINVAL;
11328a1b9b6aSSam Leffler 	if (ireq->i_len > IEEE80211_MAX_OPT_IE)
11338a1b9b6aSSam Leffler 		return EINVAL;
113468e8e04eSSam Leffler 	/* NB: data.length is validated by the wireless extensions code */
113568e8e04eSSam Leffler 	/* XXX M_WAITOK after driver lock released */
1136d3b3a464SSam Leffler 	if (ireq->i_len > 0) {
1137c2544286SSam Leffler 		MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_NOWAIT);
11388a1b9b6aSSam Leffler 		if (ie == NULL)
11398a1b9b6aSSam Leffler 			return ENOMEM;
11408a1b9b6aSSam Leffler 		error = copyin(ireq->i_data, ie, ireq->i_len);
1141d3b3a464SSam Leffler 		if (error) {
1142d3b3a464SSam Leffler 			FREE(ie, M_DEVBUF);
1143d3b3a464SSam Leffler 			return error;
1144d3b3a464SSam Leffler 		}
1145d3b3a464SSam Leffler 	} else {
1146d3b3a464SSam Leffler 		ie = NULL;
1147d3b3a464SSam Leffler 		ireq->i_len = 0;
1148d3b3a464SSam Leffler 	}
11498a1b9b6aSSam Leffler 	/* XXX sanity check data? */
1150d3b3a464SSam Leffler 	oie = ic->ic_opt_ie;
11518a1b9b6aSSam Leffler 	ic->ic_opt_ie = ie;
11528a1b9b6aSSam Leffler 	ic->ic_opt_ie_len = ireq->i_len;
1153d3b3a464SSam Leffler 	if (oie != NULL)
1154d3b3a464SSam Leffler 		FREE(oie, M_DEVBUF);
11558a1b9b6aSSam Leffler 	return 0;
11568a1b9b6aSSam Leffler }
11578a1b9b6aSSam Leffler 
11588a1b9b6aSSam Leffler static int
11598a1b9b6aSSam Leffler ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
11608a1b9b6aSSam Leffler {
11618a1b9b6aSSam Leffler 	struct ieee80211req_key ik;
11628a1b9b6aSSam Leffler 	struct ieee80211_node *ni;
11638a1b9b6aSSam Leffler 	struct ieee80211_key *wk;
116468e8e04eSSam Leffler 	uint16_t kid;
11658a1b9b6aSSam Leffler 	int error;
11668a1b9b6aSSam Leffler 
11678a1b9b6aSSam Leffler 	if (ireq->i_len != sizeof(ik))
11688a1b9b6aSSam Leffler 		return EINVAL;
11698a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, &ik, sizeof(ik));
11701a1e1d21SSam Leffler 	if (error)
11718a1b9b6aSSam Leffler 		return error;
11728a1b9b6aSSam Leffler 	/* NB: cipher support is verified by ieee80211_crypt_newkey */
11738a1b9b6aSSam Leffler 	/* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */
11748a1b9b6aSSam Leffler 	if (ik.ik_keylen > sizeof(ik.ik_keydata))
11758a1b9b6aSSam Leffler 		return E2BIG;
11768a1b9b6aSSam Leffler 	kid = ik.ik_keyix;
11778a1b9b6aSSam Leffler 	if (kid == IEEE80211_KEYIX_NONE) {
11788a1b9b6aSSam Leffler 		/* XXX unicast keys currently must be tx/rx */
11798a1b9b6aSSam Leffler 		if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
11808a1b9b6aSSam Leffler 			return EINVAL;
11818a1b9b6aSSam Leffler 		if (ic->ic_opmode == IEEE80211_M_STA) {
1182b5d4660fSSam Leffler 			ni = ieee80211_ref_node(ic->ic_bss);
1183b5d4660fSSam Leffler 			if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) {
1184b5d4660fSSam Leffler 				ieee80211_free_node(ni);
11858a1b9b6aSSam Leffler 				return EADDRNOTAVAIL;
1186b5d4660fSSam Leffler 			}
11878a1b9b6aSSam Leffler 		} else {
1188acc4f7f5SSam Leffler 			ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
11898a1b9b6aSSam Leffler 			if (ni == NULL)
11908a1b9b6aSSam Leffler 				return ENOENT;
11918a1b9b6aSSam Leffler 		}
11928a1b9b6aSSam Leffler 		wk = &ni->ni_ucastkey;
11938a1b9b6aSSam Leffler 	} else {
11948a1b9b6aSSam Leffler 		if (kid >= IEEE80211_WEP_NKID)
11958a1b9b6aSSam Leffler 			return EINVAL;
11968a1b9b6aSSam Leffler 		wk = &ic->ic_nw_keys[kid];
1197386d84f6SSam Leffler 		/*
1198386d84f6SSam Leffler 		 * Global slots start off w/o any assigned key index.
1199386d84f6SSam Leffler 		 * Force one here for consistency with IEEE80211_IOC_WEPKEY.
1200386d84f6SSam Leffler 		 */
1201386d84f6SSam Leffler 		if (wk->wk_keyix == IEEE80211_KEYIX_NONE)
1202386d84f6SSam Leffler 			wk->wk_keyix = kid;
12038a1b9b6aSSam Leffler 		ni = NULL;
12048a1b9b6aSSam Leffler 	}
12058a1b9b6aSSam Leffler 	error = 0;
12068a1b9b6aSSam Leffler 	ieee80211_key_update_begin(ic);
1207dd70e17bSSam Leffler 	if (ieee80211_crypto_newkey(ic, ik.ik_type, ik.ik_flags, wk)) {
12088a1b9b6aSSam Leffler 		wk->wk_keylen = ik.ik_keylen;
12098a1b9b6aSSam Leffler 		/* NB: MIC presence is implied by cipher type */
12108a1b9b6aSSam Leffler 		if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
12118a1b9b6aSSam Leffler 			wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
12128a1b9b6aSSam Leffler 		wk->wk_keyrsc = ik.ik_keyrsc;
12138a1b9b6aSSam Leffler 		wk->wk_keytsc = 0;			/* new key, reset */
12148a1b9b6aSSam Leffler 		memset(wk->wk_key, 0, sizeof(wk->wk_key));
12158a1b9b6aSSam Leffler 		memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen);
12168a1b9b6aSSam Leffler 		if (!ieee80211_crypto_setkey(ic, wk,
12178a1b9b6aSSam Leffler 		    ni != NULL ? ni->ni_macaddr : ik.ik_macaddr))
12188a1b9b6aSSam Leffler 			error = EIO;
12198a1b9b6aSSam Leffler 		else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT))
12208a1b9b6aSSam Leffler 			ic->ic_def_txkey = kid;
12218a1b9b6aSSam Leffler 	} else
12228a1b9b6aSSam Leffler 		error = ENXIO;
12238a1b9b6aSSam Leffler 	ieee80211_key_update_end(ic);
12248a1b9b6aSSam Leffler 	if (ni != NULL)
12258a1b9b6aSSam Leffler 		ieee80211_free_node(ni);
12268a1b9b6aSSam Leffler 	return error;
12278a1b9b6aSSam Leffler }
12288a1b9b6aSSam Leffler 
12298a1b9b6aSSam Leffler static int
12308a1b9b6aSSam Leffler ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
12318a1b9b6aSSam Leffler {
12328a1b9b6aSSam Leffler 	struct ieee80211req_del_key dk;
12338a1b9b6aSSam Leffler 	int kid, error;
12348a1b9b6aSSam Leffler 
12358a1b9b6aSSam Leffler 	if (ireq->i_len != sizeof(dk))
12368a1b9b6aSSam Leffler 		return EINVAL;
12378a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, &dk, sizeof(dk));
12388a1b9b6aSSam Leffler 	if (error)
12398a1b9b6aSSam Leffler 		return error;
12408a1b9b6aSSam Leffler 	kid = dk.idk_keyix;
124168e8e04eSSam Leffler 	/* XXX uint8_t -> uint16_t */
124268e8e04eSSam Leffler 	if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) {
12438a1b9b6aSSam Leffler 		struct ieee80211_node *ni;
12448a1b9b6aSSam Leffler 
1245b5d4660fSSam Leffler 		if (ic->ic_opmode == IEEE80211_M_STA) {
1246b5d4660fSSam Leffler 			ni = ieee80211_ref_node(ic->ic_bss);
1247b5d4660fSSam Leffler 			if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) {
1248b5d4660fSSam Leffler 				ieee80211_free_node(ni);
1249b5d4660fSSam Leffler 				return EADDRNOTAVAIL;
1250b5d4660fSSam Leffler 			}
1251b5d4660fSSam Leffler 		} else {
1252acc4f7f5SSam Leffler 			ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr);
12538a1b9b6aSSam Leffler 			if (ni == NULL)
1254b5d4660fSSam Leffler 				return ENOENT;
1255b5d4660fSSam Leffler 		}
12568a1b9b6aSSam Leffler 		/* XXX error return */
1257c1225b52SSam Leffler 		ieee80211_node_delucastkey(ni);
12588a1b9b6aSSam Leffler 		ieee80211_free_node(ni);
12598a1b9b6aSSam Leffler 	} else {
12608a1b9b6aSSam Leffler 		if (kid >= IEEE80211_WEP_NKID)
12618a1b9b6aSSam Leffler 			return EINVAL;
12628a1b9b6aSSam Leffler 		/* XXX error return */
12638a1b9b6aSSam Leffler 		ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]);
12648a1b9b6aSSam Leffler 	}
12658a1b9b6aSSam Leffler 	return 0;
12668a1b9b6aSSam Leffler }
12678a1b9b6aSSam Leffler 
12688a1b9b6aSSam Leffler static void
12698a1b9b6aSSam Leffler domlme(void *arg, struct ieee80211_node *ni)
12708a1b9b6aSSam Leffler {
12718a1b9b6aSSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
12728a1b9b6aSSam Leffler 	struct ieee80211req_mlme *mlme = arg;
12738a1b9b6aSSam Leffler 
12748a1b9b6aSSam Leffler 	if (ni->ni_associd != 0) {
12758a1b9b6aSSam Leffler 		IEEE80211_SEND_MGMT(ic, ni,
12768a1b9b6aSSam Leffler 			mlme->im_op == IEEE80211_MLME_DEAUTH ?
12778a1b9b6aSSam Leffler 				IEEE80211_FC0_SUBTYPE_DEAUTH :
12788a1b9b6aSSam Leffler 				IEEE80211_FC0_SUBTYPE_DISASSOC,
12798a1b9b6aSSam Leffler 			mlme->im_reason);
12808a1b9b6aSSam Leffler 	}
12818a1b9b6aSSam Leffler 	ieee80211_node_leave(ic, ni);
12828a1b9b6aSSam Leffler }
12838a1b9b6aSSam Leffler 
128468e8e04eSSam Leffler struct scanlookup {
128568e8e04eSSam Leffler 	const uint8_t *mac;
128668e8e04eSSam Leffler 	int esslen;
128768e8e04eSSam Leffler 	const uint8_t *essid;
128868e8e04eSSam Leffler 	const struct ieee80211_scan_entry *se;
128968e8e04eSSam Leffler };
129068e8e04eSSam Leffler 
129168e8e04eSSam Leffler /*
129268e8e04eSSam Leffler  * Match mac address and any ssid.
129368e8e04eSSam Leffler  */
129468e8e04eSSam Leffler static void
129568e8e04eSSam Leffler mlmelookup(void *arg, const struct ieee80211_scan_entry *se)
129668e8e04eSSam Leffler {
129768e8e04eSSam Leffler 	struct scanlookup *look = arg;
129868e8e04eSSam Leffler 
129968e8e04eSSam Leffler 	if (!IEEE80211_ADDR_EQ(look->mac, se->se_macaddr))
130068e8e04eSSam Leffler 		return;
130168e8e04eSSam Leffler 	if (look->esslen != 0) {
130268e8e04eSSam Leffler 		if (se->se_ssid[1] != look->esslen)
130368e8e04eSSam Leffler 			return;
130468e8e04eSSam Leffler 		if (memcmp(look->essid, se->se_ssid+2, look->esslen))
130568e8e04eSSam Leffler 			return;
130668e8e04eSSam Leffler 	}
130768e8e04eSSam Leffler 	look->se = se;
130868e8e04eSSam Leffler }
130968e8e04eSSam Leffler 
13108a1b9b6aSSam Leffler static int
13118a1b9b6aSSam Leffler ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
13128a1b9b6aSSam Leffler {
13138a1b9b6aSSam Leffler 	struct ieee80211req_mlme mlme;
13148a1b9b6aSSam Leffler 	struct ieee80211_node *ni;
13158a1b9b6aSSam Leffler 	int error;
13168a1b9b6aSSam Leffler 
13178a1b9b6aSSam Leffler 	if (ireq->i_len != sizeof(mlme))
13188a1b9b6aSSam Leffler 		return EINVAL;
13198a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, &mlme, sizeof(mlme));
13208a1b9b6aSSam Leffler 	if (error)
13218a1b9b6aSSam Leffler 		return error;
13228a1b9b6aSSam Leffler 	switch (mlme.im_op) {
13238a1b9b6aSSam Leffler 	case IEEE80211_MLME_ASSOC:
132468e8e04eSSam Leffler 		/* XXX ibss/ahdemo */
132568e8e04eSSam Leffler 		if (ic->ic_opmode == IEEE80211_M_STA) {
132668e8e04eSSam Leffler 			struct scanlookup lookup;
13278a1b9b6aSSam Leffler 
132868e8e04eSSam Leffler 			lookup.se = NULL;
132968e8e04eSSam Leffler 			lookup.mac = mlme.im_macaddr;
133068e8e04eSSam Leffler 			/* XXX use revised api w/ explicit ssid */
133168e8e04eSSam Leffler 			lookup.esslen = ic->ic_des_ssid[0].len;
133268e8e04eSSam Leffler 			lookup.essid = ic->ic_des_ssid[0].ssid;
133368e8e04eSSam Leffler 			ieee80211_scan_iterate(ic, mlmelookup, &lookup);
133468e8e04eSSam Leffler 			if (lookup.se != NULL &&
133568e8e04eSSam Leffler 			    ieee80211_sta_join(ic, lookup.se))
133668e8e04eSSam Leffler 				return 0;
13378a1b9b6aSSam Leffler 		}
13388a1b9b6aSSam Leffler 		return EINVAL;
13398a1b9b6aSSam Leffler 	case IEEE80211_MLME_DISASSOC:
13408a1b9b6aSSam Leffler 	case IEEE80211_MLME_DEAUTH:
13418a1b9b6aSSam Leffler 		switch (ic->ic_opmode) {
13428a1b9b6aSSam Leffler 		case IEEE80211_M_STA:
13438a1b9b6aSSam Leffler 			/* XXX not quite right */
13448a1b9b6aSSam Leffler 			ieee80211_new_state(ic, IEEE80211_S_INIT,
13458a1b9b6aSSam Leffler 				mlme.im_reason);
13468a1b9b6aSSam Leffler 			break;
13478a1b9b6aSSam Leffler 		case IEEE80211_M_HOSTAP:
13488a1b9b6aSSam Leffler 			/* NB: the broadcast address means do 'em all */
13498a1b9b6aSSam Leffler 			if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) {
1350acc4f7f5SSam Leffler 				if ((ni = ieee80211_find_node(&ic->ic_sta,
13518a1b9b6aSSam Leffler 						mlme.im_macaddr)) == NULL)
13528a1b9b6aSSam Leffler 					return EINVAL;
13538a1b9b6aSSam Leffler 				domlme(&mlme, ni);
13548a1b9b6aSSam Leffler 				ieee80211_free_node(ni);
13558a1b9b6aSSam Leffler 			} else {
1356acc4f7f5SSam Leffler 				ieee80211_iterate_nodes(&ic->ic_sta,
13578a1b9b6aSSam Leffler 						domlme, &mlme);
13588a1b9b6aSSam Leffler 			}
13598a1b9b6aSSam Leffler 			break;
13608a1b9b6aSSam Leffler 		default:
13618a1b9b6aSSam Leffler 			return EINVAL;
13628a1b9b6aSSam Leffler 		}
13638a1b9b6aSSam Leffler 		break;
13648a1b9b6aSSam Leffler 	case IEEE80211_MLME_AUTHORIZE:
13658a1b9b6aSSam Leffler 	case IEEE80211_MLME_UNAUTHORIZE:
13668a1b9b6aSSam Leffler 		if (ic->ic_opmode != IEEE80211_M_HOSTAP)
13678a1b9b6aSSam Leffler 			return EINVAL;
1368acc4f7f5SSam Leffler 		ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr);
13698a1b9b6aSSam Leffler 		if (ni == NULL)
13708a1b9b6aSSam Leffler 			return EINVAL;
13718a1b9b6aSSam Leffler 		if (mlme.im_op == IEEE80211_MLME_AUTHORIZE)
1372e4918ecdSSam Leffler 			ieee80211_node_authorize(ni);
13738a1b9b6aSSam Leffler 		else
1374e4918ecdSSam Leffler 			ieee80211_node_unauthorize(ni);
13758a1b9b6aSSam Leffler 		ieee80211_free_node(ni);
13768a1b9b6aSSam Leffler 		break;
13778a1b9b6aSSam Leffler 	default:
13788a1b9b6aSSam Leffler 		return EINVAL;
13798a1b9b6aSSam Leffler 	}
13808a1b9b6aSSam Leffler 	return 0;
13818a1b9b6aSSam Leffler }
13828a1b9b6aSSam Leffler 
13838a1b9b6aSSam Leffler static int
13848a1b9b6aSSam Leffler ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
13858a1b9b6aSSam Leffler {
138668e8e04eSSam Leffler 	uint8_t mac[IEEE80211_ADDR_LEN];
13878a1b9b6aSSam Leffler 	const struct ieee80211_aclator *acl = ic->ic_acl;
13888a1b9b6aSSam Leffler 	int error;
13898a1b9b6aSSam Leffler 
13908a1b9b6aSSam Leffler 	if (ireq->i_len != sizeof(mac))
13918a1b9b6aSSam Leffler 		return EINVAL;
13928a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, mac, ireq->i_len);
13938a1b9b6aSSam Leffler 	if (error)
13948a1b9b6aSSam Leffler 		return error;
13958a1b9b6aSSam Leffler 	if (acl == NULL) {
13968a1b9b6aSSam Leffler 		acl = ieee80211_aclator_get("mac");
13978a1b9b6aSSam Leffler 		if (acl == NULL || !acl->iac_attach(ic))
13988a1b9b6aSSam Leffler 			return EINVAL;
13998a1b9b6aSSam Leffler 		ic->ic_acl = acl;
14008a1b9b6aSSam Leffler 	}
14018a1b9b6aSSam Leffler 	if (ireq->i_type == IEEE80211_IOC_ADDMAC)
14028a1b9b6aSSam Leffler 		acl->iac_add(ic, mac);
14038a1b9b6aSSam Leffler 	else
14048a1b9b6aSSam Leffler 		acl->iac_remove(ic, mac);
14058a1b9b6aSSam Leffler 	return 0;
14068a1b9b6aSSam Leffler }
14078a1b9b6aSSam Leffler 
14088a1b9b6aSSam Leffler static int
1409188757f5SSam Leffler ieee80211_ioctl_setmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
14108a1b9b6aSSam Leffler {
14118a1b9b6aSSam Leffler 	const struct ieee80211_aclator *acl = ic->ic_acl;
14128a1b9b6aSSam Leffler 
14138a1b9b6aSSam Leffler 	switch (ireq->i_val) {
14148a1b9b6aSSam Leffler 	case IEEE80211_MACCMD_POLICY_OPEN:
14158a1b9b6aSSam Leffler 	case IEEE80211_MACCMD_POLICY_ALLOW:
14168a1b9b6aSSam Leffler 	case IEEE80211_MACCMD_POLICY_DENY:
14178a1b9b6aSSam Leffler 		if (acl == NULL) {
14188a1b9b6aSSam Leffler 			acl = ieee80211_aclator_get("mac");
14198a1b9b6aSSam Leffler 			if (acl == NULL || !acl->iac_attach(ic))
14208a1b9b6aSSam Leffler 				return EINVAL;
14218a1b9b6aSSam Leffler 			ic->ic_acl = acl;
14228a1b9b6aSSam Leffler 		}
14238a1b9b6aSSam Leffler 		acl->iac_setpolicy(ic, ireq->i_val);
14248a1b9b6aSSam Leffler 		break;
14258a1b9b6aSSam Leffler 	case IEEE80211_MACCMD_FLUSH:
14268a1b9b6aSSam Leffler 		if (acl != NULL)
14278a1b9b6aSSam Leffler 			acl->iac_flush(ic);
14288a1b9b6aSSam Leffler 		/* NB: silently ignore when not in use */
14298a1b9b6aSSam Leffler 		break;
14308a1b9b6aSSam Leffler 	case IEEE80211_MACCMD_DETACH:
14318a1b9b6aSSam Leffler 		if (acl != NULL) {
14328a1b9b6aSSam Leffler 			ic->ic_acl = NULL;
14338a1b9b6aSSam Leffler 			acl->iac_detach(ic);
14348a1b9b6aSSam Leffler 		}
14358a1b9b6aSSam Leffler 		break;
14368a1b9b6aSSam Leffler 	default:
1437188757f5SSam Leffler 		if (acl == NULL)
14388a1b9b6aSSam Leffler 			return EINVAL;
1439188757f5SSam Leffler 		else
1440188757f5SSam Leffler 			return acl->iac_setioctl(ic, ireq);
14418a1b9b6aSSam Leffler 	}
14428a1b9b6aSSam Leffler 	return 0;
14438a1b9b6aSSam Leffler }
14448a1b9b6aSSam Leffler 
14458a1b9b6aSSam Leffler static int
14468a1b9b6aSSam Leffler ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
14478a1b9b6aSSam Leffler {
14488a1b9b6aSSam Leffler 	struct ieee80211req_chanlist list;
14498a1b9b6aSSam Leffler 	u_char chanlist[IEEE80211_CHAN_BYTES];
145068e8e04eSSam Leffler 	int i, j, nchan, error;
14518a1b9b6aSSam Leffler 
14528a1b9b6aSSam Leffler 	if (ireq->i_len != sizeof(list))
14538a1b9b6aSSam Leffler 		return EINVAL;
14548a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, &list, sizeof(list));
14558a1b9b6aSSam Leffler 	if (error)
14568a1b9b6aSSam Leffler 		return error;
14578a1b9b6aSSam Leffler 	memset(chanlist, 0, sizeof(chanlist));
14588a1b9b6aSSam Leffler 	/*
14598a1b9b6aSSam Leffler 	 * Since channel 0 is not available for DS, channel 1
14608a1b9b6aSSam Leffler 	 * is assigned to LSB on WaveLAN.
14618a1b9b6aSSam Leffler 	 */
14628a1b9b6aSSam Leffler 	if (ic->ic_phytype == IEEE80211_T_DS)
14638a1b9b6aSSam Leffler 		i = 1;
14648a1b9b6aSSam Leffler 	else
14658a1b9b6aSSam Leffler 		i = 0;
146668e8e04eSSam Leffler 	nchan = 0;
14678a1b9b6aSSam Leffler 	for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
14688a1b9b6aSSam Leffler 		/*
14698a1b9b6aSSam Leffler 		 * NB: silently discard unavailable channels so users
14708a1b9b6aSSam Leffler 		 *     can specify 1-255 to get all available channels.
14718a1b9b6aSSam Leffler 		 */
147268e8e04eSSam Leffler 		if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) {
14738a1b9b6aSSam Leffler 			setbit(chanlist, i);
147468e8e04eSSam Leffler 			nchan++;
14758a1b9b6aSSam Leffler 		}
14768a1b9b6aSSam Leffler 	}
147768e8e04eSSam Leffler 	if (nchan == 0)
147868e8e04eSSam Leffler 		return EINVAL;
147968e8e04eSSam Leffler 	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&	/* XXX */
148068e8e04eSSam Leffler 	    isclr(chanlist, ic->ic_bsschan->ic_ieee))
148168e8e04eSSam Leffler 		ic->ic_bsschan = IEEE80211_CHAN_ANYC;
14828a1b9b6aSSam Leffler 	memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
148368e8e04eSSam Leffler 	return IS_UP_AUTO(ic) ? ieee80211_init(ic, RESCAN) : 0;
14848a1b9b6aSSam Leffler }
14858a1b9b6aSSam Leffler 
14868a1b9b6aSSam Leffler static int
148742568791SSam Leffler ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
148842568791SSam Leffler {
148942568791SSam Leffler 	struct ieee80211_node *ni;
149068e8e04eSSam Leffler 	uint8_t macaddr[IEEE80211_ADDR_LEN];
149142568791SSam Leffler 	int error;
149242568791SSam Leffler 
149342568791SSam Leffler 	/*
149442568791SSam Leffler 	 * NB: we could copyin ieee80211req_sta_stats so apps
149542568791SSam Leffler 	 *     could make selective changes but that's overkill;
149642568791SSam Leffler 	 *     just clear all stats for now.
149742568791SSam Leffler 	 */
149842568791SSam Leffler 	if (ireq->i_len < IEEE80211_ADDR_LEN)
149942568791SSam Leffler 		return EINVAL;
150042568791SSam Leffler 	error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
150142568791SSam Leffler 	if (error != 0)
150242568791SSam Leffler 		return error;
150342568791SSam Leffler 	ni = ieee80211_find_node(&ic->ic_sta, macaddr);
150442568791SSam Leffler 	if (ni == NULL)
150542568791SSam Leffler 		return EINVAL;		/* XXX */
150642568791SSam Leffler 	memset(&ni->ni_stats, 0, sizeof(ni->ni_stats));
150742568791SSam Leffler 	ieee80211_free_node(ni);
150842568791SSam Leffler 	return 0;
150942568791SSam Leffler }
151042568791SSam Leffler 
151142568791SSam Leffler static int
15128a1b9b6aSSam Leffler ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
15138a1b9b6aSSam Leffler {
15148a1b9b6aSSam Leffler 	struct ieee80211_node *ni;
15158a1b9b6aSSam Leffler 	struct ieee80211req_sta_txpow txpow;
15168a1b9b6aSSam Leffler 	int error;
15178a1b9b6aSSam Leffler 
15188a1b9b6aSSam Leffler 	if (ireq->i_len != sizeof(txpow))
15198a1b9b6aSSam Leffler 		return EINVAL;
15208a1b9b6aSSam Leffler 	error = copyin(ireq->i_data, &txpow, sizeof(txpow));
15218a1b9b6aSSam Leffler 	if (error != 0)
15228a1b9b6aSSam Leffler 		return error;
1523acc4f7f5SSam Leffler 	ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
15248a1b9b6aSSam Leffler 	if (ni == NULL)
15258a1b9b6aSSam Leffler 		return EINVAL;		/* XXX */
15268a1b9b6aSSam Leffler 	ni->ni_txpower = txpow.it_txpow;
15278a1b9b6aSSam Leffler 	ieee80211_free_node(ni);
15288a1b9b6aSSam Leffler 	return error;
15298a1b9b6aSSam Leffler }
15308a1b9b6aSSam Leffler 
15318a1b9b6aSSam Leffler static int
15328a1b9b6aSSam Leffler ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
15338a1b9b6aSSam Leffler {
15348a1b9b6aSSam Leffler 	struct ieee80211_wme_state *wme = &ic->ic_wme;
15358a1b9b6aSSam Leffler 	struct wmeParams *wmep, *chanp;
15368a1b9b6aSSam Leffler 	int isbss, ac;
15378a1b9b6aSSam Leffler 
15388a1b9b6aSSam Leffler 	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
15398a1b9b6aSSam Leffler 		return EINVAL;
15408a1b9b6aSSam Leffler 
15418a1b9b6aSSam Leffler 	isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS);
15428a1b9b6aSSam Leffler 	ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
15438a1b9b6aSSam Leffler 	if (ac >= WME_NUM_AC)
15448a1b9b6aSSam Leffler 		ac = WME_AC_BE;
15458a1b9b6aSSam Leffler 	if (isbss) {
15468a1b9b6aSSam Leffler 		chanp = &wme->wme_bssChanParams.cap_wmeParams[ac];
15478a1b9b6aSSam Leffler 		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
15488a1b9b6aSSam Leffler 	} else {
15498a1b9b6aSSam Leffler 		chanp = &wme->wme_chanParams.cap_wmeParams[ac];
15508a1b9b6aSSam Leffler 		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
15518a1b9b6aSSam Leffler 	}
15528a1b9b6aSSam Leffler 	switch (ireq->i_type) {
15538a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
15548a1b9b6aSSam Leffler 		if (isbss) {
15558a1b9b6aSSam Leffler 			wmep->wmep_logcwmin = ireq->i_val;
15568a1b9b6aSSam Leffler 			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
15578a1b9b6aSSam Leffler 				chanp->wmep_logcwmin = ireq->i_val;
15588a1b9b6aSSam Leffler 		} else {
15598a1b9b6aSSam Leffler 			wmep->wmep_logcwmin = chanp->wmep_logcwmin =
15608a1b9b6aSSam Leffler 				ireq->i_val;
15618a1b9b6aSSam Leffler 		}
15628a1b9b6aSSam Leffler 		break;
15638a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
15648a1b9b6aSSam Leffler 		if (isbss) {
15658a1b9b6aSSam Leffler 			wmep->wmep_logcwmax = ireq->i_val;
15668a1b9b6aSSam Leffler 			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
15678a1b9b6aSSam Leffler 				chanp->wmep_logcwmax = ireq->i_val;
15688a1b9b6aSSam Leffler 		} else {
15698a1b9b6aSSam Leffler 			wmep->wmep_logcwmax = chanp->wmep_logcwmax =
15708a1b9b6aSSam Leffler 				ireq->i_val;
15718a1b9b6aSSam Leffler 		}
15728a1b9b6aSSam Leffler 		break;
15738a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
15748a1b9b6aSSam Leffler 		if (isbss) {
15758a1b9b6aSSam Leffler 			wmep->wmep_aifsn = ireq->i_val;
15768a1b9b6aSSam Leffler 			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
15778a1b9b6aSSam Leffler 				chanp->wmep_aifsn = ireq->i_val;
15788a1b9b6aSSam Leffler 		} else {
15798a1b9b6aSSam Leffler 			wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val;
15808a1b9b6aSSam Leffler 		}
15818a1b9b6aSSam Leffler 		break;
15828a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
15838a1b9b6aSSam Leffler 		if (isbss) {
15848a1b9b6aSSam Leffler 			wmep->wmep_txopLimit = ireq->i_val;
15858a1b9b6aSSam Leffler 			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
15868a1b9b6aSSam Leffler 				chanp->wmep_txopLimit = ireq->i_val;
15878a1b9b6aSSam Leffler 		} else {
15888a1b9b6aSSam Leffler 			wmep->wmep_txopLimit = chanp->wmep_txopLimit =
15898a1b9b6aSSam Leffler 				ireq->i_val;
15908a1b9b6aSSam Leffler 		}
15918a1b9b6aSSam Leffler 		break;
15928a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
15938a1b9b6aSSam Leffler 		wmep->wmep_acm = ireq->i_val;
15948a1b9b6aSSam Leffler 		if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
15958a1b9b6aSSam Leffler 			chanp->wmep_acm = ireq->i_val;
15968a1b9b6aSSam Leffler 		break;
15978a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (!bss only)*/
15988a1b9b6aSSam Leffler 		wmep->wmep_noackPolicy = chanp->wmep_noackPolicy =
15998a1b9b6aSSam Leffler 			(ireq->i_val) == 0;
16008a1b9b6aSSam Leffler 		break;
16018a1b9b6aSSam Leffler 	}
16028a1b9b6aSSam Leffler 	ieee80211_wme_updateparams(ic);
16038a1b9b6aSSam Leffler 	return 0;
16048a1b9b6aSSam Leffler }
16058a1b9b6aSSam Leffler 
16068a1b9b6aSSam Leffler static int
16078a1b9b6aSSam Leffler cipher2cap(int cipher)
16088a1b9b6aSSam Leffler {
16098a1b9b6aSSam Leffler 	switch (cipher) {
16108a1b9b6aSSam Leffler 	case IEEE80211_CIPHER_WEP:	return IEEE80211_C_WEP;
16118a1b9b6aSSam Leffler 	case IEEE80211_CIPHER_AES_OCB:	return IEEE80211_C_AES;
16128a1b9b6aSSam Leffler 	case IEEE80211_CIPHER_AES_CCM:	return IEEE80211_C_AES_CCM;
16138a1b9b6aSSam Leffler 	case IEEE80211_CIPHER_CKIP:	return IEEE80211_C_CKIP;
16148a1b9b6aSSam Leffler 	case IEEE80211_CIPHER_TKIP:	return IEEE80211_C_TKIP;
16158a1b9b6aSSam Leffler 	}
16168a1b9b6aSSam Leffler 	return 0;
16178a1b9b6aSSam Leffler }
16188a1b9b6aSSam Leffler 
16198a1b9b6aSSam Leffler static int
162068e8e04eSSam Leffler find11gchannel(struct ieee80211com *ic, int start, int freq)
162168e8e04eSSam Leffler {
162268e8e04eSSam Leffler 	const struct ieee80211_channel *c;
162368e8e04eSSam Leffler 	int i;
162468e8e04eSSam Leffler 
162568e8e04eSSam Leffler 	for (i = start+1; i < ic->ic_nchans; i++) {
162668e8e04eSSam Leffler 		c = &ic->ic_channels[i];
162768e8e04eSSam Leffler 		if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
162868e8e04eSSam Leffler 			return 1;
162968e8e04eSSam Leffler 	}
163068e8e04eSSam Leffler 	/* NB: should not be needed but in case things are mis-sorted */
163168e8e04eSSam Leffler 	for (i = 0; i < start; i++) {
163268e8e04eSSam Leffler 		c = &ic->ic_channels[i];
163368e8e04eSSam Leffler 		if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
163468e8e04eSSam Leffler 			return 1;
163568e8e04eSSam Leffler 	}
163668e8e04eSSam Leffler 	return 0;
163768e8e04eSSam Leffler }
163868e8e04eSSam Leffler 
163968e8e04eSSam Leffler static struct ieee80211_channel *
164068e8e04eSSam Leffler findchannel(struct ieee80211com *ic, int ieee, int mode)
164168e8e04eSSam Leffler {
164268e8e04eSSam Leffler 	static const u_int chanflags[IEEE80211_MODE_MAX] = {
164368e8e04eSSam Leffler 		0,			/* IEEE80211_MODE_AUTO */
164468e8e04eSSam Leffler 		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11A */
164568e8e04eSSam Leffler 		IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
164668e8e04eSSam Leffler 		IEEE80211_CHAN_G,	/* IEEE80211_MODE_11G */
164768e8e04eSSam Leffler 		IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
164868e8e04eSSam Leffler 		IEEE80211_CHAN_108A,	/* IEEE80211_MODE_TURBO_A */
164968e8e04eSSam Leffler 		IEEE80211_CHAN_108G,	/* IEEE80211_MODE_TURBO_G */
165068e8e04eSSam Leffler 		IEEE80211_CHAN_STURBO,	/* IEEE80211_MODE_STURBO_A */
165168e8e04eSSam Leffler 		/* NB: handled specially below */
165268e8e04eSSam Leffler 		IEEE80211_CHAN_A,	/* IEEE80211_MODE_11NA */
165368e8e04eSSam Leffler 		IEEE80211_CHAN_G,	/* IEEE80211_MODE_11NG */
165468e8e04eSSam Leffler 	};
165568e8e04eSSam Leffler 	u_int modeflags;
165668e8e04eSSam Leffler 	int i;
165768e8e04eSSam Leffler 
165868e8e04eSSam Leffler 	KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
165968e8e04eSSam Leffler 	modeflags = chanflags[mode];
166068e8e04eSSam Leffler 	KASSERT(modeflags != 0 || mode == IEEE80211_MODE_AUTO,
166168e8e04eSSam Leffler 	    ("no chanflags for mode %u", mode));
166268e8e04eSSam Leffler 	for (i = 0; i < ic->ic_nchans; i++) {
166368e8e04eSSam Leffler 		struct ieee80211_channel *c = &ic->ic_channels[i];
166468e8e04eSSam Leffler 
166568e8e04eSSam Leffler 		if (c->ic_ieee != ieee)
166668e8e04eSSam Leffler 			continue;
166768e8e04eSSam Leffler 		if (mode == IEEE80211_MODE_AUTO) {
166868e8e04eSSam Leffler 			/* ignore turbo channels for autoselect */
166968e8e04eSSam Leffler 			if (IEEE80211_IS_CHAN_TURBO(c))
167068e8e04eSSam Leffler 				continue;
167168e8e04eSSam Leffler 			/*
167268e8e04eSSam Leffler 			 * XXX special-case 11b/g channels so we
167368e8e04eSSam Leffler 			 *     always select the g channel if both
167468e8e04eSSam Leffler 			 *     are present.
167568e8e04eSSam Leffler 			 * XXX prefer HT to non-HT?
167668e8e04eSSam Leffler 			 */
167768e8e04eSSam Leffler 			if (!IEEE80211_IS_CHAN_B(c) ||
167868e8e04eSSam Leffler 			    !find11gchannel(ic, i, c->ic_freq))
167968e8e04eSSam Leffler 				return c;
168068e8e04eSSam Leffler 		} else {
168168e8e04eSSam Leffler 			/* must check HT specially */
168268e8e04eSSam Leffler 			if ((mode == IEEE80211_MODE_11NA ||
168368e8e04eSSam Leffler 			    mode == IEEE80211_MODE_11NG) &&
168468e8e04eSSam Leffler 			    !IEEE80211_IS_CHAN_HT(c))
168568e8e04eSSam Leffler 				continue;
168668e8e04eSSam Leffler 			if ((c->ic_flags & modeflags) == modeflags)
168768e8e04eSSam Leffler 				return c;
168868e8e04eSSam Leffler 		}
168968e8e04eSSam Leffler 	}
169068e8e04eSSam Leffler 	return NULL;
169168e8e04eSSam Leffler }
169268e8e04eSSam Leffler 
169368e8e04eSSam Leffler /*
169468e8e04eSSam Leffler  * Check the specified against any desired mode (aka netband).
169568e8e04eSSam Leffler  * This is only used (presently) when operating in hostap mode
169668e8e04eSSam Leffler  * to enforce consistency.
169768e8e04eSSam Leffler  */
169868e8e04eSSam Leffler static int
169968e8e04eSSam Leffler check_mode_consistency(const struct ieee80211_channel *c, int mode)
170068e8e04eSSam Leffler {
170168e8e04eSSam Leffler 	KASSERT(c != IEEE80211_CHAN_ANYC, ("oops, no channel"));
170268e8e04eSSam Leffler 
170368e8e04eSSam Leffler 	switch (mode) {
170468e8e04eSSam Leffler 	case IEEE80211_MODE_11B:
170568e8e04eSSam Leffler 		return (IEEE80211_IS_CHAN_B(c));
170668e8e04eSSam Leffler 	case IEEE80211_MODE_11G:
170768e8e04eSSam Leffler 		return (IEEE80211_IS_CHAN_ANYG(c) && !IEEE80211_IS_CHAN_HT(c));
170868e8e04eSSam Leffler 	case IEEE80211_MODE_11A:
170968e8e04eSSam Leffler 		return (IEEE80211_IS_CHAN_A(c) && !IEEE80211_IS_CHAN_HT(c));
171068e8e04eSSam Leffler 	case IEEE80211_MODE_STURBO_A:
171168e8e04eSSam Leffler 		return (IEEE80211_IS_CHAN_STURBO(c));
171268e8e04eSSam Leffler 	case IEEE80211_MODE_11NA:
171368e8e04eSSam Leffler 		return (IEEE80211_IS_CHAN_HTA(c));
171468e8e04eSSam Leffler 	case IEEE80211_MODE_11NG:
171568e8e04eSSam Leffler 		return (IEEE80211_IS_CHAN_HTG(c));
171668e8e04eSSam Leffler 	}
171768e8e04eSSam Leffler 	return 1;
171868e8e04eSSam Leffler 
171968e8e04eSSam Leffler }
172068e8e04eSSam Leffler 
172168e8e04eSSam Leffler /*
172268e8e04eSSam Leffler  * Common code to set the current channel.  If the device
172368e8e04eSSam Leffler  * is up and running this may result in an immediate channel
172468e8e04eSSam Leffler  * change or a kick of the state machine.
172568e8e04eSSam Leffler  */
172668e8e04eSSam Leffler static int
172768e8e04eSSam Leffler setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
172868e8e04eSSam Leffler {
172968e8e04eSSam Leffler 	int error;
173068e8e04eSSam Leffler 
173168e8e04eSSam Leffler 	if (c != IEEE80211_CHAN_ANYC) {
173268e8e04eSSam Leffler 		if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
173368e8e04eSSam Leffler 		    !check_mode_consistency(c, ic->ic_des_mode))
173468e8e04eSSam Leffler 			return EINVAL;
173568e8e04eSSam Leffler 		if (ic->ic_state == IEEE80211_S_RUN && c == ic->ic_curchan)
173668e8e04eSSam Leffler 			return 0;	/* NB: nothing to do */
173768e8e04eSSam Leffler 	}
173868e8e04eSSam Leffler 	ic->ic_des_chan = c;
173968e8e04eSSam Leffler 
174068e8e04eSSam Leffler 	error = 0;
174168e8e04eSSam Leffler 	if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
174268e8e04eSSam Leffler 	    ic->ic_opmode == IEEE80211_M_WDS) &&
174368e8e04eSSam Leffler 	    ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
174468e8e04eSSam Leffler 		/*
174568e8e04eSSam Leffler 		 * Monitor and wds modes can switch directly.
174668e8e04eSSam Leffler 		 */
174768e8e04eSSam Leffler 		ic->ic_curchan = ic->ic_des_chan;
174868e8e04eSSam Leffler 		if (ic->ic_state == IEEE80211_S_RUN)
174968e8e04eSSam Leffler 			ic->ic_set_channel(ic);
175068e8e04eSSam Leffler 	} else {
175168e8e04eSSam Leffler 		/*
175268e8e04eSSam Leffler 		 * Need to go through the state machine in case we
175368e8e04eSSam Leffler 		 * need to reassociate or the like.  The state machine
175468e8e04eSSam Leffler 		 * will pickup the desired channel and avoid scanning.
175568e8e04eSSam Leffler 		 */
175668e8e04eSSam Leffler 		if (IS_UP_AUTO(ic))
175768e8e04eSSam Leffler 			error = ieee80211_init(ic, RESCAN);
175868e8e04eSSam Leffler 		else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
175968e8e04eSSam Leffler 			/*
176068e8e04eSSam Leffler 			 * When not up+running and a real channel has
176168e8e04eSSam Leffler 			 * been specified fix the current channel so
176268e8e04eSSam Leffler 			 * there is immediate feedback; e.g. via ifconfig.
176368e8e04eSSam Leffler 			 */
176468e8e04eSSam Leffler 			ic->ic_curchan = ic->ic_des_chan;
176568e8e04eSSam Leffler 		}
176668e8e04eSSam Leffler 	}
176768e8e04eSSam Leffler 	return error;
176868e8e04eSSam Leffler }
176968e8e04eSSam Leffler 
177068e8e04eSSam Leffler /*
177168e8e04eSSam Leffler  * Old api for setting the current channel; this is
177268e8e04eSSam Leffler  * deprecated because channel numbers are ambiguous.
177368e8e04eSSam Leffler  */
177468e8e04eSSam Leffler static int
177568e8e04eSSam Leffler ieee80211_ioctl_setchannel(struct ieee80211com *ic,
177668e8e04eSSam Leffler 	const struct ieee80211req *ireq)
177768e8e04eSSam Leffler {
177868e8e04eSSam Leffler 	struct ieee80211_channel *c;
177968e8e04eSSam Leffler 
178068e8e04eSSam Leffler 	/* XXX 0xffff overflows 16-bit signed */
178168e8e04eSSam Leffler 	if (ireq->i_val == 0 ||
178268e8e04eSSam Leffler 	    ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) {
178368e8e04eSSam Leffler 		c = IEEE80211_CHAN_ANYC;
178468e8e04eSSam Leffler 	} else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX) {
178568e8e04eSSam Leffler 		return EINVAL;
178668e8e04eSSam Leffler 	} else {
178768e8e04eSSam Leffler 		struct ieee80211_channel *c2;
178868e8e04eSSam Leffler 
178968e8e04eSSam Leffler 		c = findchannel(ic, ireq->i_val, ic->ic_des_mode);
179068e8e04eSSam Leffler 		if (c == NULL) {
179168e8e04eSSam Leffler 			c = findchannel(ic, ireq->i_val,
179268e8e04eSSam Leffler 				IEEE80211_MODE_AUTO);
179368e8e04eSSam Leffler 			if (c == NULL)
179468e8e04eSSam Leffler 				return EINVAL;
179568e8e04eSSam Leffler 		}
179668e8e04eSSam Leffler 		/*
179768e8e04eSSam Leffler 		 * Fine tune channel selection based on desired mode:
179868e8e04eSSam Leffler 		 *   if 11b is requested, find the 11b version of any
179968e8e04eSSam Leffler 		 *      11g channel returned,
180068e8e04eSSam Leffler 		 *   if static turbo, find the turbo version of any
180168e8e04eSSam Leffler 		 *	11a channel return,
180268e8e04eSSam Leffler 		 *   if 11na is requested, find the ht version of any
180368e8e04eSSam Leffler 		 *      11a channel returned,
180468e8e04eSSam Leffler 		 *   if 11ng is requested, find the ht version of any
180568e8e04eSSam Leffler 		 *      11g channel returned,
180668e8e04eSSam Leffler 		 *   otherwise we should be ok with what we've got.
180768e8e04eSSam Leffler 		 */
180868e8e04eSSam Leffler 		switch (ic->ic_des_mode) {
180968e8e04eSSam Leffler 		case IEEE80211_MODE_11B:
181068e8e04eSSam Leffler 			if (IEEE80211_IS_CHAN_ANYG(c)) {
181168e8e04eSSam Leffler 				c2 = findchannel(ic, ireq->i_val,
181268e8e04eSSam Leffler 					IEEE80211_MODE_11B);
181368e8e04eSSam Leffler 				/* NB: should not happen, =>'s 11g w/o 11b */
181468e8e04eSSam Leffler 				if (c2 != NULL)
181568e8e04eSSam Leffler 					c = c2;
181668e8e04eSSam Leffler 			}
181768e8e04eSSam Leffler 			break;
181868e8e04eSSam Leffler 		case IEEE80211_MODE_TURBO_A:
181968e8e04eSSam Leffler 			if (IEEE80211_IS_CHAN_A(c)) {
182068e8e04eSSam Leffler 				c2 = findchannel(ic, ireq->i_val,
182168e8e04eSSam Leffler 					IEEE80211_MODE_TURBO_A);
182268e8e04eSSam Leffler 				if (c2 != NULL)
182368e8e04eSSam Leffler 					c = c2;
182468e8e04eSSam Leffler 			}
182568e8e04eSSam Leffler 			break;
182668e8e04eSSam Leffler 		case IEEE80211_MODE_11NA:
182768e8e04eSSam Leffler 			if (IEEE80211_IS_CHAN_A(c)) {
182868e8e04eSSam Leffler 				c2 = findchannel(ic, ireq->i_val,
182968e8e04eSSam Leffler 					IEEE80211_MODE_11NA);
183068e8e04eSSam Leffler 				if (c2 != NULL)
183168e8e04eSSam Leffler 					c = c2;
183268e8e04eSSam Leffler 			}
183368e8e04eSSam Leffler 			break;
183468e8e04eSSam Leffler 		case IEEE80211_MODE_11NG:
183568e8e04eSSam Leffler 			if (IEEE80211_IS_CHAN_ANYG(c)) {
183668e8e04eSSam Leffler 				c2 = findchannel(ic, ireq->i_val,
183768e8e04eSSam Leffler 					IEEE80211_MODE_11NG);
183868e8e04eSSam Leffler 				if (c2 != NULL)
183968e8e04eSSam Leffler 					c = c2;
184068e8e04eSSam Leffler 			}
184168e8e04eSSam Leffler 			break;
184268e8e04eSSam Leffler 		default:		/* NB: no static turboG */
184368e8e04eSSam Leffler 			break;
184468e8e04eSSam Leffler 		}
184568e8e04eSSam Leffler 	}
184668e8e04eSSam Leffler 	return setcurchan(ic, c);
184768e8e04eSSam Leffler }
184868e8e04eSSam Leffler 
184968e8e04eSSam Leffler /*
185068e8e04eSSam Leffler  * New/current api for setting the current channel; a complete
185168e8e04eSSam Leffler  * channel description is provide so there is no ambiguity in
185268e8e04eSSam Leffler  * identifying the channel.
185368e8e04eSSam Leffler  */
185468e8e04eSSam Leffler static int
185568e8e04eSSam Leffler ieee80211_ioctl_setcurchan(struct ieee80211com *ic,
185668e8e04eSSam Leffler 	const struct ieee80211req *ireq)
185768e8e04eSSam Leffler {
185868e8e04eSSam Leffler 	struct ieee80211_channel chan, *c;
185968e8e04eSSam Leffler 	int error;
186068e8e04eSSam Leffler 
186168e8e04eSSam Leffler 	if (ireq->i_len != sizeof(chan))
186268e8e04eSSam Leffler 		return EINVAL;
186368e8e04eSSam Leffler 	error = copyin(ireq->i_data, &chan, sizeof(chan));
186468e8e04eSSam Leffler 	if (error != 0)
186568e8e04eSSam Leffler 		return error;
186668e8e04eSSam Leffler 	/* XXX 0xffff overflows 16-bit signed */
186768e8e04eSSam Leffler 	if (chan.ic_freq == 0 || chan.ic_freq == IEEE80211_CHAN_ANY) {
186868e8e04eSSam Leffler 		c = IEEE80211_CHAN_ANYC;
186968e8e04eSSam Leffler 	} else {
187068e8e04eSSam Leffler 		c = ieee80211_find_channel(ic, chan.ic_freq, chan.ic_flags);
187168e8e04eSSam Leffler 		if (c == NULL)
187268e8e04eSSam Leffler 			return EINVAL;
187368e8e04eSSam Leffler 	}
187468e8e04eSSam Leffler 	return setcurchan(ic, c);
187568e8e04eSSam Leffler }
187668e8e04eSSam Leffler 
187768e8e04eSSam Leffler static int
18788a1b9b6aSSam Leffler ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
18798a1b9b6aSSam Leffler {
188068e8e04eSSam Leffler 	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
18818a1b9b6aSSam Leffler 	struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
18828a1b9b6aSSam Leffler 	int error;
18838a1b9b6aSSam Leffler 	const struct ieee80211_authenticator *auth;
188468e8e04eSSam Leffler 	uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
18858a1b9b6aSSam Leffler 	char tmpssid[IEEE80211_NWID_LEN];
188668e8e04eSSam Leffler 	uint8_t tmpbssid[IEEE80211_ADDR_LEN];
18878a1b9b6aSSam Leffler 	struct ieee80211_key *k;
18888a1b9b6aSSam Leffler 	int j, caps;
18898a1b9b6aSSam Leffler 	u_int kid;
18908a1b9b6aSSam Leffler 
18918a1b9b6aSSam Leffler 	error = 0;
18921a1e1d21SSam Leffler 	switch (ireq->i_type) {
18931a1e1d21SSam Leffler 	case IEEE80211_IOC_SSID:
18941a1e1d21SSam Leffler 		if (ireq->i_val != 0 ||
18958a1b9b6aSSam Leffler 		    ireq->i_len > IEEE80211_NWID_LEN)
18968a1b9b6aSSam Leffler 			return EINVAL;
18971a1e1d21SSam Leffler 		error = copyin(ireq->i_data, tmpssid, ireq->i_len);
18981a1e1d21SSam Leffler 		if (error)
18991a1e1d21SSam Leffler 			break;
190068e8e04eSSam Leffler 		memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
190168e8e04eSSam Leffler 		ic->ic_des_ssid[0].len = ireq->i_len;
190268e8e04eSSam Leffler 		memcpy(ic->ic_des_ssid[0].ssid, tmpssid, ireq->i_len);
190368e8e04eSSam Leffler 		ic->ic_des_nssid = (ireq->i_len > 0);
190468e8e04eSSam Leffler 		if (IS_UP_AUTO(ic))
190568e8e04eSSam Leffler 			error = ieee80211_init(ic, RESCAN);
19061a1e1d21SSam Leffler 		break;
19071a1e1d21SSam Leffler 	case IEEE80211_IOC_WEP:
19088a1b9b6aSSam Leffler 		switch (ireq->i_val) {
19098a1b9b6aSSam Leffler 		case IEEE80211_WEP_OFF:
19108a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
19118a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
19128a1b9b6aSSam Leffler 			break;
19138a1b9b6aSSam Leffler 		case IEEE80211_WEP_ON:
19148a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_PRIVACY;
19158a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_DROPUNENC;
19168a1b9b6aSSam Leffler 			break;
19178a1b9b6aSSam Leffler 		case IEEE80211_WEP_MIXED:
19188a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_PRIVACY;
19198a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
19208a1b9b6aSSam Leffler 			break;
19211a1e1d21SSam Leffler 		}
192268e8e04eSSam Leffler 		if (IS_UP_AUTO(ic))
192368e8e04eSSam Leffler 			error = ieee80211_init(ic, RESCAN);
19241a1e1d21SSam Leffler 		break;
19251a1e1d21SSam Leffler 	case IEEE80211_IOC_WEPKEY:
19261a1e1d21SSam Leffler 		kid = (u_int) ireq->i_val;
19278a1b9b6aSSam Leffler 		if (kid >= IEEE80211_WEP_NKID)
19288a1b9b6aSSam Leffler 			return EINVAL;
19298a1b9b6aSSam Leffler 		k = &ic->ic_nw_keys[kid];
19308a1b9b6aSSam Leffler 		if (ireq->i_len == 0) {
19318a1b9b6aSSam Leffler 			/* zero-len =>'s delete any existing key */
19328a1b9b6aSSam Leffler 			(void) ieee80211_crypto_delkey(ic, k);
19331a1e1d21SSam Leffler 			break;
19341a1e1d21SSam Leffler 		}
19358a1b9b6aSSam Leffler 		if (ireq->i_len > sizeof(tmpkey))
19368a1b9b6aSSam Leffler 			return EINVAL;
19371a1e1d21SSam Leffler 		memset(tmpkey, 0, sizeof(tmpkey));
19381a1e1d21SSam Leffler 		error = copyin(ireq->i_data, tmpkey, ireq->i_len);
19391a1e1d21SSam Leffler 		if (error)
19401a1e1d21SSam Leffler 			break;
19418a1b9b6aSSam Leffler 		ieee80211_key_update_begin(ic);
1942dd70e17bSSam Leffler 		k->wk_keyix = kid;	/* NB: force fixed key id */
1943dd70e17bSSam Leffler 		if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP,
1944dd70e17bSSam Leffler 		    IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) {
19458a1b9b6aSSam Leffler 			k->wk_keylen = ireq->i_len;
19468a1b9b6aSSam Leffler 			memcpy(k->wk_key, tmpkey, sizeof(tmpkey));
19478a1b9b6aSSam Leffler 			if  (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr))
19488a1b9b6aSSam Leffler 				error = EINVAL;
19498a1b9b6aSSam Leffler 		} else
19508a1b9b6aSSam Leffler 			error = EINVAL;
19518a1b9b6aSSam Leffler 		ieee80211_key_update_end(ic);
19521a1e1d21SSam Leffler 		break;
19531a1e1d21SSam Leffler 	case IEEE80211_IOC_WEPTXKEY:
19541a1e1d21SSam Leffler 		kid = (u_int) ireq->i_val;
1955380c5fa9SSam Leffler 		if (kid >= IEEE80211_WEP_NKID &&
195668e8e04eSSam Leffler 		    (uint16_t) kid != IEEE80211_KEYIX_NONE)
19578a1b9b6aSSam Leffler 			return EINVAL;
19588a1b9b6aSSam Leffler 		ic->ic_def_txkey = kid;
19598a1b9b6aSSam Leffler 		break;
19608a1b9b6aSSam Leffler 	case IEEE80211_IOC_AUTHMODE:
19618a1b9b6aSSam Leffler 		switch (ireq->i_val) {
19628a1b9b6aSSam Leffler 		case IEEE80211_AUTH_WPA:
19638a1b9b6aSSam Leffler 		case IEEE80211_AUTH_8021X:	/* 802.1x */
19648a1b9b6aSSam Leffler 		case IEEE80211_AUTH_OPEN:	/* open */
19658a1b9b6aSSam Leffler 		case IEEE80211_AUTH_SHARED:	/* shared-key */
19668a1b9b6aSSam Leffler 		case IEEE80211_AUTH_AUTO:	/* auto */
19678a1b9b6aSSam Leffler 			auth = ieee80211_authenticator_get(ireq->i_val);
19688a1b9b6aSSam Leffler 			if (auth == NULL)
19698a1b9b6aSSam Leffler 				return EINVAL;
19708a1b9b6aSSam Leffler 			break;
19718a1b9b6aSSam Leffler 		default:
19728a1b9b6aSSam Leffler 			return EINVAL;
19738a1b9b6aSSam Leffler 		}
19748a1b9b6aSSam Leffler 		switch (ireq->i_val) {
19758a1b9b6aSSam Leffler 		case IEEE80211_AUTH_WPA:	/* WPA w/ 802.1x */
19768a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_PRIVACY;
19778a1b9b6aSSam Leffler 			ireq->i_val = IEEE80211_AUTH_8021X;
19788a1b9b6aSSam Leffler 			break;
19798a1b9b6aSSam Leffler 		case IEEE80211_AUTH_OPEN:	/* open */
19808a1b9b6aSSam Leffler 			ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
19818a1b9b6aSSam Leffler 			break;
19828a1b9b6aSSam Leffler 		case IEEE80211_AUTH_SHARED:	/* shared-key */
19838a1b9b6aSSam Leffler 		case IEEE80211_AUTH_8021X:	/* 802.1x */
19848a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_WPA;
19858a1b9b6aSSam Leffler 			/* both require a key so mark the PRIVACY capability */
19868a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_PRIVACY;
19878a1b9b6aSSam Leffler 			break;
19888a1b9b6aSSam Leffler 		case IEEE80211_AUTH_AUTO:	/* auto */
19898a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_WPA;
19908a1b9b6aSSam Leffler 			/* XXX PRIVACY handling? */
19918a1b9b6aSSam Leffler 			/* XXX what's the right way to do this? */
19921a1e1d21SSam Leffler 			break;
19931a1e1d21SSam Leffler 		}
19948a1b9b6aSSam Leffler 		/* NB: authenticator attach/detach happens on state change */
19958a1b9b6aSSam Leffler 		ic->ic_bss->ni_authmode = ireq->i_val;
19968a1b9b6aSSam Leffler 		/* XXX mixed/mode/usage? */
19978a1b9b6aSSam Leffler 		ic->ic_auth = auth;
199868e8e04eSSam Leffler 		if (IS_UP_AUTO(ic))
199968e8e04eSSam Leffler 			error = ieee80211_init(ic, RESCAN);
20001a1e1d21SSam Leffler 		break;
20011a1e1d21SSam Leffler 	case IEEE80211_IOC_CHANNEL:
200268e8e04eSSam Leffler 		error = ieee80211_ioctl_setchannel(ic, ireq);
20031a1e1d21SSam Leffler 		break;
20041a1e1d21SSam Leffler 	case IEEE80211_IOC_POWERSAVE:
20051a1e1d21SSam Leffler 		switch (ireq->i_val) {
20061a1e1d21SSam Leffler 		case IEEE80211_POWERSAVE_OFF:
20071a1e1d21SSam Leffler 			if (ic->ic_flags & IEEE80211_F_PMGTON) {
20081a1e1d21SSam Leffler 				ic->ic_flags &= ~IEEE80211_F_PMGTON;
20091a1e1d21SSam Leffler 				error = ENETRESET;
20101a1e1d21SSam Leffler 			}
20111a1e1d21SSam Leffler 			break;
20121a1e1d21SSam Leffler 		case IEEE80211_POWERSAVE_ON:
20131a1e1d21SSam Leffler 			if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
20141a1e1d21SSam Leffler 				error = EINVAL;
20151a1e1d21SSam Leffler 			else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
20161a1e1d21SSam Leffler 				ic->ic_flags |= IEEE80211_F_PMGTON;
20171a1e1d21SSam Leffler 				error = ENETRESET;
20181a1e1d21SSam Leffler 			}
20191a1e1d21SSam Leffler 			break;
20201a1e1d21SSam Leffler 		default:
20211a1e1d21SSam Leffler 			error = EINVAL;
20221a1e1d21SSam Leffler 			break;
20231a1e1d21SSam Leffler 		}
20240eda166bSSam Leffler 		if (error == ENETRESET) {
20250eda166bSSam Leffler 			/*
20260eda166bSSam Leffler 			 * Switching in+out of power save mode
20270eda166bSSam Leffler 			 * should not require a state change.
20280eda166bSSam Leffler 			 */
20290eda166bSSam Leffler 			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
20300eda166bSSam Leffler 		}
20311a1e1d21SSam Leffler 		break;
20321a1e1d21SSam Leffler 	case IEEE80211_IOC_POWERSAVESLEEP:
20338a1b9b6aSSam Leffler 		if (ireq->i_val < 0)
20348a1b9b6aSSam Leffler 			return EINVAL;
20351a1e1d21SSam Leffler 		ic->ic_lintval = ireq->i_val;
20368a1b9b6aSSam Leffler 		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
20371a1e1d21SSam Leffler 		break;
203813604e6bSSam Leffler 	case IEEE80211_IOC_RTSTHRESHOLD:
203970231e3dSSam Leffler 		if (!(IEEE80211_RTS_MIN <= ireq->i_val &&
204070231e3dSSam Leffler 		      ireq->i_val <= IEEE80211_RTS_MAX))
20418a1b9b6aSSam Leffler 			return EINVAL;
20421a1e1d21SSam Leffler 		ic->ic_rtsthreshold = ireq->i_val;
20438a1b9b6aSSam Leffler 		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
20441a1e1d21SSam Leffler 		break;
20452e79ca97SSam Leffler 	case IEEE80211_IOC_PROTMODE:
20468a1b9b6aSSam Leffler 		if (ireq->i_val > IEEE80211_PROT_RTSCTS)
20478a1b9b6aSSam Leffler 			return EINVAL;
20482e79ca97SSam Leffler 		ic->ic_protmode = ireq->i_val;
20492e79ca97SSam Leffler 		/* NB: if not operating in 11g this can wait */
205068e8e04eSSam Leffler 		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
205168e8e04eSSam Leffler 		    IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
20528a1b9b6aSSam Leffler 			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
20532e79ca97SSam Leffler 		break;
20542e79ca97SSam Leffler 	case IEEE80211_IOC_TXPOWER:
20558a1b9b6aSSam Leffler 		if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
20568a1b9b6aSSam Leffler 			return EINVAL;
205768e8e04eSSam Leffler 		if (!(IEEE80211_TXPOWER_MIN <= ireq->i_val &&
205868e8e04eSSam Leffler 		      ireq->i_val <= IEEE80211_TXPOWER_MAX))
20598a1b9b6aSSam Leffler 			return EINVAL;
20608a1b9b6aSSam Leffler 		ic->ic_txpowlimit = ireq->i_val;
20618a1b9b6aSSam Leffler 		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
20628a1b9b6aSSam Leffler 		break;
20638a1b9b6aSSam Leffler 	case IEEE80211_IOC_ROAMING:
20648a1b9b6aSSam Leffler 		if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val &&
20658a1b9b6aSSam Leffler 		    ireq->i_val <= IEEE80211_ROAMING_MANUAL))
20668a1b9b6aSSam Leffler 			return EINVAL;
20678a1b9b6aSSam Leffler 		ic->ic_roaming = ireq->i_val;
20688a1b9b6aSSam Leffler 		/* XXXX reset? */
20698a1b9b6aSSam Leffler 		break;
20708a1b9b6aSSam Leffler 	case IEEE80211_IOC_PRIVACY:
20718a1b9b6aSSam Leffler 		if (ireq->i_val) {
20728a1b9b6aSSam Leffler 			/* XXX check for key state? */
20738a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_PRIVACY;
20748a1b9b6aSSam Leffler 		} else
20758a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
20768a1b9b6aSSam Leffler 		break;
20778a1b9b6aSSam Leffler 	case IEEE80211_IOC_DROPUNENCRYPTED:
20788a1b9b6aSSam Leffler 		if (ireq->i_val)
20798a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_DROPUNENC;
20808a1b9b6aSSam Leffler 		else
20818a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
20828a1b9b6aSSam Leffler 		break;
20838a1b9b6aSSam Leffler 	case IEEE80211_IOC_WPAKEY:
20848a1b9b6aSSam Leffler 		error = ieee80211_ioctl_setkey(ic, ireq);
20858a1b9b6aSSam Leffler 		break;
20868a1b9b6aSSam Leffler 	case IEEE80211_IOC_DELKEY:
20878a1b9b6aSSam Leffler 		error = ieee80211_ioctl_delkey(ic, ireq);
20888a1b9b6aSSam Leffler 		break;
20898a1b9b6aSSam Leffler 	case IEEE80211_IOC_MLME:
20908a1b9b6aSSam Leffler 		error = ieee80211_ioctl_setmlme(ic, ireq);
20918a1b9b6aSSam Leffler 		break;
20928a1b9b6aSSam Leffler 	case IEEE80211_IOC_OPTIE:
20938a1b9b6aSSam Leffler 		error = ieee80211_ioctl_setoptie(ic, ireq);
20948a1b9b6aSSam Leffler 		break;
20958a1b9b6aSSam Leffler 	case IEEE80211_IOC_COUNTERMEASURES:
20968a1b9b6aSSam Leffler 		if (ireq->i_val) {
20978a1b9b6aSSam Leffler 			if ((ic->ic_flags & IEEE80211_F_WPA) == 0)
20988a1b9b6aSSam Leffler 				return EINVAL;
20998a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_COUNTERM;
21008a1b9b6aSSam Leffler 		} else
21018a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_COUNTERM;
21028a1b9b6aSSam Leffler 		break;
21038a1b9b6aSSam Leffler 	case IEEE80211_IOC_WPA:
21048a1b9b6aSSam Leffler 		if (ireq->i_val > 3)
21058a1b9b6aSSam Leffler 			return EINVAL;
21068a1b9b6aSSam Leffler 		/* XXX verify ciphers available */
21078a1b9b6aSSam Leffler 		ic->ic_flags &= ~IEEE80211_F_WPA;
21088a1b9b6aSSam Leffler 		switch (ireq->i_val) {
21098a1b9b6aSSam Leffler 		case 1:
21108a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_WPA1;
21118a1b9b6aSSam Leffler 			break;
21128a1b9b6aSSam Leffler 		case 2:
21138a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_WPA2;
21148a1b9b6aSSam Leffler 			break;
21158a1b9b6aSSam Leffler 		case 3:
21168a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
21172e79ca97SSam Leffler 			break;
21182e79ca97SSam Leffler 		}
211968e8e04eSSam Leffler 		error = ENETRESET;
21208a1b9b6aSSam Leffler 		break;
21218a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME:
21228a1b9b6aSSam Leffler 		if (ireq->i_val) {
21238a1b9b6aSSam Leffler 			if ((ic->ic_caps & IEEE80211_C_WME) == 0)
21248a1b9b6aSSam Leffler 				return EINVAL;
21258a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_WME;
21268a1b9b6aSSam Leffler 		} else
21278a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_WME;
212868e8e04eSSam Leffler 		if (IS_UP_AUTO(ic))
212968e8e04eSSam Leffler 			error = ieee80211_init(ic, 0);
21308a1b9b6aSSam Leffler 		break;
21318a1b9b6aSSam Leffler 	case IEEE80211_IOC_HIDESSID:
21328a1b9b6aSSam Leffler 		if (ireq->i_val)
21338a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_HIDESSID;
21348a1b9b6aSSam Leffler 		else
21358a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_HIDESSID;
21362e79ca97SSam Leffler 		error = ENETRESET;
21372e79ca97SSam Leffler 		break;
21388a1b9b6aSSam Leffler 	case IEEE80211_IOC_APBRIDGE:
21398a1b9b6aSSam Leffler 		if (ireq->i_val == 0)
21408a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_NOBRIDGE;
21418a1b9b6aSSam Leffler 		else
21428a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_NOBRIDGE;
21438a1b9b6aSSam Leffler 		break;
21448a1b9b6aSSam Leffler 	case IEEE80211_IOC_MCASTCIPHER:
21458a1b9b6aSSam Leffler 		if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 &&
21468a1b9b6aSSam Leffler 		    !ieee80211_crypto_available(ireq->i_val))
21478a1b9b6aSSam Leffler 			return EINVAL;
21488a1b9b6aSSam Leffler 		rsn->rsn_mcastcipher = ireq->i_val;
21498a1b9b6aSSam Leffler 		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
21508a1b9b6aSSam Leffler 		break;
21518a1b9b6aSSam Leffler 	case IEEE80211_IOC_MCASTKEYLEN:
21528a1b9b6aSSam Leffler 		if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
21538a1b9b6aSSam Leffler 			return EINVAL;
21548a1b9b6aSSam Leffler 		/* XXX no way to verify driver capability */
21558a1b9b6aSSam Leffler 		rsn->rsn_mcastkeylen = ireq->i_val;
21568a1b9b6aSSam Leffler 		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
21578a1b9b6aSSam Leffler 		break;
21588a1b9b6aSSam Leffler 	case IEEE80211_IOC_UCASTCIPHERS:
21598a1b9b6aSSam Leffler 		/*
21608a1b9b6aSSam Leffler 		 * Convert user-specified cipher set to the set
21618a1b9b6aSSam Leffler 		 * we can support (via hardware or software).
21628a1b9b6aSSam Leffler 		 * NB: this logic intentionally ignores unknown and
21638a1b9b6aSSam Leffler 		 * unsupported ciphers so folks can specify 0xff or
21648a1b9b6aSSam Leffler 		 * similar and get all available ciphers.
21658a1b9b6aSSam Leffler 		 */
21668a1b9b6aSSam Leffler 		caps = 0;
21678a1b9b6aSSam Leffler 		for (j = 1; j < 32; j++)	/* NB: skip WEP */
21688a1b9b6aSSam Leffler 			if ((ireq->i_val & (1<<j)) &&
21698a1b9b6aSSam Leffler 			    ((ic->ic_caps & cipher2cap(j)) ||
21708a1b9b6aSSam Leffler 			     ieee80211_crypto_available(j)))
21718a1b9b6aSSam Leffler 				caps |= 1<<j;
21728a1b9b6aSSam Leffler 		if (caps == 0)			/* nothing available */
21738a1b9b6aSSam Leffler 			return EINVAL;
21748a1b9b6aSSam Leffler 		/* XXX verify ciphers ok for unicast use? */
21758a1b9b6aSSam Leffler 		/* XXX disallow if running as it'll have no effect */
21768a1b9b6aSSam Leffler 		rsn->rsn_ucastcipherset = caps;
21778a1b9b6aSSam Leffler 		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
21788a1b9b6aSSam Leffler 		break;
21798a1b9b6aSSam Leffler 	case IEEE80211_IOC_UCASTCIPHER:
21808a1b9b6aSSam Leffler 		if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0)
21818a1b9b6aSSam Leffler 			return EINVAL;
21828a1b9b6aSSam Leffler 		rsn->rsn_ucastcipher = ireq->i_val;
21838a1b9b6aSSam Leffler 		break;
21848a1b9b6aSSam Leffler 	case IEEE80211_IOC_UCASTKEYLEN:
21858a1b9b6aSSam Leffler 		if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
21868a1b9b6aSSam Leffler 			return EINVAL;
21878a1b9b6aSSam Leffler 		/* XXX no way to verify driver capability */
21888a1b9b6aSSam Leffler 		rsn->rsn_ucastkeylen = ireq->i_val;
21898a1b9b6aSSam Leffler 		break;
21908a1b9b6aSSam Leffler 	case IEEE80211_IOC_DRIVER_CAPS:
21918a1b9b6aSSam Leffler 		/* NB: for testing */
219268e8e04eSSam Leffler 		ic->ic_caps = (((uint16_t) ireq->i_val) << 16) |
219368e8e04eSSam Leffler 			       ((uint16_t) ireq->i_len);
21948a1b9b6aSSam Leffler 		break;
21958a1b9b6aSSam Leffler 	case IEEE80211_IOC_KEYMGTALGS:
21968a1b9b6aSSam Leffler 		/* XXX check */
21978a1b9b6aSSam Leffler 		rsn->rsn_keymgmtset = ireq->i_val;
21988a1b9b6aSSam Leffler 		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
21998a1b9b6aSSam Leffler 		break;
22008a1b9b6aSSam Leffler 	case IEEE80211_IOC_RSNCAPS:
22018a1b9b6aSSam Leffler 		/* XXX check */
22028a1b9b6aSSam Leffler 		rsn->rsn_caps = ireq->i_val;
22038a1b9b6aSSam Leffler 		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
22048a1b9b6aSSam Leffler 		break;
22058a1b9b6aSSam Leffler 	case IEEE80211_IOC_BSSID:
22068a1b9b6aSSam Leffler 		if (ireq->i_len != sizeof(tmpbssid))
22078a1b9b6aSSam Leffler 			return EINVAL;
22088a1b9b6aSSam Leffler 		error = copyin(ireq->i_data, tmpbssid, ireq->i_len);
22098a1b9b6aSSam Leffler 		if (error)
22108a1b9b6aSSam Leffler 			break;
22118a1b9b6aSSam Leffler 		IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid);
22128a1b9b6aSSam Leffler 		if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid))
22138a1b9b6aSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_DESBSSID;
22148a1b9b6aSSam Leffler 		else
22158a1b9b6aSSam Leffler 			ic->ic_flags |= IEEE80211_F_DESBSSID;
221668e8e04eSSam Leffler 		if (IS_UP_AUTO(ic))
221768e8e04eSSam Leffler 			error = ieee80211_init(ic, RESCAN);
22188a1b9b6aSSam Leffler 		break;
22198a1b9b6aSSam Leffler 	case IEEE80211_IOC_CHANLIST:
22208a1b9b6aSSam Leffler 		error = ieee80211_ioctl_setchanlist(ic, ireq);
22218a1b9b6aSSam Leffler 		break;
22228a1b9b6aSSam Leffler 	case IEEE80211_IOC_SCAN_REQ:
222368e8e04eSSam Leffler 		if (!IS_UP(ic))
222468e8e04eSSam Leffler 			return EINVAL;
222568e8e04eSSam Leffler 		(void) ieee80211_start_scan(ic,
222668e8e04eSSam Leffler 			IEEE80211_SCAN_ACTIVE |
222768e8e04eSSam Leffler 			IEEE80211_SCAN_NOPICK |
222868e8e04eSSam Leffler 			IEEE80211_SCAN_ONCE, IEEE80211_SCAN_FOREVER,
222968e8e04eSSam Leffler 			/* XXX use ioctl params */
223068e8e04eSSam Leffler 			ic->ic_des_nssid, ic->ic_des_ssid);
22318a1b9b6aSSam Leffler 		break;
22328a1b9b6aSSam Leffler 	case IEEE80211_IOC_ADDMAC:
22338a1b9b6aSSam Leffler 	case IEEE80211_IOC_DELMAC:
22348a1b9b6aSSam Leffler 		error = ieee80211_ioctl_macmac(ic, ireq);
22358a1b9b6aSSam Leffler 		break;
22368a1b9b6aSSam Leffler 	case IEEE80211_IOC_MACCMD:
2237188757f5SSam Leffler 		error = ieee80211_ioctl_setmaccmd(ic, ireq);
22388a1b9b6aSSam Leffler 		break;
223942568791SSam Leffler 	case IEEE80211_IOC_STA_STATS:
224042568791SSam Leffler 		error = ieee80211_ioctl_setstastats(ic, ireq);
224142568791SSam Leffler 		break;
22428a1b9b6aSSam Leffler 	case IEEE80211_IOC_STA_TXPOW:
22438a1b9b6aSSam Leffler 		error = ieee80211_ioctl_setstatxpow(ic, ireq);
22448a1b9b6aSSam Leffler 		break;
22458a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
22468a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
22478a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
22488a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
22498a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
22508a1b9b6aSSam Leffler 	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (bss only) */
22518a1b9b6aSSam Leffler 		error = ieee80211_ioctl_setwmeparam(ic, ireq);
22528a1b9b6aSSam Leffler 		break;
22538a1b9b6aSSam Leffler 	case IEEE80211_IOC_DTIM_PERIOD:
22548a1b9b6aSSam Leffler 		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
22558a1b9b6aSSam Leffler 		    ic->ic_opmode != IEEE80211_M_IBSS)
22568a1b9b6aSSam Leffler 			return EINVAL;
22578a1b9b6aSSam Leffler 		if (IEEE80211_DTIM_MIN <= ireq->i_val &&
22588a1b9b6aSSam Leffler 		    ireq->i_val <= IEEE80211_DTIM_MAX) {
22598a1b9b6aSSam Leffler 			ic->ic_dtim_period = ireq->i_val;
22608a1b9b6aSSam Leffler 			error = ENETRESET;		/* requires restart */
22618a1b9b6aSSam Leffler 		} else
22628a1b9b6aSSam Leffler 			error = EINVAL;
22638a1b9b6aSSam Leffler 		break;
22648a1b9b6aSSam Leffler 	case IEEE80211_IOC_BEACON_INTERVAL:
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_BINTVAL_MIN <= ireq->i_val &&
22698a1b9b6aSSam Leffler 		    ireq->i_val <= IEEE80211_BINTVAL_MAX) {
2270d365f9c7SSam Leffler 			ic->ic_bintval = ireq->i_val;
22718a1b9b6aSSam Leffler 			error = ENETRESET;		/* requires restart */
22728a1b9b6aSSam Leffler 		} else
22738a1b9b6aSSam Leffler 			error = EINVAL;
22748a1b9b6aSSam Leffler 		break;
2275c4f040c3SSam Leffler 	case IEEE80211_IOC_PUREG:
2276c4f040c3SSam Leffler 		if (ireq->i_val)
2277c4f040c3SSam Leffler 			ic->ic_flags |= IEEE80211_F_PUREG;
2278c4f040c3SSam Leffler 		else
2279c4f040c3SSam Leffler 			ic->ic_flags &= ~IEEE80211_F_PUREG;
2280c4f040c3SSam Leffler 		/* NB: reset only if we're operating on an 11g channel */
228168e8e04eSSam Leffler 		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
228268e8e04eSSam Leffler 		    IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
2283c4f040c3SSam Leffler 			error = ENETRESET;
2284c4f040c3SSam Leffler 		break;
228568e8e04eSSam Leffler 	case IEEE80211_IOC_FF:
228668e8e04eSSam Leffler 		if (ireq->i_val) {
228768e8e04eSSam Leffler 			if ((ic->ic_caps & IEEE80211_C_FF) == 0)
228868e8e04eSSam Leffler 				return EINVAL;
228968e8e04eSSam Leffler 			ic->ic_flags |= IEEE80211_F_FF;
229068e8e04eSSam Leffler 		} else
229168e8e04eSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_FF;
229268e8e04eSSam Leffler 		error = ENETRESET;
229368e8e04eSSam Leffler 		break;
229468e8e04eSSam Leffler 	case IEEE80211_IOC_TURBOP:
229568e8e04eSSam Leffler 		if (ireq->i_val) {
229668e8e04eSSam Leffler 			if ((ic->ic_caps & IEEE80211_C_TURBOP) == 0)
229768e8e04eSSam Leffler 				return EINVAL;
229868e8e04eSSam Leffler 			ic->ic_flags |= IEEE80211_F_TURBOP;
229968e8e04eSSam Leffler 		} else
230068e8e04eSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_TURBOP;
230168e8e04eSSam Leffler 		error = ENETRESET;
230268e8e04eSSam Leffler 		break;
230368e8e04eSSam Leffler 	case IEEE80211_IOC_BGSCAN:
230468e8e04eSSam Leffler 		if (ireq->i_val) {
230568e8e04eSSam Leffler 			if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0)
230668e8e04eSSam Leffler 				return EINVAL;
230768e8e04eSSam Leffler 			ic->ic_flags |= IEEE80211_F_BGSCAN;
230868e8e04eSSam Leffler 		} else
230968e8e04eSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_BGSCAN;
231068e8e04eSSam Leffler 		break;
231168e8e04eSSam Leffler 	case IEEE80211_IOC_BGSCAN_IDLE:
231268e8e04eSSam Leffler 		if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN)
231368e8e04eSSam Leffler 			ic->ic_bgscanidle = ireq->i_val*hz/1000;
231468e8e04eSSam Leffler 		else
231568e8e04eSSam Leffler 			error = EINVAL;
231668e8e04eSSam Leffler 		break;
231768e8e04eSSam Leffler 	case IEEE80211_IOC_BGSCAN_INTERVAL:
231868e8e04eSSam Leffler 		if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN)
231968e8e04eSSam Leffler 			ic->ic_bgscanintvl = ireq->i_val*hz;
232068e8e04eSSam Leffler 		else
232168e8e04eSSam Leffler 			error = EINVAL;
232268e8e04eSSam Leffler 		break;
232368e8e04eSSam Leffler 	case IEEE80211_IOC_SCANVALID:
232468e8e04eSSam Leffler 		if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN)
232568e8e04eSSam Leffler 			ic->ic_scanvalid = ireq->i_val*hz;
232668e8e04eSSam Leffler 		else
232768e8e04eSSam Leffler 			error = EINVAL;
232868e8e04eSSam Leffler 		break;
232968e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RSSI_11A:
233068e8e04eSSam Leffler 		ic->ic_roam.rssi11a = ireq->i_val;
233168e8e04eSSam Leffler 		break;
233268e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RSSI_11B:
233368e8e04eSSam Leffler 		ic->ic_roam.rssi11bOnly = ireq->i_val;
233468e8e04eSSam Leffler 		break;
233568e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RSSI_11G:
233668e8e04eSSam Leffler 		ic->ic_roam.rssi11b = ireq->i_val;
233768e8e04eSSam Leffler 		break;
233868e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RATE_11A:
233968e8e04eSSam Leffler 		ic->ic_roam.rate11a = ireq->i_val & IEEE80211_RATE_VAL;
234068e8e04eSSam Leffler 		break;
234168e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RATE_11B:
234268e8e04eSSam Leffler 		ic->ic_roam.rate11bOnly = ireq->i_val & IEEE80211_RATE_VAL;
234368e8e04eSSam Leffler 		break;
234468e8e04eSSam Leffler 	case IEEE80211_IOC_ROAM_RATE_11G:
234568e8e04eSSam Leffler 		ic->ic_roam.rate11b = ireq->i_val & IEEE80211_RATE_VAL;
234668e8e04eSSam Leffler 		break;
234764353cb0SSam Leffler 	case IEEE80211_IOC_MCAST_RATE:
234864353cb0SSam Leffler 		ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL;
234964353cb0SSam Leffler 		break;
235070231e3dSSam Leffler 	case IEEE80211_IOC_FRAGTHRESHOLD:
235170231e3dSSam Leffler 		if ((ic->ic_caps & IEEE80211_C_TXFRAG) == 0 &&
235270231e3dSSam Leffler 		    ireq->i_val != IEEE80211_FRAG_MAX)
235370231e3dSSam Leffler 			return EINVAL;
235470231e3dSSam Leffler 		if (!(IEEE80211_FRAG_MIN <= ireq->i_val &&
235570231e3dSSam Leffler 		      ireq->i_val <= IEEE80211_FRAG_MAX))
235670231e3dSSam Leffler 			return EINVAL;
235770231e3dSSam Leffler 		ic->ic_fragthreshold = ireq->i_val;
235870231e3dSSam Leffler 		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
235970231e3dSSam Leffler 		break;
2360c27e4e31SSam Leffler 	case IEEE80211_IOC_BURST:
2361c27e4e31SSam Leffler 		if (ireq->i_val) {
2362c27e4e31SSam Leffler 			if ((ic->ic_caps & IEEE80211_C_BURST) == 0)
2363c27e4e31SSam Leffler 				return EINVAL;
2364c27e4e31SSam Leffler 			ic->ic_flags |= IEEE80211_F_BURST;
2365c27e4e31SSam Leffler 		} else
2366c27e4e31SSam Leffler 			ic->ic_flags &= ~IEEE80211_F_BURST;
2367c27e4e31SSam Leffler 		error = ENETRESET;		/* XXX maybe not for station? */
2368c27e4e31SSam Leffler 		break;
2369546786c9SSam Leffler 	case IEEE80211_IOC_BMISSTHRESHOLD:
2370546786c9SSam Leffler 		if (!(IEEE80211_HWBMISS_MIN <= ireq->i_val &&
2371546786c9SSam Leffler 		      ireq->i_val <= IEEE80211_HWBMISS_MAX))
2372546786c9SSam Leffler 			return EINVAL;
2373546786c9SSam Leffler 		ic->ic_bmissthreshold = ireq->i_val;
2374546786c9SSam Leffler 		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
2375546786c9SSam Leffler 		break;
237668e8e04eSSam Leffler 	case IEEE80211_IOC_CURCHAN:
237768e8e04eSSam Leffler 		error = ieee80211_ioctl_setcurchan(ic, ireq);
237868e8e04eSSam Leffler 		break;
237968e8e04eSSam Leffler 	case IEEE80211_IOC_SHORTGI:
238068e8e04eSSam Leffler 		if (ireq->i_val) {
238168e8e04eSSam Leffler #define	IEEE80211_HTCAP_SHORTGI \
238268e8e04eSSam Leffler 	(IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40)
238368e8e04eSSam Leffler 			if (((ireq->i_val ^ ic->ic_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0)
238468e8e04eSSam Leffler 				return EINVAL;
238568e8e04eSSam Leffler 			if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20)
238668e8e04eSSam Leffler 				ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
238768e8e04eSSam Leffler 			if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40)
238868e8e04eSSam Leffler 				ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
238968e8e04eSSam Leffler #undef IEEE80211_HTCAP_SHORTGI
239068e8e04eSSam Leffler 		} else
239168e8e04eSSam Leffler 			ic->ic_flags_ext &=
239268e8e04eSSam Leffler 			    ~(IEEE80211_FEXT_SHORTGI20 | IEEE80211_FEXT_SHORTGI40);
239368e8e04eSSam Leffler 		/* XXX kick state machine? */
239468e8e04eSSam Leffler 		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
239568e8e04eSSam Leffler 		break;
239668e8e04eSSam Leffler 	case IEEE80211_IOC_AMPDU:
239768e8e04eSSam Leffler 		if (ireq->i_val) {
239868e8e04eSSam Leffler 			if ((ic->ic_htcaps & IEEE80211_HTC_AMPDU) == 0)
239968e8e04eSSam Leffler 				return EINVAL;
240068e8e04eSSam Leffler 			if (ireq->i_val & 1)
240168e8e04eSSam Leffler 				ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
240268e8e04eSSam Leffler 			if (ireq->i_val & 2)
240368e8e04eSSam Leffler 				ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
240468e8e04eSSam Leffler 		} else
240568e8e04eSSam Leffler 			ic->ic_flags_ext &=
240668e8e04eSSam Leffler 			    ~(IEEE80211_FEXT_AMPDU_TX|IEEE80211_FEXT_AMPDU_RX);
240768e8e04eSSam Leffler 		/* NB: reset only if we're operating on an 11n channel */
240868e8e04eSSam Leffler 		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
240968e8e04eSSam Leffler 		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
241068e8e04eSSam Leffler 			error = ENETRESET;
241168e8e04eSSam Leffler 		break;
241268e8e04eSSam Leffler 	case IEEE80211_IOC_AMPDU_LIMIT:
241368e8e04eSSam Leffler 		/* XXX validate */
241468e8e04eSSam Leffler 		ic->ic_ampdu_limit = ireq->i_val;
241568e8e04eSSam Leffler 		break;
241668e8e04eSSam Leffler 	case IEEE80211_IOC_AMPDU_DENSITY:
241768e8e04eSSam Leffler 		/* XXX validate */
241868e8e04eSSam Leffler 		ic->ic_ampdu_density = ireq->i_val;
241968e8e04eSSam Leffler 		break;
242068e8e04eSSam Leffler 	case IEEE80211_IOC_AMSDU:
242168e8e04eSSam Leffler 		if (ireq->i_val) {
242268e8e04eSSam Leffler 			if ((ic->ic_htcaps & IEEE80211_HTC_AMSDU) == 0)
242368e8e04eSSam Leffler 				return EINVAL;
242468e8e04eSSam Leffler 			if (ireq->i_val & 1)
242568e8e04eSSam Leffler 				ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
242668e8e04eSSam Leffler 			if (ireq->i_val & 2)
242768e8e04eSSam Leffler 				ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
242868e8e04eSSam Leffler 		} else
242968e8e04eSSam Leffler 			ic->ic_flags_ext &=
243068e8e04eSSam Leffler 			    ~(IEEE80211_FEXT_AMSDU_TX|IEEE80211_FEXT_AMSDU_RX);
243168e8e04eSSam Leffler 		/* NB: reset only if we're operating on an 11n channel */
243268e8e04eSSam Leffler 		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
243368e8e04eSSam Leffler 		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
243468e8e04eSSam Leffler 			error = ENETRESET;
243568e8e04eSSam Leffler 		break;
243668e8e04eSSam Leffler 	case IEEE80211_IOC_AMSDU_LIMIT:
243768e8e04eSSam Leffler 		/* XXX validate */
243868e8e04eSSam Leffler 		ic->ic_amsdu_limit = ireq->i_val;	/* XXX truncation? */
243968e8e04eSSam Leffler 		break;
244068e8e04eSSam Leffler 	case IEEE80211_IOC_PUREN:
244168e8e04eSSam Leffler 		if (ireq->i_val) {
244268e8e04eSSam Leffler 			if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
244368e8e04eSSam Leffler 				return EINVAL;
244468e8e04eSSam Leffler 			ic->ic_flags_ext |= IEEE80211_FEXT_PUREN;
244568e8e04eSSam Leffler 		} else
244668e8e04eSSam Leffler 			ic->ic_flags_ext &= ~IEEE80211_FEXT_PUREN;
244768e8e04eSSam Leffler 		/* NB: reset only if we're operating on an 11n channel */
244868e8e04eSSam Leffler 		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
244968e8e04eSSam Leffler 		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
245068e8e04eSSam Leffler 			error = ENETRESET;
245168e8e04eSSam Leffler 		break;
245268e8e04eSSam Leffler 	case IEEE80211_IOC_DOTH:
245368e8e04eSSam Leffler 		if (ireq->i_val) {
245468e8e04eSSam Leffler #if 0
245568e8e04eSSam Leffler 			/* XXX no capability */
245668e8e04eSSam Leffler 			if ((ic->ic_caps & IEEE80211_C_DOTH) == 0)
245768e8e04eSSam Leffler 				return EINVAL;
245868e8e04eSSam Leffler #endif
245968e8e04eSSam Leffler 			ic->ic_flags |= IEEE80211_F_DOTH;
246068e8e04eSSam Leffler 		} else
246168e8e04eSSam Leffler 			ic->ic_flags &= ~IEEE80211_F_DOTH;
246268e8e04eSSam Leffler 		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
246368e8e04eSSam Leffler 		break;
246468e8e04eSSam Leffler 	case IEEE80211_IOC_HTCOMPAT:
246568e8e04eSSam Leffler 		if (ireq->i_val) {
246668e8e04eSSam Leffler 			if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
246768e8e04eSSam Leffler 				return EINVAL;
246868e8e04eSSam Leffler 			ic->ic_flags_ext |= IEEE80211_FEXT_HTCOMPAT;
246968e8e04eSSam Leffler 		} else
247068e8e04eSSam Leffler 			ic->ic_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT;
247168e8e04eSSam Leffler 		/* NB: reset only if we're operating on an 11n channel */
247268e8e04eSSam Leffler 		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
247368e8e04eSSam Leffler 		    IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
247468e8e04eSSam Leffler 			error = ENETRESET;
247568e8e04eSSam Leffler 		break;
2476c066143cSSam Leffler 	case IEEE80211_IOC_INACTIVITY:
2477c066143cSSam Leffler 		if (ireq->i_val)
2478c066143cSSam Leffler 			ic->ic_flags_ext |= IEEE80211_FEXT_INACT;
2479c066143cSSam Leffler 		else
2480c066143cSSam Leffler 			ic->ic_flags_ext &= ~IEEE80211_FEXT_INACT;
2481c066143cSSam Leffler 		break;
24821a1e1d21SSam Leffler 	default:
24831a1e1d21SSam Leffler 		error = EINVAL;
24841a1e1d21SSam Leffler 		break;
24851a1e1d21SSam Leffler 	}
248668e8e04eSSam Leffler 	if (error == ENETRESET)
248768e8e04eSSam Leffler 		error = IS_UP_AUTO(ic) ? ieee80211_init(ic, 0) : 0;
24888a1b9b6aSSam Leffler 	return error;
24898a1b9b6aSSam Leffler }
24908a1b9b6aSSam Leffler 
24918a1b9b6aSSam Leffler int
24928a1b9b6aSSam Leffler ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data)
24938a1b9b6aSSam Leffler {
24948a1b9b6aSSam Leffler 	struct ifnet *ifp = ic->ic_ifp;
24958a1b9b6aSSam Leffler 	int error = 0;
24968a1b9b6aSSam Leffler 	struct ifreq *ifr;
24978a1b9b6aSSam Leffler 	struct ifaddr *ifa;			/* XXX */
24988a1b9b6aSSam Leffler 
24998a1b9b6aSSam Leffler 	switch (cmd) {
25008a1b9b6aSSam Leffler 	case SIOCSIFMEDIA:
25018a1b9b6aSSam Leffler 	case SIOCGIFMEDIA:
25028a1b9b6aSSam Leffler 		error = ifmedia_ioctl(ifp, (struct ifreq *) data,
25038a1b9b6aSSam Leffler 				&ic->ic_media, cmd);
25048a1b9b6aSSam Leffler 		break;
25058a1b9b6aSSam Leffler 	case SIOCG80211:
25068a1b9b6aSSam Leffler 		error = ieee80211_ioctl_get80211(ic, cmd,
25078a1b9b6aSSam Leffler 				(struct ieee80211req *) data);
25088a1b9b6aSSam Leffler 		break;
25098a1b9b6aSSam Leffler 	case SIOCS80211:
2510acd3428bSRobert Watson 		error = priv_check(curthread, PRIV_NET80211_MANAGE);
25118a1b9b6aSSam Leffler 		if (error == 0)
25128a1b9b6aSSam Leffler 			error = ieee80211_ioctl_set80211(ic, cmd,
25138a1b9b6aSSam Leffler 					(struct ieee80211req *) data);
25141a1e1d21SSam Leffler 		break;
25151be50176SSam Leffler 	case SIOCG80211STATS:
25161be50176SSam Leffler 		ifr = (struct ifreq *)data;
25171be50176SSam Leffler 		copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats));
25181be50176SSam Leffler 		break;
25196f161f03SSam Leffler 	case SIOCSIFMTU:
25206f161f03SSam Leffler 		ifr = (struct ifreq *)data;
25216f161f03SSam Leffler 		if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu &&
25226f161f03SSam Leffler 		    ifr->ifr_mtu <= IEEE80211_MTU_MAX))
25236f161f03SSam Leffler 			error = EINVAL;
25246f161f03SSam Leffler 		else
25256f161f03SSam Leffler 			ifp->if_mtu = ifr->ifr_mtu;
25266f161f03SSam Leffler 		break;
2527b2e95691SSam Leffler 	case SIOCSIFADDR:
2528b2e95691SSam Leffler 		/*
2529b2e95691SSam Leffler 		 * XXX Handle this directly so we can supress if_init calls.
2530b2e95691SSam Leffler 		 * XXX This should be done in ether_ioctl but for the moment
2531b2e95691SSam Leffler 		 * XXX there are too many other parts of the system that
2532b2e95691SSam Leffler 		 * XXX set IFF_UP and so supress if_init being called when
2533b2e95691SSam Leffler 		 * XXX it should be.
2534b2e95691SSam Leffler 		 */
2535b2e95691SSam Leffler 		ifa = (struct ifaddr *) data;
2536b2e95691SSam Leffler 		switch (ifa->ifa_addr->sa_family) {
2537b2e95691SSam Leffler #ifdef INET
2538b2e95691SSam Leffler 		case AF_INET:
2539b2e95691SSam Leffler 			if ((ifp->if_flags & IFF_UP) == 0) {
2540b2e95691SSam Leffler 				ifp->if_flags |= IFF_UP;
2541b2e95691SSam Leffler 				ifp->if_init(ifp->if_softc);
2542b2e95691SSam Leffler 			}
2543b2e95691SSam Leffler 			arp_ifinit(ifp, ifa);
2544b2e95691SSam Leffler 			break;
2545b2e95691SSam Leffler #endif
2546b2e95691SSam Leffler #ifdef IPX
2547b2e95691SSam Leffler 		/*
2548b2e95691SSam Leffler 		 * XXX - This code is probably wrong,
2549b2e95691SSam Leffler 		 *	 but has been copied many times.
2550b2e95691SSam Leffler 		 */
2551b2e95691SSam Leffler 		case AF_IPX: {
2552b2e95691SSam Leffler 			struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr);
2553b2e95691SSam Leffler 
2554b2e95691SSam Leffler 			if (ipx_nullhost(*ina))
2555fc74a9f9SBrooks Davis 				ina->x_host = *(union ipx_host *)
25564a0d6638SRuslan Ermilov 				    IF_LLADDR(ifp);
2557b2e95691SSam Leffler 			else
2558b2e95691SSam Leffler 				bcopy((caddr_t) ina->x_host.c_host,
25594a0d6638SRuslan Ermilov 				      (caddr_t) IF_LLADDR(ifp),
2560fc74a9f9SBrooks Davis 				      ETHER_ADDR_LEN);
2561b2e95691SSam Leffler 			/* fall thru... */
2562b2e95691SSam Leffler 		}
2563b2e95691SSam Leffler #endif
2564b2e95691SSam Leffler 		default:
2565b2e95691SSam Leffler 			if ((ifp->if_flags & IFF_UP) == 0) {
2566b2e95691SSam Leffler 				ifp->if_flags |= IFF_UP;
2567b2e95691SSam Leffler 				ifp->if_init(ifp->if_softc);
2568b2e95691SSam Leffler 			}
2569b2e95691SSam Leffler 			break;
2570b2e95691SSam Leffler 		}
2571b2e95691SSam Leffler 		break;
25721a1e1d21SSam Leffler 	default:
25731a1e1d21SSam Leffler 		error = ether_ioctl(ifp, cmd, data);
25741a1e1d21SSam Leffler 		break;
25751a1e1d21SSam Leffler 	}
25761a1e1d21SSam Leffler 	return error;
25771a1e1d21SSam Leffler }
2578