11a1e1d21SSam Leffler /*- 27535e66aSSam Leffler * Copyright (c) 2001 Atsushi Onoe 31f1d7810SSam Leffler * Copyright (c) 2002-2005 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. 147535e66aSSam Leffler * 3. The name of the author may not be used to endorse or promote products 157535e66aSSam Leffler * derived from this software without specific prior written permission. 161a1e1d21SSam Leffler * 171a1e1d21SSam Leffler * Alternatively, this software may be distributed under the terms of the 181a1e1d21SSam Leffler * GNU General Public License ("GPL") version 2 as published by the Free 191a1e1d21SSam Leffler * Software Foundation. 201a1e1d21SSam Leffler * 217535e66aSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 227535e66aSSam Leffler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 237535e66aSSam Leffler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 247535e66aSSam Leffler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 257535e66aSSam Leffler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 267535e66aSSam Leffler * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 277535e66aSSam Leffler * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 287535e66aSSam Leffler * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 297535e66aSSam Leffler * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 307535e66aSSam Leffler * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 311a1e1d21SSam Leffler */ 321a1e1d21SSam Leffler 331a1e1d21SSam Leffler #include <sys/cdefs.h> 341a1e1d21SSam Leffler __FBSDID("$FreeBSD$"); 351a1e1d21SSam Leffler 36f88b3f22SRuslan Ermilov #include "opt_compat.h" 37f88b3f22SRuslan Ermilov 381a1e1d21SSam Leffler /* 391a1e1d21SSam Leffler * IEEE 802.11 ioctl support (FreeBSD-specific) 401a1e1d21SSam Leffler */ 411a1e1d21SSam Leffler 42b2e95691SSam Leffler #include "opt_inet.h" 43b2e95691SSam Leffler #include "opt_ipx.h" 44b2e95691SSam Leffler 451a1e1d21SSam Leffler #include <sys/endian.h> 461a1e1d21SSam Leffler #include <sys/param.h> 471a1e1d21SSam Leffler #include <sys/kernel.h> 48acd3428bSRobert Watson #include <sys/priv.h> 491a1e1d21SSam Leffler #include <sys/socket.h> 501a1e1d21SSam Leffler #include <sys/sockio.h> 511a1e1d21SSam Leffler #include <sys/systm.h> 521a1e1d21SSam Leffler 531a1e1d21SSam Leffler #include <net/if.h> 544a0d6638SRuslan Ermilov #include <net/if_dl.h> 551a1e1d21SSam Leffler #include <net/if_media.h> 561a1e1d21SSam Leffler #include <net/ethernet.h> 571a1e1d21SSam Leffler 58b2e95691SSam Leffler #ifdef INET 59b2e95691SSam Leffler #include <netinet/in.h> 60b2e95691SSam Leffler #include <netinet/if_ether.h> 61b2e95691SSam Leffler #endif 62b2e95691SSam Leffler 63b2e95691SSam Leffler #ifdef IPX 64b2e95691SSam Leffler #include <netipx/ipx.h> 65b2e95691SSam Leffler #include <netipx/ipx_if.h> 66b2e95691SSam Leffler #endif 67b2e95691SSam Leffler 681a1e1d21SSam Leffler #include <net80211/ieee80211_var.h> 691a1e1d21SSam Leffler #include <net80211/ieee80211_ioctl.h> 701a1e1d21SSam Leffler 711a1e1d21SSam Leffler #include <dev/wi/if_wavelan_ieee.h> 721a1e1d21SSam Leffler 738a1b9b6aSSam Leffler #define IS_UP(_ic) \ 7413f4c340SRobert Watson (((_ic)->ic_ifp->if_flags & IFF_UP) && \ 7513f4c340SRobert Watson ((_ic)->ic_ifp->if_drv_flags & IFF_DRV_RUNNING)) 768a1b9b6aSSam Leffler #define IS_UP_AUTO(_ic) \ 778a1b9b6aSSam Leffler (IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO) 788a1b9b6aSSam Leffler 791a1e1d21SSam Leffler /* 801a1e1d21SSam Leffler * XXX 811a1e1d21SSam Leffler * Wireless LAN specific configuration interface, which is compatible 821a1e1d21SSam Leffler * with wicontrol(8). 831a1e1d21SSam Leffler */ 841a1e1d21SSam Leffler 858a1b9b6aSSam Leffler struct wi_read_ap_args { 868a1b9b6aSSam Leffler int i; /* result count */ 878a1b9b6aSSam Leffler struct wi_apinfo *ap; /* current entry in result buffer */ 888a1b9b6aSSam Leffler caddr_t max; /* result buffer bound */ 898a1b9b6aSSam Leffler }; 908a1b9b6aSSam Leffler 918a1b9b6aSSam Leffler static void 928a1b9b6aSSam Leffler wi_read_ap_result(void *arg, struct ieee80211_node *ni) 931a1e1d21SSam Leffler { 948a1b9b6aSSam Leffler struct ieee80211com *ic = ni->ni_ic; 958a1b9b6aSSam Leffler struct wi_read_ap_args *sa = arg; 968a1b9b6aSSam Leffler struct wi_apinfo *ap = sa->ap; 978a1b9b6aSSam Leffler struct ieee80211_rateset *rs; 988a1b9b6aSSam Leffler int j; 998a1b9b6aSSam Leffler 1008a1b9b6aSSam Leffler if ((caddr_t)(ap + 1) > sa->max) 1018a1b9b6aSSam Leffler return; 1028a1b9b6aSSam Leffler memset(ap, 0, sizeof(struct wi_apinfo)); 1038a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 1048a1b9b6aSSam Leffler IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr); 1058a1b9b6aSSam Leffler ap->namelen = ic->ic_des_esslen; 1068a1b9b6aSSam Leffler if (ic->ic_des_esslen) 1078a1b9b6aSSam Leffler memcpy(ap->name, ic->ic_des_essid, 1088a1b9b6aSSam Leffler ic->ic_des_esslen); 1098a1b9b6aSSam Leffler } else { 1108a1b9b6aSSam Leffler IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid); 1118a1b9b6aSSam Leffler ap->namelen = ni->ni_esslen; 1128a1b9b6aSSam Leffler if (ni->ni_esslen) 1138a1b9b6aSSam Leffler memcpy(ap->name, ni->ni_essid, 1148a1b9b6aSSam Leffler ni->ni_esslen); 1158a1b9b6aSSam Leffler } 1168a1b9b6aSSam Leffler ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan); 1178a1b9b6aSSam Leffler ap->signal = ic->ic_node_getrssi(ni); 1188a1b9b6aSSam Leffler ap->capinfo = ni->ni_capinfo; 1198a1b9b6aSSam Leffler ap->interval = ni->ni_intval; 1208a1b9b6aSSam Leffler rs = &ni->ni_rates; 1218a1b9b6aSSam Leffler for (j = 0; j < rs->rs_nrates; j++) { 1228a1b9b6aSSam Leffler if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) { 1238a1b9b6aSSam Leffler ap->rate = (rs->rs_rates[j] & 1248a1b9b6aSSam Leffler IEEE80211_RATE_VAL) * 5; /* XXX */ 1258a1b9b6aSSam Leffler } 1268a1b9b6aSSam Leffler } 1278a1b9b6aSSam Leffler sa->i++; 1288a1b9b6aSSam Leffler sa->ap++; 1298a1b9b6aSSam Leffler } 1308a1b9b6aSSam Leffler 1318a1b9b6aSSam Leffler struct wi_read_prism2_args { 1328a1b9b6aSSam Leffler int i; /* result count */ 1338a1b9b6aSSam Leffler struct wi_scan_res *res;/* current entry in result buffer */ 1348a1b9b6aSSam Leffler caddr_t max; /* result buffer bound */ 1358a1b9b6aSSam Leffler }; 1368a1b9b6aSSam Leffler 1378a1b9b6aSSam Leffler static void 1388a1b9b6aSSam Leffler wi_read_prism2_result(void *arg, struct ieee80211_node *ni) 1398a1b9b6aSSam Leffler { 1408a1b9b6aSSam Leffler struct ieee80211com *ic = ni->ni_ic; 1418a1b9b6aSSam Leffler struct wi_read_prism2_args *sa = arg; 1428a1b9b6aSSam Leffler struct wi_scan_res *res = sa->res; 1438a1b9b6aSSam Leffler 1448a1b9b6aSSam Leffler if ((caddr_t)(res + 1) > sa->max) 1458a1b9b6aSSam Leffler return; 1468a1b9b6aSSam Leffler res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan); 1478a1b9b6aSSam Leffler res->wi_noise = 0; 1488a1b9b6aSSam Leffler res->wi_signal = ic->ic_node_getrssi(ni); 1498a1b9b6aSSam Leffler IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid); 1508a1b9b6aSSam Leffler res->wi_interval = ni->ni_intval; 1518a1b9b6aSSam Leffler res->wi_capinfo = ni->ni_capinfo; 1528a1b9b6aSSam Leffler res->wi_ssid_len = ni->ni_esslen; 1538a1b9b6aSSam Leffler memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN); 1548a1b9b6aSSam Leffler /* NB: assumes wi_srates holds <= ni->ni_rates */ 1558a1b9b6aSSam Leffler memcpy(res->wi_srates, ni->ni_rates.rs_rates, 1568a1b9b6aSSam Leffler sizeof(res->wi_srates)); 1578a1b9b6aSSam Leffler if (ni->ni_rates.rs_nrates < 10) 1588a1b9b6aSSam Leffler res->wi_srates[ni->ni_rates.rs_nrates] = 0; 1598a1b9b6aSSam Leffler res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate]; 1608a1b9b6aSSam Leffler res->wi_rsvd = 0; 1618a1b9b6aSSam Leffler 1628a1b9b6aSSam Leffler sa->i++; 1638a1b9b6aSSam Leffler sa->res++; 1648a1b9b6aSSam Leffler } 1658a1b9b6aSSam Leffler 1668a1b9b6aSSam Leffler struct wi_read_sigcache_args { 1678a1b9b6aSSam Leffler int i; /* result count */ 1688a1b9b6aSSam Leffler struct wi_sigcache *wsc;/* current entry in result buffer */ 1698a1b9b6aSSam Leffler caddr_t max; /* result buffer bound */ 1708a1b9b6aSSam Leffler }; 1718a1b9b6aSSam Leffler 1728a1b9b6aSSam Leffler static void 1738a1b9b6aSSam Leffler wi_read_sigcache(void *arg, struct ieee80211_node *ni) 1748a1b9b6aSSam Leffler { 1758a1b9b6aSSam Leffler struct ieee80211com *ic = ni->ni_ic; 1768a1b9b6aSSam Leffler struct wi_read_sigcache_args *sa = arg; 1778a1b9b6aSSam Leffler struct wi_sigcache *wsc = sa->wsc; 1788a1b9b6aSSam Leffler 1798a1b9b6aSSam Leffler if ((caddr_t)(wsc + 1) > sa->max) 1808a1b9b6aSSam Leffler return; 1818a1b9b6aSSam Leffler memset(wsc, 0, sizeof(struct wi_sigcache)); 1828a1b9b6aSSam Leffler IEEE80211_ADDR_COPY(wsc->macsrc, ni->ni_macaddr); 1838a1b9b6aSSam Leffler wsc->signal = ic->ic_node_getrssi(ni); 1848a1b9b6aSSam Leffler 1858a1b9b6aSSam Leffler sa->wsc++; 1868a1b9b6aSSam Leffler sa->i++; 1878a1b9b6aSSam Leffler } 1888a1b9b6aSSam Leffler 1898a1b9b6aSSam Leffler int 1908a1b9b6aSSam Leffler ieee80211_cfgget(struct ieee80211com *ic, u_long cmd, caddr_t data) 1918a1b9b6aSSam Leffler { 1928a1b9b6aSSam Leffler struct ifnet *ifp = ic->ic_ifp; 1931a1e1d21SSam Leffler int i, j, error; 1941a1e1d21SSam Leffler struct ifreq *ifr = (struct ifreq *)data; 1951a1e1d21SSam Leffler struct wi_req wreq; 1961a1e1d21SSam Leffler struct wi_ltv_keys *keys; 1971a1e1d21SSam Leffler 1981a1e1d21SSam Leffler error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 1991a1e1d21SSam Leffler if (error) 2001a1e1d21SSam Leffler return error; 2011a1e1d21SSam Leffler wreq.wi_len = 0; 2021a1e1d21SSam Leffler switch (wreq.wi_type) { 2031a1e1d21SSam Leffler case WI_RID_SERIALNO: 2041a1e1d21SSam Leffler /* nothing appropriate */ 2051a1e1d21SSam Leffler break; 2061a1e1d21SSam Leffler case WI_RID_NODENAME: 2071a1e1d21SSam Leffler strcpy((char *)&wreq.wi_val[1], hostname); 2081a1e1d21SSam Leffler wreq.wi_val[0] = htole16(strlen(hostname)); 2091a1e1d21SSam Leffler wreq.wi_len = (1 + strlen(hostname) + 1) / 2; 2101a1e1d21SSam Leffler break; 2111a1e1d21SSam Leffler case WI_RID_CURRENT_SSID: 2121a1e1d21SSam Leffler if (ic->ic_state != IEEE80211_S_RUN) { 2131a1e1d21SSam Leffler wreq.wi_val[0] = 0; 2141a1e1d21SSam Leffler wreq.wi_len = 1; 2151a1e1d21SSam Leffler break; 2161a1e1d21SSam Leffler } 2171a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_bss->ni_esslen); 2181a1e1d21SSam Leffler memcpy(&wreq.wi_val[1], ic->ic_bss->ni_essid, 2191a1e1d21SSam Leffler ic->ic_bss->ni_esslen); 2201a1e1d21SSam Leffler wreq.wi_len = (1 + ic->ic_bss->ni_esslen + 1) / 2; 2211a1e1d21SSam Leffler break; 2221a1e1d21SSam Leffler case WI_RID_OWN_SSID: 2231a1e1d21SSam Leffler case WI_RID_DESIRED_SSID: 2241a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_des_esslen); 2251a1e1d21SSam Leffler memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen); 2261a1e1d21SSam Leffler wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2; 2271a1e1d21SSam Leffler break; 2281a1e1d21SSam Leffler case WI_RID_CURRENT_BSSID: 2291a1e1d21SSam Leffler if (ic->ic_state == IEEE80211_S_RUN) 2301a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss->ni_bssid); 2311a1e1d21SSam Leffler else 2321a1e1d21SSam Leffler memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN); 2331a1e1d21SSam Leffler wreq.wi_len = IEEE80211_ADDR_LEN / 2; 2341a1e1d21SSam Leffler break; 2351a1e1d21SSam Leffler case WI_RID_CHANNEL_LIST: 2361a1e1d21SSam Leffler memset(wreq.wi_val, 0, sizeof(wreq.wi_val)); 2371a1e1d21SSam Leffler /* 2381a1e1d21SSam Leffler * Since channel 0 is not available for DS, channel 1 2391a1e1d21SSam Leffler * is assigned to LSB on WaveLAN. 2401a1e1d21SSam Leffler */ 2411a1e1d21SSam Leffler if (ic->ic_phytype == IEEE80211_T_DS) 2421a1e1d21SSam Leffler i = 1; 2431a1e1d21SSam Leffler else 2441a1e1d21SSam Leffler i = 0; 2451a1e1d21SSam Leffler for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) 2461a1e1d21SSam Leffler if (isset(ic->ic_chan_active, i)) { 2471a1e1d21SSam Leffler setbit((u_int8_t *)wreq.wi_val, j); 2481a1e1d21SSam Leffler wreq.wi_len = j / 16 + 1; 2491a1e1d21SSam Leffler } 2501a1e1d21SSam Leffler break; 2511a1e1d21SSam Leffler case WI_RID_OWN_CHNL: 2521a1e1d21SSam Leffler wreq.wi_val[0] = htole16( 2531a1e1d21SSam Leffler ieee80211_chan2ieee(ic, ic->ic_ibss_chan)); 2541a1e1d21SSam Leffler wreq.wi_len = 1; 2551a1e1d21SSam Leffler break; 2561a1e1d21SSam Leffler case WI_RID_CURRENT_CHAN: 2571a1e1d21SSam Leffler wreq.wi_val[0] = htole16( 258b5c99415SSam Leffler ieee80211_chan2ieee(ic, ic->ic_curchan)); 2591a1e1d21SSam Leffler wreq.wi_len = 1; 2601a1e1d21SSam Leffler break; 2611a1e1d21SSam Leffler case WI_RID_COMMS_QUALITY: 2621a1e1d21SSam Leffler wreq.wi_val[0] = 0; /* quality */ 2638a1b9b6aSSam Leffler wreq.wi_val[1] = htole16(ic->ic_node_getrssi(ic->ic_bss)); 2641a1e1d21SSam Leffler wreq.wi_val[2] = 0; /* noise */ 2651a1e1d21SSam Leffler wreq.wi_len = 3; 2661a1e1d21SSam Leffler break; 2671a1e1d21SSam Leffler case WI_RID_PROMISC: 2681a1e1d21SSam Leffler wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0); 2691a1e1d21SSam Leffler wreq.wi_len = 1; 2701a1e1d21SSam Leffler break; 2711a1e1d21SSam Leffler case WI_RID_PORTTYPE: 2721a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_opmode); 2731a1e1d21SSam Leffler wreq.wi_len = 1; 2741a1e1d21SSam Leffler break; 2751a1e1d21SSam Leffler case WI_RID_MAC_NODE: 2761a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr); 2771a1e1d21SSam Leffler wreq.wi_len = IEEE80211_ADDR_LEN / 2; 2781a1e1d21SSam Leffler break; 2791a1e1d21SSam Leffler case WI_RID_TX_RATE: 2802c39b32cSSam Leffler if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) 2811a1e1d21SSam Leffler wreq.wi_val[0] = 0; /* auto */ 2821a1e1d21SSam Leffler else 2831a1e1d21SSam Leffler wreq.wi_val[0] = htole16( 2841a1e1d21SSam Leffler (ic->ic_sup_rates[ic->ic_curmode].rs_rates[ic->ic_fixed_rate] & 2851a1e1d21SSam Leffler IEEE80211_RATE_VAL) / 2); 2861a1e1d21SSam Leffler wreq.wi_len = 1; 2871a1e1d21SSam Leffler break; 2881a1e1d21SSam Leffler case WI_RID_CUR_TX_RATE: 2891a1e1d21SSam Leffler wreq.wi_val[0] = htole16( 2901a1e1d21SSam Leffler (ic->ic_bss->ni_rates.rs_rates[ic->ic_bss->ni_txrate] & 2911a1e1d21SSam Leffler IEEE80211_RATE_VAL) / 2); 2921a1e1d21SSam Leffler wreq.wi_len = 1; 2931a1e1d21SSam Leffler break; 2941a1e1d21SSam Leffler case WI_RID_RTS_THRESH: 2951a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_rtsthreshold); 2961a1e1d21SSam Leffler wreq.wi_len = 1; 2971a1e1d21SSam Leffler break; 2981a1e1d21SSam Leffler case WI_RID_CREATE_IBSS: 2991a1e1d21SSam Leffler wreq.wi_val[0] = 3001a1e1d21SSam Leffler htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0); 3011a1e1d21SSam Leffler wreq.wi_len = 1; 3021a1e1d21SSam Leffler break; 3031a1e1d21SSam Leffler case WI_RID_MICROWAVE_OVEN: 3041a1e1d21SSam Leffler wreq.wi_val[0] = 0; /* no ... not supported */ 3051a1e1d21SSam Leffler wreq.wi_len = 1; 3061a1e1d21SSam Leffler break; 3071a1e1d21SSam Leffler case WI_RID_ROAMING_MODE: 3088a1b9b6aSSam Leffler wreq.wi_val[0] = htole16(ic->ic_roaming); /* XXX map */ 3091a1e1d21SSam Leffler wreq.wi_len = 1; 3101a1e1d21SSam Leffler break; 3111a1e1d21SSam Leffler case WI_RID_SYSTEM_SCALE: 3121a1e1d21SSam Leffler wreq.wi_val[0] = htole16(1); /* low density ... not supp */ 3131a1e1d21SSam Leffler wreq.wi_len = 1; 3141a1e1d21SSam Leffler break; 3151a1e1d21SSam Leffler case WI_RID_PM_ENABLED: 3161a1e1d21SSam Leffler wreq.wi_val[0] = 3171a1e1d21SSam Leffler htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0); 3181a1e1d21SSam Leffler wreq.wi_len = 1; 3191a1e1d21SSam Leffler break; 3201a1e1d21SSam Leffler case WI_RID_MAX_SLEEP: 3211a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_lintval); 3221a1e1d21SSam Leffler wreq.wi_len = 1; 3231a1e1d21SSam Leffler break; 3241a1e1d21SSam Leffler case WI_RID_CUR_BEACON_INT: 3251a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_bss->ni_intval); 3261a1e1d21SSam Leffler wreq.wi_len = 1; 3271a1e1d21SSam Leffler break; 3281a1e1d21SSam Leffler case WI_RID_WEP_AVAIL: 3298a1b9b6aSSam Leffler wreq.wi_val[0] = htole16(1); /* always available */ 3301a1e1d21SSam Leffler wreq.wi_len = 1; 3311a1e1d21SSam Leffler break; 3321a1e1d21SSam Leffler case WI_RID_CNFAUTHMODE: 3331a1e1d21SSam Leffler wreq.wi_val[0] = htole16(1); /* TODO: open system only */ 3341a1e1d21SSam Leffler wreq.wi_len = 1; 3351a1e1d21SSam Leffler break; 3361a1e1d21SSam Leffler case WI_RID_ENCRYPTION: 3371a1e1d21SSam Leffler wreq.wi_val[0] = 3388a1b9b6aSSam Leffler htole16((ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0); 3391a1e1d21SSam Leffler wreq.wi_len = 1; 3401a1e1d21SSam Leffler break; 3411a1e1d21SSam Leffler case WI_RID_TX_CRYPT_KEY: 3428a1b9b6aSSam Leffler wreq.wi_val[0] = htole16(ic->ic_def_txkey); 3431a1e1d21SSam Leffler wreq.wi_len = 1; 3441a1e1d21SSam Leffler break; 3451a1e1d21SSam Leffler case WI_RID_DEFLT_CRYPT_KEYS: 3461a1e1d21SSam Leffler keys = (struct wi_ltv_keys *)&wreq; 3471a1e1d21SSam Leffler /* do not show keys to non-root user */ 348acd3428bSRobert Watson error = priv_check(curthread, PRIV_NET80211_GETKEY); 3491a1e1d21SSam Leffler if (error) { 3501a1e1d21SSam Leffler memset(keys, 0, sizeof(*keys)); 3511a1e1d21SSam Leffler error = 0; 3521a1e1d21SSam Leffler break; 3531a1e1d21SSam Leffler } 3541a1e1d21SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) { 3551a1e1d21SSam Leffler keys->wi_keys[i].wi_keylen = 3568a1b9b6aSSam Leffler htole16(ic->ic_nw_keys[i].wk_keylen); 3571a1e1d21SSam Leffler memcpy(keys->wi_keys[i].wi_keydat, 3588a1b9b6aSSam Leffler ic->ic_nw_keys[i].wk_key, 3598a1b9b6aSSam Leffler ic->ic_nw_keys[i].wk_keylen); 3601a1e1d21SSam Leffler } 3611a1e1d21SSam Leffler wreq.wi_len = sizeof(*keys) / 2; 3621a1e1d21SSam Leffler break; 3631a1e1d21SSam Leffler case WI_RID_MAX_DATALEN: 3648a1b9b6aSSam Leffler wreq.wi_val[0] = htole16(ic->ic_fragthreshold); 3651a1e1d21SSam Leffler wreq.wi_len = 1; 3661a1e1d21SSam Leffler break; 3671a1e1d21SSam Leffler case WI_RID_IFACE_STATS: 3681a1e1d21SSam Leffler /* XXX: should be implemented in lower drivers */ 3691a1e1d21SSam Leffler break; 3701a1e1d21SSam Leffler case WI_RID_READ_APS: 3711a1e1d21SSam Leffler /* 3721a1e1d21SSam Leffler * Don't return results until active scan completes. 3731a1e1d21SSam Leffler */ 3748a1b9b6aSSam Leffler if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) { 3758a1b9b6aSSam Leffler struct wi_read_ap_args args; 3768a1b9b6aSSam Leffler 3778a1b9b6aSSam Leffler args.i = 0; 3788a1b9b6aSSam Leffler args.ap = (void *)((char *)wreq.wi_val + sizeof(i)); 3798a1b9b6aSSam Leffler args.max = (void *)(&wreq + 1); 3808a1b9b6aSSam Leffler ieee80211_iterate_nodes(&ic->ic_scan, 3818a1b9b6aSSam Leffler wi_read_ap_result, &args); 3828a1b9b6aSSam Leffler memcpy(wreq.wi_val, &args.i, sizeof(args.i)); 3838a1b9b6aSSam Leffler wreq.wi_len = (sizeof(int) + 3848a1b9b6aSSam Leffler sizeof(struct wi_apinfo) * args.i) / 2; 3858a1b9b6aSSam Leffler } else 3861a1e1d21SSam Leffler error = EINPROGRESS; 3871a1e1d21SSam Leffler break; 3881a1e1d21SSam Leffler case WI_RID_PRISM2: 3898a1b9b6aSSam Leffler /* NB: we lie so WI_RID_SCAN_RES can include rates */ 3908a1b9b6aSSam Leffler wreq.wi_val[0] = 1; 3911a1e1d21SSam Leffler wreq.wi_len = sizeof(u_int16_t) / 2; 3921a1e1d21SSam Leffler break; 3931a1e1d21SSam Leffler case WI_RID_SCAN_RES: /* compatibility interface */ 3948a1b9b6aSSam Leffler if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) { 3958a1b9b6aSSam Leffler struct wi_read_prism2_args args; 3968a1b9b6aSSam Leffler struct wi_scan_p2_hdr *p2; 3978a1b9b6aSSam Leffler 3988a1b9b6aSSam Leffler /* NB: use Prism2 format so we can include rate info */ 3998a1b9b6aSSam Leffler p2 = (struct wi_scan_p2_hdr *)wreq.wi_val; 4008a1b9b6aSSam Leffler args.i = 0; 4018a1b9b6aSSam Leffler args.res = (void *)&p2[1]; 4028a1b9b6aSSam Leffler args.max = (void *)(&wreq + 1); 4038a1b9b6aSSam Leffler ieee80211_iterate_nodes(&ic->ic_scan, 4048a1b9b6aSSam Leffler wi_read_prism2_result, &args); 4058a1b9b6aSSam Leffler p2->wi_rsvd = 0; 4068a1b9b6aSSam Leffler p2->wi_reason = args.i; 4078a1b9b6aSSam Leffler wreq.wi_len = (sizeof(*p2) + 4088a1b9b6aSSam Leffler sizeof(struct wi_scan_res) * args.i) / 2; 4098a1b9b6aSSam Leffler } else 4101a1e1d21SSam Leffler error = EINPROGRESS; 4111a1e1d21SSam Leffler break; 4128a1b9b6aSSam Leffler case WI_RID_READ_CACHE: { 4138a1b9b6aSSam Leffler struct wi_read_sigcache_args args; 4148a1b9b6aSSam Leffler args.i = 0; 4158a1b9b6aSSam Leffler args.wsc = (struct wi_sigcache *) wreq.wi_val; 4168a1b9b6aSSam Leffler args.max = (void *)(&wreq + 1); 4178a1b9b6aSSam Leffler ieee80211_iterate_nodes(&ic->ic_scan, wi_read_sigcache, &args); 4188a1b9b6aSSam Leffler wreq.wi_len = sizeof(struct wi_sigcache) * args.i / 2; 4198a1b9b6aSSam Leffler break; 4201a1e1d21SSam Leffler } 4211a1e1d21SSam Leffler default: 4221a1e1d21SSam Leffler error = EINVAL; 4231a1e1d21SSam Leffler break; 4241a1e1d21SSam Leffler } 4251a1e1d21SSam Leffler if (error == 0) { 4261a1e1d21SSam Leffler wreq.wi_len++; 4271a1e1d21SSam Leffler error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); 4281a1e1d21SSam Leffler } 4291a1e1d21SSam Leffler return error; 4301a1e1d21SSam Leffler } 4311a1e1d21SSam Leffler 4321a1e1d21SSam Leffler static int 4331a1e1d21SSam Leffler findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) 4341a1e1d21SSam Leffler { 4351a1e1d21SSam Leffler #define IEEERATE(_ic,_m,_i) \ 4361a1e1d21SSam Leffler ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) 4371a1e1d21SSam Leffler int i, nrates = ic->ic_sup_rates[mode].rs_nrates; 4381a1e1d21SSam Leffler for (i = 0; i < nrates; i++) 4391a1e1d21SSam Leffler if (IEEERATE(ic, mode, i) == rate) 4401a1e1d21SSam Leffler return i; 4411a1e1d21SSam Leffler return -1; 4421a1e1d21SSam Leffler #undef IEEERATE 4431a1e1d21SSam Leffler } 4441a1e1d21SSam Leffler 44593685685SSam Leffler /* 44693685685SSam Leffler * Prepare to do a user-initiated scan for AP's. If no 44793685685SSam Leffler * current/default channel is setup or the current channel 44893685685SSam Leffler * is invalid then pick the first available channel from 44993685685SSam Leffler * the active list as the place to start the scan. 45093685685SSam Leffler */ 45193685685SSam Leffler static int 4528a1b9b6aSSam Leffler ieee80211_setupscan(struct ieee80211com *ic, const u_int8_t chanlist[]) 45393685685SSam Leffler { 45493685685SSam Leffler 4558a1b9b6aSSam Leffler /* 4568a1b9b6aSSam Leffler * XXX don't permit a scan to be started unless we 4578a1b9b6aSSam Leffler * know the device is ready. For the moment this means 4588a1b9b6aSSam Leffler * the device is marked up as this is the required to 4598a1b9b6aSSam Leffler * initialize the hardware. It would be better to permit 4608a1b9b6aSSam Leffler * scanning prior to being up but that'll require some 4618a1b9b6aSSam Leffler * changes to the infrastructure. 4628a1b9b6aSSam Leffler */ 4638a1b9b6aSSam Leffler if (!IS_UP(ic)) 4648a1b9b6aSSam Leffler return EINVAL; 4658a1b9b6aSSam Leffler memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); 46693685685SSam Leffler /* 4678a1b9b6aSSam Leffler * We force the state to INIT before calling ieee80211_new_state 4688a1b9b6aSSam Leffler * to get ieee80211_begin_scan called. We really want to scan w/o 4698a1b9b6aSSam Leffler * altering the current state but that's not possible right now. 47093685685SSam Leffler */ 4718a1b9b6aSSam Leffler /* XXX handle proberequest case */ 4728a1b9b6aSSam Leffler ic->ic_state = IEEE80211_S_INIT; /* XXX bypass state machine */ 4738a1b9b6aSSam Leffler return 0; 47493685685SSam Leffler } 47593685685SSam Leffler 4761a1e1d21SSam Leffler int 4778a1b9b6aSSam Leffler ieee80211_cfgset(struct ieee80211com *ic, u_long cmd, caddr_t data) 4781a1e1d21SSam Leffler { 4798a1b9b6aSSam Leffler struct ifnet *ifp = ic->ic_ifp; 4801a1e1d21SSam Leffler int i, j, len, error, rate; 4811a1e1d21SSam Leffler struct ifreq *ifr = (struct ifreq *)data; 4821a1e1d21SSam Leffler struct wi_ltv_keys *keys; 4831a1e1d21SSam Leffler struct wi_req wreq; 4841a1e1d21SSam Leffler u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)]; 4851a1e1d21SSam Leffler 4861a1e1d21SSam Leffler error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 4871a1e1d21SSam Leffler if (error) 4881a1e1d21SSam Leffler return error; 4891a1e1d21SSam Leffler len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0; 4901a1e1d21SSam Leffler switch (wreq.wi_type) { 4911a1e1d21SSam Leffler case WI_RID_SERIALNO: 4921a1e1d21SSam Leffler case WI_RID_NODENAME: 4931a1e1d21SSam Leffler return EPERM; 4941a1e1d21SSam Leffler case WI_RID_CURRENT_SSID: 4951a1e1d21SSam Leffler return EPERM; 4961a1e1d21SSam Leffler case WI_RID_OWN_SSID: 4971a1e1d21SSam Leffler case WI_RID_DESIRED_SSID: 4981a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) * 2 > len || 4991a1e1d21SSam Leffler le16toh(wreq.wi_val[0]) > IEEE80211_NWID_LEN) { 5001a1e1d21SSam Leffler error = ENOSPC; 5011a1e1d21SSam Leffler break; 5021a1e1d21SSam Leffler } 5031a1e1d21SSam Leffler memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid)); 5041a1e1d21SSam Leffler ic->ic_des_esslen = le16toh(wreq.wi_val[0]) * 2; 505b9ee58c4SSam Leffler memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen); 5061a1e1d21SSam Leffler error = ENETRESET; 5071a1e1d21SSam Leffler break; 5081a1e1d21SSam Leffler case WI_RID_CURRENT_BSSID: 5091a1e1d21SSam Leffler return EPERM; 5101a1e1d21SSam Leffler case WI_RID_OWN_CHNL: 5111a1e1d21SSam Leffler if (len != 2) 5121a1e1d21SSam Leffler return EINVAL; 5131a1e1d21SSam Leffler i = le16toh(wreq.wi_val[0]); 5141a1e1d21SSam Leffler if (i < 0 || 5151a1e1d21SSam Leffler i > IEEE80211_CHAN_MAX || 5161a1e1d21SSam Leffler isclr(ic->ic_chan_active, i)) 5171a1e1d21SSam Leffler return EINVAL; 5181a1e1d21SSam Leffler ic->ic_ibss_chan = &ic->ic_channels[i]; 5198a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR) 5208a1b9b6aSSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 5218a1b9b6aSSam Leffler else 5221a1e1d21SSam Leffler error = ENETRESET; 5231a1e1d21SSam Leffler break; 5241a1e1d21SSam Leffler case WI_RID_CURRENT_CHAN: 5251a1e1d21SSam Leffler return EPERM; 5261a1e1d21SSam Leffler case WI_RID_COMMS_QUALITY: 5271a1e1d21SSam Leffler return EPERM; 5281a1e1d21SSam Leffler case WI_RID_PROMISC: 5291a1e1d21SSam Leffler if (len != 2) 5301a1e1d21SSam Leffler return EINVAL; 5311a1e1d21SSam Leffler if (ifp->if_flags & IFF_PROMISC) { 5321a1e1d21SSam Leffler if (wreq.wi_val[0] == 0) { 5331a1e1d21SSam Leffler ifp->if_flags &= ~IFF_PROMISC; 5341a1e1d21SSam Leffler error = ENETRESET; 5351a1e1d21SSam Leffler } 5361a1e1d21SSam Leffler } else { 5371a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) { 5381a1e1d21SSam Leffler ifp->if_flags |= IFF_PROMISC; 5391a1e1d21SSam Leffler error = ENETRESET; 5401a1e1d21SSam Leffler } 5411a1e1d21SSam Leffler } 5421a1e1d21SSam Leffler break; 5431a1e1d21SSam Leffler case WI_RID_PORTTYPE: 5441a1e1d21SSam Leffler if (len != 2) 5451a1e1d21SSam Leffler return EINVAL; 5461a1e1d21SSam Leffler switch (le16toh(wreq.wi_val[0])) { 5471a1e1d21SSam Leffler case IEEE80211_M_STA: 5481a1e1d21SSam Leffler break; 5491a1e1d21SSam Leffler case IEEE80211_M_IBSS: 5501a1e1d21SSam Leffler if (!(ic->ic_caps & IEEE80211_C_IBSS)) 5511a1e1d21SSam Leffler return EINVAL; 5521a1e1d21SSam Leffler break; 5531a1e1d21SSam Leffler case IEEE80211_M_AHDEMO: 5541a1e1d21SSam Leffler if (ic->ic_phytype != IEEE80211_T_DS || 5551a1e1d21SSam Leffler !(ic->ic_caps & IEEE80211_C_AHDEMO)) 5561a1e1d21SSam Leffler return EINVAL; 5571a1e1d21SSam Leffler break; 5581a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 5591a1e1d21SSam Leffler if (!(ic->ic_caps & IEEE80211_C_HOSTAP)) 5601a1e1d21SSam Leffler return EINVAL; 5611a1e1d21SSam Leffler break; 5621a1e1d21SSam Leffler default: 5631a1e1d21SSam Leffler return EINVAL; 5641a1e1d21SSam Leffler } 5651a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) { 5661a1e1d21SSam Leffler ic->ic_opmode = le16toh(wreq.wi_val[0]); 5678a1b9b6aSSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 5681a1e1d21SSam Leffler } 5691a1e1d21SSam Leffler break; 5701a1e1d21SSam Leffler #if 0 5711a1e1d21SSam Leffler case WI_RID_MAC_NODE: 5721a1e1d21SSam Leffler if (len != IEEE80211_ADDR_LEN) 5731a1e1d21SSam Leffler return EINVAL; 5741a1e1d21SSam Leffler IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val); 5751a1e1d21SSam Leffler /* if_init will copy lladdr into ic_myaddr */ 5761a1e1d21SSam Leffler error = ENETRESET; 5771a1e1d21SSam Leffler break; 5781a1e1d21SSam Leffler #endif 5791a1e1d21SSam Leffler case WI_RID_TX_RATE: 5801a1e1d21SSam Leffler if (len != 2) 5811a1e1d21SSam Leffler return EINVAL; 5821a1e1d21SSam Leffler if (wreq.wi_val[0] == 0) { 5831a1e1d21SSam Leffler /* auto */ 5842c39b32cSSam Leffler ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; 5851a1e1d21SSam Leffler break; 5861a1e1d21SSam Leffler } 5871a1e1d21SSam Leffler rate = 2 * le16toh(wreq.wi_val[0]); 5881a1e1d21SSam Leffler if (ic->ic_curmode == IEEE80211_MODE_AUTO) { 5891a1e1d21SSam Leffler /* 5901a1e1d21SSam Leffler * In autoselect mode search for the rate. We take 5911a1e1d21SSam Leffler * the first instance which may not be right, but we 5921a1e1d21SSam Leffler * are limited by the interface. Note that we also 5931a1e1d21SSam Leffler * lock the mode to insure the rate is meaningful 5941a1e1d21SSam Leffler * when it is used. 5951a1e1d21SSam Leffler */ 5961a1e1d21SSam Leffler for (j = IEEE80211_MODE_11A; 5971a1e1d21SSam Leffler j < IEEE80211_MODE_MAX; j++) { 5981a1e1d21SSam Leffler if ((ic->ic_modecaps & (1<<j)) == 0) 5991a1e1d21SSam Leffler continue; 6001a1e1d21SSam Leffler i = findrate(ic, j, rate); 6011a1e1d21SSam Leffler if (i != -1) { 6021a1e1d21SSam Leffler /* lock mode too */ 6031a1e1d21SSam Leffler ic->ic_curmode = j; 6041a1e1d21SSam Leffler goto setrate; 6051a1e1d21SSam Leffler } 6061a1e1d21SSam Leffler } 6071a1e1d21SSam Leffler } else { 6081a1e1d21SSam Leffler i = findrate(ic, ic->ic_curmode, rate); 6091a1e1d21SSam Leffler if (i != -1) 6101a1e1d21SSam Leffler goto setrate; 6111a1e1d21SSam Leffler } 6121a1e1d21SSam Leffler return EINVAL; 6131a1e1d21SSam Leffler setrate: 6141a1e1d21SSam Leffler ic->ic_fixed_rate = i; 6158a1b9b6aSSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 6161a1e1d21SSam Leffler break; 6171a1e1d21SSam Leffler case WI_RID_CUR_TX_RATE: 6181a1e1d21SSam Leffler return EPERM; 6191a1e1d21SSam Leffler case WI_RID_RTS_THRESH: 6201a1e1d21SSam Leffler if (len != 2) 6211a1e1d21SSam Leffler return EINVAL; 6221a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) != IEEE80211_MAX_LEN) 6231a1e1d21SSam Leffler return EINVAL; /* TODO: RTS */ 6241a1e1d21SSam Leffler break; 6251a1e1d21SSam Leffler case WI_RID_CREATE_IBSS: 6261a1e1d21SSam Leffler if (len != 2) 6271a1e1d21SSam Leffler return EINVAL; 6281a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) { 6291a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_IBSS) == 0) 6301a1e1d21SSam Leffler return EINVAL; 6311a1e1d21SSam Leffler if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) { 6321a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_IBSSON; 6331a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS && 6341a1e1d21SSam Leffler ic->ic_state == IEEE80211_S_SCAN) 6358a1b9b6aSSam Leffler error = IS_UP_AUTO(ic) ? ENETRESET : 0; 6361a1e1d21SSam Leffler } 6371a1e1d21SSam Leffler } else { 6381a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_IBSSON) { 6391a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_IBSSON; 6401a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_SIBSS) { 6411a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_SIBSS; 6428a1b9b6aSSam Leffler error = IS_UP_AUTO(ic) ? ENETRESET : 0; 6431a1e1d21SSam Leffler } 6441a1e1d21SSam Leffler } 6451a1e1d21SSam Leffler } 6461a1e1d21SSam Leffler break; 6471a1e1d21SSam Leffler case WI_RID_MICROWAVE_OVEN: 6481a1e1d21SSam Leffler if (len != 2) 6491a1e1d21SSam Leffler return EINVAL; 6501a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) 6511a1e1d21SSam Leffler return EINVAL; /* not supported */ 6521a1e1d21SSam Leffler break; 6531a1e1d21SSam Leffler case WI_RID_ROAMING_MODE: 6541a1e1d21SSam Leffler if (len != 2) 6551a1e1d21SSam Leffler return EINVAL; 6568a1b9b6aSSam Leffler i = le16toh(wreq.wi_val[0]); 6578a1b9b6aSSam Leffler if (i > IEEE80211_ROAMING_MANUAL) 6581a1e1d21SSam Leffler return EINVAL; /* not supported */ 6598a1b9b6aSSam Leffler ic->ic_roaming = i; 6601a1e1d21SSam Leffler break; 6611a1e1d21SSam Leffler case WI_RID_SYSTEM_SCALE: 6621a1e1d21SSam Leffler if (len != 2) 6631a1e1d21SSam Leffler return EINVAL; 6641a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) != 1) 6651a1e1d21SSam Leffler return EINVAL; /* not supported */ 6661a1e1d21SSam Leffler break; 6671a1e1d21SSam Leffler case WI_RID_PM_ENABLED: 6681a1e1d21SSam Leffler if (len != 2) 6691a1e1d21SSam Leffler return EINVAL; 6701a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) { 6711a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) 6721a1e1d21SSam Leffler return EINVAL; 6731a1e1d21SSam Leffler if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { 6741a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_PMGTON; 6758a1b9b6aSSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 6761a1e1d21SSam Leffler } 6771a1e1d21SSam Leffler } else { 6781a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_PMGTON) { 6791a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_PMGTON; 6808a1b9b6aSSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 6811a1e1d21SSam Leffler } 6821a1e1d21SSam Leffler } 6831a1e1d21SSam Leffler break; 6841a1e1d21SSam Leffler case WI_RID_MAX_SLEEP: 6851a1e1d21SSam Leffler if (len != 2) 6861a1e1d21SSam Leffler return EINVAL; 6871a1e1d21SSam Leffler ic->ic_lintval = le16toh(wreq.wi_val[0]); 6881a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_PMGTON) 6898a1b9b6aSSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 6901a1e1d21SSam Leffler break; 6911a1e1d21SSam Leffler case WI_RID_CUR_BEACON_INT: 6921a1e1d21SSam Leffler return EPERM; 6931a1e1d21SSam Leffler case WI_RID_WEP_AVAIL: 6941a1e1d21SSam Leffler return EPERM; 6951a1e1d21SSam Leffler case WI_RID_CNFAUTHMODE: 6961a1e1d21SSam Leffler if (len != 2) 6971a1e1d21SSam Leffler return EINVAL; 6988a1b9b6aSSam Leffler i = le16toh(wreq.wi_val[0]); 6998a1b9b6aSSam Leffler if (i > IEEE80211_AUTH_WPA) 7008a1b9b6aSSam Leffler return EINVAL; 7018a1b9b6aSSam Leffler ic->ic_bss->ni_authmode = i; /* XXX ENETRESET? */ 7028a1b9b6aSSam Leffler error = ENETRESET; 7031a1e1d21SSam Leffler break; 7041a1e1d21SSam Leffler case WI_RID_ENCRYPTION: 7051a1e1d21SSam Leffler if (len != 2) 7061a1e1d21SSam Leffler return EINVAL; 7071a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) { 7081a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_WEP) == 0) 7091a1e1d21SSam Leffler return EINVAL; 7108a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { 7118a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_PRIVACY; 7121a1e1d21SSam Leffler error = ENETRESET; 7131a1e1d21SSam Leffler } 7141a1e1d21SSam Leffler } else { 7158a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_PRIVACY) { 7168a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_PRIVACY; 7171a1e1d21SSam Leffler error = ENETRESET; 7181a1e1d21SSam Leffler } 7191a1e1d21SSam Leffler } 7201a1e1d21SSam Leffler break; 7211a1e1d21SSam Leffler case WI_RID_TX_CRYPT_KEY: 7221a1e1d21SSam Leffler if (len != 2) 7231a1e1d21SSam Leffler return EINVAL; 7241a1e1d21SSam Leffler i = le16toh(wreq.wi_val[0]); 7251a1e1d21SSam Leffler if (i >= IEEE80211_WEP_NKID) 7261a1e1d21SSam Leffler return EINVAL; 7278a1b9b6aSSam Leffler ic->ic_def_txkey = i; 7288a1b9b6aSSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 7291a1e1d21SSam Leffler break; 7301a1e1d21SSam Leffler case WI_RID_DEFLT_CRYPT_KEYS: 7311a1e1d21SSam Leffler if (len != sizeof(struct wi_ltv_keys)) 7321a1e1d21SSam Leffler return EINVAL; 7331a1e1d21SSam Leffler keys = (struct wi_ltv_keys *)&wreq; 7341a1e1d21SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) { 7351a1e1d21SSam Leffler len = le16toh(keys->wi_keys[i].wi_keylen); 7361a1e1d21SSam Leffler if (len != 0 && len < IEEE80211_WEP_KEYLEN) 7371a1e1d21SSam Leffler return EINVAL; 7388a1b9b6aSSam Leffler if (len > IEEE80211_KEYBUF_SIZE) 7391a1e1d21SSam Leffler return EINVAL; 7401a1e1d21SSam Leffler } 7411a1e1d21SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) { 7428a1b9b6aSSam Leffler struct ieee80211_key *k = &ic->ic_nw_keys[i]; 7438a1b9b6aSSam Leffler 7441a1e1d21SSam Leffler len = le16toh(keys->wi_keys[i].wi_keylen); 7458a1b9b6aSSam Leffler k->wk_keylen = len; 7468a1b9b6aSSam Leffler k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; 7478a1b9b6aSSam Leffler memset(k->wk_key, 0, sizeof(k->wk_key)); 7488a1b9b6aSSam Leffler memcpy(k->wk_key, keys->wi_keys[i].wi_keydat, len); 7498a1b9b6aSSam Leffler #if 0 7508a1b9b6aSSam Leffler k->wk_type = IEEE80211_CIPHER_WEP; 7518a1b9b6aSSam Leffler #endif 7521a1e1d21SSam Leffler } 7531a1e1d21SSam Leffler error = ENETRESET; 7541a1e1d21SSam Leffler break; 7551a1e1d21SSam Leffler case WI_RID_MAX_DATALEN: 7561a1e1d21SSam Leffler if (len != 2) 7571a1e1d21SSam Leffler return EINVAL; 7581a1e1d21SSam Leffler len = le16toh(wreq.wi_val[0]); 7591a1e1d21SSam Leffler if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN) 7601a1e1d21SSam Leffler return EINVAL; 7611a1e1d21SSam Leffler ic->ic_fragthreshold = len; 7628a1b9b6aSSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 7631a1e1d21SSam Leffler break; 7641a1e1d21SSam Leffler case WI_RID_IFACE_STATS: 7651a1e1d21SSam Leffler error = EPERM; 7661a1e1d21SSam Leffler break; 7671a1e1d21SSam Leffler case WI_RID_SCAN_REQ: /* XXX wicontrol */ 7681a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) 7691a1e1d21SSam Leffler break; 7708a1b9b6aSSam Leffler error = ieee80211_setupscan(ic, ic->ic_chan_avail); 77193685685SSam Leffler if (error == 0) 772a11c9a5cSSam Leffler error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 7731a1e1d21SSam Leffler break; 7741a1e1d21SSam Leffler case WI_RID_SCAN_APS: 7751a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) 7761a1e1d21SSam Leffler break; 7771a1e1d21SSam Leffler len--; /* XXX: tx rate? */ 7781a1e1d21SSam Leffler /* FALLTHRU */ 7791a1e1d21SSam Leffler case WI_RID_CHANNEL_LIST: 7801a1e1d21SSam Leffler memset(chanlist, 0, sizeof(chanlist)); 7811a1e1d21SSam Leffler /* 7821a1e1d21SSam Leffler * Since channel 0 is not available for DS, channel 1 7831a1e1d21SSam Leffler * is assigned to LSB on WaveLAN. 7841a1e1d21SSam Leffler */ 7851a1e1d21SSam Leffler if (ic->ic_phytype == IEEE80211_T_DS) 7861a1e1d21SSam Leffler i = 1; 7871a1e1d21SSam Leffler else 7881a1e1d21SSam Leffler i = 0; 7891a1e1d21SSam Leffler for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { 7901a1e1d21SSam Leffler if ((j / 8) >= len) 7911a1e1d21SSam Leffler break; 7921a1e1d21SSam Leffler if (isclr((u_int8_t *)wreq.wi_val, j)) 7931a1e1d21SSam Leffler continue; 7941a1e1d21SSam Leffler if (isclr(ic->ic_chan_active, i)) { 7951a1e1d21SSam Leffler if (wreq.wi_type != WI_RID_CHANNEL_LIST) 7961a1e1d21SSam Leffler continue; 7971a1e1d21SSam Leffler if (isclr(ic->ic_chan_avail, i)) 7981a1e1d21SSam Leffler return EPERM; 7991a1e1d21SSam Leffler } 8001a1e1d21SSam Leffler setbit(chanlist, i); 8011a1e1d21SSam Leffler } 8028a1b9b6aSSam Leffler error = ieee80211_setupscan(ic, chanlist); 80393685685SSam Leffler if (wreq.wi_type == WI_RID_CHANNEL_LIST) { 80493685685SSam Leffler /* NB: ignore error from ieee80211_setupscan */ 8051a1e1d21SSam Leffler error = ENETRESET; 80693685685SSam Leffler } else if (error == 0) 807a11c9a5cSSam Leffler error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 8081a1e1d21SSam Leffler break; 8091a1e1d21SSam Leffler default: 8101a1e1d21SSam Leffler error = EINVAL; 8111a1e1d21SSam Leffler break; 8121a1e1d21SSam Leffler } 8138a1b9b6aSSam Leffler if (error == ENETRESET && !IS_UP_AUTO(ic)) 8148a1b9b6aSSam Leffler error = 0; 8151a1e1d21SSam Leffler return error; 8161a1e1d21SSam Leffler } 8171a1e1d21SSam Leffler 8188a1b9b6aSSam Leffler static int 8198a1b9b6aSSam Leffler cap2cipher(int flag) 8208a1b9b6aSSam Leffler { 8218a1b9b6aSSam Leffler switch (flag) { 8228a1b9b6aSSam Leffler case IEEE80211_C_WEP: return IEEE80211_CIPHER_WEP; 8238a1b9b6aSSam Leffler case IEEE80211_C_AES: return IEEE80211_CIPHER_AES_OCB; 8248a1b9b6aSSam Leffler case IEEE80211_C_AES_CCM: return IEEE80211_CIPHER_AES_CCM; 8258a1b9b6aSSam Leffler case IEEE80211_C_CKIP: return IEEE80211_CIPHER_CKIP; 8268a1b9b6aSSam Leffler case IEEE80211_C_TKIP: return IEEE80211_CIPHER_TKIP; 8278a1b9b6aSSam Leffler } 8288a1b9b6aSSam Leffler return -1; 8298a1b9b6aSSam Leffler } 8308a1b9b6aSSam Leffler 8318a1b9b6aSSam Leffler static int 8328a1b9b6aSSam Leffler ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq) 8338a1b9b6aSSam Leffler { 8348a1b9b6aSSam Leffler struct ieee80211_node *ni; 8358a1b9b6aSSam Leffler struct ieee80211req_key ik; 8368a1b9b6aSSam Leffler struct ieee80211_key *wk; 8378a1b9b6aSSam Leffler const struct ieee80211_cipher *cip; 8388a1b9b6aSSam Leffler u_int kid; 8398a1b9b6aSSam Leffler int error; 8408a1b9b6aSSam Leffler 8418a1b9b6aSSam Leffler if (ireq->i_len != sizeof(ik)) 8428a1b9b6aSSam Leffler return EINVAL; 8438a1b9b6aSSam Leffler error = copyin(ireq->i_data, &ik, sizeof(ik)); 8448a1b9b6aSSam Leffler if (error) 8458a1b9b6aSSam Leffler return error; 8468a1b9b6aSSam Leffler kid = ik.ik_keyix; 8478a1b9b6aSSam Leffler if (kid == IEEE80211_KEYIX_NONE) { 848acc4f7f5SSam Leffler ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr); 8498a1b9b6aSSam Leffler if (ni == NULL) 8508a1b9b6aSSam Leffler return EINVAL; /* XXX */ 8518a1b9b6aSSam Leffler wk = &ni->ni_ucastkey; 8528a1b9b6aSSam Leffler } else { 8538a1b9b6aSSam Leffler if (kid >= IEEE80211_WEP_NKID) 8548a1b9b6aSSam Leffler return EINVAL; 8558a1b9b6aSSam Leffler wk = &ic->ic_nw_keys[kid]; 8568a1b9b6aSSam Leffler IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr); 8578a1b9b6aSSam Leffler ni = NULL; 8588a1b9b6aSSam Leffler } 8598a1b9b6aSSam Leffler cip = wk->wk_cipher; 8608a1b9b6aSSam Leffler ik.ik_type = cip->ic_cipher; 8618a1b9b6aSSam Leffler ik.ik_keylen = wk->wk_keylen; 8628a1b9b6aSSam Leffler ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV); 8638a1b9b6aSSam Leffler if (wk->wk_keyix == ic->ic_def_txkey) 8648a1b9b6aSSam Leffler ik.ik_flags |= IEEE80211_KEY_DEFAULT; 865acd3428bSRobert Watson if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) { 8668a1b9b6aSSam Leffler /* NB: only root can read key data */ 8678a1b9b6aSSam Leffler ik.ik_keyrsc = wk->wk_keyrsc; 8688a1b9b6aSSam Leffler ik.ik_keytsc = wk->wk_keytsc; 8698a1b9b6aSSam Leffler memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen); 8708a1b9b6aSSam Leffler if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) { 8718a1b9b6aSSam Leffler memcpy(ik.ik_keydata+wk->wk_keylen, 8728a1b9b6aSSam Leffler wk->wk_key + IEEE80211_KEYBUF_SIZE, 8738a1b9b6aSSam Leffler IEEE80211_MICBUF_SIZE); 8748a1b9b6aSSam Leffler ik.ik_keylen += IEEE80211_MICBUF_SIZE; 8758a1b9b6aSSam Leffler } 8768a1b9b6aSSam Leffler } else { 8778a1b9b6aSSam Leffler ik.ik_keyrsc = 0; 8788a1b9b6aSSam Leffler ik.ik_keytsc = 0; 8798a1b9b6aSSam Leffler memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata)); 8808a1b9b6aSSam Leffler } 8818a1b9b6aSSam Leffler if (ni != NULL) 8828a1b9b6aSSam Leffler ieee80211_free_node(ni); 8838a1b9b6aSSam Leffler return copyout(&ik, ireq->i_data, sizeof(ik)); 8848a1b9b6aSSam Leffler } 8858a1b9b6aSSam Leffler 8868a1b9b6aSSam Leffler static int 8878a1b9b6aSSam Leffler ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) 8888a1b9b6aSSam Leffler { 8898a1b9b6aSSam Leffler 890e55e5e42SSam Leffler if (sizeof(ic->ic_chan_active) < ireq->i_len) 8918a1b9b6aSSam Leffler ireq->i_len = sizeof(ic->ic_chan_active); 8928a1b9b6aSSam Leffler return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len); 8938a1b9b6aSSam Leffler } 8948a1b9b6aSSam Leffler 8958a1b9b6aSSam Leffler static int 8968a1b9b6aSSam Leffler ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq) 8978a1b9b6aSSam Leffler { 8988a1b9b6aSSam Leffler struct ieee80211req_chaninfo chans; /* XXX off stack? */ 8998a1b9b6aSSam Leffler int i, space; 9008a1b9b6aSSam Leffler 9018a1b9b6aSSam Leffler /* 9028a1b9b6aSSam Leffler * Since channel 0 is not available for DS, channel 1 9038a1b9b6aSSam Leffler * is assigned to LSB on WaveLAN. 9048a1b9b6aSSam Leffler */ 9058a1b9b6aSSam Leffler if (ic->ic_phytype == IEEE80211_T_DS) 9068a1b9b6aSSam Leffler i = 1; 9078a1b9b6aSSam Leffler else 9088a1b9b6aSSam Leffler i = 0; 9098a1b9b6aSSam Leffler memset(&chans, 0, sizeof(chans)); 9108a1b9b6aSSam Leffler for (; i <= IEEE80211_CHAN_MAX; i++) 9118a1b9b6aSSam Leffler if (isset(ic->ic_chan_avail, i)) { 9128a1b9b6aSSam Leffler struct ieee80211_channel *c = &ic->ic_channels[i]; 9138a1b9b6aSSam Leffler chans.ic_chans[chans.ic_nchans].ic_freq = c->ic_freq; 9148a1b9b6aSSam Leffler chans.ic_chans[chans.ic_nchans].ic_flags = c->ic_flags; 9158a1b9b6aSSam Leffler chans.ic_nchans++; 9168a1b9b6aSSam Leffler } 9178a1b9b6aSSam Leffler space = __offsetof(struct ieee80211req_chaninfo, 9188a1b9b6aSSam Leffler ic_chans[chans.ic_nchans]); 9198a1b9b6aSSam Leffler if (space > ireq->i_len) 9208a1b9b6aSSam Leffler space = ireq->i_len; 9218a1b9b6aSSam Leffler return copyout(&chans, ireq->i_data, space); 9228a1b9b6aSSam Leffler } 9238a1b9b6aSSam Leffler 9248a1b9b6aSSam Leffler static int 9258a1b9b6aSSam Leffler ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq) 9268a1b9b6aSSam Leffler { 9278a1b9b6aSSam Leffler struct ieee80211_node *ni; 9288a1b9b6aSSam Leffler struct ieee80211req_wpaie wpaie; 9298a1b9b6aSSam Leffler int error; 9308a1b9b6aSSam Leffler 9318a1b9b6aSSam Leffler if (ireq->i_len < IEEE80211_ADDR_LEN) 9328a1b9b6aSSam Leffler return EINVAL; 9338a1b9b6aSSam Leffler error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN); 9348a1b9b6aSSam Leffler if (error != 0) 9358a1b9b6aSSam Leffler return error; 936acc4f7f5SSam Leffler ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr); 9378a1b9b6aSSam Leffler if (ni == NULL) 9388a1b9b6aSSam Leffler return EINVAL; /* XXX */ 9398a1b9b6aSSam Leffler memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie)); 9408a1b9b6aSSam Leffler if (ni->ni_wpa_ie != NULL) { 9418a1b9b6aSSam Leffler int ielen = ni->ni_wpa_ie[1] + 2; 9428a1b9b6aSSam Leffler if (ielen > sizeof(wpaie.wpa_ie)) 9438a1b9b6aSSam Leffler ielen = sizeof(wpaie.wpa_ie); 9448a1b9b6aSSam Leffler memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen); 9458a1b9b6aSSam Leffler } 9468a1b9b6aSSam Leffler ieee80211_free_node(ni); 9478a1b9b6aSSam Leffler if (ireq->i_len > sizeof(wpaie)) 9488a1b9b6aSSam Leffler ireq->i_len = sizeof(wpaie); 9498a1b9b6aSSam Leffler return copyout(&wpaie, ireq->i_data, ireq->i_len); 9508a1b9b6aSSam Leffler } 9518a1b9b6aSSam Leffler 9528a1b9b6aSSam Leffler static int 9538a1b9b6aSSam Leffler ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq) 9548a1b9b6aSSam Leffler { 9558a1b9b6aSSam Leffler struct ieee80211_node *ni; 9568a1b9b6aSSam Leffler u_int8_t macaddr[IEEE80211_ADDR_LEN]; 9578a1b9b6aSSam Leffler const int off = __offsetof(struct ieee80211req_sta_stats, is_stats); 9588a1b9b6aSSam Leffler int error; 9598a1b9b6aSSam Leffler 9608a1b9b6aSSam Leffler if (ireq->i_len < off) 9618a1b9b6aSSam Leffler return EINVAL; 9628a1b9b6aSSam Leffler error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); 9638a1b9b6aSSam Leffler if (error != 0) 9648a1b9b6aSSam Leffler return error; 965acc4f7f5SSam Leffler ni = ieee80211_find_node(&ic->ic_sta, macaddr); 966d1c85daeSSam Leffler if (ni == NULL) { 967d1c85daeSSam Leffler /* XXX special-case sta-mode until bss is node in ic_sta */ 968d1c85daeSSam Leffler if (ic->ic_opmode != IEEE80211_M_STA) 969d1c85daeSSam Leffler return ENOENT; 970d1c85daeSSam Leffler ni = ieee80211_ref_node(ic->ic_bss); 971d1c85daeSSam Leffler } 9728a1b9b6aSSam Leffler if (ireq->i_len > sizeof(struct ieee80211req_sta_stats)) 9738a1b9b6aSSam Leffler ireq->i_len = sizeof(struct ieee80211req_sta_stats); 9748a1b9b6aSSam Leffler /* NB: copy out only the statistics */ 9758a1b9b6aSSam Leffler error = copyout(&ni->ni_stats, (u_int8_t *) ireq->i_data + off, 9768a1b9b6aSSam Leffler ireq->i_len - off); 9778a1b9b6aSSam Leffler ieee80211_free_node(ni); 9788a1b9b6aSSam Leffler return error; 9798a1b9b6aSSam Leffler } 9808a1b9b6aSSam Leffler 981239cc3b6SSam Leffler #ifdef COMPAT_FREEBSD6 982239cc3b6SSam Leffler #define IEEE80211_IOC_SCAN_RESULTS_OLD 24 983239cc3b6SSam Leffler 984239cc3b6SSam Leffler struct scan_result_old { 985239cc3b6SSam Leffler u_int16_t isr_len; /* length (mult of 4) */ 986239cc3b6SSam Leffler u_int16_t isr_freq; /* MHz */ 987239cc3b6SSam Leffler u_int16_t isr_flags; /* channel flags */ 988239cc3b6SSam Leffler u_int8_t isr_noise; 989239cc3b6SSam Leffler u_int8_t isr_rssi; 990239cc3b6SSam Leffler u_int8_t isr_intval; /* beacon interval */ 991239cc3b6SSam Leffler u_int8_t isr_capinfo; /* capabilities */ 992239cc3b6SSam Leffler u_int8_t isr_erp; /* ERP element */ 993239cc3b6SSam Leffler u_int8_t isr_bssid[IEEE80211_ADDR_LEN]; 994239cc3b6SSam Leffler u_int8_t isr_nrates; 995239cc3b6SSam Leffler u_int8_t isr_rates[IEEE80211_RATE_MAXSIZE]; 996239cc3b6SSam Leffler u_int8_t isr_ssid_len; /* SSID length */ 997239cc3b6SSam Leffler u_int8_t isr_ie_len; /* IE length */ 998239cc3b6SSam Leffler u_int8_t isr_pad[5]; 999239cc3b6SSam Leffler /* variable length SSID followed by IE data */ 1000239cc3b6SSam Leffler }; 1001239cc3b6SSam Leffler 10028a1b9b6aSSam Leffler static void 1003239cc3b6SSam Leffler old_get_scan_result(struct scan_result_old *sr, 10048a1b9b6aSSam Leffler const struct ieee80211_node *ni) 10058a1b9b6aSSam Leffler { 10068a1b9b6aSSam Leffler struct ieee80211com *ic = ni->ni_ic; 1007239cc3b6SSam Leffler u_int ielen; 10088a1b9b6aSSam Leffler 10098a1b9b6aSSam Leffler memset(sr, 0, sizeof(*sr)); 10108a1b9b6aSSam Leffler sr->isr_ssid_len = ni->ni_esslen; 1011239cc3b6SSam Leffler ielen = 0; 10128a1b9b6aSSam Leffler if (ni->ni_wpa_ie != NULL) 10139238d0afSColin Percival ielen += 2+ni->ni_wpa_ie[1]; 10148a1b9b6aSSam Leffler if (ni->ni_wme_ie != NULL) 10159238d0afSColin Percival ielen += 2+ni->ni_wme_ie[1]; 1016239cc3b6SSam Leffler /* NB: beware of overflow, isr_ie_len is 8 bits */ 1017239cc3b6SSam Leffler sr->isr_ie_len = (ielen > 255 ? 0 : ielen); 10188a1b9b6aSSam Leffler sr->isr_len = sizeof(*sr) + sr->isr_ssid_len + sr->isr_ie_len; 10198a1b9b6aSSam Leffler sr->isr_len = roundup(sr->isr_len, sizeof(u_int32_t)); 10208a1b9b6aSSam Leffler if (ni->ni_chan != IEEE80211_CHAN_ANYC) { 10218a1b9b6aSSam Leffler sr->isr_freq = ni->ni_chan->ic_freq; 10228a1b9b6aSSam Leffler sr->isr_flags = ni->ni_chan->ic_flags; 10238a1b9b6aSSam Leffler } 10248a1b9b6aSSam Leffler sr->isr_rssi = ic->ic_node_getrssi(ni); 10258a1b9b6aSSam Leffler sr->isr_intval = ni->ni_intval; 10268a1b9b6aSSam Leffler sr->isr_capinfo = ni->ni_capinfo; 10278a1b9b6aSSam Leffler sr->isr_erp = ni->ni_erp; 10288a1b9b6aSSam Leffler IEEE80211_ADDR_COPY(sr->isr_bssid, ni->ni_bssid); 10298a1b9b6aSSam Leffler sr->isr_nrates = ni->ni_rates.rs_nrates; 10308a1b9b6aSSam Leffler if (sr->isr_nrates > 15) 10318a1b9b6aSSam Leffler sr->isr_nrates = 15; 10328a1b9b6aSSam Leffler memcpy(sr->isr_rates, ni->ni_rates.rs_rates, sr->isr_nrates); 10338a1b9b6aSSam Leffler } 10348a1b9b6aSSam Leffler 10358a1b9b6aSSam Leffler static int 1036239cc3b6SSam Leffler old_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) 10378a1b9b6aSSam Leffler { 10388a1b9b6aSSam Leffler union { 1039239cc3b6SSam Leffler struct scan_result_old res; 10408a1b9b6aSSam Leffler char data[512]; /* XXX shrink? */ 10418a1b9b6aSSam Leffler } u; 1042239cc3b6SSam Leffler struct scan_result_old *sr = &u.res; 10438a1b9b6aSSam Leffler struct ieee80211_node_table *nt; 10448a1b9b6aSSam Leffler struct ieee80211_node *ni; 10458a1b9b6aSSam Leffler int error, space; 10468a1b9b6aSSam Leffler u_int8_t *p, *cp; 10478a1b9b6aSSam Leffler 10488a1b9b6aSSam Leffler p = ireq->i_data; 10498a1b9b6aSSam Leffler space = ireq->i_len; 10508a1b9b6aSSam Leffler error = 0; 10518a1b9b6aSSam Leffler /* XXX locking */ 10528a1b9b6aSSam Leffler nt = &ic->ic_scan; 10538a1b9b6aSSam Leffler TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { 10548a1b9b6aSSam Leffler /* NB: skip pre-scan node state */ 10558a1b9b6aSSam Leffler if (ni->ni_chan == IEEE80211_CHAN_ANYC) 10568a1b9b6aSSam Leffler continue; 1057239cc3b6SSam Leffler old_get_scan_result(sr, ni); 10588a1b9b6aSSam Leffler if (sr->isr_len > sizeof(u)) 10598a1b9b6aSSam Leffler continue; /* XXX */ 10608a1b9b6aSSam Leffler if (space < sr->isr_len) 10618a1b9b6aSSam Leffler break; 10628a1b9b6aSSam Leffler cp = (u_int8_t *)(sr+1); 10638a1b9b6aSSam Leffler memcpy(cp, ni->ni_essid, ni->ni_esslen); 10648a1b9b6aSSam Leffler cp += ni->ni_esslen; 1065239cc3b6SSam Leffler if (sr->isr_ie_len) { 1066239cc3b6SSam Leffler if (ni->ni_wpa_ie != NULL) { 10678a1b9b6aSSam Leffler memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); 10688a1b9b6aSSam Leffler cp += 2+ni->ni_wpa_ie[1]; 10698a1b9b6aSSam Leffler } 1070239cc3b6SSam Leffler if (ni->ni_wme_ie != NULL) { 10718a1b9b6aSSam Leffler memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); 10728a1b9b6aSSam Leffler cp += 2+ni->ni_wme_ie[1]; 10738a1b9b6aSSam Leffler } 1074239cc3b6SSam Leffler } 10758a1b9b6aSSam Leffler error = copyout(sr, p, sr->isr_len); 10768a1b9b6aSSam Leffler if (error) 10778a1b9b6aSSam Leffler break; 10788a1b9b6aSSam Leffler p += sr->isr_len; 10798a1b9b6aSSam Leffler space -= sr->isr_len; 10808a1b9b6aSSam Leffler } 10818a1b9b6aSSam Leffler ireq->i_len -= space; 10828a1b9b6aSSam Leffler return error; 10838a1b9b6aSSam Leffler } 1084239cc3b6SSam Leffler #endif /* COMPAT_FREEBSD6 */ 1085239cc3b6SSam Leffler 1086239cc3b6SSam Leffler struct scanresultsreq { 1087239cc3b6SSam Leffler struct ieee80211req_scan_result *sr; 1088239cc3b6SSam Leffler size_t space; 1089239cc3b6SSam Leffler }; 1090239cc3b6SSam Leffler 1091239cc3b6SSam Leffler static size_t 1092239cc3b6SSam Leffler scan_space(const struct ieee80211_node *ni, size_t *ielen) 1093239cc3b6SSam Leffler { 1094239cc3b6SSam Leffler size_t len; 1095239cc3b6SSam Leffler 1096239cc3b6SSam Leffler *ielen = 0; 1097239cc3b6SSam Leffler if (ni->ni_wpa_ie != NULL) 1098239cc3b6SSam Leffler *ielen += 2+ni->ni_wpa_ie[1]; 1099239cc3b6SSam Leffler if (ni->ni_wme_ie != NULL) 1100239cc3b6SSam Leffler *ielen += 2+ni->ni_wme_ie[1]; 1101239cc3b6SSam Leffler /* 1102239cc3b6SSam Leffler * NB: ie's can be no more than 255 bytes and the max 802.11 1103239cc3b6SSam Leffler * packet is <3Kbytes so we are sure this doesn't overflow 1104239cc3b6SSam Leffler * 16-bits; if this is a concern we can drop the ie's. 1105239cc3b6SSam Leffler */ 1106239cc3b6SSam Leffler len = sizeof(struct ieee80211req_scan_result) + ni->ni_esslen + *ielen; 1107239cc3b6SSam Leffler return roundup(len, sizeof(u_int32_t)); 1108239cc3b6SSam Leffler } 1109239cc3b6SSam Leffler 1110239cc3b6SSam Leffler static void 1111239cc3b6SSam Leffler get_scan_space(void *arg, struct ieee80211_node *ni) 1112239cc3b6SSam Leffler { 1113239cc3b6SSam Leffler struct scanresultsreq *req = arg; 1114239cc3b6SSam Leffler size_t ielen; 1115239cc3b6SSam Leffler 1116239cc3b6SSam Leffler req->space += scan_space(ni, &ielen); 1117239cc3b6SSam Leffler } 1118239cc3b6SSam Leffler 1119239cc3b6SSam Leffler static void 1120239cc3b6SSam Leffler get_scan_result(void *arg, struct ieee80211_node *ni) 1121239cc3b6SSam Leffler { 1122239cc3b6SSam Leffler struct scanresultsreq *req = arg; 1123239cc3b6SSam Leffler struct ieee80211com *ic = ni->ni_ic; 1124239cc3b6SSam Leffler struct ieee80211req_scan_result *sr; 1125239cc3b6SSam Leffler size_t ielen, len; 1126239cc3b6SSam Leffler u_int8_t *cp; 1127239cc3b6SSam Leffler 1128239cc3b6SSam Leffler len = scan_space(ni, &ielen); 1129239cc3b6SSam Leffler if (len > req->space) 1130239cc3b6SSam Leffler return; 1131239cc3b6SSam Leffler sr = req->sr; 1132239cc3b6SSam Leffler KASSERT(len <= 65535 && ielen <= 65535, 1133239cc3b6SSam Leffler ("len %zu ssid %u ie %zu", len, ni->ni_esslen, ielen)); 1134239cc3b6SSam Leffler sr->isr_len = len; 1135239cc3b6SSam Leffler sr->isr_ssid_len = ni->ni_esslen; 1136239cc3b6SSam Leffler sr->isr_ie_len = ielen; 1137239cc3b6SSam Leffler if (ni->ni_chan != IEEE80211_CHAN_ANYC) { 1138239cc3b6SSam Leffler sr->isr_freq = ni->ni_chan->ic_freq; 1139239cc3b6SSam Leffler sr->isr_flags = ni->ni_chan->ic_flags; 1140239cc3b6SSam Leffler } 1141239cc3b6SSam Leffler /* XXX need to rev driver apis for signal data */ 1142239cc3b6SSam Leffler sr->isr_rssi = (int8_t) ic->ic_node_getrssi(ni); 1143239cc3b6SSam Leffler sr->isr_intval = ni->ni_intval; 1144239cc3b6SSam Leffler sr->isr_capinfo = ni->ni_capinfo; 1145239cc3b6SSam Leffler sr->isr_erp = ni->ni_erp; 1146239cc3b6SSam Leffler IEEE80211_ADDR_COPY(sr->isr_bssid, ni->ni_bssid); 1147239cc3b6SSam Leffler sr->isr_nrates = ni->ni_rates.rs_nrates; 1148239cc3b6SSam Leffler if (sr->isr_nrates > 15) 1149239cc3b6SSam Leffler sr->isr_nrates = 15; 1150239cc3b6SSam Leffler memcpy(sr->isr_rates, ni->ni_rates.rs_rates, sr->isr_nrates); 1151239cc3b6SSam Leffler cp = (u_int8_t *)(sr+1); 1152239cc3b6SSam Leffler memcpy(cp, ni->ni_essid, ni->ni_esslen); 1153239cc3b6SSam Leffler cp += ni->ni_esslen; 1154239cc3b6SSam Leffler if (sr->isr_ie_len) { 1155239cc3b6SSam Leffler if (ni->ni_wpa_ie != NULL) { 1156239cc3b6SSam Leffler memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); 1157239cc3b6SSam Leffler cp += 2+ni->ni_wpa_ie[1]; 1158239cc3b6SSam Leffler } 1159239cc3b6SSam Leffler if (ni->ni_wme_ie != NULL) { 1160239cc3b6SSam Leffler memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); 1161239cc3b6SSam Leffler cp += 2+ni->ni_wme_ie[1]; 1162239cc3b6SSam Leffler } 1163239cc3b6SSam Leffler } 1164239cc3b6SSam Leffler 1165239cc3b6SSam Leffler req->sr = (struct ieee80211req_scan_result *)(((u_int8_t *)sr) + len); 1166239cc3b6SSam Leffler req->space -= len; 1167239cc3b6SSam Leffler } 1168239cc3b6SSam Leffler 1169239cc3b6SSam Leffler static int 1170239cc3b6SSam Leffler ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) 1171239cc3b6SSam Leffler { 1172239cc3b6SSam Leffler struct scanresultsreq req; 1173239cc3b6SSam Leffler int error; 1174239cc3b6SSam Leffler 117589f0549dSSam Leffler if (ireq->i_len < sizeof(struct ieee80211req_scan_result)) 1176239cc3b6SSam Leffler return EFAULT; 1177239cc3b6SSam Leffler 1178239cc3b6SSam Leffler error = 0; 1179239cc3b6SSam Leffler req.space = 0; 1180239cc3b6SSam Leffler ieee80211_iterate_nodes(&ic->ic_scan, get_scan_space, &req); 1181239cc3b6SSam Leffler if (req.space > ireq->i_len) 1182239cc3b6SSam Leffler req.space = ireq->i_len; 1183239cc3b6SSam Leffler if (req.space > 0) { 1184239cc3b6SSam Leffler size_t space; 1185239cc3b6SSam Leffler void *p; 1186239cc3b6SSam Leffler 1187239cc3b6SSam Leffler space = req.space; 1188239cc3b6SSam Leffler /* XXX M_WAITOK after driver lock released */ 1189239cc3b6SSam Leffler MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO); 1190239cc3b6SSam Leffler if (p == NULL) 1191239cc3b6SSam Leffler return ENOMEM; 1192239cc3b6SSam Leffler req.sr = p; 1193239cc3b6SSam Leffler ieee80211_iterate_nodes(&ic->ic_scan, get_scan_result, &req); 1194239cc3b6SSam Leffler ireq->i_len = space - req.space; 1195239cc3b6SSam Leffler error = copyout(p, ireq->i_data, ireq->i_len); 1196239cc3b6SSam Leffler FREE(p, M_TEMP); 1197239cc3b6SSam Leffler } else 1198239cc3b6SSam Leffler ireq->i_len = 0; 1199239cc3b6SSam Leffler 1200239cc3b6SSam Leffler return error; 1201239cc3b6SSam Leffler } 12028a1b9b6aSSam Leffler 12032cab1d3dSSam Leffler struct stainforeq { 12042cab1d3dSSam Leffler struct ieee80211com *ic; 12052cab1d3dSSam Leffler struct ieee80211req_sta_info *si; 12062cab1d3dSSam Leffler size_t space; 12072cab1d3dSSam Leffler }; 12088a1b9b6aSSam Leffler 12092cab1d3dSSam Leffler static size_t 12102cab1d3dSSam Leffler sta_space(const struct ieee80211_node *ni, size_t *ielen) 12112cab1d3dSSam Leffler { 12122cab1d3dSSam Leffler *ielen = 0; 12138a1b9b6aSSam Leffler if (ni->ni_wpa_ie != NULL) 12142cab1d3dSSam Leffler *ielen += 2+ni->ni_wpa_ie[1]; 12158a1b9b6aSSam Leffler if (ni->ni_wme_ie != NULL) 12162cab1d3dSSam Leffler *ielen += 2+ni->ni_wme_ie[1]; 12172cab1d3dSSam Leffler return roundup(sizeof(struct ieee80211req_sta_info) + *ielen, 12182cab1d3dSSam Leffler sizeof(u_int32_t)); 12192cab1d3dSSam Leffler } 12202cab1d3dSSam Leffler 12212cab1d3dSSam Leffler static void 12222cab1d3dSSam Leffler get_sta_space(void *arg, struct ieee80211_node *ni) 12232cab1d3dSSam Leffler { 12242cab1d3dSSam Leffler struct stainforeq *req = arg; 12252cab1d3dSSam Leffler struct ieee80211com *ic = ni->ni_ic; 12262cab1d3dSSam Leffler size_t ielen; 12272cab1d3dSSam Leffler 12282cab1d3dSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP && 12292cab1d3dSSam Leffler ni->ni_associd == 0) /* only associated stations */ 12302cab1d3dSSam Leffler return; 12312cab1d3dSSam Leffler req->space += sta_space(ni, &ielen); 12322cab1d3dSSam Leffler } 12332cab1d3dSSam Leffler 12342cab1d3dSSam Leffler static void 12352cab1d3dSSam Leffler get_sta_info(void *arg, struct ieee80211_node *ni) 12362cab1d3dSSam Leffler { 12372cab1d3dSSam Leffler struct stainforeq *req = arg; 12382cab1d3dSSam Leffler struct ieee80211com *ic = ni->ni_ic; 12392cab1d3dSSam Leffler struct ieee80211req_sta_info *si; 12402cab1d3dSSam Leffler size_t ielen, len; 12412cab1d3dSSam Leffler u_int8_t *cp; 12422cab1d3dSSam Leffler 12432cab1d3dSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP && 12442cab1d3dSSam Leffler ni->ni_associd == 0) /* only associated stations */ 12452cab1d3dSSam Leffler return; 12462cab1d3dSSam Leffler if (ni->ni_chan == IEEE80211_CHAN_ANYC) /* XXX bogus entry */ 12472cab1d3dSSam Leffler return; 12482cab1d3dSSam Leffler len = sta_space(ni, &ielen); 12492cab1d3dSSam Leffler if (len > req->space) 12502cab1d3dSSam Leffler return; 12512cab1d3dSSam Leffler si = req->si; 1252239cc3b6SSam Leffler KASSERT(len <= 65535 && ielen <= 65535, ("len %zu ie %zu", len, ielen)); 12532cab1d3dSSam Leffler si->isi_len = len; 12542cab1d3dSSam Leffler si->isi_ie_len = ielen; 12558a1b9b6aSSam Leffler si->isi_freq = ni->ni_chan->ic_freq; 12568a1b9b6aSSam Leffler si->isi_flags = ni->ni_chan->ic_flags; 12578a1b9b6aSSam Leffler si->isi_state = ni->ni_flags; 12588a1b9b6aSSam Leffler si->isi_authmode = ni->ni_authmode; 12598a1b9b6aSSam Leffler si->isi_rssi = ic->ic_node_getrssi(ni); 1260d1c85daeSSam Leffler si->isi_noise = 0; /* XXX */ 12618a1b9b6aSSam Leffler si->isi_capinfo = ni->ni_capinfo; 12628a1b9b6aSSam Leffler si->isi_erp = ni->ni_erp; 12638a1b9b6aSSam Leffler IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr); 12648a1b9b6aSSam Leffler si->isi_nrates = ni->ni_rates.rs_nrates; 12658a1b9b6aSSam Leffler if (si->isi_nrates > 15) 12668a1b9b6aSSam Leffler si->isi_nrates = 15; 12678a1b9b6aSSam Leffler memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates); 12688a1b9b6aSSam Leffler si->isi_txrate = ni->ni_txrate; 12698a1b9b6aSSam Leffler si->isi_associd = ni->ni_associd; 12708a1b9b6aSSam Leffler si->isi_txpower = ni->ni_txpower; 12718a1b9b6aSSam Leffler si->isi_vlan = ni->ni_vlan; 12728a1b9b6aSSam Leffler if (ni->ni_flags & IEEE80211_NODE_QOS) { 12738a1b9b6aSSam Leffler memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs)); 12748a1b9b6aSSam Leffler memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs)); 12758a1b9b6aSSam Leffler } else { 1276801df4a5SSam Leffler si->isi_txseqs[0] = ni->ni_txseqs[IEEE80211_NONQOS_TID]; 1277801df4a5SSam Leffler si->isi_rxseqs[0] = ni->ni_rxseqs[IEEE80211_NONQOS_TID]; 12788a1b9b6aSSam Leffler } 12792cab1d3dSSam Leffler /* NB: leave all cases in case we relax ni_associd == 0 check */ 12802cab1d3dSSam Leffler if (ieee80211_node_is_authorized(ni)) 12818a1b9b6aSSam Leffler si->isi_inact = ic->ic_inact_run; 12822cab1d3dSSam Leffler else if (ni->ni_associd != 0) 12838a1b9b6aSSam Leffler si->isi_inact = ic->ic_inact_auth; 12848a1b9b6aSSam Leffler else 12858a1b9b6aSSam Leffler si->isi_inact = ic->ic_inact_init; 12868a1b9b6aSSam Leffler si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT; 12878a1b9b6aSSam Leffler 12888a1b9b6aSSam Leffler cp = (u_int8_t *)(si+1); 12898a1b9b6aSSam Leffler if (ni->ni_wpa_ie != NULL) { 12908a1b9b6aSSam Leffler memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); 12918a1b9b6aSSam Leffler cp += 2+ni->ni_wpa_ie[1]; 12928a1b9b6aSSam Leffler } 12938a1b9b6aSSam Leffler if (ni->ni_wme_ie != NULL) { 12948a1b9b6aSSam Leffler memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); 12958a1b9b6aSSam Leffler cp += 2+ni->ni_wme_ie[1]; 12968a1b9b6aSSam Leffler } 12972cab1d3dSSam Leffler 12982cab1d3dSSam Leffler req->si = (struct ieee80211req_sta_info *)(((u_int8_t *)si) + len); 12992cab1d3dSSam Leffler req->space -= len; 13008a1b9b6aSSam Leffler } 13012cab1d3dSSam Leffler 13022cab1d3dSSam Leffler static int 1303d1c85daeSSam Leffler getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq, 1304d1c85daeSSam Leffler struct ieee80211_node *ni, int off) 13052cab1d3dSSam Leffler { 13062cab1d3dSSam Leffler struct stainforeq req; 1307d1c85daeSSam Leffler size_t space; 1308d1c85daeSSam Leffler void *p; 13092cab1d3dSSam Leffler int error; 13102cab1d3dSSam Leffler 13112cab1d3dSSam Leffler error = 0; 13122cab1d3dSSam Leffler req.space = 0; 1313d1c85daeSSam Leffler if (ni == NULL) 13142cab1d3dSSam Leffler ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req); 1315d1c85daeSSam Leffler else 1316d1c85daeSSam Leffler get_sta_space(&req, ni); 13172cab1d3dSSam Leffler if (req.space > ireq->i_len) 13182cab1d3dSSam Leffler req.space = ireq->i_len; 13192cab1d3dSSam Leffler if (req.space > 0) { 13202cab1d3dSSam Leffler space = req.space; 13212cab1d3dSSam Leffler /* XXX M_WAITOK after driver lock released */ 13222cab1d3dSSam Leffler MALLOC(p, void *, space, M_TEMP, M_NOWAIT); 1323d1c85daeSSam Leffler if (p == NULL) { 1324d1c85daeSSam Leffler error = ENOMEM; 1325d1c85daeSSam Leffler goto bad; 1326d1c85daeSSam Leffler } 13272cab1d3dSSam Leffler req.si = p; 1328d1c85daeSSam Leffler if (ni == NULL) 13292cab1d3dSSam Leffler ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req); 1330d1c85daeSSam Leffler else 1331d1c85daeSSam Leffler get_sta_info(&req, ni); 13322cab1d3dSSam Leffler ireq->i_len = space - req.space; 1333d1c85daeSSam Leffler error = copyout(p, (u_int8_t *) ireq->i_data+off, ireq->i_len); 13342cab1d3dSSam Leffler FREE(p, M_TEMP); 13352cab1d3dSSam Leffler } else 13362cab1d3dSSam Leffler ireq->i_len = 0; 1337d1c85daeSSam Leffler bad: 1338d1c85daeSSam Leffler if (ni != NULL) 1339d1c85daeSSam Leffler ieee80211_free_node(ni); 13408a1b9b6aSSam Leffler return error; 13418a1b9b6aSSam Leffler } 13428a1b9b6aSSam Leffler 13438a1b9b6aSSam Leffler static int 1344d1c85daeSSam Leffler ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) 1345d1c85daeSSam Leffler { 1346d1c85daeSSam Leffler u_int8_t macaddr[IEEE80211_ADDR_LEN]; 1347d1c85daeSSam Leffler const int off = __offsetof(struct ieee80211req_sta_req, info); 1348d1c85daeSSam Leffler struct ieee80211_node *ni; 1349d1c85daeSSam Leffler int error; 1350d1c85daeSSam Leffler 1351d1c85daeSSam Leffler if (ireq->i_len < sizeof(struct ieee80211req_sta_req)) 1352d1c85daeSSam Leffler return EFAULT; 1353d1c85daeSSam Leffler error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); 1354d1c85daeSSam Leffler if (error != 0) 1355d1c85daeSSam Leffler return error; 1356d1c85daeSSam Leffler if (IEEE80211_ADDR_EQ(macaddr, ic->ic_ifp->if_broadcastaddr)) { 1357d1c85daeSSam Leffler ni = NULL; 1358d1c85daeSSam Leffler } else { 1359d1c85daeSSam Leffler ni = ieee80211_find_node(&ic->ic_sta, macaddr); 1360d1c85daeSSam Leffler if (ni == NULL) { 1361d1c85daeSSam Leffler /* XXX special-case sta-mode until bss is in ic_sta */ 1362d1c85daeSSam Leffler if (ic->ic_opmode != IEEE80211_M_STA) 1363d1c85daeSSam Leffler return EINVAL; /* XXX */ 1364d1c85daeSSam Leffler ni = ieee80211_ref_node(ic->ic_bss); 1365d1c85daeSSam Leffler } 1366d1c85daeSSam Leffler } 1367d1c85daeSSam Leffler return getstainfo_common(ic, ireq, ni, off); 1368d1c85daeSSam Leffler } 1369d1c85daeSSam Leffler 1370d1c85daeSSam Leffler #ifdef COMPAT_FREEBSD6 1371d1c85daeSSam Leffler #define IEEE80211_IOC_STA_INFO_OLD 45 1372d1c85daeSSam Leffler 1373d1c85daeSSam Leffler static int 1374d1c85daeSSam Leffler old_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) 1375d1c85daeSSam Leffler { 1376d1c85daeSSam Leffler if (ireq->i_len < sizeof(struct ieee80211req_sta_info)) 1377d1c85daeSSam Leffler return EFAULT; 1378d1c85daeSSam Leffler return getstainfo_common(ic, ireq, NULL, 0); 1379d1c85daeSSam Leffler } 1380d1c85daeSSam Leffler #endif /* COMPAT_FREEBSD6 */ 1381d1c85daeSSam Leffler 1382d1c85daeSSam Leffler static int 13838a1b9b6aSSam Leffler ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) 13848a1b9b6aSSam Leffler { 13858a1b9b6aSSam Leffler struct ieee80211_node *ni; 13868a1b9b6aSSam Leffler struct ieee80211req_sta_txpow txpow; 13878a1b9b6aSSam Leffler int error; 13888a1b9b6aSSam Leffler 13898a1b9b6aSSam Leffler if (ireq->i_len != sizeof(txpow)) 13908a1b9b6aSSam Leffler return EINVAL; 13918a1b9b6aSSam Leffler error = copyin(ireq->i_data, &txpow, sizeof(txpow)); 13928a1b9b6aSSam Leffler if (error != 0) 13938a1b9b6aSSam Leffler return error; 1394acc4f7f5SSam Leffler ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr); 13958a1b9b6aSSam Leffler if (ni == NULL) 13968a1b9b6aSSam Leffler return EINVAL; /* XXX */ 13978a1b9b6aSSam Leffler txpow.it_txpow = ni->ni_txpower; 13988a1b9b6aSSam Leffler error = copyout(&txpow, ireq->i_data, sizeof(txpow)); 13998a1b9b6aSSam Leffler ieee80211_free_node(ni); 14008a1b9b6aSSam Leffler return error; 14018a1b9b6aSSam Leffler } 14028a1b9b6aSSam Leffler 14038a1b9b6aSSam Leffler static int 14048a1b9b6aSSam Leffler ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) 14058a1b9b6aSSam Leffler { 14068a1b9b6aSSam Leffler struct ieee80211_wme_state *wme = &ic->ic_wme; 14078a1b9b6aSSam Leffler struct wmeParams *wmep; 14088a1b9b6aSSam Leffler int ac; 14098a1b9b6aSSam Leffler 14108a1b9b6aSSam Leffler if ((ic->ic_caps & IEEE80211_C_WME) == 0) 14118a1b9b6aSSam Leffler return EINVAL; 14128a1b9b6aSSam Leffler 14138a1b9b6aSSam Leffler ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); 14148a1b9b6aSSam Leffler if (ac >= WME_NUM_AC) 14158a1b9b6aSSam Leffler ac = WME_AC_BE; 14168a1b9b6aSSam Leffler if (ireq->i_len & IEEE80211_WMEPARAM_BSS) 14178a1b9b6aSSam Leffler wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; 14188a1b9b6aSSam Leffler else 14198a1b9b6aSSam Leffler wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; 14208a1b9b6aSSam Leffler switch (ireq->i_type) { 14218a1b9b6aSSam Leffler case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 14228a1b9b6aSSam Leffler ireq->i_val = wmep->wmep_logcwmin; 14238a1b9b6aSSam Leffler break; 14248a1b9b6aSSam Leffler case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 14258a1b9b6aSSam Leffler ireq->i_val = wmep->wmep_logcwmax; 14268a1b9b6aSSam Leffler break; 14278a1b9b6aSSam Leffler case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 14288a1b9b6aSSam Leffler ireq->i_val = wmep->wmep_aifsn; 14298a1b9b6aSSam Leffler break; 14308a1b9b6aSSam Leffler case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 14318a1b9b6aSSam Leffler ireq->i_val = wmep->wmep_txopLimit; 14328a1b9b6aSSam Leffler break; 14338a1b9b6aSSam Leffler case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 14348a1b9b6aSSam Leffler wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; 14358a1b9b6aSSam Leffler ireq->i_val = wmep->wmep_acm; 14368a1b9b6aSSam Leffler break; 14378a1b9b6aSSam Leffler case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ 14388a1b9b6aSSam Leffler wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; 14398a1b9b6aSSam Leffler ireq->i_val = !wmep->wmep_noackPolicy; 14408a1b9b6aSSam Leffler break; 14418a1b9b6aSSam Leffler } 14428a1b9b6aSSam Leffler return 0; 14438a1b9b6aSSam Leffler } 14448a1b9b6aSSam Leffler 1445188757f5SSam Leffler static int 1446188757f5SSam Leffler ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq) 1447188757f5SSam Leffler { 1448188757f5SSam Leffler const struct ieee80211_aclator *acl = ic->ic_acl; 1449188757f5SSam Leffler 1450188757f5SSam Leffler return (acl == NULL ? EINVAL : acl->iac_getioctl(ic, ireq)); 1451188757f5SSam Leffler } 1452188757f5SSam Leffler 1453c788ca3eSBill Paul /* 1454c788ca3eSBill Paul * When building the kernel with -O2 on the i386 architecture, gcc 1455c788ca3eSBill Paul * seems to want to inline this function into ieee80211_ioctl() 1456c788ca3eSBill Paul * (which is the only routine that calls it). When this happens, 1457c788ca3eSBill Paul * ieee80211_ioctl() ends up consuming an additional 2K of stack 1458c788ca3eSBill Paul * space. (Exactly why it needs so much is unclear.) The problem 1459c788ca3eSBill Paul * is that it's possible for ieee80211_ioctl() to invoke other 1460c788ca3eSBill Paul * routines (including driver init functions) which could then find 1461c788ca3eSBill Paul * themselves perilously close to exhausting the stack. 1462c788ca3eSBill Paul * 1463c788ca3eSBill Paul * To avoid this, we deliberately prevent gcc from inlining this 1464c788ca3eSBill Paul * routine. Another way to avoid this is to use less agressive 1465c788ca3eSBill Paul * optimization when compiling this file (i.e. -O instead of -O2) 1466c788ca3eSBill Paul * but special-casing the compilation of this one module in the 1467c788ca3eSBill Paul * build system would be awkward. 1468c788ca3eSBill Paul */ 1469c788ca3eSBill Paul #ifdef __GNUC__ 1470c788ca3eSBill Paul __attribute__ ((noinline)) 1471c788ca3eSBill Paul #endif 14728a1b9b6aSSam Leffler static int 14738a1b9b6aSSam Leffler ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) 14748a1b9b6aSSam Leffler { 14758a1b9b6aSSam Leffler const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; 14761a1e1d21SSam Leffler int error = 0; 14778a1b9b6aSSam Leffler u_int kid, len, m; 14781a1e1d21SSam Leffler u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; 14791a1e1d21SSam Leffler char tmpssid[IEEE80211_NWID_LEN]; 14801a1e1d21SSam Leffler 14811a1e1d21SSam Leffler switch (ireq->i_type) { 14821a1e1d21SSam Leffler case IEEE80211_IOC_SSID: 14831a1e1d21SSam Leffler switch (ic->ic_state) { 14841a1e1d21SSam Leffler case IEEE80211_S_INIT: 14851a1e1d21SSam Leffler case IEEE80211_S_SCAN: 14861a1e1d21SSam Leffler ireq->i_len = ic->ic_des_esslen; 14871a1e1d21SSam Leffler memcpy(tmpssid, ic->ic_des_essid, ireq->i_len); 14881a1e1d21SSam Leffler break; 14891a1e1d21SSam Leffler default: 14901a1e1d21SSam Leffler ireq->i_len = ic->ic_bss->ni_esslen; 14911a1e1d21SSam Leffler memcpy(tmpssid, ic->ic_bss->ni_essid, 14921a1e1d21SSam Leffler ireq->i_len); 14931a1e1d21SSam Leffler break; 14941a1e1d21SSam Leffler } 14951a1e1d21SSam Leffler error = copyout(tmpssid, ireq->i_data, ireq->i_len); 14961a1e1d21SSam Leffler break; 14971a1e1d21SSam Leffler case IEEE80211_IOC_NUMSSIDS: 14981a1e1d21SSam Leffler ireq->i_val = 1; 14991a1e1d21SSam Leffler break; 15001a1e1d21SSam Leffler case IEEE80211_IOC_WEP: 15018a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) 15028a1b9b6aSSam Leffler ireq->i_val = IEEE80211_WEP_OFF; 15038a1b9b6aSSam Leffler else if (ic->ic_flags & IEEE80211_F_DROPUNENC) 15048a1b9b6aSSam Leffler ireq->i_val = IEEE80211_WEP_ON; 15058a1b9b6aSSam Leffler else 15068a1b9b6aSSam Leffler ireq->i_val = IEEE80211_WEP_MIXED; 15071a1e1d21SSam Leffler break; 15081a1e1d21SSam Leffler case IEEE80211_IOC_WEPKEY: 15091a1e1d21SSam Leffler kid = (u_int) ireq->i_val; 15108a1b9b6aSSam Leffler if (kid >= IEEE80211_WEP_NKID) 15118a1b9b6aSSam Leffler return EINVAL; 15128a1b9b6aSSam Leffler len = (u_int) ic->ic_nw_keys[kid].wk_keylen; 15131a1e1d21SSam Leffler /* NB: only root can read WEP keys */ 1514acd3428bSRobert Watson if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) { 15151a1e1d21SSam Leffler bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len); 15161a1e1d21SSam Leffler } else { 15171a1e1d21SSam Leffler bzero(tmpkey, len); 15181a1e1d21SSam Leffler } 15191a1e1d21SSam Leffler ireq->i_len = len; 15201a1e1d21SSam Leffler error = copyout(tmpkey, ireq->i_data, len); 15211a1e1d21SSam Leffler break; 15221a1e1d21SSam Leffler case IEEE80211_IOC_NUMWEPKEYS: 15231a1e1d21SSam Leffler ireq->i_val = IEEE80211_WEP_NKID; 15241a1e1d21SSam Leffler break; 15251a1e1d21SSam Leffler case IEEE80211_IOC_WEPTXKEY: 15268a1b9b6aSSam Leffler ireq->i_val = ic->ic_def_txkey; 15271a1e1d21SSam Leffler break; 15281a1e1d21SSam Leffler case IEEE80211_IOC_AUTHMODE: 15298a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_WPA) 15308a1b9b6aSSam Leffler ireq->i_val = IEEE80211_AUTH_WPA; 15318a1b9b6aSSam Leffler else 15328a1b9b6aSSam Leffler ireq->i_val = ic->ic_bss->ni_authmode; 15331a1e1d21SSam Leffler break; 15341a1e1d21SSam Leffler case IEEE80211_IOC_CHANNEL: 1535b5c99415SSam Leffler ireq->i_val = ieee80211_chan2ieee(ic, ic->ic_curchan); 15361a1e1d21SSam Leffler break; 15371a1e1d21SSam Leffler case IEEE80211_IOC_POWERSAVE: 15381a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_PMGTON) 15391a1e1d21SSam Leffler ireq->i_val = IEEE80211_POWERSAVE_ON; 15401a1e1d21SSam Leffler else 15411a1e1d21SSam Leffler ireq->i_val = IEEE80211_POWERSAVE_OFF; 15421a1e1d21SSam Leffler break; 15431a1e1d21SSam Leffler case IEEE80211_IOC_POWERSAVESLEEP: 15441a1e1d21SSam Leffler ireq->i_val = ic->ic_lintval; 15451a1e1d21SSam Leffler break; 154613604e6bSSam Leffler case IEEE80211_IOC_RTSTHRESHOLD: 15471a1e1d21SSam Leffler ireq->i_val = ic->ic_rtsthreshold; 15481a1e1d21SSam Leffler break; 15492e79ca97SSam Leffler case IEEE80211_IOC_PROTMODE: 15502e79ca97SSam Leffler ireq->i_val = ic->ic_protmode; 15512e79ca97SSam Leffler break; 15522e79ca97SSam Leffler case IEEE80211_IOC_TXPOWER: 15532e79ca97SSam Leffler if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) 15548a1b9b6aSSam Leffler return EINVAL; 15558a1b9b6aSSam Leffler ireq->i_val = ic->ic_txpowlimit; 15568a1b9b6aSSam Leffler break; 15578a1b9b6aSSam Leffler case IEEE80211_IOC_MCASTCIPHER: 15588a1b9b6aSSam Leffler ireq->i_val = rsn->rsn_mcastcipher; 15598a1b9b6aSSam Leffler break; 15608a1b9b6aSSam Leffler case IEEE80211_IOC_MCASTKEYLEN: 15618a1b9b6aSSam Leffler ireq->i_val = rsn->rsn_mcastkeylen; 15628a1b9b6aSSam Leffler break; 15638a1b9b6aSSam Leffler case IEEE80211_IOC_UCASTCIPHERS: 15648a1b9b6aSSam Leffler ireq->i_val = 0; 15658a1b9b6aSSam Leffler for (m = 0x1; m != 0; m <<= 1) 15668a1b9b6aSSam Leffler if (rsn->rsn_ucastcipherset & m) 15678a1b9b6aSSam Leffler ireq->i_val |= 1<<cap2cipher(m); 15688a1b9b6aSSam Leffler break; 15698a1b9b6aSSam Leffler case IEEE80211_IOC_UCASTCIPHER: 15708a1b9b6aSSam Leffler ireq->i_val = rsn->rsn_ucastcipher; 15718a1b9b6aSSam Leffler break; 15728a1b9b6aSSam Leffler case IEEE80211_IOC_UCASTKEYLEN: 15738a1b9b6aSSam Leffler ireq->i_val = rsn->rsn_ucastkeylen; 15748a1b9b6aSSam Leffler break; 15758a1b9b6aSSam Leffler case IEEE80211_IOC_KEYMGTALGS: 15768a1b9b6aSSam Leffler ireq->i_val = rsn->rsn_keymgmtset; 15778a1b9b6aSSam Leffler break; 15788a1b9b6aSSam Leffler case IEEE80211_IOC_RSNCAPS: 15798a1b9b6aSSam Leffler ireq->i_val = rsn->rsn_caps; 15808a1b9b6aSSam Leffler break; 15818a1b9b6aSSam Leffler case IEEE80211_IOC_WPA: 15828a1b9b6aSSam Leffler switch (ic->ic_flags & IEEE80211_F_WPA) { 15838a1b9b6aSSam Leffler case IEEE80211_F_WPA1: 15848a1b9b6aSSam Leffler ireq->i_val = 1; 15858a1b9b6aSSam Leffler break; 15868a1b9b6aSSam Leffler case IEEE80211_F_WPA2: 15878a1b9b6aSSam Leffler ireq->i_val = 2; 15888a1b9b6aSSam Leffler break; 15898a1b9b6aSSam Leffler case IEEE80211_F_WPA1 | IEEE80211_F_WPA2: 15908a1b9b6aSSam Leffler ireq->i_val = 3; 15918a1b9b6aSSam Leffler break; 15928a1b9b6aSSam Leffler default: 15938a1b9b6aSSam Leffler ireq->i_val = 0; 15948a1b9b6aSSam Leffler break; 15958a1b9b6aSSam Leffler } 15968a1b9b6aSSam Leffler break; 15978a1b9b6aSSam Leffler case IEEE80211_IOC_CHANLIST: 15988a1b9b6aSSam Leffler error = ieee80211_ioctl_getchanlist(ic, ireq); 15998a1b9b6aSSam Leffler break; 16008a1b9b6aSSam Leffler case IEEE80211_IOC_ROAMING: 16018a1b9b6aSSam Leffler ireq->i_val = ic->ic_roaming; 16028a1b9b6aSSam Leffler break; 16038a1b9b6aSSam Leffler case IEEE80211_IOC_PRIVACY: 16048a1b9b6aSSam Leffler ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0; 16058a1b9b6aSSam Leffler break; 16068a1b9b6aSSam Leffler case IEEE80211_IOC_DROPUNENCRYPTED: 16078a1b9b6aSSam Leffler ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0; 16088a1b9b6aSSam Leffler break; 16098a1b9b6aSSam Leffler case IEEE80211_IOC_COUNTERMEASURES: 16108a1b9b6aSSam Leffler ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0; 16118a1b9b6aSSam Leffler break; 16128a1b9b6aSSam Leffler case IEEE80211_IOC_DRIVER_CAPS: 16138a1b9b6aSSam Leffler ireq->i_val = ic->ic_caps>>16; 16148a1b9b6aSSam Leffler ireq->i_len = ic->ic_caps&0xffff; 16158a1b9b6aSSam Leffler break; 16168a1b9b6aSSam Leffler case IEEE80211_IOC_WME: 16178a1b9b6aSSam Leffler ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0; 16188a1b9b6aSSam Leffler break; 16198a1b9b6aSSam Leffler case IEEE80211_IOC_HIDESSID: 16208a1b9b6aSSam Leffler ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0; 16218a1b9b6aSSam Leffler break; 16228a1b9b6aSSam Leffler case IEEE80211_IOC_APBRIDGE: 16238a1b9b6aSSam Leffler ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0; 16248a1b9b6aSSam Leffler break; 16258a1b9b6aSSam Leffler case IEEE80211_IOC_OPTIE: 16268a1b9b6aSSam Leffler if (ic->ic_opt_ie == NULL) 16278a1b9b6aSSam Leffler return EINVAL; 16288a1b9b6aSSam Leffler /* NB: truncate, caller can check length */ 16298a1b9b6aSSam Leffler if (ireq->i_len > ic->ic_opt_ie_len) 16308a1b9b6aSSam Leffler ireq->i_len = ic->ic_opt_ie_len; 16318a1b9b6aSSam Leffler error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len); 16328a1b9b6aSSam Leffler break; 16338a1b9b6aSSam Leffler case IEEE80211_IOC_WPAKEY: 16348a1b9b6aSSam Leffler error = ieee80211_ioctl_getkey(ic, ireq); 16358a1b9b6aSSam Leffler break; 16368a1b9b6aSSam Leffler case IEEE80211_IOC_CHANINFO: 16378a1b9b6aSSam Leffler error = ieee80211_ioctl_getchaninfo(ic, ireq); 16388a1b9b6aSSam Leffler break; 16398a1b9b6aSSam Leffler case IEEE80211_IOC_BSSID: 16408a1b9b6aSSam Leffler if (ireq->i_len != IEEE80211_ADDR_LEN) 16418a1b9b6aSSam Leffler return EINVAL; 16428a1b9b6aSSam Leffler error = copyout(ic->ic_state == IEEE80211_S_RUN ? 16438a1b9b6aSSam Leffler ic->ic_bss->ni_bssid : 16448a1b9b6aSSam Leffler ic->ic_des_bssid, 16458a1b9b6aSSam Leffler ireq->i_data, ireq->i_len); 16468a1b9b6aSSam Leffler break; 16478a1b9b6aSSam Leffler case IEEE80211_IOC_WPAIE: 16488a1b9b6aSSam Leffler error = ieee80211_ioctl_getwpaie(ic, ireq); 16498a1b9b6aSSam Leffler break; 1650239cc3b6SSam Leffler #ifdef COMPAT_FREEBSD6 1651239cc3b6SSam Leffler case IEEE80211_IOC_SCAN_RESULTS_OLD: 1652239cc3b6SSam Leffler error = old_getscanresults(ic, ireq); 1653239cc3b6SSam Leffler break; 1654239cc3b6SSam Leffler #endif 16558a1b9b6aSSam Leffler case IEEE80211_IOC_SCAN_RESULTS: 16568a1b9b6aSSam Leffler error = ieee80211_ioctl_getscanresults(ic, ireq); 16578a1b9b6aSSam Leffler break; 16588a1b9b6aSSam Leffler case IEEE80211_IOC_STA_STATS: 16598a1b9b6aSSam Leffler error = ieee80211_ioctl_getstastats(ic, ireq); 16608a1b9b6aSSam Leffler break; 16618a1b9b6aSSam Leffler case IEEE80211_IOC_TXPOWMAX: 16628a1b9b6aSSam Leffler ireq->i_val = ic->ic_bss->ni_txpower; 16638a1b9b6aSSam Leffler break; 16648a1b9b6aSSam Leffler case IEEE80211_IOC_STA_TXPOW: 16658a1b9b6aSSam Leffler error = ieee80211_ioctl_getstatxpow(ic, ireq); 16668a1b9b6aSSam Leffler break; 1667d1c85daeSSam Leffler #ifdef COMPAT_FREEBSD6 1668d1c85daeSSam Leffler case IEEE80211_IOC_STA_INFO_OLD: 1669d1c85daeSSam Leffler error = old_getstainfo(ic, ireq); 1670d1c85daeSSam Leffler break; 1671d1c85daeSSam Leffler #endif 16728a1b9b6aSSam Leffler case IEEE80211_IOC_STA_INFO: 16738a1b9b6aSSam Leffler error = ieee80211_ioctl_getstainfo(ic, ireq); 16748a1b9b6aSSam Leffler break; 16758a1b9b6aSSam Leffler case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 16768a1b9b6aSSam Leffler case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 16778a1b9b6aSSam Leffler case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 16788a1b9b6aSSam Leffler case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 16798a1b9b6aSSam Leffler case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 16808a1b9b6aSSam Leffler case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ 16818a1b9b6aSSam Leffler error = ieee80211_ioctl_getwmeparam(ic, ireq); 16828a1b9b6aSSam Leffler break; 16838a1b9b6aSSam Leffler case IEEE80211_IOC_DTIM_PERIOD: 16848a1b9b6aSSam Leffler ireq->i_val = ic->ic_dtim_period; 16858a1b9b6aSSam Leffler break; 16868a1b9b6aSSam Leffler case IEEE80211_IOC_BEACON_INTERVAL: 16878a1b9b6aSSam Leffler /* NB: get from ic_bss for station mode */ 16888a1b9b6aSSam Leffler ireq->i_val = ic->ic_bss->ni_intval; 16892e79ca97SSam Leffler break; 1690c4f040c3SSam Leffler case IEEE80211_IOC_PUREG: 1691c4f040c3SSam Leffler ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0; 1692c4f040c3SSam Leffler break; 169364353cb0SSam Leffler case IEEE80211_IOC_MCAST_RATE: 169464353cb0SSam Leffler ireq->i_val = ic->ic_mcast_rate; 169564353cb0SSam Leffler break; 169670231e3dSSam Leffler case IEEE80211_IOC_FRAGTHRESHOLD: 169770231e3dSSam Leffler ireq->i_val = ic->ic_fragthreshold; 169870231e3dSSam Leffler break; 1699188757f5SSam Leffler case IEEE80211_IOC_MACCMD: 1700188757f5SSam Leffler error = ieee80211_ioctl_getmaccmd(ic, ireq); 1701188757f5SSam Leffler break; 1702c27e4e31SSam Leffler case IEEE80211_IOC_BURST: 1703c27e4e31SSam Leffler ireq->i_val = (ic->ic_flags & IEEE80211_F_BURST) != 0; 1704c27e4e31SSam Leffler break; 1705546786c9SSam Leffler case IEEE80211_IOC_BMISSTHRESHOLD: 1706546786c9SSam Leffler ireq->i_val = ic->ic_bmissthreshold; 1707546786c9SSam Leffler break; 17081a1e1d21SSam Leffler default: 17091a1e1d21SSam Leffler error = EINVAL; 1710b2e95691SSam Leffler break; 17111a1e1d21SSam Leffler } 17128a1b9b6aSSam Leffler return error; 17138a1b9b6aSSam Leffler } 17148a1b9b6aSSam Leffler 17158a1b9b6aSSam Leffler static int 17168a1b9b6aSSam Leffler ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq) 17178a1b9b6aSSam Leffler { 17188a1b9b6aSSam Leffler int error; 1719d3b3a464SSam Leffler void *ie, *oie; 17208a1b9b6aSSam Leffler 17218a1b9b6aSSam Leffler /* 17228a1b9b6aSSam Leffler * NB: Doing this for ap operation could be useful (e.g. for 17238a1b9b6aSSam Leffler * WPA and/or WME) except that it typically is worthless 17248a1b9b6aSSam Leffler * without being able to intervene when processing 17258a1b9b6aSSam Leffler * association response frames--so disallow it for now. 17268a1b9b6aSSam Leffler */ 17278a1b9b6aSSam Leffler if (ic->ic_opmode != IEEE80211_M_STA) 17288a1b9b6aSSam Leffler return EINVAL; 17298a1b9b6aSSam Leffler if (ireq->i_len > IEEE80211_MAX_OPT_IE) 17308a1b9b6aSSam Leffler return EINVAL; 1731d3b3a464SSam Leffler if (ireq->i_len > 0) { 1732c2544286SSam Leffler MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_NOWAIT); 17338a1b9b6aSSam Leffler if (ie == NULL) 17348a1b9b6aSSam Leffler return ENOMEM; 17358a1b9b6aSSam Leffler error = copyin(ireq->i_data, ie, ireq->i_len); 1736d3b3a464SSam Leffler if (error) { 1737d3b3a464SSam Leffler FREE(ie, M_DEVBUF); 1738d3b3a464SSam Leffler return error; 1739d3b3a464SSam Leffler } 1740d3b3a464SSam Leffler } else { 1741d3b3a464SSam Leffler ie = NULL; 1742d3b3a464SSam Leffler ireq->i_len = 0; 1743d3b3a464SSam Leffler } 17448a1b9b6aSSam Leffler /* XXX sanity check data? */ 1745d3b3a464SSam Leffler oie = ic->ic_opt_ie; 17468a1b9b6aSSam Leffler ic->ic_opt_ie = ie; 17478a1b9b6aSSam Leffler ic->ic_opt_ie_len = ireq->i_len; 1748d3b3a464SSam Leffler if (oie != NULL) 1749d3b3a464SSam Leffler FREE(oie, M_DEVBUF); 17508a1b9b6aSSam Leffler return 0; 17518a1b9b6aSSam Leffler } 17528a1b9b6aSSam Leffler 17538a1b9b6aSSam Leffler static int 17548a1b9b6aSSam Leffler ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq) 17558a1b9b6aSSam Leffler { 17568a1b9b6aSSam Leffler struct ieee80211req_key ik; 17578a1b9b6aSSam Leffler struct ieee80211_node *ni; 17588a1b9b6aSSam Leffler struct ieee80211_key *wk; 17598a1b9b6aSSam Leffler u_int16_t kid; 17608a1b9b6aSSam Leffler int error; 17618a1b9b6aSSam Leffler 17628a1b9b6aSSam Leffler if (ireq->i_len != sizeof(ik)) 17638a1b9b6aSSam Leffler return EINVAL; 17648a1b9b6aSSam Leffler error = copyin(ireq->i_data, &ik, sizeof(ik)); 17651a1e1d21SSam Leffler if (error) 17668a1b9b6aSSam Leffler return error; 17678a1b9b6aSSam Leffler /* NB: cipher support is verified by ieee80211_crypt_newkey */ 17688a1b9b6aSSam Leffler /* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */ 17698a1b9b6aSSam Leffler if (ik.ik_keylen > sizeof(ik.ik_keydata)) 17708a1b9b6aSSam Leffler return E2BIG; 17718a1b9b6aSSam Leffler kid = ik.ik_keyix; 17728a1b9b6aSSam Leffler if (kid == IEEE80211_KEYIX_NONE) { 17738a1b9b6aSSam Leffler /* XXX unicast keys currently must be tx/rx */ 17748a1b9b6aSSam Leffler if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)) 17758a1b9b6aSSam Leffler return EINVAL; 17768a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) { 1777b5d4660fSSam Leffler ni = ieee80211_ref_node(ic->ic_bss); 1778b5d4660fSSam Leffler if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) { 1779b5d4660fSSam Leffler ieee80211_free_node(ni); 17808a1b9b6aSSam Leffler return EADDRNOTAVAIL; 1781b5d4660fSSam Leffler } 17828a1b9b6aSSam Leffler } else { 1783acc4f7f5SSam Leffler ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr); 17848a1b9b6aSSam Leffler if (ni == NULL) 17858a1b9b6aSSam Leffler return ENOENT; 17868a1b9b6aSSam Leffler } 17878a1b9b6aSSam Leffler wk = &ni->ni_ucastkey; 17888a1b9b6aSSam Leffler } else { 17898a1b9b6aSSam Leffler if (kid >= IEEE80211_WEP_NKID) 17908a1b9b6aSSam Leffler return EINVAL; 17918a1b9b6aSSam Leffler wk = &ic->ic_nw_keys[kid]; 1792386d84f6SSam Leffler /* 1793386d84f6SSam Leffler * Global slots start off w/o any assigned key index. 1794386d84f6SSam Leffler * Force one here for consistency with IEEE80211_IOC_WEPKEY. 1795386d84f6SSam Leffler */ 1796386d84f6SSam Leffler if (wk->wk_keyix == IEEE80211_KEYIX_NONE) 1797386d84f6SSam Leffler wk->wk_keyix = kid; 17988a1b9b6aSSam Leffler ni = NULL; 17998a1b9b6aSSam Leffler } 18008a1b9b6aSSam Leffler error = 0; 18018a1b9b6aSSam Leffler ieee80211_key_update_begin(ic); 1802dd70e17bSSam Leffler if (ieee80211_crypto_newkey(ic, ik.ik_type, ik.ik_flags, wk)) { 18038a1b9b6aSSam Leffler wk->wk_keylen = ik.ik_keylen; 18048a1b9b6aSSam Leffler /* NB: MIC presence is implied by cipher type */ 18058a1b9b6aSSam Leffler if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE) 18068a1b9b6aSSam Leffler wk->wk_keylen = IEEE80211_KEYBUF_SIZE; 18078a1b9b6aSSam Leffler wk->wk_keyrsc = ik.ik_keyrsc; 18088a1b9b6aSSam Leffler wk->wk_keytsc = 0; /* new key, reset */ 18098a1b9b6aSSam Leffler memset(wk->wk_key, 0, sizeof(wk->wk_key)); 18108a1b9b6aSSam Leffler memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen); 18118a1b9b6aSSam Leffler if (!ieee80211_crypto_setkey(ic, wk, 18128a1b9b6aSSam Leffler ni != NULL ? ni->ni_macaddr : ik.ik_macaddr)) 18138a1b9b6aSSam Leffler error = EIO; 18148a1b9b6aSSam Leffler else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT)) 18158a1b9b6aSSam Leffler ic->ic_def_txkey = kid; 18168a1b9b6aSSam Leffler } else 18178a1b9b6aSSam Leffler error = ENXIO; 18188a1b9b6aSSam Leffler ieee80211_key_update_end(ic); 18198a1b9b6aSSam Leffler if (ni != NULL) 18208a1b9b6aSSam Leffler ieee80211_free_node(ni); 18218a1b9b6aSSam Leffler return error; 18228a1b9b6aSSam Leffler } 18238a1b9b6aSSam Leffler 18248a1b9b6aSSam Leffler static int 18258a1b9b6aSSam Leffler ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq) 18268a1b9b6aSSam Leffler { 18278a1b9b6aSSam Leffler struct ieee80211req_del_key dk; 18288a1b9b6aSSam Leffler int kid, error; 18298a1b9b6aSSam Leffler 18308a1b9b6aSSam Leffler if (ireq->i_len != sizeof(dk)) 18318a1b9b6aSSam Leffler return EINVAL; 18328a1b9b6aSSam Leffler error = copyin(ireq->i_data, &dk, sizeof(dk)); 18338a1b9b6aSSam Leffler if (error) 18348a1b9b6aSSam Leffler return error; 18358a1b9b6aSSam Leffler kid = dk.idk_keyix; 18368a1b9b6aSSam Leffler /* XXX u_int8_t -> u_int16_t */ 18378a1b9b6aSSam Leffler if (dk.idk_keyix == (u_int8_t) IEEE80211_KEYIX_NONE) { 18388a1b9b6aSSam Leffler struct ieee80211_node *ni; 18398a1b9b6aSSam Leffler 1840b5d4660fSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) { 1841b5d4660fSSam Leffler ni = ieee80211_ref_node(ic->ic_bss); 1842b5d4660fSSam Leffler if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) { 1843b5d4660fSSam Leffler ieee80211_free_node(ni); 1844b5d4660fSSam Leffler return EADDRNOTAVAIL; 1845b5d4660fSSam Leffler } 1846b5d4660fSSam Leffler } else { 1847acc4f7f5SSam Leffler ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr); 18488a1b9b6aSSam Leffler if (ni == NULL) 1849b5d4660fSSam Leffler return ENOENT; 1850b5d4660fSSam Leffler } 18518a1b9b6aSSam Leffler /* XXX error return */ 1852c1225b52SSam Leffler ieee80211_node_delucastkey(ni); 18538a1b9b6aSSam Leffler ieee80211_free_node(ni); 18548a1b9b6aSSam Leffler } else { 18558a1b9b6aSSam Leffler if (kid >= IEEE80211_WEP_NKID) 18568a1b9b6aSSam Leffler return EINVAL; 18578a1b9b6aSSam Leffler /* XXX error return */ 18588a1b9b6aSSam Leffler ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]); 18598a1b9b6aSSam Leffler } 18608a1b9b6aSSam Leffler return 0; 18618a1b9b6aSSam Leffler } 18628a1b9b6aSSam Leffler 18638a1b9b6aSSam Leffler static void 18648a1b9b6aSSam Leffler domlme(void *arg, struct ieee80211_node *ni) 18658a1b9b6aSSam Leffler { 18668a1b9b6aSSam Leffler struct ieee80211com *ic = ni->ni_ic; 18678a1b9b6aSSam Leffler struct ieee80211req_mlme *mlme = arg; 18688a1b9b6aSSam Leffler 18698a1b9b6aSSam Leffler if (ni->ni_associd != 0) { 18708a1b9b6aSSam Leffler IEEE80211_SEND_MGMT(ic, ni, 18718a1b9b6aSSam Leffler mlme->im_op == IEEE80211_MLME_DEAUTH ? 18728a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH : 18738a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_DISASSOC, 18748a1b9b6aSSam Leffler mlme->im_reason); 18758a1b9b6aSSam Leffler } 18768a1b9b6aSSam Leffler ieee80211_node_leave(ic, ni); 18778a1b9b6aSSam Leffler } 18788a1b9b6aSSam Leffler 18798a1b9b6aSSam Leffler static int 18808a1b9b6aSSam Leffler ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) 18818a1b9b6aSSam Leffler { 18828a1b9b6aSSam Leffler struct ieee80211req_mlme mlme; 18838a1b9b6aSSam Leffler struct ieee80211_node *ni; 18848a1b9b6aSSam Leffler int error; 18858a1b9b6aSSam Leffler 18868a1b9b6aSSam Leffler if (ireq->i_len != sizeof(mlme)) 18878a1b9b6aSSam Leffler return EINVAL; 18888a1b9b6aSSam Leffler error = copyin(ireq->i_data, &mlme, sizeof(mlme)); 18898a1b9b6aSSam Leffler if (error) 18908a1b9b6aSSam Leffler return error; 18918a1b9b6aSSam Leffler switch (mlme.im_op) { 18928a1b9b6aSSam Leffler case IEEE80211_MLME_ASSOC: 18938a1b9b6aSSam Leffler if (ic->ic_opmode != IEEE80211_M_STA) 18948a1b9b6aSSam Leffler return EINVAL; 18958a1b9b6aSSam Leffler /* XXX must be in S_SCAN state? */ 18968a1b9b6aSSam Leffler 1897f02a0bd2SSam Leffler if (mlme.im_ssid_len != 0) { 18988a1b9b6aSSam Leffler /* 18998a1b9b6aSSam Leffler * Desired ssid specified; must match both bssid and 19008a1b9b6aSSam Leffler * ssid to distinguish ap advertising multiple ssid's. 19018a1b9b6aSSam Leffler */ 19028a1b9b6aSSam Leffler ni = ieee80211_find_node_with_ssid(&ic->ic_scan, 19038a1b9b6aSSam Leffler mlme.im_macaddr, 1904f02a0bd2SSam Leffler mlme.im_ssid_len, mlme.im_ssid); 19058a1b9b6aSSam Leffler } else { 19068a1b9b6aSSam Leffler /* 19078a1b9b6aSSam Leffler * Normal case; just match bssid. 19088a1b9b6aSSam Leffler */ 19098a1b9b6aSSam Leffler ni = ieee80211_find_node(&ic->ic_scan, mlme.im_macaddr); 19108a1b9b6aSSam Leffler } 19118a1b9b6aSSam Leffler if (ni == NULL) 19128a1b9b6aSSam Leffler return EINVAL; 19138a1b9b6aSSam Leffler if (!ieee80211_sta_join(ic, ni)) { 19148a1b9b6aSSam Leffler ieee80211_free_node(ni); 19158a1b9b6aSSam Leffler return EINVAL; 19168a1b9b6aSSam Leffler } 19171a1e1d21SSam Leffler break; 19188a1b9b6aSSam Leffler case IEEE80211_MLME_DISASSOC: 19198a1b9b6aSSam Leffler case IEEE80211_MLME_DEAUTH: 19208a1b9b6aSSam Leffler switch (ic->ic_opmode) { 19218a1b9b6aSSam Leffler case IEEE80211_M_STA: 19228a1b9b6aSSam Leffler /* XXX not quite right */ 19238a1b9b6aSSam Leffler ieee80211_new_state(ic, IEEE80211_S_INIT, 19248a1b9b6aSSam Leffler mlme.im_reason); 19258a1b9b6aSSam Leffler break; 19268a1b9b6aSSam Leffler case IEEE80211_M_HOSTAP: 19278a1b9b6aSSam Leffler /* NB: the broadcast address means do 'em all */ 19288a1b9b6aSSam Leffler if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) { 1929acc4f7f5SSam Leffler if ((ni = ieee80211_find_node(&ic->ic_sta, 19308a1b9b6aSSam Leffler mlme.im_macaddr)) == NULL) 19318a1b9b6aSSam Leffler return EINVAL; 19328a1b9b6aSSam Leffler domlme(&mlme, ni); 19338a1b9b6aSSam Leffler ieee80211_free_node(ni); 19348a1b9b6aSSam Leffler } else { 1935acc4f7f5SSam Leffler ieee80211_iterate_nodes(&ic->ic_sta, 19368a1b9b6aSSam Leffler domlme, &mlme); 19378a1b9b6aSSam Leffler } 19388a1b9b6aSSam Leffler break; 19398a1b9b6aSSam Leffler default: 19408a1b9b6aSSam Leffler return EINVAL; 19418a1b9b6aSSam Leffler } 19428a1b9b6aSSam Leffler break; 19438a1b9b6aSSam Leffler case IEEE80211_MLME_AUTHORIZE: 19448a1b9b6aSSam Leffler case IEEE80211_MLME_UNAUTHORIZE: 19458a1b9b6aSSam Leffler if (ic->ic_opmode != IEEE80211_M_HOSTAP) 19468a1b9b6aSSam Leffler return EINVAL; 1947acc4f7f5SSam Leffler ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr); 19488a1b9b6aSSam Leffler if (ni == NULL) 19498a1b9b6aSSam Leffler return EINVAL; 19508a1b9b6aSSam Leffler if (mlme.im_op == IEEE80211_MLME_AUTHORIZE) 1951e4918ecdSSam Leffler ieee80211_node_authorize(ni); 19528a1b9b6aSSam Leffler else 1953e4918ecdSSam Leffler ieee80211_node_unauthorize(ni); 19548a1b9b6aSSam Leffler ieee80211_free_node(ni); 19558a1b9b6aSSam Leffler break; 19568a1b9b6aSSam Leffler default: 19578a1b9b6aSSam Leffler return EINVAL; 19588a1b9b6aSSam Leffler } 19598a1b9b6aSSam Leffler return 0; 19608a1b9b6aSSam Leffler } 19618a1b9b6aSSam Leffler 19628a1b9b6aSSam Leffler static int 19638a1b9b6aSSam Leffler ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq) 19648a1b9b6aSSam Leffler { 19658a1b9b6aSSam Leffler u_int8_t mac[IEEE80211_ADDR_LEN]; 19668a1b9b6aSSam Leffler const struct ieee80211_aclator *acl = ic->ic_acl; 19678a1b9b6aSSam Leffler int error; 19688a1b9b6aSSam Leffler 19698a1b9b6aSSam Leffler if (ireq->i_len != sizeof(mac)) 19708a1b9b6aSSam Leffler return EINVAL; 19718a1b9b6aSSam Leffler error = copyin(ireq->i_data, mac, ireq->i_len); 19728a1b9b6aSSam Leffler if (error) 19738a1b9b6aSSam Leffler return error; 19748a1b9b6aSSam Leffler if (acl == NULL) { 19758a1b9b6aSSam Leffler acl = ieee80211_aclator_get("mac"); 19768a1b9b6aSSam Leffler if (acl == NULL || !acl->iac_attach(ic)) 19778a1b9b6aSSam Leffler return EINVAL; 19788a1b9b6aSSam Leffler ic->ic_acl = acl; 19798a1b9b6aSSam Leffler } 19808a1b9b6aSSam Leffler if (ireq->i_type == IEEE80211_IOC_ADDMAC) 19818a1b9b6aSSam Leffler acl->iac_add(ic, mac); 19828a1b9b6aSSam Leffler else 19838a1b9b6aSSam Leffler acl->iac_remove(ic, mac); 19848a1b9b6aSSam Leffler return 0; 19858a1b9b6aSSam Leffler } 19868a1b9b6aSSam Leffler 19878a1b9b6aSSam Leffler static int 1988188757f5SSam Leffler ieee80211_ioctl_setmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq) 19898a1b9b6aSSam Leffler { 19908a1b9b6aSSam Leffler const struct ieee80211_aclator *acl = ic->ic_acl; 19918a1b9b6aSSam Leffler 19928a1b9b6aSSam Leffler switch (ireq->i_val) { 19938a1b9b6aSSam Leffler case IEEE80211_MACCMD_POLICY_OPEN: 19948a1b9b6aSSam Leffler case IEEE80211_MACCMD_POLICY_ALLOW: 19958a1b9b6aSSam Leffler case IEEE80211_MACCMD_POLICY_DENY: 19968a1b9b6aSSam Leffler if (acl == NULL) { 19978a1b9b6aSSam Leffler acl = ieee80211_aclator_get("mac"); 19988a1b9b6aSSam Leffler if (acl == NULL || !acl->iac_attach(ic)) 19998a1b9b6aSSam Leffler return EINVAL; 20008a1b9b6aSSam Leffler ic->ic_acl = acl; 20018a1b9b6aSSam Leffler } 20028a1b9b6aSSam Leffler acl->iac_setpolicy(ic, ireq->i_val); 20038a1b9b6aSSam Leffler break; 20048a1b9b6aSSam Leffler case IEEE80211_MACCMD_FLUSH: 20058a1b9b6aSSam Leffler if (acl != NULL) 20068a1b9b6aSSam Leffler acl->iac_flush(ic); 20078a1b9b6aSSam Leffler /* NB: silently ignore when not in use */ 20088a1b9b6aSSam Leffler break; 20098a1b9b6aSSam Leffler case IEEE80211_MACCMD_DETACH: 20108a1b9b6aSSam Leffler if (acl != NULL) { 20118a1b9b6aSSam Leffler ic->ic_acl = NULL; 20128a1b9b6aSSam Leffler acl->iac_detach(ic); 20138a1b9b6aSSam Leffler } 20148a1b9b6aSSam Leffler break; 20158a1b9b6aSSam Leffler default: 2016188757f5SSam Leffler if (acl == NULL) 20178a1b9b6aSSam Leffler return EINVAL; 2018188757f5SSam Leffler else 2019188757f5SSam Leffler return acl->iac_setioctl(ic, ireq); 20208a1b9b6aSSam Leffler } 20218a1b9b6aSSam Leffler return 0; 20228a1b9b6aSSam Leffler } 20238a1b9b6aSSam Leffler 20248a1b9b6aSSam Leffler static int 20258a1b9b6aSSam Leffler ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) 20268a1b9b6aSSam Leffler { 20278a1b9b6aSSam Leffler struct ieee80211req_chanlist list; 20288a1b9b6aSSam Leffler u_char chanlist[IEEE80211_CHAN_BYTES]; 20298a1b9b6aSSam Leffler int i, j, error; 20308a1b9b6aSSam Leffler 20318a1b9b6aSSam Leffler if (ireq->i_len != sizeof(list)) 20328a1b9b6aSSam Leffler return EINVAL; 20338a1b9b6aSSam Leffler error = copyin(ireq->i_data, &list, sizeof(list)); 20348a1b9b6aSSam Leffler if (error) 20358a1b9b6aSSam Leffler return error; 20368a1b9b6aSSam Leffler memset(chanlist, 0, sizeof(chanlist)); 20378a1b9b6aSSam Leffler /* 20388a1b9b6aSSam Leffler * Since channel 0 is not available for DS, channel 1 20398a1b9b6aSSam Leffler * is assigned to LSB on WaveLAN. 20408a1b9b6aSSam Leffler */ 20418a1b9b6aSSam Leffler if (ic->ic_phytype == IEEE80211_T_DS) 20428a1b9b6aSSam Leffler i = 1; 20438a1b9b6aSSam Leffler else 20448a1b9b6aSSam Leffler i = 0; 20458a1b9b6aSSam Leffler for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { 20468a1b9b6aSSam Leffler /* 20478a1b9b6aSSam Leffler * NB: silently discard unavailable channels so users 20488a1b9b6aSSam Leffler * can specify 1-255 to get all available channels. 20498a1b9b6aSSam Leffler */ 20508a1b9b6aSSam Leffler if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) 20518a1b9b6aSSam Leffler setbit(chanlist, i); 20528a1b9b6aSSam Leffler } 20538a1b9b6aSSam Leffler if (ic->ic_ibss_chan == NULL || 20548a1b9b6aSSam Leffler isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { 20558a1b9b6aSSam Leffler for (i = 0; i <= IEEE80211_CHAN_MAX; i++) 20568a1b9b6aSSam Leffler if (isset(chanlist, i)) { 20578a1b9b6aSSam Leffler ic->ic_ibss_chan = &ic->ic_channels[i]; 20588a1b9b6aSSam Leffler goto found; 20598a1b9b6aSSam Leffler } 20608a1b9b6aSSam Leffler return EINVAL; /* no active channels */ 20618a1b9b6aSSam Leffler found: 20628a1b9b6aSSam Leffler ; 20638a1b9b6aSSam Leffler } 20648a1b9b6aSSam Leffler memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); 20658a1b9b6aSSam Leffler return IS_UP_AUTO(ic) ? ENETRESET : 0; 20668a1b9b6aSSam Leffler } 20678a1b9b6aSSam Leffler 20688a1b9b6aSSam Leffler static int 206942568791SSam Leffler ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq) 207042568791SSam Leffler { 207142568791SSam Leffler struct ieee80211_node *ni; 207242568791SSam Leffler u_int8_t macaddr[IEEE80211_ADDR_LEN]; 207342568791SSam Leffler int error; 207442568791SSam Leffler 207542568791SSam Leffler /* 207642568791SSam Leffler * NB: we could copyin ieee80211req_sta_stats so apps 207742568791SSam Leffler * could make selective changes but that's overkill; 207842568791SSam Leffler * just clear all stats for now. 207942568791SSam Leffler */ 208042568791SSam Leffler if (ireq->i_len < IEEE80211_ADDR_LEN) 208142568791SSam Leffler return EINVAL; 208242568791SSam Leffler error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); 208342568791SSam Leffler if (error != 0) 208442568791SSam Leffler return error; 208542568791SSam Leffler ni = ieee80211_find_node(&ic->ic_sta, macaddr); 208642568791SSam Leffler if (ni == NULL) 208742568791SSam Leffler return EINVAL; /* XXX */ 208842568791SSam Leffler memset(&ni->ni_stats, 0, sizeof(ni->ni_stats)); 208942568791SSam Leffler ieee80211_free_node(ni); 209042568791SSam Leffler return 0; 209142568791SSam Leffler } 209242568791SSam Leffler 209342568791SSam Leffler static int 20948a1b9b6aSSam Leffler ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) 20958a1b9b6aSSam Leffler { 20968a1b9b6aSSam Leffler struct ieee80211_node *ni; 20978a1b9b6aSSam Leffler struct ieee80211req_sta_txpow txpow; 20988a1b9b6aSSam Leffler int error; 20998a1b9b6aSSam Leffler 21008a1b9b6aSSam Leffler if (ireq->i_len != sizeof(txpow)) 21018a1b9b6aSSam Leffler return EINVAL; 21028a1b9b6aSSam Leffler error = copyin(ireq->i_data, &txpow, sizeof(txpow)); 21038a1b9b6aSSam Leffler if (error != 0) 21048a1b9b6aSSam Leffler return error; 2105acc4f7f5SSam Leffler ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr); 21068a1b9b6aSSam Leffler if (ni == NULL) 21078a1b9b6aSSam Leffler return EINVAL; /* XXX */ 21088a1b9b6aSSam Leffler ni->ni_txpower = txpow.it_txpow; 21098a1b9b6aSSam Leffler ieee80211_free_node(ni); 21108a1b9b6aSSam Leffler return error; 21118a1b9b6aSSam Leffler } 21128a1b9b6aSSam Leffler 21138a1b9b6aSSam Leffler static int 21148a1b9b6aSSam Leffler ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) 21158a1b9b6aSSam Leffler { 21168a1b9b6aSSam Leffler struct ieee80211_wme_state *wme = &ic->ic_wme; 21178a1b9b6aSSam Leffler struct wmeParams *wmep, *chanp; 21188a1b9b6aSSam Leffler int isbss, ac; 21198a1b9b6aSSam Leffler 21208a1b9b6aSSam Leffler if ((ic->ic_caps & IEEE80211_C_WME) == 0) 21218a1b9b6aSSam Leffler return EINVAL; 21228a1b9b6aSSam Leffler 21238a1b9b6aSSam Leffler isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS); 21248a1b9b6aSSam Leffler ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); 21258a1b9b6aSSam Leffler if (ac >= WME_NUM_AC) 21268a1b9b6aSSam Leffler ac = WME_AC_BE; 21278a1b9b6aSSam Leffler if (isbss) { 21288a1b9b6aSSam Leffler chanp = &wme->wme_bssChanParams.cap_wmeParams[ac]; 21298a1b9b6aSSam Leffler wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; 21308a1b9b6aSSam Leffler } else { 21318a1b9b6aSSam Leffler chanp = &wme->wme_chanParams.cap_wmeParams[ac]; 21328a1b9b6aSSam Leffler wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; 21338a1b9b6aSSam Leffler } 21348a1b9b6aSSam Leffler switch (ireq->i_type) { 21358a1b9b6aSSam Leffler case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 21368a1b9b6aSSam Leffler if (isbss) { 21378a1b9b6aSSam Leffler wmep->wmep_logcwmin = ireq->i_val; 21388a1b9b6aSSam Leffler if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 21398a1b9b6aSSam Leffler chanp->wmep_logcwmin = ireq->i_val; 21408a1b9b6aSSam Leffler } else { 21418a1b9b6aSSam Leffler wmep->wmep_logcwmin = chanp->wmep_logcwmin = 21428a1b9b6aSSam Leffler ireq->i_val; 21438a1b9b6aSSam Leffler } 21448a1b9b6aSSam Leffler break; 21458a1b9b6aSSam Leffler case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 21468a1b9b6aSSam Leffler if (isbss) { 21478a1b9b6aSSam Leffler wmep->wmep_logcwmax = ireq->i_val; 21488a1b9b6aSSam Leffler if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 21498a1b9b6aSSam Leffler chanp->wmep_logcwmax = ireq->i_val; 21508a1b9b6aSSam Leffler } else { 21518a1b9b6aSSam Leffler wmep->wmep_logcwmax = chanp->wmep_logcwmax = 21528a1b9b6aSSam Leffler ireq->i_val; 21538a1b9b6aSSam Leffler } 21548a1b9b6aSSam Leffler break; 21558a1b9b6aSSam Leffler case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 21568a1b9b6aSSam Leffler if (isbss) { 21578a1b9b6aSSam Leffler wmep->wmep_aifsn = ireq->i_val; 21588a1b9b6aSSam Leffler if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 21598a1b9b6aSSam Leffler chanp->wmep_aifsn = ireq->i_val; 21608a1b9b6aSSam Leffler } else { 21618a1b9b6aSSam Leffler wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val; 21628a1b9b6aSSam Leffler } 21638a1b9b6aSSam Leffler break; 21648a1b9b6aSSam Leffler case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 21658a1b9b6aSSam Leffler if (isbss) { 21668a1b9b6aSSam Leffler wmep->wmep_txopLimit = ireq->i_val; 21678a1b9b6aSSam Leffler if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 21688a1b9b6aSSam Leffler chanp->wmep_txopLimit = ireq->i_val; 21698a1b9b6aSSam Leffler } else { 21708a1b9b6aSSam Leffler wmep->wmep_txopLimit = chanp->wmep_txopLimit = 21718a1b9b6aSSam Leffler ireq->i_val; 21728a1b9b6aSSam Leffler } 21738a1b9b6aSSam Leffler break; 21748a1b9b6aSSam Leffler case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 21758a1b9b6aSSam Leffler wmep->wmep_acm = ireq->i_val; 21768a1b9b6aSSam Leffler if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 21778a1b9b6aSSam Leffler chanp->wmep_acm = ireq->i_val; 21788a1b9b6aSSam Leffler break; 21798a1b9b6aSSam Leffler case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ 21808a1b9b6aSSam Leffler wmep->wmep_noackPolicy = chanp->wmep_noackPolicy = 21818a1b9b6aSSam Leffler (ireq->i_val) == 0; 21828a1b9b6aSSam Leffler break; 21838a1b9b6aSSam Leffler } 21848a1b9b6aSSam Leffler ieee80211_wme_updateparams(ic); 21858a1b9b6aSSam Leffler return 0; 21868a1b9b6aSSam Leffler } 21878a1b9b6aSSam Leffler 21888a1b9b6aSSam Leffler static int 21898a1b9b6aSSam Leffler cipher2cap(int cipher) 21908a1b9b6aSSam Leffler { 21918a1b9b6aSSam Leffler switch (cipher) { 21928a1b9b6aSSam Leffler case IEEE80211_CIPHER_WEP: return IEEE80211_C_WEP; 21938a1b9b6aSSam Leffler case IEEE80211_CIPHER_AES_OCB: return IEEE80211_C_AES; 21948a1b9b6aSSam Leffler case IEEE80211_CIPHER_AES_CCM: return IEEE80211_C_AES_CCM; 21958a1b9b6aSSam Leffler case IEEE80211_CIPHER_CKIP: return IEEE80211_C_CKIP; 21968a1b9b6aSSam Leffler case IEEE80211_CIPHER_TKIP: return IEEE80211_C_TKIP; 21978a1b9b6aSSam Leffler } 21988a1b9b6aSSam Leffler return 0; 21998a1b9b6aSSam Leffler } 22008a1b9b6aSSam Leffler 22018a1b9b6aSSam Leffler static int 22028a1b9b6aSSam Leffler ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) 22038a1b9b6aSSam Leffler { 22048a1b9b6aSSam Leffler static const u_int8_t zerobssid[IEEE80211_ADDR_LEN]; 22058a1b9b6aSSam Leffler struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; 22068a1b9b6aSSam Leffler int error; 22078a1b9b6aSSam Leffler const struct ieee80211_authenticator *auth; 22088a1b9b6aSSam Leffler u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; 22098a1b9b6aSSam Leffler char tmpssid[IEEE80211_NWID_LEN]; 22108a1b9b6aSSam Leffler u_int8_t tmpbssid[IEEE80211_ADDR_LEN]; 22118a1b9b6aSSam Leffler struct ieee80211_key *k; 22128a1b9b6aSSam Leffler int j, caps; 22138a1b9b6aSSam Leffler u_int kid; 22148a1b9b6aSSam Leffler 22158a1b9b6aSSam Leffler error = 0; 22161a1e1d21SSam Leffler switch (ireq->i_type) { 22171a1e1d21SSam Leffler case IEEE80211_IOC_SSID: 22181a1e1d21SSam Leffler if (ireq->i_val != 0 || 22198a1b9b6aSSam Leffler ireq->i_len > IEEE80211_NWID_LEN) 22208a1b9b6aSSam Leffler return EINVAL; 22211a1e1d21SSam Leffler error = copyin(ireq->i_data, tmpssid, ireq->i_len); 22221a1e1d21SSam Leffler if (error) 22231a1e1d21SSam Leffler break; 22241a1e1d21SSam Leffler memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); 22251a1e1d21SSam Leffler ic->ic_des_esslen = ireq->i_len; 22261a1e1d21SSam Leffler memcpy(ic->ic_des_essid, tmpssid, ireq->i_len); 22271a1e1d21SSam Leffler error = ENETRESET; 22281a1e1d21SSam Leffler break; 22291a1e1d21SSam Leffler case IEEE80211_IOC_WEP: 22308a1b9b6aSSam Leffler switch (ireq->i_val) { 22318a1b9b6aSSam Leffler case IEEE80211_WEP_OFF: 22328a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_PRIVACY; 22338a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_DROPUNENC; 22348a1b9b6aSSam Leffler break; 22358a1b9b6aSSam Leffler case IEEE80211_WEP_ON: 22368a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_PRIVACY; 22378a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_DROPUNENC; 22388a1b9b6aSSam Leffler break; 22398a1b9b6aSSam Leffler case IEEE80211_WEP_MIXED: 22408a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_PRIVACY; 22418a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_DROPUNENC; 22428a1b9b6aSSam Leffler break; 22431a1e1d21SSam Leffler } 22441a1e1d21SSam Leffler error = ENETRESET; 22451a1e1d21SSam Leffler break; 22461a1e1d21SSam Leffler case IEEE80211_IOC_WEPKEY: 22471a1e1d21SSam Leffler kid = (u_int) ireq->i_val; 22488a1b9b6aSSam Leffler if (kid >= IEEE80211_WEP_NKID) 22498a1b9b6aSSam Leffler return EINVAL; 22508a1b9b6aSSam Leffler k = &ic->ic_nw_keys[kid]; 22518a1b9b6aSSam Leffler if (ireq->i_len == 0) { 22528a1b9b6aSSam Leffler /* zero-len =>'s delete any existing key */ 22538a1b9b6aSSam Leffler (void) ieee80211_crypto_delkey(ic, k); 22541a1e1d21SSam Leffler break; 22551a1e1d21SSam Leffler } 22568a1b9b6aSSam Leffler if (ireq->i_len > sizeof(tmpkey)) 22578a1b9b6aSSam Leffler return EINVAL; 22581a1e1d21SSam Leffler memset(tmpkey, 0, sizeof(tmpkey)); 22591a1e1d21SSam Leffler error = copyin(ireq->i_data, tmpkey, ireq->i_len); 22601a1e1d21SSam Leffler if (error) 22611a1e1d21SSam Leffler break; 22628a1b9b6aSSam Leffler ieee80211_key_update_begin(ic); 2263dd70e17bSSam Leffler k->wk_keyix = kid; /* NB: force fixed key id */ 2264dd70e17bSSam Leffler if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP, 2265dd70e17bSSam Leffler IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) { 22668a1b9b6aSSam Leffler k->wk_keylen = ireq->i_len; 22678a1b9b6aSSam Leffler memcpy(k->wk_key, tmpkey, sizeof(tmpkey)); 22688a1b9b6aSSam Leffler if (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr)) 22698a1b9b6aSSam Leffler error = EINVAL; 22708a1b9b6aSSam Leffler } else 22718a1b9b6aSSam Leffler error = EINVAL; 22728a1b9b6aSSam Leffler ieee80211_key_update_end(ic); 2273b5d4660fSSam Leffler if (!error) /* NB: for compatibility */ 2274b5d4660fSSam Leffler error = ENETRESET; 22751a1e1d21SSam Leffler break; 22761a1e1d21SSam Leffler case IEEE80211_IOC_WEPTXKEY: 22771a1e1d21SSam Leffler kid = (u_int) ireq->i_val; 2278380c5fa9SSam Leffler if (kid >= IEEE80211_WEP_NKID && 2279380c5fa9SSam Leffler (u_int16_t) kid != IEEE80211_KEYIX_NONE) 22808a1b9b6aSSam Leffler return EINVAL; 22818a1b9b6aSSam Leffler ic->ic_def_txkey = kid; 2282b8d2606bSSam Leffler error = ENETRESET; /* push to hardware */ 22838a1b9b6aSSam Leffler break; 22848a1b9b6aSSam Leffler case IEEE80211_IOC_AUTHMODE: 22858a1b9b6aSSam Leffler switch (ireq->i_val) { 22868a1b9b6aSSam Leffler case IEEE80211_AUTH_WPA: 22878a1b9b6aSSam Leffler case IEEE80211_AUTH_8021X: /* 802.1x */ 22888a1b9b6aSSam Leffler case IEEE80211_AUTH_OPEN: /* open */ 22898a1b9b6aSSam Leffler case IEEE80211_AUTH_SHARED: /* shared-key */ 22908a1b9b6aSSam Leffler case IEEE80211_AUTH_AUTO: /* auto */ 22918a1b9b6aSSam Leffler auth = ieee80211_authenticator_get(ireq->i_val); 22928a1b9b6aSSam Leffler if (auth == NULL) 22938a1b9b6aSSam Leffler return EINVAL; 22948a1b9b6aSSam Leffler break; 22958a1b9b6aSSam Leffler default: 22968a1b9b6aSSam Leffler return EINVAL; 22978a1b9b6aSSam Leffler } 22988a1b9b6aSSam Leffler switch (ireq->i_val) { 22998a1b9b6aSSam Leffler case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */ 23008a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_PRIVACY; 23018a1b9b6aSSam Leffler ireq->i_val = IEEE80211_AUTH_8021X; 23028a1b9b6aSSam Leffler break; 23038a1b9b6aSSam Leffler case IEEE80211_AUTH_OPEN: /* open */ 23048a1b9b6aSSam Leffler ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY); 23058a1b9b6aSSam Leffler break; 23068a1b9b6aSSam Leffler case IEEE80211_AUTH_SHARED: /* shared-key */ 23078a1b9b6aSSam Leffler case IEEE80211_AUTH_8021X: /* 802.1x */ 23088a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_WPA; 23098a1b9b6aSSam Leffler /* both require a key so mark the PRIVACY capability */ 23108a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_PRIVACY; 23118a1b9b6aSSam Leffler break; 23128a1b9b6aSSam Leffler case IEEE80211_AUTH_AUTO: /* auto */ 23138a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_WPA; 23148a1b9b6aSSam Leffler /* XXX PRIVACY handling? */ 23158a1b9b6aSSam Leffler /* XXX what's the right way to do this? */ 23161a1e1d21SSam Leffler break; 23171a1e1d21SSam Leffler } 23188a1b9b6aSSam Leffler /* NB: authenticator attach/detach happens on state change */ 23198a1b9b6aSSam Leffler ic->ic_bss->ni_authmode = ireq->i_val; 23208a1b9b6aSSam Leffler /* XXX mixed/mode/usage? */ 23218a1b9b6aSSam Leffler ic->ic_auth = auth; 23221a1e1d21SSam Leffler error = ENETRESET; 23231a1e1d21SSam Leffler break; 23241a1e1d21SSam Leffler case IEEE80211_IOC_CHANNEL: 23251a1e1d21SSam Leffler /* XXX 0xffff overflows 16-bit signed */ 23261a1e1d21SSam Leffler if (ireq->i_val == 0 || 23271a1e1d21SSam Leffler ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) 23281a1e1d21SSam Leffler ic->ic_des_chan = IEEE80211_CHAN_ANYC; 23291a1e1d21SSam Leffler else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX || 23301a1e1d21SSam Leffler isclr(ic->ic_chan_active, ireq->i_val)) { 23318a1b9b6aSSam Leffler return EINVAL; 23321a1e1d21SSam Leffler } else 23331a1e1d21SSam Leffler ic->ic_ibss_chan = ic->ic_des_chan = 23341a1e1d21SSam Leffler &ic->ic_channels[ireq->i_val]; 23351a1e1d21SSam Leffler switch (ic->ic_state) { 23361a1e1d21SSam Leffler case IEEE80211_S_INIT: 23371a1e1d21SSam Leffler case IEEE80211_S_SCAN: 23381a1e1d21SSam Leffler error = ENETRESET; 23391a1e1d21SSam Leffler break; 23401a1e1d21SSam Leffler default: 23418a1b9b6aSSam Leffler /* 23428a1b9b6aSSam Leffler * If the desired channel has changed (to something 23438a1b9b6aSSam Leffler * other than any) and we're not already scanning, 23448a1b9b6aSSam Leffler * then kick the state machine. 23458a1b9b6aSSam Leffler */ 23461a1e1d21SSam Leffler if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 23478a1b9b6aSSam Leffler ic->ic_bss->ni_chan != ic->ic_des_chan && 23488a1b9b6aSSam Leffler (ic->ic_flags & IEEE80211_F_SCAN) == 0) 23491a1e1d21SSam Leffler error = ENETRESET; 23501a1e1d21SSam Leffler break; 23511a1e1d21SSam Leffler } 23528157976aSTai-hwa Liang if (error == ENETRESET && 23538157976aSTai-hwa Liang ic->ic_opmode == IEEE80211_M_MONITOR) { 23548157976aSTai-hwa Liang if (IS_UP(ic)) { 23558157976aSTai-hwa Liang /* 23568157976aSTai-hwa Liang * Monitor mode can switch directly. 23578157976aSTai-hwa Liang */ 23588157976aSTai-hwa Liang if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) 23598157976aSTai-hwa Liang ic->ic_curchan = ic->ic_des_chan; 23608157976aSTai-hwa Liang error = ic->ic_reset(ic->ic_ifp); 23618157976aSTai-hwa Liang } else 23628157976aSTai-hwa Liang error = 0; 23638157976aSTai-hwa Liang } 23641a1e1d21SSam Leffler break; 23651a1e1d21SSam Leffler case IEEE80211_IOC_POWERSAVE: 23661a1e1d21SSam Leffler switch (ireq->i_val) { 23671a1e1d21SSam Leffler case IEEE80211_POWERSAVE_OFF: 23681a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_PMGTON) { 23691a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_PMGTON; 23701a1e1d21SSam Leffler error = ENETRESET; 23711a1e1d21SSam Leffler } 23721a1e1d21SSam Leffler break; 23731a1e1d21SSam Leffler case IEEE80211_POWERSAVE_ON: 23741a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) 23751a1e1d21SSam Leffler error = EINVAL; 23761a1e1d21SSam Leffler else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { 23771a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_PMGTON; 23781a1e1d21SSam Leffler error = ENETRESET; 23791a1e1d21SSam Leffler } 23801a1e1d21SSam Leffler break; 23811a1e1d21SSam Leffler default: 23821a1e1d21SSam Leffler error = EINVAL; 23831a1e1d21SSam Leffler break; 23841a1e1d21SSam Leffler } 23850eda166bSSam Leffler if (error == ENETRESET) { 23860eda166bSSam Leffler /* 23870eda166bSSam Leffler * Switching in+out of power save mode 23880eda166bSSam Leffler * should not require a state change. 23890eda166bSSam Leffler */ 23900eda166bSSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 23910eda166bSSam Leffler } 23921a1e1d21SSam Leffler break; 23931a1e1d21SSam Leffler case IEEE80211_IOC_POWERSAVESLEEP: 23948a1b9b6aSSam Leffler if (ireq->i_val < 0) 23958a1b9b6aSSam Leffler return EINVAL; 23961a1e1d21SSam Leffler ic->ic_lintval = ireq->i_val; 23978a1b9b6aSSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 23981a1e1d21SSam Leffler break; 239913604e6bSSam Leffler case IEEE80211_IOC_RTSTHRESHOLD: 240070231e3dSSam Leffler if (!(IEEE80211_RTS_MIN <= ireq->i_val && 240170231e3dSSam Leffler ireq->i_val <= IEEE80211_RTS_MAX)) 24028a1b9b6aSSam Leffler return EINVAL; 24031a1e1d21SSam Leffler ic->ic_rtsthreshold = ireq->i_val; 24048a1b9b6aSSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 24051a1e1d21SSam Leffler break; 24062e79ca97SSam Leffler case IEEE80211_IOC_PROTMODE: 24078a1b9b6aSSam Leffler if (ireq->i_val > IEEE80211_PROT_RTSCTS) 24088a1b9b6aSSam Leffler return EINVAL; 24092e79ca97SSam Leffler ic->ic_protmode = ireq->i_val; 24102e79ca97SSam Leffler /* NB: if not operating in 11g this can wait */ 24112e79ca97SSam Leffler if (ic->ic_curmode == IEEE80211_MODE_11G) 24128a1b9b6aSSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 24132e79ca97SSam Leffler break; 24142e79ca97SSam Leffler case IEEE80211_IOC_TXPOWER: 24158a1b9b6aSSam Leffler if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) 24168a1b9b6aSSam Leffler return EINVAL; 24172e79ca97SSam Leffler if (!(IEEE80211_TXPOWER_MIN < ireq->i_val && 24188a1b9b6aSSam Leffler ireq->i_val < IEEE80211_TXPOWER_MAX)) 24198a1b9b6aSSam Leffler return EINVAL; 24208a1b9b6aSSam Leffler ic->ic_txpowlimit = ireq->i_val; 24218a1b9b6aSSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 24228a1b9b6aSSam Leffler break; 24238a1b9b6aSSam Leffler case IEEE80211_IOC_ROAMING: 24248a1b9b6aSSam Leffler if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val && 24258a1b9b6aSSam Leffler ireq->i_val <= IEEE80211_ROAMING_MANUAL)) 24268a1b9b6aSSam Leffler return EINVAL; 24278a1b9b6aSSam Leffler ic->ic_roaming = ireq->i_val; 24288a1b9b6aSSam Leffler /* XXXX reset? */ 24298a1b9b6aSSam Leffler break; 24308a1b9b6aSSam Leffler case IEEE80211_IOC_PRIVACY: 24318a1b9b6aSSam Leffler if (ireq->i_val) { 24328a1b9b6aSSam Leffler /* XXX check for key state? */ 24338a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_PRIVACY; 24348a1b9b6aSSam Leffler } else 24358a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_PRIVACY; 24368a1b9b6aSSam Leffler break; 24378a1b9b6aSSam Leffler case IEEE80211_IOC_DROPUNENCRYPTED: 24388a1b9b6aSSam Leffler if (ireq->i_val) 24398a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_DROPUNENC; 24408a1b9b6aSSam Leffler else 24418a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_DROPUNENC; 24428a1b9b6aSSam Leffler break; 24438a1b9b6aSSam Leffler case IEEE80211_IOC_WPAKEY: 24448a1b9b6aSSam Leffler error = ieee80211_ioctl_setkey(ic, ireq); 24458a1b9b6aSSam Leffler break; 24468a1b9b6aSSam Leffler case IEEE80211_IOC_DELKEY: 24478a1b9b6aSSam Leffler error = ieee80211_ioctl_delkey(ic, ireq); 24488a1b9b6aSSam Leffler break; 24498a1b9b6aSSam Leffler case IEEE80211_IOC_MLME: 24508a1b9b6aSSam Leffler error = ieee80211_ioctl_setmlme(ic, ireq); 24518a1b9b6aSSam Leffler break; 24528a1b9b6aSSam Leffler case IEEE80211_IOC_OPTIE: 24538a1b9b6aSSam Leffler error = ieee80211_ioctl_setoptie(ic, ireq); 24548a1b9b6aSSam Leffler break; 24558a1b9b6aSSam Leffler case IEEE80211_IOC_COUNTERMEASURES: 24568a1b9b6aSSam Leffler if (ireq->i_val) { 24578a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_WPA) == 0) 24588a1b9b6aSSam Leffler return EINVAL; 24598a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_COUNTERM; 24608a1b9b6aSSam Leffler } else 24618a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_COUNTERM; 24628a1b9b6aSSam Leffler break; 24638a1b9b6aSSam Leffler case IEEE80211_IOC_WPA: 24648a1b9b6aSSam Leffler if (ireq->i_val > 3) 24658a1b9b6aSSam Leffler return EINVAL; 24668a1b9b6aSSam Leffler /* XXX verify ciphers available */ 24678a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_WPA; 24688a1b9b6aSSam Leffler switch (ireq->i_val) { 24698a1b9b6aSSam Leffler case 1: 24708a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_WPA1; 24718a1b9b6aSSam Leffler break; 24728a1b9b6aSSam Leffler case 2: 24738a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_WPA2; 24748a1b9b6aSSam Leffler break; 24758a1b9b6aSSam Leffler case 3: 24768a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2; 24772e79ca97SSam Leffler break; 24782e79ca97SSam Leffler } 24798a1b9b6aSSam Leffler error = ENETRESET; /* XXX? */ 24808a1b9b6aSSam Leffler break; 24818a1b9b6aSSam Leffler case IEEE80211_IOC_WME: 24828a1b9b6aSSam Leffler if (ireq->i_val) { 24838a1b9b6aSSam Leffler if ((ic->ic_caps & IEEE80211_C_WME) == 0) 24848a1b9b6aSSam Leffler return EINVAL; 24858a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_WME; 24868a1b9b6aSSam Leffler } else 24878a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_WME; 24888a1b9b6aSSam Leffler error = ENETRESET; /* XXX maybe not for station? */ 24898a1b9b6aSSam Leffler break; 24908a1b9b6aSSam Leffler case IEEE80211_IOC_HIDESSID: 24918a1b9b6aSSam Leffler if (ireq->i_val) 24928a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_HIDESSID; 24938a1b9b6aSSam Leffler else 24948a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_HIDESSID; 24952e79ca97SSam Leffler error = ENETRESET; 24962e79ca97SSam Leffler break; 24978a1b9b6aSSam Leffler case IEEE80211_IOC_APBRIDGE: 24988a1b9b6aSSam Leffler if (ireq->i_val == 0) 24998a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_NOBRIDGE; 25008a1b9b6aSSam Leffler else 25018a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_NOBRIDGE; 25028a1b9b6aSSam Leffler break; 25038a1b9b6aSSam Leffler case IEEE80211_IOC_MCASTCIPHER: 25048a1b9b6aSSam Leffler if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 && 25058a1b9b6aSSam Leffler !ieee80211_crypto_available(ireq->i_val)) 25068a1b9b6aSSam Leffler return EINVAL; 25078a1b9b6aSSam Leffler rsn->rsn_mcastcipher = ireq->i_val; 25088a1b9b6aSSam Leffler error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 25098a1b9b6aSSam Leffler break; 25108a1b9b6aSSam Leffler case IEEE80211_IOC_MCASTKEYLEN: 25118a1b9b6aSSam Leffler if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE)) 25128a1b9b6aSSam Leffler return EINVAL; 25138a1b9b6aSSam Leffler /* XXX no way to verify driver capability */ 25148a1b9b6aSSam Leffler rsn->rsn_mcastkeylen = ireq->i_val; 25158a1b9b6aSSam Leffler error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 25168a1b9b6aSSam Leffler break; 25178a1b9b6aSSam Leffler case IEEE80211_IOC_UCASTCIPHERS: 25188a1b9b6aSSam Leffler /* 25198a1b9b6aSSam Leffler * Convert user-specified cipher set to the set 25208a1b9b6aSSam Leffler * we can support (via hardware or software). 25218a1b9b6aSSam Leffler * NB: this logic intentionally ignores unknown and 25228a1b9b6aSSam Leffler * unsupported ciphers so folks can specify 0xff or 25238a1b9b6aSSam Leffler * similar and get all available ciphers. 25248a1b9b6aSSam Leffler */ 25258a1b9b6aSSam Leffler caps = 0; 25268a1b9b6aSSam Leffler for (j = 1; j < 32; j++) /* NB: skip WEP */ 25278a1b9b6aSSam Leffler if ((ireq->i_val & (1<<j)) && 25288a1b9b6aSSam Leffler ((ic->ic_caps & cipher2cap(j)) || 25298a1b9b6aSSam Leffler ieee80211_crypto_available(j))) 25308a1b9b6aSSam Leffler caps |= 1<<j; 25318a1b9b6aSSam Leffler if (caps == 0) /* nothing available */ 25328a1b9b6aSSam Leffler return EINVAL; 25338a1b9b6aSSam Leffler /* XXX verify ciphers ok for unicast use? */ 25348a1b9b6aSSam Leffler /* XXX disallow if running as it'll have no effect */ 25358a1b9b6aSSam Leffler rsn->rsn_ucastcipherset = caps; 25368a1b9b6aSSam Leffler error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 25378a1b9b6aSSam Leffler break; 25388a1b9b6aSSam Leffler case IEEE80211_IOC_UCASTCIPHER: 25398a1b9b6aSSam Leffler if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0) 25408a1b9b6aSSam Leffler return EINVAL; 25418a1b9b6aSSam Leffler rsn->rsn_ucastcipher = ireq->i_val; 25428a1b9b6aSSam Leffler break; 25438a1b9b6aSSam Leffler case IEEE80211_IOC_UCASTKEYLEN: 25448a1b9b6aSSam Leffler if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE)) 25458a1b9b6aSSam Leffler return EINVAL; 25468a1b9b6aSSam Leffler /* XXX no way to verify driver capability */ 25478a1b9b6aSSam Leffler rsn->rsn_ucastkeylen = ireq->i_val; 25488a1b9b6aSSam Leffler break; 25498a1b9b6aSSam Leffler case IEEE80211_IOC_DRIVER_CAPS: 25508a1b9b6aSSam Leffler /* NB: for testing */ 25518a1b9b6aSSam Leffler ic->ic_caps = (((u_int16_t) ireq->i_val) << 16) | 25528a1b9b6aSSam Leffler ((u_int16_t) ireq->i_len); 25538a1b9b6aSSam Leffler break; 25548a1b9b6aSSam Leffler case IEEE80211_IOC_KEYMGTALGS: 25558a1b9b6aSSam Leffler /* XXX check */ 25568a1b9b6aSSam Leffler rsn->rsn_keymgmtset = ireq->i_val; 25578a1b9b6aSSam Leffler error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 25588a1b9b6aSSam Leffler break; 25598a1b9b6aSSam Leffler case IEEE80211_IOC_RSNCAPS: 25608a1b9b6aSSam Leffler /* XXX check */ 25618a1b9b6aSSam Leffler rsn->rsn_caps = ireq->i_val; 25628a1b9b6aSSam Leffler error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 25638a1b9b6aSSam Leffler break; 25648a1b9b6aSSam Leffler case IEEE80211_IOC_BSSID: 25658a1b9b6aSSam Leffler if (ireq->i_len != sizeof(tmpbssid)) 25668a1b9b6aSSam Leffler return EINVAL; 25678a1b9b6aSSam Leffler error = copyin(ireq->i_data, tmpbssid, ireq->i_len); 25688a1b9b6aSSam Leffler if (error) 25698a1b9b6aSSam Leffler break; 25708a1b9b6aSSam Leffler IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid); 25718a1b9b6aSSam Leffler if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid)) 25728a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_DESBSSID; 25738a1b9b6aSSam Leffler else 25748a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_DESBSSID; 25758a1b9b6aSSam Leffler error = ENETRESET; 25768a1b9b6aSSam Leffler break; 25778a1b9b6aSSam Leffler case IEEE80211_IOC_CHANLIST: 25788a1b9b6aSSam Leffler error = ieee80211_ioctl_setchanlist(ic, ireq); 25798a1b9b6aSSam Leffler break; 25808a1b9b6aSSam Leffler case IEEE80211_IOC_SCAN_REQ: 25818a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) /* XXX ignore */ 25828a1b9b6aSSam Leffler break; 25838a1b9b6aSSam Leffler error = ieee80211_setupscan(ic, ic->ic_chan_avail); 25848a1b9b6aSSam Leffler if (error == 0) /* XXX background scan */ 25858a1b9b6aSSam Leffler error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 25868a1b9b6aSSam Leffler break; 25878a1b9b6aSSam Leffler case IEEE80211_IOC_ADDMAC: 25888a1b9b6aSSam Leffler case IEEE80211_IOC_DELMAC: 25898a1b9b6aSSam Leffler error = ieee80211_ioctl_macmac(ic, ireq); 25908a1b9b6aSSam Leffler break; 25918a1b9b6aSSam Leffler case IEEE80211_IOC_MACCMD: 2592188757f5SSam Leffler error = ieee80211_ioctl_setmaccmd(ic, ireq); 25938a1b9b6aSSam Leffler break; 259442568791SSam Leffler case IEEE80211_IOC_STA_STATS: 259542568791SSam Leffler error = ieee80211_ioctl_setstastats(ic, ireq); 259642568791SSam Leffler break; 25978a1b9b6aSSam Leffler case IEEE80211_IOC_STA_TXPOW: 25988a1b9b6aSSam Leffler error = ieee80211_ioctl_setstatxpow(ic, ireq); 25998a1b9b6aSSam Leffler break; 26008a1b9b6aSSam Leffler case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 26018a1b9b6aSSam Leffler case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 26028a1b9b6aSSam Leffler case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 26038a1b9b6aSSam Leffler case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 26048a1b9b6aSSam Leffler case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 26058a1b9b6aSSam Leffler case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ 26068a1b9b6aSSam Leffler error = ieee80211_ioctl_setwmeparam(ic, ireq); 26078a1b9b6aSSam Leffler break; 26088a1b9b6aSSam Leffler case IEEE80211_IOC_DTIM_PERIOD: 26098a1b9b6aSSam Leffler if (ic->ic_opmode != IEEE80211_M_HOSTAP && 26108a1b9b6aSSam Leffler ic->ic_opmode != IEEE80211_M_IBSS) 26118a1b9b6aSSam Leffler return EINVAL; 26128a1b9b6aSSam Leffler if (IEEE80211_DTIM_MIN <= ireq->i_val && 26138a1b9b6aSSam Leffler ireq->i_val <= IEEE80211_DTIM_MAX) { 26148a1b9b6aSSam Leffler ic->ic_dtim_period = ireq->i_val; 26158a1b9b6aSSam Leffler error = ENETRESET; /* requires restart */ 26168a1b9b6aSSam Leffler } else 26178a1b9b6aSSam Leffler error = EINVAL; 26188a1b9b6aSSam Leffler break; 26198a1b9b6aSSam Leffler case IEEE80211_IOC_BEACON_INTERVAL: 26208a1b9b6aSSam Leffler if (ic->ic_opmode != IEEE80211_M_HOSTAP && 26218a1b9b6aSSam Leffler ic->ic_opmode != IEEE80211_M_IBSS) 26228a1b9b6aSSam Leffler return EINVAL; 26238a1b9b6aSSam Leffler if (IEEE80211_BINTVAL_MIN <= ireq->i_val && 26248a1b9b6aSSam Leffler ireq->i_val <= IEEE80211_BINTVAL_MAX) { 2625d365f9c7SSam Leffler ic->ic_bintval = ireq->i_val; 26268a1b9b6aSSam Leffler error = ENETRESET; /* requires restart */ 26278a1b9b6aSSam Leffler } else 26288a1b9b6aSSam Leffler error = EINVAL; 26298a1b9b6aSSam Leffler break; 2630c4f040c3SSam Leffler case IEEE80211_IOC_PUREG: 2631c4f040c3SSam Leffler if (ireq->i_val) 2632c4f040c3SSam Leffler ic->ic_flags |= IEEE80211_F_PUREG; 2633c4f040c3SSam Leffler else 2634c4f040c3SSam Leffler ic->ic_flags &= ~IEEE80211_F_PUREG; 2635c4f040c3SSam Leffler /* NB: reset only if we're operating on an 11g channel */ 2636c4f040c3SSam Leffler if (ic->ic_curmode == IEEE80211_MODE_11G) 2637c4f040c3SSam Leffler error = ENETRESET; 2638c4f040c3SSam Leffler break; 263964353cb0SSam Leffler case IEEE80211_IOC_MCAST_RATE: 264064353cb0SSam Leffler ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL; 264164353cb0SSam Leffler break; 264270231e3dSSam Leffler case IEEE80211_IOC_FRAGTHRESHOLD: 264370231e3dSSam Leffler if ((ic->ic_caps & IEEE80211_C_TXFRAG) == 0 && 264470231e3dSSam Leffler ireq->i_val != IEEE80211_FRAG_MAX) 264570231e3dSSam Leffler return EINVAL; 264670231e3dSSam Leffler if (!(IEEE80211_FRAG_MIN <= ireq->i_val && 264770231e3dSSam Leffler ireq->i_val <= IEEE80211_FRAG_MAX)) 264870231e3dSSam Leffler return EINVAL; 264970231e3dSSam Leffler ic->ic_fragthreshold = ireq->i_val; 265070231e3dSSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 265170231e3dSSam Leffler break; 2652c27e4e31SSam Leffler case IEEE80211_IOC_BURST: 2653c27e4e31SSam Leffler if (ireq->i_val) { 2654c27e4e31SSam Leffler if ((ic->ic_caps & IEEE80211_C_BURST) == 0) 2655c27e4e31SSam Leffler return EINVAL; 2656c27e4e31SSam Leffler ic->ic_flags |= IEEE80211_F_BURST; 2657c27e4e31SSam Leffler } else 2658c27e4e31SSam Leffler ic->ic_flags &= ~IEEE80211_F_BURST; 2659c27e4e31SSam Leffler error = ENETRESET; /* XXX maybe not for station? */ 2660c27e4e31SSam Leffler break; 2661546786c9SSam Leffler case IEEE80211_IOC_BMISSTHRESHOLD: 2662546786c9SSam Leffler if (!(IEEE80211_HWBMISS_MIN <= ireq->i_val && 2663546786c9SSam Leffler ireq->i_val <= IEEE80211_HWBMISS_MAX)) 2664546786c9SSam Leffler return EINVAL; 2665546786c9SSam Leffler ic->ic_bmissthreshold = ireq->i_val; 2666546786c9SSam Leffler error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2667546786c9SSam Leffler break; 26681a1e1d21SSam Leffler default: 26691a1e1d21SSam Leffler error = EINVAL; 26701a1e1d21SSam Leffler break; 26711a1e1d21SSam Leffler } 26728a1b9b6aSSam Leffler if (error == ENETRESET && !IS_UP_AUTO(ic)) 26738a1b9b6aSSam Leffler error = 0; 26748a1b9b6aSSam Leffler return error; 26758a1b9b6aSSam Leffler } 26768a1b9b6aSSam Leffler 26778a1b9b6aSSam Leffler int 26788a1b9b6aSSam Leffler ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data) 26798a1b9b6aSSam Leffler { 26808a1b9b6aSSam Leffler struct ifnet *ifp = ic->ic_ifp; 26818a1b9b6aSSam Leffler int error = 0; 26828a1b9b6aSSam Leffler struct ifreq *ifr; 26838a1b9b6aSSam Leffler struct ifaddr *ifa; /* XXX */ 26848a1b9b6aSSam Leffler 26858a1b9b6aSSam Leffler switch (cmd) { 26868a1b9b6aSSam Leffler case SIOCSIFMEDIA: 26878a1b9b6aSSam Leffler case SIOCGIFMEDIA: 26888a1b9b6aSSam Leffler error = ifmedia_ioctl(ifp, (struct ifreq *) data, 26898a1b9b6aSSam Leffler &ic->ic_media, cmd); 26908a1b9b6aSSam Leffler break; 26918a1b9b6aSSam Leffler case SIOCG80211: 26928a1b9b6aSSam Leffler error = ieee80211_ioctl_get80211(ic, cmd, 26938a1b9b6aSSam Leffler (struct ieee80211req *) data); 26948a1b9b6aSSam Leffler break; 26958a1b9b6aSSam Leffler case SIOCS80211: 2696acd3428bSRobert Watson error = priv_check(curthread, PRIV_NET80211_MANAGE); 26978a1b9b6aSSam Leffler if (error == 0) 26988a1b9b6aSSam Leffler error = ieee80211_ioctl_set80211(ic, cmd, 26998a1b9b6aSSam Leffler (struct ieee80211req *) data); 27001a1e1d21SSam Leffler break; 27011a1e1d21SSam Leffler case SIOCGIFGENERIC: 27028a1b9b6aSSam Leffler error = ieee80211_cfgget(ic, cmd, data); 27031a1e1d21SSam Leffler break; 27041a1e1d21SSam Leffler case SIOCSIFGENERIC: 2705acd3428bSRobert Watson error = priv_check(curthread, PRIV_NET80211_MANAGE); 27061a1e1d21SSam Leffler if (error) 27071a1e1d21SSam Leffler break; 27088a1b9b6aSSam Leffler error = ieee80211_cfgset(ic, cmd, data); 27091a1e1d21SSam Leffler break; 27101be50176SSam Leffler case SIOCG80211STATS: 27111be50176SSam Leffler ifr = (struct ifreq *)data; 27121be50176SSam Leffler copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats)); 27131be50176SSam Leffler break; 27146f161f03SSam Leffler case SIOCSIFMTU: 27156f161f03SSam Leffler ifr = (struct ifreq *)data; 27166f161f03SSam Leffler if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu && 27176f161f03SSam Leffler ifr->ifr_mtu <= IEEE80211_MTU_MAX)) 27186f161f03SSam Leffler error = EINVAL; 27196f161f03SSam Leffler else 27206f161f03SSam Leffler ifp->if_mtu = ifr->ifr_mtu; 27216f161f03SSam Leffler break; 2722b2e95691SSam Leffler case SIOCSIFADDR: 2723b2e95691SSam Leffler /* 2724b2e95691SSam Leffler * XXX Handle this directly so we can supress if_init calls. 2725b2e95691SSam Leffler * XXX This should be done in ether_ioctl but for the moment 2726b2e95691SSam Leffler * XXX there are too many other parts of the system that 2727b2e95691SSam Leffler * XXX set IFF_UP and so supress if_init being called when 2728b2e95691SSam Leffler * XXX it should be. 2729b2e95691SSam Leffler */ 2730b2e95691SSam Leffler ifa = (struct ifaddr *) data; 2731b2e95691SSam Leffler switch (ifa->ifa_addr->sa_family) { 2732b2e95691SSam Leffler #ifdef INET 2733b2e95691SSam Leffler case AF_INET: 2734b2e95691SSam Leffler if ((ifp->if_flags & IFF_UP) == 0) { 2735b2e95691SSam Leffler ifp->if_flags |= IFF_UP; 2736b2e95691SSam Leffler ifp->if_init(ifp->if_softc); 2737b2e95691SSam Leffler } 2738b2e95691SSam Leffler arp_ifinit(ifp, ifa); 2739b2e95691SSam Leffler break; 2740b2e95691SSam Leffler #endif 2741b2e95691SSam Leffler #ifdef IPX 2742b2e95691SSam Leffler /* 2743b2e95691SSam Leffler * XXX - This code is probably wrong, 2744b2e95691SSam Leffler * but has been copied many times. 2745b2e95691SSam Leffler */ 2746b2e95691SSam Leffler case AF_IPX: { 2747b2e95691SSam Leffler struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); 2748b2e95691SSam Leffler 2749b2e95691SSam Leffler if (ipx_nullhost(*ina)) 2750fc74a9f9SBrooks Davis ina->x_host = *(union ipx_host *) 27514a0d6638SRuslan Ermilov IF_LLADDR(ifp); 2752b2e95691SSam Leffler else 2753b2e95691SSam Leffler bcopy((caddr_t) ina->x_host.c_host, 27544a0d6638SRuslan Ermilov (caddr_t) IF_LLADDR(ifp), 2755fc74a9f9SBrooks Davis ETHER_ADDR_LEN); 2756b2e95691SSam Leffler /* fall thru... */ 2757b2e95691SSam Leffler } 2758b2e95691SSam Leffler #endif 2759b2e95691SSam Leffler default: 2760b2e95691SSam Leffler if ((ifp->if_flags & IFF_UP) == 0) { 2761b2e95691SSam Leffler ifp->if_flags |= IFF_UP; 2762b2e95691SSam Leffler ifp->if_init(ifp->if_softc); 2763b2e95691SSam Leffler } 2764b2e95691SSam Leffler break; 2765b2e95691SSam Leffler } 2766b2e95691SSam Leffler break; 27671a1e1d21SSam Leffler default: 27681a1e1d21SSam Leffler error = ether_ioctl(ifp, cmd, data); 27691a1e1d21SSam Leffler break; 27701a1e1d21SSam Leffler } 27711a1e1d21SSam Leffler return error; 27721a1e1d21SSam Leffler } 2773