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