ieee80211_ioctl.c revision 143110
1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3139530Ssam * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting 4116742Ssam * All rights reserved. 5116742Ssam * 6116742Ssam * Redistribution and use in source and binary forms, with or without 7116742Ssam * modification, are permitted provided that the following conditions 8116742Ssam * are met: 9116742Ssam * 1. Redistributions of source code must retain the above copyright 10116904Ssam * notice, this list of conditions and the following disclaimer. 11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright 12116904Ssam * notice, this list of conditions and the following disclaimer in the 13116904Ssam * documentation and/or other materials provided with the distribution. 14116904Ssam * 3. The name of the author may not be used to endorse or promote products 15116904Ssam * derived from this software without specific prior written permission. 16116742Ssam * 17116742Ssam * Alternatively, this software may be distributed under the terms of the 18116742Ssam * GNU General Public License ("GPL") version 2 as published by the Free 19116742Ssam * Software Foundation. 20116742Ssam * 21116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31116742Ssam */ 32116742Ssam 33116742Ssam#include <sys/cdefs.h> 34116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ioctl.c 143110 2005-03-03 17:35:05Z wpaul $"); 35116742Ssam 36116742Ssam/* 37116742Ssam * IEEE 802.11 ioctl support (FreeBSD-specific) 38116742Ssam */ 39116742Ssam 40127646Ssam#include "opt_inet.h" 41127646Ssam#include "opt_ipx.h" 42127646Ssam 43116742Ssam#include <sys/endian.h> 44116742Ssam#include <sys/param.h> 45116742Ssam#include <sys/kernel.h> 46116742Ssam#include <sys/socket.h> 47116742Ssam#include <sys/sockio.h> 48116742Ssam#include <sys/systm.h> 49116742Ssam 50116742Ssam#include <net/if.h> 51116742Ssam#include <net/if_arp.h> 52116742Ssam#include <net/if_media.h> 53116742Ssam#include <net/ethernet.h> 54116742Ssam 55127646Ssam#ifdef INET 56127646Ssam#include <netinet/in.h> 57127646Ssam#include <netinet/if_ether.h> 58127646Ssam#endif 59127646Ssam 60127646Ssam#ifdef IPX 61127646Ssam#include <netipx/ipx.h> 62127646Ssam#include <netipx/ipx_if.h> 63127646Ssam#endif 64127646Ssam 65116742Ssam#include <net80211/ieee80211_var.h> 66116742Ssam#include <net80211/ieee80211_ioctl.h> 67116742Ssam 68116742Ssam#include <dev/wi/if_wavelan_ieee.h> 69116742Ssam 70138568Ssam#define IS_UP(_ic) \ 71138568Ssam (((_ic)->ic_ifp->if_flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP)) 72138568Ssam#define IS_UP_AUTO(_ic) \ 73138568Ssam (IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO) 74138568Ssam 75116742Ssam/* 76116742Ssam * XXX 77116742Ssam * Wireless LAN specific configuration interface, which is compatible 78116742Ssam * with wicontrol(8). 79116742Ssam */ 80116742Ssam 81138568Ssamstruct wi_read_ap_args { 82138568Ssam int i; /* result count */ 83138568Ssam struct wi_apinfo *ap; /* current entry in result buffer */ 84138568Ssam caddr_t max; /* result buffer bound */ 85138568Ssam}; 86138568Ssam 87138568Ssamstatic void 88138568Ssamwi_read_ap_result(void *arg, struct ieee80211_node *ni) 89138568Ssam{ 90138568Ssam struct ieee80211com *ic = ni->ni_ic; 91138568Ssam struct wi_read_ap_args *sa = arg; 92138568Ssam struct wi_apinfo *ap = sa->ap; 93138568Ssam struct ieee80211_rateset *rs; 94138568Ssam int j; 95138568Ssam 96138568Ssam if ((caddr_t)(ap + 1) > sa->max) 97138568Ssam return; 98138568Ssam memset(ap, 0, sizeof(struct wi_apinfo)); 99138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 100138568Ssam IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr); 101138568Ssam ap->namelen = ic->ic_des_esslen; 102138568Ssam if (ic->ic_des_esslen) 103138568Ssam memcpy(ap->name, ic->ic_des_essid, 104138568Ssam ic->ic_des_esslen); 105138568Ssam } else { 106138568Ssam IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid); 107138568Ssam ap->namelen = ni->ni_esslen; 108138568Ssam if (ni->ni_esslen) 109138568Ssam memcpy(ap->name, ni->ni_essid, 110138568Ssam ni->ni_esslen); 111138568Ssam } 112138568Ssam ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan); 113138568Ssam ap->signal = ic->ic_node_getrssi(ni); 114138568Ssam ap->capinfo = ni->ni_capinfo; 115138568Ssam ap->interval = ni->ni_intval; 116138568Ssam rs = &ni->ni_rates; 117138568Ssam for (j = 0; j < rs->rs_nrates; j++) { 118138568Ssam if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) { 119138568Ssam ap->rate = (rs->rs_rates[j] & 120138568Ssam IEEE80211_RATE_VAL) * 5; /* XXX */ 121138568Ssam } 122138568Ssam } 123138568Ssam sa->i++; 124138568Ssam sa->ap++; 125138568Ssam} 126138568Ssam 127138568Ssamstruct wi_read_prism2_args { 128138568Ssam int i; /* result count */ 129138568Ssam struct wi_scan_res *res;/* current entry in result buffer */ 130138568Ssam caddr_t max; /* result buffer bound */ 131138568Ssam}; 132138568Ssam 133138568Ssamstatic void 134138568Ssamwi_read_prism2_result(void *arg, struct ieee80211_node *ni) 135138568Ssam{ 136138568Ssam struct ieee80211com *ic = ni->ni_ic; 137138568Ssam struct wi_read_prism2_args *sa = arg; 138138568Ssam struct wi_scan_res *res = sa->res; 139138568Ssam 140138568Ssam if ((caddr_t)(res + 1) > sa->max) 141138568Ssam return; 142138568Ssam res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan); 143138568Ssam res->wi_noise = 0; 144138568Ssam res->wi_signal = ic->ic_node_getrssi(ni); 145138568Ssam IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid); 146138568Ssam res->wi_interval = ni->ni_intval; 147138568Ssam res->wi_capinfo = ni->ni_capinfo; 148138568Ssam res->wi_ssid_len = ni->ni_esslen; 149138568Ssam memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN); 150138568Ssam /* NB: assumes wi_srates holds <= ni->ni_rates */ 151138568Ssam memcpy(res->wi_srates, ni->ni_rates.rs_rates, 152138568Ssam sizeof(res->wi_srates)); 153138568Ssam if (ni->ni_rates.rs_nrates < 10) 154138568Ssam res->wi_srates[ni->ni_rates.rs_nrates] = 0; 155138568Ssam res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate]; 156138568Ssam res->wi_rsvd = 0; 157138568Ssam 158138568Ssam sa->i++; 159138568Ssam sa->res++; 160138568Ssam} 161138568Ssam 162138568Ssamstruct wi_read_sigcache_args { 163138568Ssam int i; /* result count */ 164138568Ssam struct wi_sigcache *wsc;/* current entry in result buffer */ 165138568Ssam caddr_t max; /* result buffer bound */ 166138568Ssam}; 167138568Ssam 168138568Ssamstatic void 169138568Ssamwi_read_sigcache(void *arg, struct ieee80211_node *ni) 170138568Ssam{ 171138568Ssam struct ieee80211com *ic = ni->ni_ic; 172138568Ssam struct wi_read_sigcache_args *sa = arg; 173138568Ssam struct wi_sigcache *wsc = sa->wsc; 174138568Ssam 175138568Ssam if ((caddr_t)(wsc + 1) > sa->max) 176138568Ssam return; 177138568Ssam memset(wsc, 0, sizeof(struct wi_sigcache)); 178138568Ssam IEEE80211_ADDR_COPY(wsc->macsrc, ni->ni_macaddr); 179138568Ssam wsc->signal = ic->ic_node_getrssi(ni); 180138568Ssam 181138568Ssam sa->wsc++; 182138568Ssam sa->i++; 183138568Ssam} 184138568Ssam 185116742Ssamint 186138568Ssamieee80211_cfgget(struct ieee80211com *ic, u_long cmd, caddr_t data) 187116742Ssam{ 188138568Ssam struct ifnet *ifp = ic->ic_ifp; 189116742Ssam int i, j, error; 190116742Ssam struct ifreq *ifr = (struct ifreq *)data; 191116742Ssam struct wi_req wreq; 192116742Ssam struct wi_ltv_keys *keys; 193116742Ssam 194116742Ssam error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 195116742Ssam if (error) 196116742Ssam return error; 197116742Ssam wreq.wi_len = 0; 198116742Ssam switch (wreq.wi_type) { 199116742Ssam case WI_RID_SERIALNO: 200116742Ssam /* nothing appropriate */ 201116742Ssam break; 202116742Ssam case WI_RID_NODENAME: 203116742Ssam strcpy((char *)&wreq.wi_val[1], hostname); 204116742Ssam wreq.wi_val[0] = htole16(strlen(hostname)); 205116742Ssam wreq.wi_len = (1 + strlen(hostname) + 1) / 2; 206116742Ssam break; 207116742Ssam case WI_RID_CURRENT_SSID: 208116742Ssam if (ic->ic_state != IEEE80211_S_RUN) { 209116742Ssam wreq.wi_val[0] = 0; 210116742Ssam wreq.wi_len = 1; 211116742Ssam break; 212116742Ssam } 213116742Ssam wreq.wi_val[0] = htole16(ic->ic_bss->ni_esslen); 214116742Ssam memcpy(&wreq.wi_val[1], ic->ic_bss->ni_essid, 215116742Ssam ic->ic_bss->ni_esslen); 216116742Ssam wreq.wi_len = (1 + ic->ic_bss->ni_esslen + 1) / 2; 217116742Ssam break; 218116742Ssam case WI_RID_OWN_SSID: 219116742Ssam case WI_RID_DESIRED_SSID: 220116742Ssam wreq.wi_val[0] = htole16(ic->ic_des_esslen); 221116742Ssam memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen); 222116742Ssam wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2; 223116742Ssam break; 224116742Ssam case WI_RID_CURRENT_BSSID: 225116742Ssam if (ic->ic_state == IEEE80211_S_RUN) 226116742Ssam IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss->ni_bssid); 227116742Ssam else 228116742Ssam memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN); 229116742Ssam wreq.wi_len = IEEE80211_ADDR_LEN / 2; 230116742Ssam break; 231116742Ssam case WI_RID_CHANNEL_LIST: 232116742Ssam memset(wreq.wi_val, 0, sizeof(wreq.wi_val)); 233116742Ssam /* 234116742Ssam * Since channel 0 is not available for DS, channel 1 235116742Ssam * is assigned to LSB on WaveLAN. 236116742Ssam */ 237116742Ssam if (ic->ic_phytype == IEEE80211_T_DS) 238116742Ssam i = 1; 239116742Ssam else 240116742Ssam i = 0; 241116742Ssam for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) 242116742Ssam if (isset(ic->ic_chan_active, i)) { 243116742Ssam setbit((u_int8_t *)wreq.wi_val, j); 244116742Ssam wreq.wi_len = j / 16 + 1; 245116742Ssam } 246116742Ssam break; 247116742Ssam case WI_RID_OWN_CHNL: 248116742Ssam wreq.wi_val[0] = htole16( 249116742Ssam ieee80211_chan2ieee(ic, ic->ic_ibss_chan)); 250116742Ssam wreq.wi_len = 1; 251116742Ssam break; 252116742Ssam case WI_RID_CURRENT_CHAN: 253116742Ssam wreq.wi_val[0] = htole16( 254116742Ssam ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)); 255116742Ssam wreq.wi_len = 1; 256116742Ssam break; 257116742Ssam case WI_RID_COMMS_QUALITY: 258116742Ssam wreq.wi_val[0] = 0; /* quality */ 259138568Ssam wreq.wi_val[1] = htole16(ic->ic_node_getrssi(ic->ic_bss)); 260116742Ssam wreq.wi_val[2] = 0; /* noise */ 261116742Ssam wreq.wi_len = 3; 262116742Ssam break; 263116742Ssam case WI_RID_PROMISC: 264116742Ssam wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0); 265116742Ssam wreq.wi_len = 1; 266116742Ssam break; 267116742Ssam case WI_RID_PORTTYPE: 268116742Ssam wreq.wi_val[0] = htole16(ic->ic_opmode); 269116742Ssam wreq.wi_len = 1; 270116742Ssam break; 271116742Ssam case WI_RID_MAC_NODE: 272116742Ssam IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr); 273116742Ssam wreq.wi_len = IEEE80211_ADDR_LEN / 2; 274116742Ssam break; 275116742Ssam case WI_RID_TX_RATE: 276116742Ssam if (ic->ic_fixed_rate == -1) 277116742Ssam wreq.wi_val[0] = 0; /* auto */ 278116742Ssam else 279116742Ssam wreq.wi_val[0] = htole16( 280116742Ssam (ic->ic_sup_rates[ic->ic_curmode].rs_rates[ic->ic_fixed_rate] & 281116742Ssam IEEE80211_RATE_VAL) / 2); 282116742Ssam wreq.wi_len = 1; 283116742Ssam break; 284116742Ssam case WI_RID_CUR_TX_RATE: 285116742Ssam wreq.wi_val[0] = htole16( 286116742Ssam (ic->ic_bss->ni_rates.rs_rates[ic->ic_bss->ni_txrate] & 287116742Ssam IEEE80211_RATE_VAL) / 2); 288116742Ssam wreq.wi_len = 1; 289116742Ssam break; 290116742Ssam case WI_RID_RTS_THRESH: 291116742Ssam wreq.wi_val[0] = htole16(ic->ic_rtsthreshold); 292116742Ssam wreq.wi_len = 1; 293116742Ssam break; 294116742Ssam case WI_RID_CREATE_IBSS: 295116742Ssam wreq.wi_val[0] = 296116742Ssam htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0); 297116742Ssam wreq.wi_len = 1; 298116742Ssam break; 299116742Ssam case WI_RID_MICROWAVE_OVEN: 300116742Ssam wreq.wi_val[0] = 0; /* no ... not supported */ 301116742Ssam wreq.wi_len = 1; 302116742Ssam break; 303116742Ssam case WI_RID_ROAMING_MODE: 304138568Ssam wreq.wi_val[0] = htole16(ic->ic_roaming); /* XXX map */ 305116742Ssam wreq.wi_len = 1; 306116742Ssam break; 307116742Ssam case WI_RID_SYSTEM_SCALE: 308116742Ssam wreq.wi_val[0] = htole16(1); /* low density ... not supp */ 309116742Ssam wreq.wi_len = 1; 310116742Ssam break; 311116742Ssam case WI_RID_PM_ENABLED: 312116742Ssam wreq.wi_val[0] = 313116742Ssam htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0); 314116742Ssam wreq.wi_len = 1; 315116742Ssam break; 316116742Ssam case WI_RID_MAX_SLEEP: 317116742Ssam wreq.wi_val[0] = htole16(ic->ic_lintval); 318116742Ssam wreq.wi_len = 1; 319116742Ssam break; 320116742Ssam case WI_RID_CUR_BEACON_INT: 321116742Ssam wreq.wi_val[0] = htole16(ic->ic_bss->ni_intval); 322116742Ssam wreq.wi_len = 1; 323116742Ssam break; 324116742Ssam case WI_RID_WEP_AVAIL: 325138568Ssam wreq.wi_val[0] = htole16(1); /* always available */ 326116742Ssam wreq.wi_len = 1; 327116742Ssam break; 328116742Ssam case WI_RID_CNFAUTHMODE: 329116742Ssam wreq.wi_val[0] = htole16(1); /* TODO: open system only */ 330116742Ssam wreq.wi_len = 1; 331116742Ssam break; 332116742Ssam case WI_RID_ENCRYPTION: 333116742Ssam wreq.wi_val[0] = 334138568Ssam htole16((ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0); 335116742Ssam wreq.wi_len = 1; 336116742Ssam break; 337116742Ssam case WI_RID_TX_CRYPT_KEY: 338138568Ssam wreq.wi_val[0] = htole16(ic->ic_def_txkey); 339116742Ssam wreq.wi_len = 1; 340116742Ssam break; 341116742Ssam case WI_RID_DEFLT_CRYPT_KEYS: 342116742Ssam keys = (struct wi_ltv_keys *)&wreq; 343116742Ssam /* do not show keys to non-root user */ 344116742Ssam error = suser(curthread); 345116742Ssam if (error) { 346116742Ssam memset(keys, 0, sizeof(*keys)); 347116742Ssam error = 0; 348116742Ssam break; 349116742Ssam } 350116742Ssam for (i = 0; i < IEEE80211_WEP_NKID; i++) { 351116742Ssam keys->wi_keys[i].wi_keylen = 352138568Ssam htole16(ic->ic_nw_keys[i].wk_keylen); 353116742Ssam memcpy(keys->wi_keys[i].wi_keydat, 354138568Ssam ic->ic_nw_keys[i].wk_key, 355138568Ssam ic->ic_nw_keys[i].wk_keylen); 356116742Ssam } 357116742Ssam wreq.wi_len = sizeof(*keys) / 2; 358116742Ssam break; 359116742Ssam case WI_RID_MAX_DATALEN: 360138568Ssam wreq.wi_val[0] = htole16(ic->ic_fragthreshold); 361116742Ssam wreq.wi_len = 1; 362116742Ssam break; 363116742Ssam case WI_RID_IFACE_STATS: 364116742Ssam /* XXX: should be implemented in lower drivers */ 365116742Ssam break; 366116742Ssam case WI_RID_READ_APS: 367138568Ssam /* 368138568Ssam * Don't return results until active scan completes. 369138568Ssam */ 370138568Ssam if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) { 371138568Ssam struct wi_read_ap_args args; 372138568Ssam 373138568Ssam args.i = 0; 374138568Ssam args.ap = (void *)((char *)wreq.wi_val + sizeof(i)); 375138568Ssam args.max = (void *)(&wreq + 1); 376138568Ssam ieee80211_iterate_nodes(&ic->ic_scan, 377138568Ssam wi_read_ap_result, &args); 378138568Ssam memcpy(wreq.wi_val, &args.i, sizeof(args.i)); 379138568Ssam wreq.wi_len = (sizeof(int) + 380138568Ssam sizeof(struct wi_apinfo) * args.i) / 2; 381138568Ssam } else 382138568Ssam error = EINPROGRESS; 383116742Ssam break; 384116742Ssam case WI_RID_PRISM2: 385138568Ssam /* NB: we lie so WI_RID_SCAN_RES can include rates */ 386138568Ssam wreq.wi_val[0] = 1; 387116742Ssam wreq.wi_len = sizeof(u_int16_t) / 2; 388116742Ssam break; 389116742Ssam case WI_RID_SCAN_RES: /* compatibility interface */ 390138568Ssam if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) { 391138568Ssam struct wi_read_prism2_args args; 392138568Ssam struct wi_scan_p2_hdr *p2; 393138568Ssam 394138568Ssam /* NB: use Prism2 format so we can include rate info */ 395138568Ssam p2 = (struct wi_scan_p2_hdr *)wreq.wi_val; 396138568Ssam args.i = 0; 397138568Ssam args.res = (void *)&p2[1]; 398138568Ssam args.max = (void *)(&wreq + 1); 399138568Ssam ieee80211_iterate_nodes(&ic->ic_scan, 400138568Ssam wi_read_prism2_result, &args); 401138568Ssam p2->wi_rsvd = 0; 402138568Ssam p2->wi_reason = args.i; 403138568Ssam wreq.wi_len = (sizeof(*p2) + 404138568Ssam sizeof(struct wi_scan_res) * args.i) / 2; 405138568Ssam } else 406116742Ssam error = EINPROGRESS; 407116742Ssam break; 408138568Ssam case WI_RID_READ_CACHE: { 409138568Ssam struct wi_read_sigcache_args args; 410138568Ssam args.i = 0; 411138568Ssam args.wsc = (struct wi_sigcache *) wreq.wi_val; 412138568Ssam args.max = (void *)(&wreq + 1); 413138568Ssam ieee80211_iterate_nodes(&ic->ic_scan, wi_read_sigcache, &args); 414138568Ssam wreq.wi_len = sizeof(struct wi_sigcache) * args.i / 2; 415116742Ssam break; 416138568Ssam } 417116742Ssam default: 418116742Ssam error = EINVAL; 419116742Ssam break; 420116742Ssam } 421116742Ssam if (error == 0) { 422116742Ssam wreq.wi_len++; 423116742Ssam error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); 424116742Ssam } 425116742Ssam return error; 426116742Ssam} 427116742Ssam 428116742Ssamstatic int 429116742Ssamfindrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) 430116742Ssam{ 431116742Ssam#define IEEERATE(_ic,_m,_i) \ 432116742Ssam ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) 433116742Ssam int i, nrates = ic->ic_sup_rates[mode].rs_nrates; 434116742Ssam for (i = 0; i < nrates; i++) 435116742Ssam if (IEEERATE(ic, mode, i) == rate) 436116742Ssam return i; 437116742Ssam return -1; 438116742Ssam#undef IEEERATE 439116742Ssam} 440116742Ssam 441122600Ssam/* 442122600Ssam * Prepare to do a user-initiated scan for AP's. If no 443122600Ssam * current/default channel is setup or the current channel 444122600Ssam * is invalid then pick the first available channel from 445122600Ssam * the active list as the place to start the scan. 446122600Ssam */ 447122600Ssamstatic int 448138568Ssamieee80211_setupscan(struct ieee80211com *ic, const u_int8_t chanlist[]) 449122600Ssam{ 450122600Ssam int i; 451122600Ssam 452138568Ssam /* 453138568Ssam * XXX don't permit a scan to be started unless we 454138568Ssam * know the device is ready. For the moment this means 455138568Ssam * the device is marked up as this is the required to 456138568Ssam * initialize the hardware. It would be better to permit 457138568Ssam * scanning prior to being up but that'll require some 458138568Ssam * changes to the infrastructure. 459138568Ssam */ 460138568Ssam if (!IS_UP(ic)) 461138568Ssam return EINVAL; 462122600Ssam if (ic->ic_ibss_chan == NULL || 463122600Ssam isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { 464122600Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) 465122600Ssam if (isset(chanlist, i)) { 466122600Ssam ic->ic_ibss_chan = &ic->ic_channels[i]; 467122600Ssam goto found; 468122600Ssam } 469122600Ssam return EINVAL; /* no active channels */ 470122600Ssamfound: 471122600Ssam ; 472122600Ssam } 473122600Ssam if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC || 474122600Ssam isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan))) 475122600Ssam ic->ic_bss->ni_chan = ic->ic_ibss_chan; 476138568Ssam memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); 477122600Ssam /* 478138568Ssam * We force the state to INIT before calling ieee80211_new_state 479138568Ssam * to get ieee80211_begin_scan called. We really want to scan w/o 480138568Ssam * altering the current state but that's not possible right now. 481122600Ssam */ 482138568Ssam /* XXX handle proberequest case */ 483138568Ssam ic->ic_state = IEEE80211_S_INIT; /* XXX bypass state machine */ 484138568Ssam return 0; 485122600Ssam} 486122600Ssam 487116742Ssamint 488138568Ssamieee80211_cfgset(struct ieee80211com *ic, u_long cmd, caddr_t data) 489116742Ssam{ 490138568Ssam struct ifnet *ifp = ic->ic_ifp; 491116742Ssam int i, j, len, error, rate; 492116742Ssam struct ifreq *ifr = (struct ifreq *)data; 493116742Ssam struct wi_ltv_keys *keys; 494116742Ssam struct wi_req wreq; 495116742Ssam u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)]; 496116742Ssam 497116742Ssam error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 498116742Ssam if (error) 499116742Ssam return error; 500116742Ssam len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0; 501116742Ssam switch (wreq.wi_type) { 502116742Ssam case WI_RID_SERIALNO: 503116742Ssam case WI_RID_NODENAME: 504116742Ssam return EPERM; 505116742Ssam case WI_RID_CURRENT_SSID: 506116742Ssam return EPERM; 507116742Ssam case WI_RID_OWN_SSID: 508116742Ssam case WI_RID_DESIRED_SSID: 509116742Ssam if (le16toh(wreq.wi_val[0]) * 2 > len || 510116742Ssam le16toh(wreq.wi_val[0]) > IEEE80211_NWID_LEN) { 511116742Ssam error = ENOSPC; 512116742Ssam break; 513116742Ssam } 514116742Ssam memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid)); 515116742Ssam ic->ic_des_esslen = le16toh(wreq.wi_val[0]) * 2; 516117040Ssam memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen); 517116742Ssam error = ENETRESET; 518116742Ssam break; 519116742Ssam case WI_RID_CURRENT_BSSID: 520116742Ssam return EPERM; 521116742Ssam case WI_RID_OWN_CHNL: 522116742Ssam if (len != 2) 523116742Ssam return EINVAL; 524116742Ssam i = le16toh(wreq.wi_val[0]); 525116742Ssam if (i < 0 || 526116742Ssam i > IEEE80211_CHAN_MAX || 527116742Ssam isclr(ic->ic_chan_active, i)) 528116742Ssam return EINVAL; 529116742Ssam ic->ic_ibss_chan = &ic->ic_channels[i]; 530138568Ssam if (ic->ic_opmode == IEEE80211_M_MONITOR) 531138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 532138568Ssam else 533116742Ssam error = ENETRESET; 534116742Ssam break; 535116742Ssam case WI_RID_CURRENT_CHAN: 536116742Ssam return EPERM; 537116742Ssam case WI_RID_COMMS_QUALITY: 538116742Ssam return EPERM; 539116742Ssam case WI_RID_PROMISC: 540116742Ssam if (len != 2) 541116742Ssam return EINVAL; 542116742Ssam if (ifp->if_flags & IFF_PROMISC) { 543116742Ssam if (wreq.wi_val[0] == 0) { 544116742Ssam ifp->if_flags &= ~IFF_PROMISC; 545116742Ssam error = ENETRESET; 546116742Ssam } 547116742Ssam } else { 548116742Ssam if (wreq.wi_val[0] != 0) { 549116742Ssam ifp->if_flags |= IFF_PROMISC; 550116742Ssam error = ENETRESET; 551116742Ssam } 552116742Ssam } 553116742Ssam break; 554116742Ssam case WI_RID_PORTTYPE: 555116742Ssam if (len != 2) 556116742Ssam return EINVAL; 557116742Ssam switch (le16toh(wreq.wi_val[0])) { 558116742Ssam case IEEE80211_M_STA: 559116742Ssam break; 560116742Ssam case IEEE80211_M_IBSS: 561116742Ssam if (!(ic->ic_caps & IEEE80211_C_IBSS)) 562116742Ssam return EINVAL; 563116742Ssam break; 564116742Ssam case IEEE80211_M_AHDEMO: 565116742Ssam if (ic->ic_phytype != IEEE80211_T_DS || 566116742Ssam !(ic->ic_caps & IEEE80211_C_AHDEMO)) 567116742Ssam return EINVAL; 568116742Ssam break; 569116742Ssam case IEEE80211_M_HOSTAP: 570116742Ssam if (!(ic->ic_caps & IEEE80211_C_HOSTAP)) 571116742Ssam return EINVAL; 572116742Ssam break; 573116742Ssam default: 574116742Ssam return EINVAL; 575116742Ssam } 576116742Ssam if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) { 577116742Ssam ic->ic_opmode = le16toh(wreq.wi_val[0]); 578138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 579116742Ssam } 580116742Ssam break; 581116742Ssam#if 0 582116742Ssam case WI_RID_MAC_NODE: 583116742Ssam if (len != IEEE80211_ADDR_LEN) 584116742Ssam return EINVAL; 585116742Ssam IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val); 586116742Ssam /* if_init will copy lladdr into ic_myaddr */ 587116742Ssam error = ENETRESET; 588116742Ssam break; 589116742Ssam#endif 590116742Ssam case WI_RID_TX_RATE: 591116742Ssam if (len != 2) 592116742Ssam return EINVAL; 593116742Ssam if (wreq.wi_val[0] == 0) { 594116742Ssam /* auto */ 595116742Ssam ic->ic_fixed_rate = -1; 596116742Ssam break; 597116742Ssam } 598116742Ssam rate = 2 * le16toh(wreq.wi_val[0]); 599116742Ssam if (ic->ic_curmode == IEEE80211_MODE_AUTO) { 600116742Ssam /* 601116742Ssam * In autoselect mode search for the rate. We take 602116742Ssam * the first instance which may not be right, but we 603116742Ssam * are limited by the interface. Note that we also 604116742Ssam * lock the mode to insure the rate is meaningful 605116742Ssam * when it is used. 606116742Ssam */ 607116742Ssam for (j = IEEE80211_MODE_11A; 608116742Ssam j < IEEE80211_MODE_MAX; j++) { 609116742Ssam if ((ic->ic_modecaps & (1<<j)) == 0) 610116742Ssam continue; 611116742Ssam i = findrate(ic, j, rate); 612116742Ssam if (i != -1) { 613116742Ssam /* lock mode too */ 614116742Ssam ic->ic_curmode = j; 615116742Ssam goto setrate; 616116742Ssam } 617116742Ssam } 618116742Ssam } else { 619116742Ssam i = findrate(ic, ic->ic_curmode, rate); 620116742Ssam if (i != -1) 621116742Ssam goto setrate; 622116742Ssam } 623116742Ssam return EINVAL; 624116742Ssam setrate: 625116742Ssam ic->ic_fixed_rate = i; 626138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 627116742Ssam break; 628116742Ssam case WI_RID_CUR_TX_RATE: 629116742Ssam return EPERM; 630116742Ssam case WI_RID_RTS_THRESH: 631116742Ssam if (len != 2) 632116742Ssam return EINVAL; 633116742Ssam if (le16toh(wreq.wi_val[0]) != IEEE80211_MAX_LEN) 634116742Ssam return EINVAL; /* TODO: RTS */ 635116742Ssam break; 636116742Ssam case WI_RID_CREATE_IBSS: 637116742Ssam if (len != 2) 638116742Ssam return EINVAL; 639116742Ssam if (wreq.wi_val[0] != 0) { 640116742Ssam if ((ic->ic_caps & IEEE80211_C_IBSS) == 0) 641116742Ssam return EINVAL; 642116742Ssam if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) { 643116742Ssam ic->ic_flags |= IEEE80211_F_IBSSON; 644116742Ssam if (ic->ic_opmode == IEEE80211_M_IBSS && 645116742Ssam ic->ic_state == IEEE80211_S_SCAN) 646138568Ssam error = IS_UP_AUTO(ic) ? ENETRESET : 0; 647116742Ssam } 648116742Ssam } else { 649116742Ssam if (ic->ic_flags & IEEE80211_F_IBSSON) { 650116742Ssam ic->ic_flags &= ~IEEE80211_F_IBSSON; 651116742Ssam if (ic->ic_flags & IEEE80211_F_SIBSS) { 652116742Ssam ic->ic_flags &= ~IEEE80211_F_SIBSS; 653138568Ssam error = IS_UP_AUTO(ic) ? ENETRESET : 0; 654116742Ssam } 655116742Ssam } 656116742Ssam } 657116742Ssam break; 658116742Ssam case WI_RID_MICROWAVE_OVEN: 659116742Ssam if (len != 2) 660116742Ssam return EINVAL; 661116742Ssam if (wreq.wi_val[0] != 0) 662116742Ssam return EINVAL; /* not supported */ 663116742Ssam break; 664116742Ssam case WI_RID_ROAMING_MODE: 665116742Ssam if (len != 2) 666116742Ssam return EINVAL; 667138568Ssam i = le16toh(wreq.wi_val[0]); 668138568Ssam if (i > IEEE80211_ROAMING_MANUAL) 669116742Ssam return EINVAL; /* not supported */ 670138568Ssam ic->ic_roaming = i; 671116742Ssam break; 672116742Ssam case WI_RID_SYSTEM_SCALE: 673116742Ssam if (len != 2) 674116742Ssam return EINVAL; 675116742Ssam if (le16toh(wreq.wi_val[0]) != 1) 676116742Ssam return EINVAL; /* not supported */ 677116742Ssam break; 678116742Ssam case WI_RID_PM_ENABLED: 679116742Ssam if (len != 2) 680116742Ssam return EINVAL; 681116742Ssam if (wreq.wi_val[0] != 0) { 682116742Ssam if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) 683116742Ssam return EINVAL; 684116742Ssam if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { 685116742Ssam ic->ic_flags |= IEEE80211_F_PMGTON; 686138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 687116742Ssam } 688116742Ssam } else { 689116742Ssam if (ic->ic_flags & IEEE80211_F_PMGTON) { 690116742Ssam ic->ic_flags &= ~IEEE80211_F_PMGTON; 691138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 692116742Ssam } 693116742Ssam } 694116742Ssam break; 695116742Ssam case WI_RID_MAX_SLEEP: 696116742Ssam if (len != 2) 697116742Ssam return EINVAL; 698116742Ssam ic->ic_lintval = le16toh(wreq.wi_val[0]); 699116742Ssam if (ic->ic_flags & IEEE80211_F_PMGTON) 700138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 701116742Ssam break; 702116742Ssam case WI_RID_CUR_BEACON_INT: 703116742Ssam return EPERM; 704116742Ssam case WI_RID_WEP_AVAIL: 705116742Ssam return EPERM; 706116742Ssam case WI_RID_CNFAUTHMODE: 707116742Ssam if (len != 2) 708116742Ssam return EINVAL; 709138568Ssam i = le16toh(wreq.wi_val[0]); 710138568Ssam if (i > IEEE80211_AUTH_WPA) 711138568Ssam return EINVAL; 712138568Ssam ic->ic_bss->ni_authmode = i; /* XXX ENETRESET? */ 713138568Ssam error = ENETRESET; 714116742Ssam break; 715116742Ssam case WI_RID_ENCRYPTION: 716116742Ssam if (len != 2) 717116742Ssam return EINVAL; 718116742Ssam if (wreq.wi_val[0] != 0) { 719116742Ssam if ((ic->ic_caps & IEEE80211_C_WEP) == 0) 720116742Ssam return EINVAL; 721138568Ssam if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { 722138568Ssam ic->ic_flags |= IEEE80211_F_PRIVACY; 723116742Ssam error = ENETRESET; 724116742Ssam } 725116742Ssam } else { 726138568Ssam if (ic->ic_flags & IEEE80211_F_PRIVACY) { 727138568Ssam ic->ic_flags &= ~IEEE80211_F_PRIVACY; 728116742Ssam error = ENETRESET; 729116742Ssam } 730116742Ssam } 731116742Ssam break; 732116742Ssam case WI_RID_TX_CRYPT_KEY: 733116742Ssam if (len != 2) 734116742Ssam return EINVAL; 735116742Ssam i = le16toh(wreq.wi_val[0]); 736116742Ssam if (i >= IEEE80211_WEP_NKID) 737116742Ssam return EINVAL; 738138568Ssam ic->ic_def_txkey = i; 739138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 740116742Ssam break; 741116742Ssam case WI_RID_DEFLT_CRYPT_KEYS: 742116742Ssam if (len != sizeof(struct wi_ltv_keys)) 743116742Ssam return EINVAL; 744116742Ssam keys = (struct wi_ltv_keys *)&wreq; 745116742Ssam for (i = 0; i < IEEE80211_WEP_NKID; i++) { 746116742Ssam len = le16toh(keys->wi_keys[i].wi_keylen); 747116742Ssam if (len != 0 && len < IEEE80211_WEP_KEYLEN) 748116742Ssam return EINVAL; 749138568Ssam if (len > IEEE80211_KEYBUF_SIZE) 750116742Ssam return EINVAL; 751116742Ssam } 752116742Ssam for (i = 0; i < IEEE80211_WEP_NKID; i++) { 753138568Ssam struct ieee80211_key *k = &ic->ic_nw_keys[i]; 754138568Ssam 755116742Ssam len = le16toh(keys->wi_keys[i].wi_keylen); 756138568Ssam k->wk_keylen = len; 757138568Ssam k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; 758138568Ssam memset(k->wk_key, 0, sizeof(k->wk_key)); 759138568Ssam memcpy(k->wk_key, keys->wi_keys[i].wi_keydat, len); 760138568Ssam#if 0 761138568Ssam k->wk_type = IEEE80211_CIPHER_WEP; 762138568Ssam#endif 763116742Ssam } 764116742Ssam error = ENETRESET; 765116742Ssam break; 766116742Ssam case WI_RID_MAX_DATALEN: 767116742Ssam if (len != 2) 768116742Ssam return EINVAL; 769116742Ssam len = le16toh(wreq.wi_val[0]); 770116742Ssam if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN) 771116742Ssam return EINVAL; 772116742Ssam ic->ic_fragthreshold = len; 773138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 774116742Ssam break; 775116742Ssam case WI_RID_IFACE_STATS: 776116742Ssam error = EPERM; 777116742Ssam break; 778116742Ssam case WI_RID_SCAN_REQ: /* XXX wicontrol */ 779116742Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) 780116742Ssam break; 781138568Ssam error = ieee80211_setupscan(ic, ic->ic_chan_avail); 782122600Ssam if (error == 0) 783122600Ssam error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 784116742Ssam break; 785116742Ssam case WI_RID_SCAN_APS: 786116742Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) 787116742Ssam break; 788116742Ssam len--; /* XXX: tx rate? */ 789116742Ssam /* FALLTHRU */ 790116742Ssam case WI_RID_CHANNEL_LIST: 791116742Ssam memset(chanlist, 0, sizeof(chanlist)); 792116742Ssam /* 793116742Ssam * Since channel 0 is not available for DS, channel 1 794116742Ssam * is assigned to LSB on WaveLAN. 795116742Ssam */ 796116742Ssam if (ic->ic_phytype == IEEE80211_T_DS) 797116742Ssam i = 1; 798116742Ssam else 799116742Ssam i = 0; 800116742Ssam for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { 801116742Ssam if ((j / 8) >= len) 802116742Ssam break; 803116742Ssam if (isclr((u_int8_t *)wreq.wi_val, j)) 804116742Ssam continue; 805116742Ssam if (isclr(ic->ic_chan_active, i)) { 806116742Ssam if (wreq.wi_type != WI_RID_CHANNEL_LIST) 807116742Ssam continue; 808116742Ssam if (isclr(ic->ic_chan_avail, i)) 809116742Ssam return EPERM; 810116742Ssam } 811116742Ssam setbit(chanlist, i); 812116742Ssam } 813138568Ssam error = ieee80211_setupscan(ic, chanlist); 814122600Ssam if (wreq.wi_type == WI_RID_CHANNEL_LIST) { 815122600Ssam /* NB: ignore error from ieee80211_setupscan */ 816116742Ssam error = ENETRESET; 817122600Ssam } else if (error == 0) 818117811Ssam error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 819116742Ssam break; 820116742Ssam default: 821116742Ssam error = EINVAL; 822116742Ssam break; 823116742Ssam } 824138568Ssam if (error == ENETRESET && !IS_UP_AUTO(ic)) 825138568Ssam error = 0; 826116742Ssam return error; 827116742Ssam} 828116742Ssam 829138568Ssamstatic struct ieee80211_channel * 830138568Ssamgetcurchan(struct ieee80211com *ic) 831116742Ssam{ 832138568Ssam switch (ic->ic_state) { 833138568Ssam case IEEE80211_S_INIT: 834138568Ssam case IEEE80211_S_SCAN: 835138568Ssam return ic->ic_des_chan; 836138568Ssam default: 837138568Ssam return ic->ic_ibss_chan; 838138568Ssam } 839138568Ssam} 840138568Ssam 841138568Ssamstatic int 842138568Ssamcap2cipher(int flag) 843138568Ssam{ 844138568Ssam switch (flag) { 845138568Ssam case IEEE80211_C_WEP: return IEEE80211_CIPHER_WEP; 846138568Ssam case IEEE80211_C_AES: return IEEE80211_CIPHER_AES_OCB; 847138568Ssam case IEEE80211_C_AES_CCM: return IEEE80211_CIPHER_AES_CCM; 848138568Ssam case IEEE80211_C_CKIP: return IEEE80211_CIPHER_CKIP; 849138568Ssam case IEEE80211_C_TKIP: return IEEE80211_CIPHER_TKIP; 850138568Ssam } 851138568Ssam return -1; 852138568Ssam} 853138568Ssam 854138568Ssamstatic int 855138568Ssamieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq) 856138568Ssam{ 857138568Ssam struct ieee80211_node *ni; 858138568Ssam struct ieee80211req_key ik; 859138568Ssam struct ieee80211_key *wk; 860138568Ssam const struct ieee80211_cipher *cip; 861138568Ssam u_int kid; 862138568Ssam int error; 863138568Ssam 864138568Ssam if (ireq->i_len != sizeof(ik)) 865138568Ssam return EINVAL; 866138568Ssam error = copyin(ireq->i_data, &ik, sizeof(ik)); 867138568Ssam if (error) 868138568Ssam return error; 869138568Ssam kid = ik.ik_keyix; 870138568Ssam if (kid == IEEE80211_KEYIX_NONE) { 871140753Ssam ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr); 872138568Ssam if (ni == NULL) 873138568Ssam return EINVAL; /* XXX */ 874138568Ssam wk = &ni->ni_ucastkey; 875138568Ssam } else { 876138568Ssam if (kid >= IEEE80211_WEP_NKID) 877138568Ssam return EINVAL; 878138568Ssam wk = &ic->ic_nw_keys[kid]; 879138568Ssam IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr); 880138568Ssam ni = NULL; 881138568Ssam } 882138568Ssam cip = wk->wk_cipher; 883138568Ssam ik.ik_type = cip->ic_cipher; 884138568Ssam ik.ik_keylen = wk->wk_keylen; 885138568Ssam ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV); 886138568Ssam if (wk->wk_keyix == ic->ic_def_txkey) 887138568Ssam ik.ik_flags |= IEEE80211_KEY_DEFAULT; 888138568Ssam if (suser(curthread) == 0) { 889138568Ssam /* NB: only root can read key data */ 890138568Ssam ik.ik_keyrsc = wk->wk_keyrsc; 891138568Ssam ik.ik_keytsc = wk->wk_keytsc; 892138568Ssam memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen); 893138568Ssam if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) { 894138568Ssam memcpy(ik.ik_keydata+wk->wk_keylen, 895138568Ssam wk->wk_key + IEEE80211_KEYBUF_SIZE, 896138568Ssam IEEE80211_MICBUF_SIZE); 897138568Ssam ik.ik_keylen += IEEE80211_MICBUF_SIZE; 898138568Ssam } 899138568Ssam } else { 900138568Ssam ik.ik_keyrsc = 0; 901138568Ssam ik.ik_keytsc = 0; 902138568Ssam memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata)); 903138568Ssam } 904138568Ssam if (ni != NULL) 905138568Ssam ieee80211_free_node(ni); 906138568Ssam return copyout(&ik, ireq->i_data, sizeof(ik)); 907138568Ssam} 908138568Ssam 909138568Ssamstatic int 910138568Ssamieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) 911138568Ssam{ 912138568Ssam 913138568Ssam if (sizeof(ic->ic_chan_active) > ireq->i_len) 914138568Ssam ireq->i_len = sizeof(ic->ic_chan_active); 915138568Ssam return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len); 916138568Ssam} 917138568Ssam 918138568Ssamstatic int 919138568Ssamieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq) 920138568Ssam{ 921138568Ssam struct ieee80211req_chaninfo chans; /* XXX off stack? */ 922138568Ssam int i, space; 923138568Ssam 924138568Ssam /* 925138568Ssam * Since channel 0 is not available for DS, channel 1 926138568Ssam * is assigned to LSB on WaveLAN. 927138568Ssam */ 928138568Ssam if (ic->ic_phytype == IEEE80211_T_DS) 929138568Ssam i = 1; 930138568Ssam else 931138568Ssam i = 0; 932138568Ssam memset(&chans, 0, sizeof(chans)); 933138568Ssam for (; i <= IEEE80211_CHAN_MAX; i++) 934138568Ssam if (isset(ic->ic_chan_avail, i)) { 935138568Ssam struct ieee80211_channel *c = &ic->ic_channels[i]; 936138568Ssam chans.ic_chans[chans.ic_nchans].ic_freq = c->ic_freq; 937138568Ssam chans.ic_chans[chans.ic_nchans].ic_flags = c->ic_flags; 938138568Ssam chans.ic_nchans++; 939138568Ssam } 940138568Ssam space = __offsetof(struct ieee80211req_chaninfo, 941138568Ssam ic_chans[chans.ic_nchans]); 942138568Ssam if (space > ireq->i_len) 943138568Ssam space = ireq->i_len; 944138568Ssam return copyout(&chans, ireq->i_data, space); 945138568Ssam} 946138568Ssam 947138568Ssamstatic int 948138568Ssamieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq) 949138568Ssam{ 950138568Ssam struct ieee80211_node *ni; 951138568Ssam struct ieee80211req_wpaie wpaie; 952138568Ssam int error; 953138568Ssam 954138568Ssam if (ireq->i_len < IEEE80211_ADDR_LEN) 955138568Ssam return EINVAL; 956138568Ssam error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN); 957138568Ssam if (error != 0) 958138568Ssam return error; 959140753Ssam ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr); 960138568Ssam if (ni == NULL) 961138568Ssam return EINVAL; /* XXX */ 962138568Ssam memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie)); 963138568Ssam if (ni->ni_wpa_ie != NULL) { 964138568Ssam int ielen = ni->ni_wpa_ie[1] + 2; 965138568Ssam if (ielen > sizeof(wpaie.wpa_ie)) 966138568Ssam ielen = sizeof(wpaie.wpa_ie); 967138568Ssam memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen); 968138568Ssam } 969138568Ssam ieee80211_free_node(ni); 970138568Ssam if (ireq->i_len > sizeof(wpaie)) 971138568Ssam ireq->i_len = sizeof(wpaie); 972138568Ssam return copyout(&wpaie, ireq->i_data, ireq->i_len); 973138568Ssam} 974138568Ssam 975138568Ssamstatic int 976138568Ssamieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq) 977138568Ssam{ 978138568Ssam struct ieee80211_node *ni; 979138568Ssam u_int8_t macaddr[IEEE80211_ADDR_LEN]; 980138568Ssam const int off = __offsetof(struct ieee80211req_sta_stats, is_stats); 981138568Ssam int error; 982138568Ssam 983138568Ssam if (ireq->i_len < off) 984138568Ssam return EINVAL; 985138568Ssam error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); 986138568Ssam if (error != 0) 987138568Ssam return error; 988140753Ssam ni = ieee80211_find_node(&ic->ic_sta, macaddr); 989138568Ssam if (ni == NULL) 990138568Ssam return EINVAL; /* XXX */ 991138568Ssam if (ireq->i_len > sizeof(struct ieee80211req_sta_stats)) 992138568Ssam ireq->i_len = sizeof(struct ieee80211req_sta_stats); 993138568Ssam /* NB: copy out only the statistics */ 994138568Ssam error = copyout(&ni->ni_stats, (u_int8_t *) ireq->i_data + off, 995138568Ssam ireq->i_len - off); 996138568Ssam ieee80211_free_node(ni); 997138568Ssam return error; 998138568Ssam} 999138568Ssam 1000138568Ssamstatic void 1001138568Ssamget_scan_result(struct ieee80211req_scan_result *sr, 1002138568Ssam const struct ieee80211_node *ni) 1003138568Ssam{ 1004138568Ssam struct ieee80211com *ic = ni->ni_ic; 1005138568Ssam 1006138568Ssam memset(sr, 0, sizeof(*sr)); 1007138568Ssam sr->isr_ssid_len = ni->ni_esslen; 1008138568Ssam if (ni->ni_wpa_ie != NULL) 1009138568Ssam sr->isr_ie_len += 2+ni->ni_wpa_ie[1]; 1010138568Ssam if (ni->ni_wme_ie != NULL) 1011138568Ssam sr->isr_ie_len += 2+ni->ni_wme_ie[1]; 1012138568Ssam sr->isr_len = sizeof(*sr) + sr->isr_ssid_len + sr->isr_ie_len; 1013138568Ssam sr->isr_len = roundup(sr->isr_len, sizeof(u_int32_t)); 1014138568Ssam if (ni->ni_chan != IEEE80211_CHAN_ANYC) { 1015138568Ssam sr->isr_freq = ni->ni_chan->ic_freq; 1016138568Ssam sr->isr_flags = ni->ni_chan->ic_flags; 1017138568Ssam } 1018138568Ssam sr->isr_rssi = ic->ic_node_getrssi(ni); 1019138568Ssam sr->isr_intval = ni->ni_intval; 1020138568Ssam sr->isr_capinfo = ni->ni_capinfo; 1021138568Ssam sr->isr_erp = ni->ni_erp; 1022138568Ssam IEEE80211_ADDR_COPY(sr->isr_bssid, ni->ni_bssid); 1023138568Ssam sr->isr_nrates = ni->ni_rates.rs_nrates; 1024138568Ssam if (sr->isr_nrates > 15) 1025138568Ssam sr->isr_nrates = 15; 1026138568Ssam memcpy(sr->isr_rates, ni->ni_rates.rs_rates, sr->isr_nrates); 1027138568Ssam} 1028138568Ssam 1029138568Ssamstatic int 1030138568Ssamieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) 1031138568Ssam{ 1032138568Ssam union { 1033138568Ssam struct ieee80211req_scan_result res; 1034138568Ssam char data[512]; /* XXX shrink? */ 1035138568Ssam } u; 1036138568Ssam struct ieee80211req_scan_result *sr = &u.res; 1037138568Ssam struct ieee80211_node_table *nt; 1038138568Ssam struct ieee80211_node *ni; 1039138568Ssam int error, space; 1040138568Ssam u_int8_t *p, *cp; 1041138568Ssam 1042138568Ssam p = ireq->i_data; 1043138568Ssam space = ireq->i_len; 1044138568Ssam error = 0; 1045138568Ssam /* XXX locking */ 1046138568Ssam nt = &ic->ic_scan; 1047138568Ssam TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { 1048138568Ssam /* NB: skip pre-scan node state */ 1049138568Ssam if (ni->ni_chan == IEEE80211_CHAN_ANYC) 1050138568Ssam continue; 1051138568Ssam get_scan_result(sr, ni); 1052138568Ssam if (sr->isr_len > sizeof(u)) 1053138568Ssam continue; /* XXX */ 1054138568Ssam if (space < sr->isr_len) 1055138568Ssam break; 1056138568Ssam cp = (u_int8_t *)(sr+1); 1057138568Ssam memcpy(cp, ni->ni_essid, ni->ni_esslen); 1058138568Ssam cp += ni->ni_esslen; 1059138568Ssam if (ni->ni_wpa_ie != NULL) { 1060138568Ssam memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); 1061138568Ssam cp += 2+ni->ni_wpa_ie[1]; 1062138568Ssam } 1063138568Ssam if (ni->ni_wme_ie != NULL) { 1064138568Ssam memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); 1065138568Ssam cp += 2+ni->ni_wme_ie[1]; 1066138568Ssam } 1067138568Ssam error = copyout(sr, p, sr->isr_len); 1068138568Ssam if (error) 1069138568Ssam break; 1070138568Ssam p += sr->isr_len; 1071138568Ssam space -= sr->isr_len; 1072138568Ssam } 1073138568Ssam ireq->i_len -= space; 1074138568Ssam return error; 1075138568Ssam} 1076138568Ssam 1077138568Ssamstatic void 1078138568Ssamget_sta_info(struct ieee80211req_sta_info *si, const struct ieee80211_node *ni) 1079138568Ssam{ 1080138568Ssam struct ieee80211com *ic = ni->ni_ic; 1081138568Ssam 1082138568Ssam si->isi_ie_len = 0; 1083138568Ssam if (ni->ni_wpa_ie != NULL) 1084138568Ssam si->isi_ie_len += 2+ni->ni_wpa_ie[1]; 1085138568Ssam if (ni->ni_wme_ie != NULL) 1086138568Ssam si->isi_ie_len += 2+ni->ni_wme_ie[1]; 1087138568Ssam si->isi_len = sizeof(*si) + si->isi_ie_len, sizeof(u_int32_t); 1088138568Ssam si->isi_len = roundup(si->isi_len, sizeof(u_int32_t)); 1089138568Ssam si->isi_freq = ni->ni_chan->ic_freq; 1090138568Ssam si->isi_flags = ni->ni_chan->ic_flags; 1091138568Ssam si->isi_state = ni->ni_flags; 1092138568Ssam si->isi_authmode = ni->ni_authmode; 1093138568Ssam si->isi_rssi = ic->ic_node_getrssi(ni); 1094138568Ssam si->isi_capinfo = ni->ni_capinfo; 1095138568Ssam si->isi_erp = ni->ni_erp; 1096138568Ssam IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr); 1097138568Ssam si->isi_nrates = ni->ni_rates.rs_nrates; 1098138568Ssam if (si->isi_nrates > 15) 1099138568Ssam si->isi_nrates = 15; 1100138568Ssam memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates); 1101138568Ssam si->isi_txrate = ni->ni_txrate; 1102138568Ssam si->isi_associd = ni->ni_associd; 1103138568Ssam si->isi_txpower = ni->ni_txpower; 1104138568Ssam si->isi_vlan = ni->ni_vlan; 1105138568Ssam if (ni->ni_flags & IEEE80211_NODE_QOS) { 1106138568Ssam memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs)); 1107138568Ssam memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs)); 1108138568Ssam } else { 1109138568Ssam si->isi_txseqs[0] = ni->ni_txseqs[0]; 1110138568Ssam si->isi_rxseqs[0] = ni->ni_rxseqs[0]; 1111138568Ssam } 1112138568Ssam if (ic->ic_opmode == IEEE80211_M_IBSS || ni->ni_associd != 0) 1113138568Ssam si->isi_inact = ic->ic_inact_run; 1114138568Ssam else if (ieee80211_node_is_authorized(ni)) 1115138568Ssam si->isi_inact = ic->ic_inact_auth; 1116138568Ssam else 1117138568Ssam si->isi_inact = ic->ic_inact_init; 1118138568Ssam si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT; 1119138568Ssam} 1120138568Ssam 1121138568Ssamstatic int 1122138568Ssamieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) 1123138568Ssam{ 1124138568Ssam union { 1125138568Ssam struct ieee80211req_sta_info info; 1126138568Ssam char data[512]; /* XXX shrink? */ 1127138568Ssam } u; 1128138568Ssam struct ieee80211req_sta_info *si = &u.info; 1129138568Ssam struct ieee80211_node_table *nt; 1130138568Ssam struct ieee80211_node *ni; 1131138568Ssam int error, space; 1132138568Ssam u_int8_t *p, *cp; 1133138568Ssam 1134140753Ssam nt = &ic->ic_sta; 1135138568Ssam p = ireq->i_data; 1136138568Ssam space = ireq->i_len; 1137138568Ssam error = 0; 1138138568Ssam /* XXX locking */ 1139138568Ssam TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { 1140138568Ssam get_sta_info(si, ni); 1141138568Ssam if (si->isi_len > sizeof(u)) 1142138568Ssam continue; /* XXX */ 1143138568Ssam if (space < si->isi_len) 1144138568Ssam break; 1145138568Ssam cp = (u_int8_t *)(si+1); 1146138568Ssam if (ni->ni_wpa_ie != NULL) { 1147138568Ssam memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); 1148138568Ssam cp += 2+ni->ni_wpa_ie[1]; 1149138568Ssam } 1150138568Ssam if (ni->ni_wme_ie != NULL) { 1151138568Ssam memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); 1152138568Ssam cp += 2+ni->ni_wme_ie[1]; 1153138568Ssam } 1154138568Ssam error = copyout(si, p, si->isi_len); 1155138568Ssam if (error) 1156138568Ssam break; 1157138568Ssam p += si->isi_len; 1158138568Ssam space -= si->isi_len; 1159138568Ssam } 1160138568Ssam ireq->i_len -= space; 1161138568Ssam return error; 1162138568Ssam} 1163138568Ssam 1164138568Ssamstatic int 1165138568Ssamieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) 1166138568Ssam{ 1167138568Ssam struct ieee80211_node *ni; 1168138568Ssam struct ieee80211req_sta_txpow txpow; 1169138568Ssam int error; 1170138568Ssam 1171138568Ssam if (ireq->i_len != sizeof(txpow)) 1172138568Ssam return EINVAL; 1173138568Ssam error = copyin(ireq->i_data, &txpow, sizeof(txpow)); 1174138568Ssam if (error != 0) 1175138568Ssam return error; 1176140753Ssam ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr); 1177138568Ssam if (ni == NULL) 1178138568Ssam return EINVAL; /* XXX */ 1179138568Ssam txpow.it_txpow = ni->ni_txpower; 1180138568Ssam error = copyout(&txpow, ireq->i_data, sizeof(txpow)); 1181138568Ssam ieee80211_free_node(ni); 1182138568Ssam return error; 1183138568Ssam} 1184138568Ssam 1185138568Ssamstatic int 1186138568Ssamieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) 1187138568Ssam{ 1188138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 1189138568Ssam struct wmeParams *wmep; 1190138568Ssam int ac; 1191138568Ssam 1192138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 1193138568Ssam return EINVAL; 1194138568Ssam 1195138568Ssam ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); 1196138568Ssam if (ac >= WME_NUM_AC) 1197138568Ssam ac = WME_AC_BE; 1198138568Ssam if (ireq->i_len & IEEE80211_WMEPARAM_BSS) 1199138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; 1200138568Ssam else 1201138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; 1202138568Ssam switch (ireq->i_type) { 1203138568Ssam case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 1204138568Ssam ireq->i_val = wmep->wmep_logcwmin; 1205138568Ssam break; 1206138568Ssam case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 1207138568Ssam ireq->i_val = wmep->wmep_logcwmax; 1208138568Ssam break; 1209138568Ssam case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 1210138568Ssam ireq->i_val = wmep->wmep_aifsn; 1211138568Ssam break; 1212138568Ssam case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 1213138568Ssam ireq->i_val = wmep->wmep_txopLimit; 1214138568Ssam break; 1215138568Ssam case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 1216138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; 1217138568Ssam ireq->i_val = wmep->wmep_acm; 1218138568Ssam break; 1219138568Ssam case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ 1220138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; 1221138568Ssam ireq->i_val = !wmep->wmep_noackPolicy; 1222138568Ssam break; 1223138568Ssam } 1224138568Ssam return 0; 1225138568Ssam} 1226138568Ssam 1227143110Swpaul/* 1228143110Swpaul * When building the kernel with -O2 on the i386 architecture, gcc 1229143110Swpaul * seems to want to inline this function into ieee80211_ioctl() 1230143110Swpaul * (which is the only routine that calls it). When this happens, 1231143110Swpaul * ieee80211_ioctl() ends up consuming an additional 2K of stack 1232143110Swpaul * space. (Exactly why it needs so much is unclear.) The problem 1233143110Swpaul * is that it's possible for ieee80211_ioctl() to invoke other 1234143110Swpaul * routines (including driver init functions) which could then find 1235143110Swpaul * themselves perilously close to exhausting the stack. 1236143110Swpaul * 1237143110Swpaul * To avoid this, we deliberately prevent gcc from inlining this 1238143110Swpaul * routine. Another way to avoid this is to use less agressive 1239143110Swpaul * optimization when compiling this file (i.e. -O instead of -O2) 1240143110Swpaul * but special-casing the compilation of this one module in the 1241143110Swpaul * build system would be awkward. 1242143110Swpaul */ 1243143110Swpaul#ifdef __GNUC__ 1244143110Swpaul__attribute__ ((noinline)) 1245143110Swpaul#endif 1246138568Ssamstatic int 1247138568Ssamieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) 1248138568Ssam{ 1249138568Ssam const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; 1250116742Ssam int error = 0; 1251138568Ssam u_int kid, len, m; 1252116742Ssam u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; 1253116742Ssam char tmpssid[IEEE80211_NWID_LEN]; 1254116742Ssam 1255138568Ssam switch (ireq->i_type) { 1256138568Ssam case IEEE80211_IOC_SSID: 1257138568Ssam switch (ic->ic_state) { 1258138568Ssam case IEEE80211_S_INIT: 1259138568Ssam case IEEE80211_S_SCAN: 1260138568Ssam ireq->i_len = ic->ic_des_esslen; 1261138568Ssam memcpy(tmpssid, ic->ic_des_essid, ireq->i_len); 1262138568Ssam break; 1263138568Ssam default: 1264138568Ssam ireq->i_len = ic->ic_bss->ni_esslen; 1265138568Ssam memcpy(tmpssid, ic->ic_bss->ni_essid, 1266138568Ssam ireq->i_len); 1267138568Ssam break; 1268138568Ssam } 1269138568Ssam error = copyout(tmpssid, ireq->i_data, ireq->i_len); 1270116742Ssam break; 1271138568Ssam case IEEE80211_IOC_NUMSSIDS: 1272138568Ssam ireq->i_val = 1; 1273138568Ssam break; 1274138568Ssam case IEEE80211_IOC_WEP: 1275138568Ssam if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) 1276138568Ssam ireq->i_val = IEEE80211_WEP_OFF; 1277138568Ssam else if (ic->ic_flags & IEEE80211_F_DROPUNENC) 1278138568Ssam ireq->i_val = IEEE80211_WEP_ON; 1279138568Ssam else 1280138568Ssam ireq->i_val = IEEE80211_WEP_MIXED; 1281138568Ssam break; 1282138568Ssam case IEEE80211_IOC_WEPKEY: 1283138568Ssam kid = (u_int) ireq->i_val; 1284138568Ssam if (kid >= IEEE80211_WEP_NKID) 1285138568Ssam return EINVAL; 1286138568Ssam len = (u_int) ic->ic_nw_keys[kid].wk_keylen; 1287138568Ssam /* NB: only root can read WEP keys */ 1288138568Ssam if (suser(curthread) == 0) { 1289138568Ssam bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len); 1290138568Ssam } else { 1291138568Ssam bzero(tmpkey, len); 1292138568Ssam } 1293138568Ssam ireq->i_len = len; 1294138568Ssam error = copyout(tmpkey, ireq->i_data, len); 1295138568Ssam break; 1296138568Ssam case IEEE80211_IOC_NUMWEPKEYS: 1297138568Ssam ireq->i_val = IEEE80211_WEP_NKID; 1298138568Ssam break; 1299138568Ssam case IEEE80211_IOC_WEPTXKEY: 1300138568Ssam ireq->i_val = ic->ic_def_txkey; 1301138568Ssam break; 1302138568Ssam case IEEE80211_IOC_AUTHMODE: 1303138568Ssam if (ic->ic_flags & IEEE80211_F_WPA) 1304138568Ssam ireq->i_val = IEEE80211_AUTH_WPA; 1305138568Ssam else 1306138568Ssam ireq->i_val = ic->ic_bss->ni_authmode; 1307138568Ssam break; 1308138568Ssam case IEEE80211_IOC_CHANNEL: 1309138568Ssam ireq->i_val = ieee80211_chan2ieee(ic, getcurchan(ic)); 1310138568Ssam break; 1311138568Ssam case IEEE80211_IOC_POWERSAVE: 1312138568Ssam if (ic->ic_flags & IEEE80211_F_PMGTON) 1313138568Ssam ireq->i_val = IEEE80211_POWERSAVE_ON; 1314138568Ssam else 1315138568Ssam ireq->i_val = IEEE80211_POWERSAVE_OFF; 1316138568Ssam break; 1317138568Ssam case IEEE80211_IOC_POWERSAVESLEEP: 1318138568Ssam ireq->i_val = ic->ic_lintval; 1319138568Ssam break; 1320138568Ssam case IEEE80211_IOC_RTSTHRESHOLD: 1321138568Ssam ireq->i_val = ic->ic_rtsthreshold; 1322138568Ssam break; 1323138568Ssam case IEEE80211_IOC_PROTMODE: 1324138568Ssam ireq->i_val = ic->ic_protmode; 1325138568Ssam break; 1326138568Ssam case IEEE80211_IOC_TXPOWER: 1327138568Ssam if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) 1328138568Ssam return EINVAL; 1329138568Ssam ireq->i_val = ic->ic_txpowlimit; 1330138568Ssam break; 1331138568Ssam case IEEE80211_IOC_MCASTCIPHER: 1332138568Ssam ireq->i_val = rsn->rsn_mcastcipher; 1333138568Ssam break; 1334138568Ssam case IEEE80211_IOC_MCASTKEYLEN: 1335138568Ssam ireq->i_val = rsn->rsn_mcastkeylen; 1336138568Ssam break; 1337138568Ssam case IEEE80211_IOC_UCASTCIPHERS: 1338138568Ssam ireq->i_val = 0; 1339138568Ssam for (m = 0x1; m != 0; m <<= 1) 1340138568Ssam if (rsn->rsn_ucastcipherset & m) 1341138568Ssam ireq->i_val |= 1<<cap2cipher(m); 1342138568Ssam break; 1343138568Ssam case IEEE80211_IOC_UCASTCIPHER: 1344138568Ssam ireq->i_val = rsn->rsn_ucastcipher; 1345138568Ssam break; 1346138568Ssam case IEEE80211_IOC_UCASTKEYLEN: 1347138568Ssam ireq->i_val = rsn->rsn_ucastkeylen; 1348138568Ssam break; 1349138568Ssam case IEEE80211_IOC_KEYMGTALGS: 1350138568Ssam ireq->i_val = rsn->rsn_keymgmtset; 1351138568Ssam break; 1352138568Ssam case IEEE80211_IOC_RSNCAPS: 1353138568Ssam ireq->i_val = rsn->rsn_caps; 1354138568Ssam break; 1355138568Ssam case IEEE80211_IOC_WPA: 1356138568Ssam switch (ic->ic_flags & IEEE80211_F_WPA) { 1357138568Ssam case IEEE80211_F_WPA1: 1358116742Ssam ireq->i_val = 1; 1359116742Ssam break; 1360138568Ssam case IEEE80211_F_WPA2: 1361138568Ssam ireq->i_val = 2; 1362116742Ssam break; 1363138568Ssam case IEEE80211_F_WPA1 | IEEE80211_F_WPA2: 1364138568Ssam ireq->i_val = 3; 1365116742Ssam break; 1366138568Ssam default: 1367138568Ssam ireq->i_val = 0; 1368116742Ssam break; 1369138568Ssam } 1370138568Ssam break; 1371138568Ssam case IEEE80211_IOC_CHANLIST: 1372138568Ssam error = ieee80211_ioctl_getchanlist(ic, ireq); 1373138568Ssam break; 1374138568Ssam case IEEE80211_IOC_ROAMING: 1375138568Ssam ireq->i_val = ic->ic_roaming; 1376138568Ssam break; 1377138568Ssam case IEEE80211_IOC_PRIVACY: 1378138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0; 1379138568Ssam break; 1380138568Ssam case IEEE80211_IOC_DROPUNENCRYPTED: 1381138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0; 1382138568Ssam break; 1383138568Ssam case IEEE80211_IOC_COUNTERMEASURES: 1384138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0; 1385138568Ssam break; 1386138568Ssam case IEEE80211_IOC_DRIVER_CAPS: 1387138568Ssam ireq->i_val = ic->ic_caps>>16; 1388138568Ssam ireq->i_len = ic->ic_caps&0xffff; 1389138568Ssam break; 1390138568Ssam case IEEE80211_IOC_WME: 1391138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0; 1392138568Ssam break; 1393138568Ssam case IEEE80211_IOC_HIDESSID: 1394138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0; 1395138568Ssam break; 1396138568Ssam case IEEE80211_IOC_APBRIDGE: 1397138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0; 1398138568Ssam break; 1399138568Ssam case IEEE80211_IOC_OPTIE: 1400138568Ssam if (ic->ic_opt_ie == NULL) 1401138568Ssam return EINVAL; 1402138568Ssam /* NB: truncate, caller can check length */ 1403138568Ssam if (ireq->i_len > ic->ic_opt_ie_len) 1404138568Ssam ireq->i_len = ic->ic_opt_ie_len; 1405138568Ssam error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len); 1406138568Ssam break; 1407138568Ssam case IEEE80211_IOC_WPAKEY: 1408138568Ssam error = ieee80211_ioctl_getkey(ic, ireq); 1409138568Ssam break; 1410138568Ssam case IEEE80211_IOC_CHANINFO: 1411138568Ssam error = ieee80211_ioctl_getchaninfo(ic, ireq); 1412138568Ssam break; 1413138568Ssam case IEEE80211_IOC_BSSID: 1414138568Ssam if (ireq->i_len != IEEE80211_ADDR_LEN) 1415138568Ssam return EINVAL; 1416138568Ssam error = copyout(ic->ic_state == IEEE80211_S_RUN ? 1417138568Ssam ic->ic_bss->ni_bssid : 1418138568Ssam ic->ic_des_bssid, 1419138568Ssam ireq->i_data, ireq->i_len); 1420138568Ssam break; 1421138568Ssam case IEEE80211_IOC_WPAIE: 1422138568Ssam error = ieee80211_ioctl_getwpaie(ic, ireq); 1423138568Ssam break; 1424138568Ssam case IEEE80211_IOC_SCAN_RESULTS: 1425138568Ssam error = ieee80211_ioctl_getscanresults(ic, ireq); 1426138568Ssam break; 1427138568Ssam case IEEE80211_IOC_STA_STATS: 1428138568Ssam error = ieee80211_ioctl_getstastats(ic, ireq); 1429138568Ssam break; 1430138568Ssam case IEEE80211_IOC_TXPOWMAX: 1431138568Ssam ireq->i_val = ic->ic_bss->ni_txpower; 1432138568Ssam break; 1433138568Ssam case IEEE80211_IOC_STA_TXPOW: 1434138568Ssam error = ieee80211_ioctl_getstatxpow(ic, ireq); 1435138568Ssam break; 1436138568Ssam case IEEE80211_IOC_STA_INFO: 1437138568Ssam error = ieee80211_ioctl_getstainfo(ic, ireq); 1438138568Ssam break; 1439138568Ssam case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 1440138568Ssam case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 1441138568Ssam case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 1442138568Ssam case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 1443138568Ssam case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 1444138568Ssam case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ 1445138568Ssam error = ieee80211_ioctl_getwmeparam(ic, ireq); 1446138568Ssam break; 1447138568Ssam case IEEE80211_IOC_DTIM_PERIOD: 1448138568Ssam ireq->i_val = ic->ic_dtim_period; 1449138568Ssam break; 1450138568Ssam case IEEE80211_IOC_BEACON_INTERVAL: 1451138568Ssam /* NB: get from ic_bss for station mode */ 1452138568Ssam ireq->i_val = ic->ic_bss->ni_intval; 1453138568Ssam break; 1454138568Ssam default: 1455138568Ssam error = EINVAL; 1456138568Ssam break; 1457138568Ssam } 1458138568Ssam return error; 1459138568Ssam} 1460138568Ssam 1461138568Ssamstatic int 1462138568Ssamieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq) 1463138568Ssam{ 1464138568Ssam int error; 1465138568Ssam void *ie; 1466138568Ssam 1467138568Ssam /* 1468138568Ssam * NB: Doing this for ap operation could be useful (e.g. for 1469138568Ssam * WPA and/or WME) except that it typically is worthless 1470138568Ssam * without being able to intervene when processing 1471138568Ssam * association response frames--so disallow it for now. 1472138568Ssam */ 1473138568Ssam if (ic->ic_opmode != IEEE80211_M_STA) 1474138568Ssam return EINVAL; 1475138568Ssam if (ireq->i_len > IEEE80211_MAX_OPT_IE) 1476138568Ssam return EINVAL; 1477138568Ssam /* NB: data.length is validated by the wireless extensions code */ 1478138568Ssam MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_WAITOK); 1479138568Ssam if (ie == NULL) 1480138568Ssam return ENOMEM; 1481138568Ssam error = copyin(ireq->i_data, ie, ireq->i_len); 1482138568Ssam /* XXX sanity check data? */ 1483138568Ssam if (ic->ic_opt_ie != NULL) 1484138568Ssam FREE(ic->ic_opt_ie, M_DEVBUF); 1485138568Ssam ic->ic_opt_ie = ie; 1486138568Ssam ic->ic_opt_ie_len = ireq->i_len; 1487138568Ssam return 0; 1488138568Ssam} 1489138568Ssam 1490138568Ssamstatic int 1491138568Ssamieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq) 1492138568Ssam{ 1493138568Ssam struct ieee80211req_key ik; 1494138568Ssam struct ieee80211_node *ni; 1495138568Ssam struct ieee80211_key *wk; 1496138568Ssam u_int16_t kid; 1497138568Ssam int error; 1498138568Ssam 1499138568Ssam if (ireq->i_len != sizeof(ik)) 1500138568Ssam return EINVAL; 1501138568Ssam error = copyin(ireq->i_data, &ik, sizeof(ik)); 1502138568Ssam if (error) 1503138568Ssam return error; 1504138568Ssam /* NB: cipher support is verified by ieee80211_crypt_newkey */ 1505138568Ssam /* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */ 1506138568Ssam if (ik.ik_keylen > sizeof(ik.ik_keydata)) 1507138568Ssam return E2BIG; 1508138568Ssam kid = ik.ik_keyix; 1509138568Ssam if (kid == IEEE80211_KEYIX_NONE) { 1510138568Ssam /* XXX unicast keys currently must be tx/rx */ 1511138568Ssam if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)) 1512138568Ssam return EINVAL; 1513138568Ssam if (ic->ic_opmode == IEEE80211_M_STA) { 1514138568Ssam ni = ic->ic_bss; 1515138568Ssam if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) 1516138568Ssam return EADDRNOTAVAIL; 1517138568Ssam } else { 1518140753Ssam ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr); 1519138568Ssam if (ni == NULL) 1520138568Ssam return ENOENT; 1521138568Ssam } 1522138568Ssam wk = &ni->ni_ucastkey; 1523138568Ssam } else { 1524138568Ssam if (kid >= IEEE80211_WEP_NKID) 1525138568Ssam return EINVAL; 1526138568Ssam wk = &ic->ic_nw_keys[kid]; 1527138568Ssam ni = NULL; 1528138568Ssam } 1529138568Ssam error = 0; 1530138568Ssam ieee80211_key_update_begin(ic); 1531138568Ssam if (ieee80211_crypto_newkey(ic, ik.ik_type, wk)) { 1532138568Ssam wk->wk_keylen = ik.ik_keylen; 1533138568Ssam /* NB: MIC presence is implied by cipher type */ 1534138568Ssam if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE) 1535138568Ssam wk->wk_keylen = IEEE80211_KEYBUF_SIZE; 1536138568Ssam wk->wk_keyrsc = ik.ik_keyrsc; 1537138568Ssam wk->wk_keytsc = 0; /* new key, reset */ 1538138568Ssam wk->wk_flags |= 1539138568Ssam ik.ik_flags & (IEEE80211_KEY_XMIT|IEEE80211_KEY_RECV); 1540138568Ssam memset(wk->wk_key, 0, sizeof(wk->wk_key)); 1541138568Ssam memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen); 1542138568Ssam if (!ieee80211_crypto_setkey(ic, wk, 1543138568Ssam ni != NULL ? ni->ni_macaddr : ik.ik_macaddr)) 1544138568Ssam error = EIO; 1545138568Ssam else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT)) 1546138568Ssam ic->ic_def_txkey = kid; 1547138568Ssam } else 1548138568Ssam error = ENXIO; 1549138568Ssam ieee80211_key_update_end(ic); 1550138568Ssam if (ni != NULL) 1551138568Ssam ieee80211_free_node(ni); 1552138568Ssam return error; 1553138568Ssam} 1554138568Ssam 1555138568Ssamstatic int 1556138568Ssamieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq) 1557138568Ssam{ 1558138568Ssam struct ieee80211req_del_key dk; 1559138568Ssam int kid, error; 1560138568Ssam 1561138568Ssam if (ireq->i_len != sizeof(dk)) 1562138568Ssam return EINVAL; 1563138568Ssam error = copyin(ireq->i_data, &dk, sizeof(dk)); 1564138568Ssam if (error) 1565138568Ssam return error; 1566138568Ssam kid = dk.idk_keyix; 1567138568Ssam /* XXX u_int8_t -> u_int16_t */ 1568138568Ssam if (dk.idk_keyix == (u_int8_t) IEEE80211_KEYIX_NONE) { 1569138568Ssam struct ieee80211_node *ni; 1570138568Ssam 1571140753Ssam ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr); 1572138568Ssam if (ni == NULL) 1573138568Ssam return EINVAL; /* XXX */ 1574138568Ssam /* XXX error return */ 1575138568Ssam ieee80211_crypto_delkey(ic, &ni->ni_ucastkey); 1576138568Ssam ieee80211_free_node(ni); 1577138568Ssam } else { 1578138568Ssam if (kid >= IEEE80211_WEP_NKID) 1579138568Ssam return EINVAL; 1580138568Ssam /* XXX error return */ 1581138568Ssam ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]); 1582138568Ssam } 1583138568Ssam return 0; 1584138568Ssam} 1585138568Ssam 1586138568Ssamstatic void 1587138568Ssamdomlme(void *arg, struct ieee80211_node *ni) 1588138568Ssam{ 1589138568Ssam struct ieee80211com *ic = ni->ni_ic; 1590138568Ssam struct ieee80211req_mlme *mlme = arg; 1591138568Ssam 1592138568Ssam if (ni->ni_associd != 0) { 1593138568Ssam IEEE80211_SEND_MGMT(ic, ni, 1594138568Ssam mlme->im_op == IEEE80211_MLME_DEAUTH ? 1595138568Ssam IEEE80211_FC0_SUBTYPE_DEAUTH : 1596138568Ssam IEEE80211_FC0_SUBTYPE_DISASSOC, 1597138568Ssam mlme->im_reason); 1598138568Ssam } 1599138568Ssam ieee80211_node_leave(ic, ni); 1600138568Ssam} 1601138568Ssam 1602138568Ssamstatic int 1603138568Ssamieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) 1604138568Ssam{ 1605138568Ssam struct ieee80211req_mlme mlme; 1606138568Ssam struct ieee80211_node *ni; 1607138568Ssam int error; 1608138568Ssam 1609138568Ssam if (ireq->i_len != sizeof(mlme)) 1610138568Ssam return EINVAL; 1611138568Ssam error = copyin(ireq->i_data, &mlme, sizeof(mlme)); 1612138568Ssam if (error) 1613138568Ssam return error; 1614138568Ssam switch (mlme.im_op) { 1615138568Ssam case IEEE80211_MLME_ASSOC: 1616138568Ssam if (ic->ic_opmode != IEEE80211_M_STA) 1617138568Ssam return EINVAL; 1618138568Ssam /* XXX must be in S_SCAN state? */ 1619138568Ssam 1620138568Ssam if (ic->ic_des_esslen != 0) { 1621138568Ssam /* 1622138568Ssam * Desired ssid specified; must match both bssid and 1623138568Ssam * ssid to distinguish ap advertising multiple ssid's. 1624138568Ssam */ 1625138568Ssam ni = ieee80211_find_node_with_ssid(&ic->ic_scan, 1626138568Ssam mlme.im_macaddr, 1627138568Ssam ic->ic_des_esslen, ic->ic_des_essid); 1628138568Ssam } else { 1629138568Ssam /* 1630138568Ssam * Normal case; just match bssid. 1631138568Ssam */ 1632138568Ssam ni = ieee80211_find_node(&ic->ic_scan, mlme.im_macaddr); 1633138568Ssam } 1634138568Ssam if (ni == NULL) 1635138568Ssam return EINVAL; 1636138568Ssam if (!ieee80211_sta_join(ic, ni)) { 1637138568Ssam ieee80211_free_node(ni); 1638138568Ssam return EINVAL; 1639138568Ssam } 1640138568Ssam break; 1641138568Ssam case IEEE80211_MLME_DISASSOC: 1642138568Ssam case IEEE80211_MLME_DEAUTH: 1643138568Ssam switch (ic->ic_opmode) { 1644138568Ssam case IEEE80211_M_STA: 1645138568Ssam /* XXX not quite right */ 1646138568Ssam ieee80211_new_state(ic, IEEE80211_S_INIT, 1647138568Ssam mlme.im_reason); 1648116742Ssam break; 1649138568Ssam case IEEE80211_M_HOSTAP: 1650138568Ssam /* NB: the broadcast address means do 'em all */ 1651138568Ssam if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) { 1652140753Ssam if ((ni = ieee80211_find_node(&ic->ic_sta, 1653138568Ssam mlme.im_macaddr)) == NULL) 1654138568Ssam return EINVAL; 1655138568Ssam domlme(&mlme, ni); 1656138568Ssam ieee80211_free_node(ni); 1657138568Ssam } else { 1658140753Ssam ieee80211_iterate_nodes(&ic->ic_sta, 1659138568Ssam domlme, &mlme); 1660138568Ssam } 1661116742Ssam break; 1662138568Ssam default: 1663138568Ssam return EINVAL; 1664138568Ssam } 1665138568Ssam break; 1666138568Ssam case IEEE80211_MLME_AUTHORIZE: 1667138568Ssam case IEEE80211_MLME_UNAUTHORIZE: 1668138568Ssam if (ic->ic_opmode != IEEE80211_M_HOSTAP) 1669138568Ssam return EINVAL; 1670140753Ssam ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr); 1671138568Ssam if (ni == NULL) 1672138568Ssam return EINVAL; 1673138568Ssam if (mlme.im_op == IEEE80211_MLME_AUTHORIZE) 1674138568Ssam ieee80211_node_authorize(ic, ni); 1675138568Ssam else 1676138568Ssam ieee80211_node_unauthorize(ic, ni); 1677138568Ssam ieee80211_free_node(ni); 1678138568Ssam break; 1679138568Ssam default: 1680138568Ssam return EINVAL; 1681138568Ssam } 1682138568Ssam return 0; 1683138568Ssam} 1684138568Ssam 1685138568Ssamstatic int 1686138568Ssamieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq) 1687138568Ssam{ 1688138568Ssam u_int8_t mac[IEEE80211_ADDR_LEN]; 1689138568Ssam const struct ieee80211_aclator *acl = ic->ic_acl; 1690138568Ssam int error; 1691138568Ssam 1692138568Ssam if (ireq->i_len != sizeof(mac)) 1693138568Ssam return EINVAL; 1694138568Ssam error = copyin(ireq->i_data, mac, ireq->i_len); 1695138568Ssam if (error) 1696138568Ssam return error; 1697138568Ssam if (acl == NULL) { 1698138568Ssam acl = ieee80211_aclator_get("mac"); 1699138568Ssam if (acl == NULL || !acl->iac_attach(ic)) 1700138568Ssam return EINVAL; 1701138568Ssam ic->ic_acl = acl; 1702138568Ssam } 1703138568Ssam if (ireq->i_type == IEEE80211_IOC_ADDMAC) 1704138568Ssam acl->iac_add(ic, mac); 1705138568Ssam else 1706138568Ssam acl->iac_remove(ic, mac); 1707138568Ssam return 0; 1708138568Ssam} 1709138568Ssam 1710138568Ssamstatic int 1711138568Ssamieee80211_ioctl_maccmd(struct ieee80211com *ic, struct ieee80211req *ireq) 1712138568Ssam{ 1713138568Ssam const struct ieee80211_aclator *acl = ic->ic_acl; 1714138568Ssam 1715138568Ssam switch (ireq->i_val) { 1716138568Ssam case IEEE80211_MACCMD_POLICY_OPEN: 1717138568Ssam case IEEE80211_MACCMD_POLICY_ALLOW: 1718138568Ssam case IEEE80211_MACCMD_POLICY_DENY: 1719138568Ssam if (acl == NULL) { 1720138568Ssam acl = ieee80211_aclator_get("mac"); 1721138568Ssam if (acl == NULL || !acl->iac_attach(ic)) 1722138568Ssam return EINVAL; 1723138568Ssam ic->ic_acl = acl; 1724138568Ssam } 1725138568Ssam acl->iac_setpolicy(ic, ireq->i_val); 1726138568Ssam break; 1727138568Ssam case IEEE80211_MACCMD_FLUSH: 1728138568Ssam if (acl != NULL) 1729138568Ssam acl->iac_flush(ic); 1730138568Ssam /* NB: silently ignore when not in use */ 1731138568Ssam break; 1732138568Ssam case IEEE80211_MACCMD_DETACH: 1733138568Ssam if (acl != NULL) { 1734138568Ssam ic->ic_acl = NULL; 1735138568Ssam acl->iac_detach(ic); 1736138568Ssam } 1737138568Ssam break; 1738138568Ssam default: 1739138568Ssam return EINVAL; 1740138568Ssam } 1741138568Ssam return 0; 1742138568Ssam} 1743138568Ssam 1744138568Ssamstatic int 1745138568Ssamieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) 1746138568Ssam{ 1747138568Ssam struct ieee80211req_chanlist list; 1748138568Ssam u_char chanlist[IEEE80211_CHAN_BYTES]; 1749138568Ssam int i, j, error; 1750138568Ssam 1751138568Ssam if (ireq->i_len != sizeof(list)) 1752138568Ssam return EINVAL; 1753138568Ssam error = copyin(ireq->i_data, &list, sizeof(list)); 1754138568Ssam if (error) 1755138568Ssam return error; 1756138568Ssam memset(chanlist, 0, sizeof(chanlist)); 1757138568Ssam /* 1758138568Ssam * Since channel 0 is not available for DS, channel 1 1759138568Ssam * is assigned to LSB on WaveLAN. 1760138568Ssam */ 1761138568Ssam if (ic->ic_phytype == IEEE80211_T_DS) 1762138568Ssam i = 1; 1763138568Ssam else 1764138568Ssam i = 0; 1765138568Ssam for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { 1766138568Ssam /* 1767138568Ssam * NB: silently discard unavailable channels so users 1768138568Ssam * can specify 1-255 to get all available channels. 1769138568Ssam */ 1770138568Ssam if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) 1771138568Ssam setbit(chanlist, i); 1772138568Ssam } 1773138568Ssam if (ic->ic_ibss_chan == NULL || 1774138568Ssam isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { 1775138568Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) 1776138568Ssam if (isset(chanlist, i)) { 1777138568Ssam ic->ic_ibss_chan = &ic->ic_channels[i]; 1778138568Ssam goto found; 1779116742Ssam } 1780138568Ssam return EINVAL; /* no active channels */ 1781138568Ssamfound: 1782138568Ssam ; 1783138568Ssam } 1784138568Ssam memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); 1785138568Ssam if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC || 1786138568Ssam isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan))) 1787138568Ssam ic->ic_bss->ni_chan = ic->ic_ibss_chan; 1788138568Ssam return IS_UP_AUTO(ic) ? ENETRESET : 0; 1789138568Ssam} 1790138568Ssam 1791138568Ssamstatic int 1792138568Ssamieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) 1793138568Ssam{ 1794138568Ssam struct ieee80211_node *ni; 1795138568Ssam struct ieee80211req_sta_txpow txpow; 1796138568Ssam int error; 1797138568Ssam 1798138568Ssam if (ireq->i_len != sizeof(txpow)) 1799138568Ssam return EINVAL; 1800138568Ssam error = copyin(ireq->i_data, &txpow, sizeof(txpow)); 1801138568Ssam if (error != 0) 1802138568Ssam return error; 1803140753Ssam ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr); 1804138568Ssam if (ni == NULL) 1805138568Ssam return EINVAL; /* XXX */ 1806138568Ssam ni->ni_txpower = txpow.it_txpow; 1807138568Ssam ieee80211_free_node(ni); 1808138568Ssam return error; 1809138568Ssam} 1810138568Ssam 1811138568Ssamstatic int 1812138568Ssamieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) 1813138568Ssam{ 1814138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 1815138568Ssam struct wmeParams *wmep, *chanp; 1816138568Ssam int isbss, ac; 1817138568Ssam 1818138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 1819138568Ssam return EINVAL; 1820138568Ssam 1821138568Ssam isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS); 1822138568Ssam ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); 1823138568Ssam if (ac >= WME_NUM_AC) 1824138568Ssam ac = WME_AC_BE; 1825138568Ssam if (isbss) { 1826138568Ssam chanp = &wme->wme_bssChanParams.cap_wmeParams[ac]; 1827138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; 1828138568Ssam } else { 1829138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[ac]; 1830138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; 1831138568Ssam } 1832138568Ssam switch (ireq->i_type) { 1833138568Ssam case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 1834138568Ssam if (isbss) { 1835138568Ssam wmep->wmep_logcwmin = ireq->i_val; 1836138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1837138568Ssam chanp->wmep_logcwmin = ireq->i_val; 1838138568Ssam } else { 1839138568Ssam wmep->wmep_logcwmin = chanp->wmep_logcwmin = 1840138568Ssam ireq->i_val; 1841138568Ssam } 1842138568Ssam break; 1843138568Ssam case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 1844138568Ssam if (isbss) { 1845138568Ssam wmep->wmep_logcwmax = ireq->i_val; 1846138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1847138568Ssam chanp->wmep_logcwmax = ireq->i_val; 1848138568Ssam } else { 1849138568Ssam wmep->wmep_logcwmax = chanp->wmep_logcwmax = 1850138568Ssam ireq->i_val; 1851138568Ssam } 1852138568Ssam break; 1853138568Ssam case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 1854138568Ssam if (isbss) { 1855138568Ssam wmep->wmep_aifsn = ireq->i_val; 1856138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1857138568Ssam chanp->wmep_aifsn = ireq->i_val; 1858138568Ssam } else { 1859138568Ssam wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val; 1860138568Ssam } 1861138568Ssam break; 1862138568Ssam case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 1863138568Ssam if (isbss) { 1864138568Ssam wmep->wmep_txopLimit = ireq->i_val; 1865138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1866138568Ssam chanp->wmep_txopLimit = ireq->i_val; 1867138568Ssam } else { 1868138568Ssam wmep->wmep_txopLimit = chanp->wmep_txopLimit = 1869138568Ssam ireq->i_val; 1870138568Ssam } 1871138568Ssam break; 1872138568Ssam case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 1873138568Ssam wmep->wmep_acm = ireq->i_val; 1874138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1875138568Ssam chanp->wmep_acm = ireq->i_val; 1876138568Ssam break; 1877138568Ssam case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ 1878138568Ssam wmep->wmep_noackPolicy = chanp->wmep_noackPolicy = 1879138568Ssam (ireq->i_val) == 0; 1880138568Ssam break; 1881138568Ssam } 1882138568Ssam ieee80211_wme_updateparams(ic); 1883138568Ssam return 0; 1884138568Ssam} 1885138568Ssam 1886138568Ssamstatic int 1887138568Ssamcipher2cap(int cipher) 1888138568Ssam{ 1889138568Ssam switch (cipher) { 1890138568Ssam case IEEE80211_CIPHER_WEP: return IEEE80211_C_WEP; 1891138568Ssam case IEEE80211_CIPHER_AES_OCB: return IEEE80211_C_AES; 1892138568Ssam case IEEE80211_CIPHER_AES_CCM: return IEEE80211_C_AES_CCM; 1893138568Ssam case IEEE80211_CIPHER_CKIP: return IEEE80211_C_CKIP; 1894138568Ssam case IEEE80211_CIPHER_TKIP: return IEEE80211_C_TKIP; 1895138568Ssam } 1896138568Ssam return 0; 1897138568Ssam} 1898138568Ssam 1899138568Ssamstatic int 1900138568Ssamieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) 1901138568Ssam{ 1902138568Ssam static const u_int8_t zerobssid[IEEE80211_ADDR_LEN]; 1903138568Ssam struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; 1904138568Ssam int error; 1905138568Ssam const struct ieee80211_authenticator *auth; 1906138568Ssam u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; 1907138568Ssam char tmpssid[IEEE80211_NWID_LEN]; 1908138568Ssam u_int8_t tmpbssid[IEEE80211_ADDR_LEN]; 1909138568Ssam struct ieee80211_key *k; 1910138568Ssam int j, caps; 1911138568Ssam u_int kid; 1912138568Ssam 1913138568Ssam error = 0; 1914138568Ssam switch (ireq->i_type) { 1915138568Ssam case IEEE80211_IOC_SSID: 1916138568Ssam if (ireq->i_val != 0 || 1917138568Ssam ireq->i_len > IEEE80211_NWID_LEN) 1918138568Ssam return EINVAL; 1919138568Ssam error = copyin(ireq->i_data, tmpssid, ireq->i_len); 1920138568Ssam if (error) 1921116742Ssam break; 1922138568Ssam memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); 1923138568Ssam ic->ic_des_esslen = ireq->i_len; 1924138568Ssam memcpy(ic->ic_des_essid, tmpssid, ireq->i_len); 1925138568Ssam error = ENETRESET; 1926138568Ssam break; 1927138568Ssam case IEEE80211_IOC_WEP: 1928138568Ssam switch (ireq->i_val) { 1929138568Ssam case IEEE80211_WEP_OFF: 1930138568Ssam ic->ic_flags &= ~IEEE80211_F_PRIVACY; 1931138568Ssam ic->ic_flags &= ~IEEE80211_F_DROPUNENC; 1932116742Ssam break; 1933138568Ssam case IEEE80211_WEP_ON: 1934138568Ssam ic->ic_flags |= IEEE80211_F_PRIVACY; 1935138568Ssam ic->ic_flags |= IEEE80211_F_DROPUNENC; 1936116742Ssam break; 1937138568Ssam case IEEE80211_WEP_MIXED: 1938138568Ssam ic->ic_flags |= IEEE80211_F_PRIVACY; 1939138568Ssam ic->ic_flags &= ~IEEE80211_F_DROPUNENC; 1940116742Ssam break; 1941138568Ssam } 1942138568Ssam error = ENETRESET; 1943138568Ssam break; 1944138568Ssam case IEEE80211_IOC_WEPKEY: 1945138568Ssam kid = (u_int) ireq->i_val; 1946138568Ssam if (kid >= IEEE80211_WEP_NKID) 1947138568Ssam return EINVAL; 1948138568Ssam k = &ic->ic_nw_keys[kid]; 1949138568Ssam if (ireq->i_len == 0) { 1950138568Ssam /* zero-len =>'s delete any existing key */ 1951138568Ssam (void) ieee80211_crypto_delkey(ic, k); 1952127648Ssam break; 1953138568Ssam } 1954138568Ssam if (ireq->i_len > sizeof(tmpkey)) 1955138568Ssam return EINVAL; 1956138568Ssam memset(tmpkey, 0, sizeof(tmpkey)); 1957138568Ssam error = copyin(ireq->i_data, tmpkey, ireq->i_len); 1958138568Ssam if (error) 1959138568Ssam break; 1960138568Ssam ieee80211_key_update_begin(ic); 1961138568Ssam if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP, k)) { 1962138568Ssam k->wk_keylen = ireq->i_len; 1963138568Ssam k->wk_flags |= 1964138568Ssam IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; 1965138568Ssam memcpy(k->wk_key, tmpkey, sizeof(tmpkey)); 1966138568Ssam if (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr)) 1967127648Ssam error = EINVAL; 1968138568Ssam } else 1969138568Ssam error = EINVAL; 1970138568Ssam ieee80211_key_update_end(ic); 1971138568Ssam break; 1972138568Ssam case IEEE80211_IOC_WEPTXKEY: 1973138568Ssam kid = (u_int) ireq->i_val; 1974139519Ssam if (kid >= IEEE80211_WEP_NKID && 1975139519Ssam (u_int16_t) kid != IEEE80211_KEYIX_NONE) 1976138568Ssam return EINVAL; 1977138568Ssam ic->ic_def_txkey = kid; 1978138663Ssam error = ENETRESET; /* push to hardware */ 1979138568Ssam break; 1980138568Ssam case IEEE80211_IOC_AUTHMODE: 1981138568Ssam switch (ireq->i_val) { 1982138568Ssam case IEEE80211_AUTH_WPA: 1983138568Ssam case IEEE80211_AUTH_8021X: /* 802.1x */ 1984138568Ssam case IEEE80211_AUTH_OPEN: /* open */ 1985138568Ssam case IEEE80211_AUTH_SHARED: /* shared-key */ 1986138568Ssam case IEEE80211_AUTH_AUTO: /* auto */ 1987138568Ssam auth = ieee80211_authenticator_get(ireq->i_val); 1988138568Ssam if (auth == NULL) 1989138568Ssam return EINVAL; 1990127648Ssam break; 1991116742Ssam default: 1992138568Ssam return EINVAL; 1993138568Ssam } 1994138568Ssam switch (ireq->i_val) { 1995138568Ssam case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */ 1996138568Ssam ic->ic_flags |= IEEE80211_F_PRIVACY; 1997138568Ssam ireq->i_val = IEEE80211_AUTH_8021X; 1998127646Ssam break; 1999138568Ssam case IEEE80211_AUTH_OPEN: /* open */ 2000138568Ssam ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY); 2001138568Ssam break; 2002138568Ssam case IEEE80211_AUTH_SHARED: /* shared-key */ 2003138568Ssam case IEEE80211_AUTH_8021X: /* 802.1x */ 2004138568Ssam ic->ic_flags &= ~IEEE80211_F_WPA; 2005138568Ssam /* both require a key so mark the PRIVACY capability */ 2006138568Ssam ic->ic_flags |= IEEE80211_F_PRIVACY; 2007138568Ssam break; 2008138568Ssam case IEEE80211_AUTH_AUTO: /* auto */ 2009138568Ssam ic->ic_flags &= ~IEEE80211_F_WPA; 2010138568Ssam /* XXX PRIVACY handling? */ 2011138568Ssam /* XXX what's the right way to do this? */ 2012138568Ssam break; 2013116742Ssam } 2014138568Ssam /* NB: authenticator attach/detach happens on state change */ 2015138568Ssam ic->ic_bss->ni_authmode = ireq->i_val; 2016138568Ssam /* XXX mixed/mode/usage? */ 2017138568Ssam ic->ic_auth = auth; 2018138568Ssam error = ENETRESET; 2019116742Ssam break; 2020138568Ssam case IEEE80211_IOC_CHANNEL: 2021138568Ssam /* XXX 0xffff overflows 16-bit signed */ 2022138568Ssam if (ireq->i_val == 0 || 2023138568Ssam ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) 2024138568Ssam ic->ic_des_chan = IEEE80211_CHAN_ANYC; 2025138568Ssam else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX || 2026138568Ssam isclr(ic->ic_chan_active, ireq->i_val)) { 2027138568Ssam return EINVAL; 2028138568Ssam } else 2029138568Ssam ic->ic_ibss_chan = ic->ic_des_chan = 2030138568Ssam &ic->ic_channels[ireq->i_val]; 2031138568Ssam switch (ic->ic_state) { 2032138568Ssam case IEEE80211_S_INIT: 2033138568Ssam case IEEE80211_S_SCAN: 2034116742Ssam error = ENETRESET; 2035116742Ssam break; 2036138568Ssam default: 2037116742Ssam /* 2038138568Ssam * If the desired channel has changed (to something 2039138568Ssam * other than any) and we're not already scanning, 2040138568Ssam * then kick the state machine. 2041116742Ssam */ 2042138568Ssam if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 2043138568Ssam ic->ic_bss->ni_chan != ic->ic_des_chan && 2044138568Ssam (ic->ic_flags & IEEE80211_F_SCAN) == 0) 2045138568Ssam error = ENETRESET; 2046116742Ssam break; 2047138568Ssam } 2048138568Ssam if (error == ENETRESET && ic->ic_opmode == IEEE80211_M_MONITOR) 2049138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2050138568Ssam break; 2051138568Ssam case IEEE80211_IOC_POWERSAVE: 2052138568Ssam switch (ireq->i_val) { 2053138568Ssam case IEEE80211_POWERSAVE_OFF: 2054138568Ssam if (ic->ic_flags & IEEE80211_F_PMGTON) { 2055138568Ssam ic->ic_flags &= ~IEEE80211_F_PMGTON; 2056138568Ssam error = ENETRESET; 2057116742Ssam } 2058116742Ssam break; 2059138568Ssam case IEEE80211_POWERSAVE_ON: 2060138568Ssam if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) 2061116742Ssam error = EINVAL; 2062138568Ssam else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { 2063138568Ssam ic->ic_flags |= IEEE80211_F_PMGTON; 2064116742Ssam error = ENETRESET; 2065116742Ssam } 2066116742Ssam break; 2067138568Ssam default: 2068138568Ssam error = EINVAL; 2069116742Ssam break; 2070138568Ssam } 2071138568Ssam break; 2072138568Ssam case IEEE80211_IOC_POWERSAVESLEEP: 2073138568Ssam if (ireq->i_val < 0) 2074138568Ssam return EINVAL; 2075138568Ssam ic->ic_lintval = ireq->i_val; 2076138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2077138568Ssam break; 2078138568Ssam case IEEE80211_IOC_RTSTHRESHOLD: 2079138568Ssam if (!(IEEE80211_RTS_MIN < ireq->i_val && 2080138568Ssam ireq->i_val < IEEE80211_RTS_MAX)) 2081138568Ssam return EINVAL; 2082138568Ssam ic->ic_rtsthreshold = ireq->i_val; 2083138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2084138568Ssam break; 2085138568Ssam case IEEE80211_IOC_PROTMODE: 2086138568Ssam if (ireq->i_val > IEEE80211_PROT_RTSCTS) 2087138568Ssam return EINVAL; 2088138568Ssam ic->ic_protmode = ireq->i_val; 2089138568Ssam /* NB: if not operating in 11g this can wait */ 2090138568Ssam if (ic->ic_curmode == IEEE80211_MODE_11G) 2091138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2092138568Ssam break; 2093138568Ssam case IEEE80211_IOC_TXPOWER: 2094138568Ssam if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) 2095138568Ssam return EINVAL; 2096138568Ssam if (!(IEEE80211_TXPOWER_MIN < ireq->i_val && 2097138568Ssam ireq->i_val < IEEE80211_TXPOWER_MAX)) 2098138568Ssam return EINVAL; 2099138568Ssam ic->ic_txpowlimit = ireq->i_val; 2100138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2101138568Ssam break; 2102138568Ssam case IEEE80211_IOC_ROAMING: 2103138568Ssam if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val && 2104138568Ssam ireq->i_val <= IEEE80211_ROAMING_MANUAL)) 2105138568Ssam return EINVAL; 2106138568Ssam ic->ic_roaming = ireq->i_val; 2107138568Ssam /* XXXX reset? */ 2108138568Ssam break; 2109138568Ssam case IEEE80211_IOC_PRIVACY: 2110138568Ssam if (ireq->i_val) { 2111138568Ssam /* XXX check for key state? */ 2112138568Ssam ic->ic_flags |= IEEE80211_F_PRIVACY; 2113138568Ssam } else 2114138568Ssam ic->ic_flags &= ~IEEE80211_F_PRIVACY; 2115138568Ssam break; 2116138568Ssam case IEEE80211_IOC_DROPUNENCRYPTED: 2117138568Ssam if (ireq->i_val) 2118138568Ssam ic->ic_flags |= IEEE80211_F_DROPUNENC; 2119138568Ssam else 2120138568Ssam ic->ic_flags &= ~IEEE80211_F_DROPUNENC; 2121138568Ssam break; 2122138568Ssam case IEEE80211_IOC_WPAKEY: 2123138568Ssam error = ieee80211_ioctl_setkey(ic, ireq); 2124138568Ssam break; 2125138568Ssam case IEEE80211_IOC_DELKEY: 2126138568Ssam error = ieee80211_ioctl_delkey(ic, ireq); 2127138568Ssam break; 2128138568Ssam case IEEE80211_IOC_MLME: 2129138568Ssam error = ieee80211_ioctl_setmlme(ic, ireq); 2130138568Ssam break; 2131138568Ssam case IEEE80211_IOC_OPTIE: 2132138568Ssam error = ieee80211_ioctl_setoptie(ic, ireq); 2133138568Ssam break; 2134138568Ssam case IEEE80211_IOC_COUNTERMEASURES: 2135138568Ssam if (ireq->i_val) { 2136138568Ssam if ((ic->ic_flags & IEEE80211_F_WPA) == 0) 2137138568Ssam return EINVAL; 2138138568Ssam ic->ic_flags |= IEEE80211_F_COUNTERM; 2139138568Ssam } else 2140138568Ssam ic->ic_flags &= ~IEEE80211_F_COUNTERM; 2141138568Ssam break; 2142138568Ssam case IEEE80211_IOC_WPA: 2143138568Ssam if (ireq->i_val > 3) 2144138568Ssam return EINVAL; 2145138568Ssam /* XXX verify ciphers available */ 2146138568Ssam ic->ic_flags &= ~IEEE80211_F_WPA; 2147138568Ssam switch (ireq->i_val) { 2148138568Ssam case 1: 2149138568Ssam ic->ic_flags |= IEEE80211_F_WPA1; 2150116742Ssam break; 2151138568Ssam case 2: 2152138568Ssam ic->ic_flags |= IEEE80211_F_WPA2; 2153116742Ssam break; 2154138568Ssam case 3: 2155138568Ssam ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2; 2156127648Ssam break; 2157138568Ssam } 2158138568Ssam error = ENETRESET; /* XXX? */ 2159138568Ssam break; 2160138568Ssam case IEEE80211_IOC_WME: 2161138568Ssam if (ireq->i_val) { 2162138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 2163138568Ssam return EINVAL; 2164138568Ssam ic->ic_flags |= IEEE80211_F_WME; 2165138568Ssam } else 2166138568Ssam ic->ic_flags &= ~IEEE80211_F_WME; 2167138568Ssam error = ENETRESET; /* XXX maybe not for station? */ 2168138568Ssam break; 2169138568Ssam case IEEE80211_IOC_HIDESSID: 2170138568Ssam if (ireq->i_val) 2171138568Ssam ic->ic_flags |= IEEE80211_F_HIDESSID; 2172138568Ssam else 2173138568Ssam ic->ic_flags &= ~IEEE80211_F_HIDESSID; 2174138568Ssam error = ENETRESET; 2175138568Ssam break; 2176138568Ssam case IEEE80211_IOC_APBRIDGE: 2177138568Ssam if (ireq->i_val == 0) 2178138568Ssam ic->ic_flags |= IEEE80211_F_NOBRIDGE; 2179138568Ssam else 2180138568Ssam ic->ic_flags &= ~IEEE80211_F_NOBRIDGE; 2181138568Ssam break; 2182138568Ssam case IEEE80211_IOC_MCASTCIPHER: 2183138568Ssam if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 && 2184138568Ssam !ieee80211_crypto_available(ireq->i_val)) 2185138568Ssam return EINVAL; 2186138568Ssam rsn->rsn_mcastcipher = ireq->i_val; 2187138568Ssam error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 2188138568Ssam break; 2189138568Ssam case IEEE80211_IOC_MCASTKEYLEN: 2190138568Ssam if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE)) 2191138568Ssam return EINVAL; 2192138568Ssam /* XXX no way to verify driver capability */ 2193138568Ssam rsn->rsn_mcastkeylen = ireq->i_val; 2194138568Ssam error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 2195138568Ssam break; 2196138568Ssam case IEEE80211_IOC_UCASTCIPHERS: 2197138568Ssam /* 2198138568Ssam * Convert user-specified cipher set to the set 2199138568Ssam * we can support (via hardware or software). 2200138568Ssam * NB: this logic intentionally ignores unknown and 2201138568Ssam * unsupported ciphers so folks can specify 0xff or 2202138568Ssam * similar and get all available ciphers. 2203138568Ssam */ 2204138568Ssam caps = 0; 2205138568Ssam for (j = 1; j < 32; j++) /* NB: skip WEP */ 2206138568Ssam if ((ireq->i_val & (1<<j)) && 2207138568Ssam ((ic->ic_caps & cipher2cap(j)) || 2208138568Ssam ieee80211_crypto_available(j))) 2209138568Ssam caps |= 1<<j; 2210138568Ssam if (caps == 0) /* nothing available */ 2211138568Ssam return EINVAL; 2212138568Ssam /* XXX verify ciphers ok for unicast use? */ 2213138568Ssam /* XXX disallow if running as it'll have no effect */ 2214138568Ssam rsn->rsn_ucastcipherset = caps; 2215138568Ssam error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 2216138568Ssam break; 2217138568Ssam case IEEE80211_IOC_UCASTCIPHER: 2218138568Ssam if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0) 2219138568Ssam return EINVAL; 2220138568Ssam rsn->rsn_ucastcipher = ireq->i_val; 2221138568Ssam break; 2222138568Ssam case IEEE80211_IOC_UCASTKEYLEN: 2223138568Ssam if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE)) 2224138568Ssam return EINVAL; 2225138568Ssam /* XXX no way to verify driver capability */ 2226138568Ssam rsn->rsn_ucastkeylen = ireq->i_val; 2227138568Ssam break; 2228138568Ssam case IEEE80211_IOC_DRIVER_CAPS: 2229138568Ssam /* NB: for testing */ 2230138568Ssam ic->ic_caps = (((u_int16_t) ireq->i_val) << 16) | 2231138568Ssam ((u_int16_t) ireq->i_len); 2232138568Ssam break; 2233138568Ssam case IEEE80211_IOC_KEYMGTALGS: 2234138568Ssam /* XXX check */ 2235138568Ssam rsn->rsn_keymgmtset = ireq->i_val; 2236138568Ssam error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 2237138568Ssam break; 2238138568Ssam case IEEE80211_IOC_RSNCAPS: 2239138568Ssam /* XXX check */ 2240138568Ssam rsn->rsn_caps = ireq->i_val; 2241138568Ssam error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 2242138568Ssam break; 2243138568Ssam case IEEE80211_IOC_BSSID: 2244138568Ssam /* NB: should only be set when in STA mode */ 2245138568Ssam if (ic->ic_opmode != IEEE80211_M_STA) 2246138568Ssam return EINVAL; 2247138568Ssam if (ireq->i_len != sizeof(tmpbssid)) 2248138568Ssam return EINVAL; 2249138568Ssam error = copyin(ireq->i_data, tmpbssid, ireq->i_len); 2250138568Ssam if (error) 2251127648Ssam break; 2252138568Ssam IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid); 2253138568Ssam if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid)) 2254138568Ssam ic->ic_flags &= ~IEEE80211_F_DESBSSID; 2255138568Ssam else 2256138568Ssam ic->ic_flags |= IEEE80211_F_DESBSSID; 2257138568Ssam error = ENETRESET; 2258138568Ssam break; 2259138568Ssam case IEEE80211_IOC_CHANLIST: 2260138568Ssam error = ieee80211_ioctl_setchanlist(ic, ireq); 2261138568Ssam break; 2262138568Ssam case IEEE80211_IOC_SCAN_REQ: 2263138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) /* XXX ignore */ 2264116742Ssam break; 2265138568Ssam error = ieee80211_setupscan(ic, ic->ic_chan_avail); 2266138568Ssam if (error == 0) /* XXX background scan */ 2267138568Ssam error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 2268116742Ssam break; 2269138568Ssam case IEEE80211_IOC_ADDMAC: 2270138568Ssam case IEEE80211_IOC_DELMAC: 2271138568Ssam error = ieee80211_ioctl_macmac(ic, ireq); 2272138568Ssam break; 2273138568Ssam case IEEE80211_IOC_MACCMD: 2274138568Ssam error = ieee80211_ioctl_maccmd(ic, ireq); 2275138568Ssam break; 2276138568Ssam case IEEE80211_IOC_STA_TXPOW: 2277138568Ssam error = ieee80211_ioctl_setstatxpow(ic, ireq); 2278138568Ssam break; 2279138568Ssam case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 2280138568Ssam case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 2281138568Ssam case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 2282138568Ssam case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 2283138568Ssam case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 2284138568Ssam case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ 2285138568Ssam error = ieee80211_ioctl_setwmeparam(ic, ireq); 2286138568Ssam break; 2287138568Ssam case IEEE80211_IOC_DTIM_PERIOD: 2288138568Ssam if (ic->ic_opmode != IEEE80211_M_HOSTAP && 2289138568Ssam ic->ic_opmode != IEEE80211_M_IBSS) 2290138568Ssam return EINVAL; 2291138568Ssam if (IEEE80211_DTIM_MIN <= ireq->i_val && 2292138568Ssam ireq->i_val <= IEEE80211_DTIM_MAX) { 2293138568Ssam ic->ic_dtim_period = ireq->i_val; 2294138568Ssam error = ENETRESET; /* requires restart */ 2295138568Ssam } else 2296138568Ssam error = EINVAL; 2297138568Ssam break; 2298138568Ssam case IEEE80211_IOC_BEACON_INTERVAL: 2299138568Ssam if (ic->ic_opmode != IEEE80211_M_HOSTAP && 2300138568Ssam ic->ic_opmode != IEEE80211_M_IBSS) 2301138568Ssam return EINVAL; 2302138568Ssam if (IEEE80211_BINTVAL_MIN <= ireq->i_val && 2303138568Ssam ireq->i_val <= IEEE80211_BINTVAL_MAX) { 2304138568Ssam ic->ic_lintval = ireq->i_val; 2305138568Ssam error = ENETRESET; /* requires restart */ 2306138568Ssam } else 2307138568Ssam error = EINVAL; 2308138568Ssam break; 2309138568Ssam default: 2310138568Ssam error = EINVAL; 2311138568Ssam break; 2312138568Ssam } 2313138568Ssam if (error == ENETRESET && !IS_UP_AUTO(ic)) 2314138568Ssam error = 0; 2315138568Ssam return error; 2316138568Ssam} 2317138568Ssam 2318138568Ssamint 2319138568Ssamieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data) 2320138568Ssam{ 2321138568Ssam struct ifnet *ifp = ic->ic_ifp; 2322138568Ssam int error = 0; 2323138568Ssam struct ifreq *ifr; 2324138568Ssam struct ifaddr *ifa; /* XXX */ 2325138568Ssam 2326138568Ssam switch (cmd) { 2327138568Ssam case SIOCSIFMEDIA: 2328138568Ssam case SIOCGIFMEDIA: 2329138568Ssam error = ifmedia_ioctl(ifp, (struct ifreq *) data, 2330138568Ssam &ic->ic_media, cmd); 2331138568Ssam break; 2332138568Ssam case SIOCG80211: 2333138568Ssam error = ieee80211_ioctl_get80211(ic, cmd, 2334138568Ssam (struct ieee80211req *) data); 2335138568Ssam break; 2336138568Ssam case SIOCS80211: 2337138568Ssam error = suser(curthread); 2338138568Ssam if (error == 0) 2339138568Ssam error = ieee80211_ioctl_set80211(ic, cmd, 2340138568Ssam (struct ieee80211req *) data); 2341138568Ssam break; 2342116742Ssam case SIOCGIFGENERIC: 2343138568Ssam error = ieee80211_cfgget(ic, cmd, data); 2344116742Ssam break; 2345116742Ssam case SIOCSIFGENERIC: 2346116742Ssam error = suser(curthread); 2347116742Ssam if (error) 2348116742Ssam break; 2349138568Ssam error = ieee80211_cfgset(ic, cmd, data); 2350116742Ssam break; 2351121180Ssam case SIOCG80211STATS: 2352121180Ssam ifr = (struct ifreq *)data; 2353121180Ssam copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats)); 2354121180Ssam break; 2355124457Ssam case SIOCSIFMTU: 2356124457Ssam ifr = (struct ifreq *)data; 2357124457Ssam if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu && 2358124457Ssam ifr->ifr_mtu <= IEEE80211_MTU_MAX)) 2359124457Ssam error = EINVAL; 2360124457Ssam else 2361124457Ssam ifp->if_mtu = ifr->ifr_mtu; 2362124457Ssam break; 2363127646Ssam case SIOCSIFADDR: 2364127646Ssam /* 2365127646Ssam * XXX Handle this directly so we can supress if_init calls. 2366127646Ssam * XXX This should be done in ether_ioctl but for the moment 2367127646Ssam * XXX there are too many other parts of the system that 2368127646Ssam * XXX set IFF_UP and so supress if_init being called when 2369127646Ssam * XXX it should be. 2370127646Ssam */ 2371127646Ssam ifa = (struct ifaddr *) data; 2372127646Ssam switch (ifa->ifa_addr->sa_family) { 2373127646Ssam#ifdef INET 2374127646Ssam case AF_INET: 2375127646Ssam if ((ifp->if_flags & IFF_UP) == 0) { 2376127646Ssam ifp->if_flags |= IFF_UP; 2377127646Ssam ifp->if_init(ifp->if_softc); 2378127646Ssam } 2379127646Ssam arp_ifinit(ifp, ifa); 2380127646Ssam break; 2381127646Ssam#endif 2382127646Ssam#ifdef IPX 2383127646Ssam /* 2384127646Ssam * XXX - This code is probably wrong, 2385127646Ssam * but has been copied many times. 2386127646Ssam */ 2387127646Ssam case AF_IPX: { 2388127646Ssam struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); 2389127646Ssam struct arpcom *ac = (struct arpcom *)ifp; 2390127646Ssam 2391127646Ssam if (ipx_nullhost(*ina)) 2392127646Ssam ina->x_host = *(union ipx_host *) ac->ac_enaddr; 2393127646Ssam else 2394127646Ssam bcopy((caddr_t) ina->x_host.c_host, 2395127646Ssam (caddr_t) ac->ac_enaddr, 2396127646Ssam sizeof(ac->ac_enaddr)); 2397127646Ssam /* fall thru... */ 2398127646Ssam } 2399127646Ssam#endif 2400127646Ssam default: 2401127646Ssam if ((ifp->if_flags & IFF_UP) == 0) { 2402127646Ssam ifp->if_flags |= IFF_UP; 2403127646Ssam ifp->if_init(ifp->if_softc); 2404127646Ssam } 2405127646Ssam break; 2406127646Ssam } 2407127646Ssam break; 2408116742Ssam default: 2409116742Ssam error = ether_ioctl(ifp, cmd, data); 2410116742Ssam break; 2411116742Ssam } 2412116742Ssam return error; 2413116742Ssam} 2414