ieee80211_ioctl.c revision 138663
1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3138568Ssam * Copyright (c) 2002-2004 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 138663 2004-12-10 16:35:37Z sam $"); 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) { 871138568Ssam if (ic->ic_sta == NULL) 872138568Ssam return EINVAL; 873138568Ssam ni = ieee80211_find_node(ic->ic_sta, ik.ik_macaddr); 874138568Ssam if (ni == NULL) 875138568Ssam return EINVAL; /* XXX */ 876138568Ssam wk = &ni->ni_ucastkey; 877138568Ssam } else { 878138568Ssam if (kid >= IEEE80211_WEP_NKID) 879138568Ssam return EINVAL; 880138568Ssam wk = &ic->ic_nw_keys[kid]; 881138568Ssam IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr); 882138568Ssam ni = NULL; 883138568Ssam } 884138568Ssam cip = wk->wk_cipher; 885138568Ssam ik.ik_type = cip->ic_cipher; 886138568Ssam ik.ik_keylen = wk->wk_keylen; 887138568Ssam ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV); 888138568Ssam if (wk->wk_keyix == ic->ic_def_txkey) 889138568Ssam ik.ik_flags |= IEEE80211_KEY_DEFAULT; 890138568Ssam if (suser(curthread) == 0) { 891138568Ssam /* NB: only root can read key data */ 892138568Ssam ik.ik_keyrsc = wk->wk_keyrsc; 893138568Ssam ik.ik_keytsc = wk->wk_keytsc; 894138568Ssam memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen); 895138568Ssam if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) { 896138568Ssam memcpy(ik.ik_keydata+wk->wk_keylen, 897138568Ssam wk->wk_key + IEEE80211_KEYBUF_SIZE, 898138568Ssam IEEE80211_MICBUF_SIZE); 899138568Ssam ik.ik_keylen += IEEE80211_MICBUF_SIZE; 900138568Ssam } 901138568Ssam } else { 902138568Ssam ik.ik_keyrsc = 0; 903138568Ssam ik.ik_keytsc = 0; 904138568Ssam memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata)); 905138568Ssam } 906138568Ssam if (ni != NULL) 907138568Ssam ieee80211_free_node(ni); 908138568Ssam return copyout(&ik, ireq->i_data, sizeof(ik)); 909138568Ssam} 910138568Ssam 911138568Ssamstatic int 912138568Ssamieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) 913138568Ssam{ 914138568Ssam 915138568Ssam if (sizeof(ic->ic_chan_active) > ireq->i_len) 916138568Ssam ireq->i_len = sizeof(ic->ic_chan_active); 917138568Ssam return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len); 918138568Ssam} 919138568Ssam 920138568Ssamstatic int 921138568Ssamieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq) 922138568Ssam{ 923138568Ssam struct ieee80211req_chaninfo chans; /* XXX off stack? */ 924138568Ssam int i, space; 925138568Ssam 926138568Ssam /* 927138568Ssam * Since channel 0 is not available for DS, channel 1 928138568Ssam * is assigned to LSB on WaveLAN. 929138568Ssam */ 930138568Ssam if (ic->ic_phytype == IEEE80211_T_DS) 931138568Ssam i = 1; 932138568Ssam else 933138568Ssam i = 0; 934138568Ssam memset(&chans, 0, sizeof(chans)); 935138568Ssam for (; i <= IEEE80211_CHAN_MAX; i++) 936138568Ssam if (isset(ic->ic_chan_avail, i)) { 937138568Ssam struct ieee80211_channel *c = &ic->ic_channels[i]; 938138568Ssam chans.ic_chans[chans.ic_nchans].ic_freq = c->ic_freq; 939138568Ssam chans.ic_chans[chans.ic_nchans].ic_flags = c->ic_flags; 940138568Ssam chans.ic_nchans++; 941138568Ssam } 942138568Ssam space = __offsetof(struct ieee80211req_chaninfo, 943138568Ssam ic_chans[chans.ic_nchans]); 944138568Ssam if (space > ireq->i_len) 945138568Ssam space = ireq->i_len; 946138568Ssam return copyout(&chans, ireq->i_data, space); 947138568Ssam} 948138568Ssam 949138568Ssamstatic int 950138568Ssamieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq) 951138568Ssam{ 952138568Ssam struct ieee80211_node *ni; 953138568Ssam struct ieee80211req_wpaie wpaie; 954138568Ssam int error; 955138568Ssam 956138568Ssam if (ireq->i_len < IEEE80211_ADDR_LEN) 957138568Ssam return EINVAL; 958138568Ssam error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN); 959138568Ssam if (error != 0) 960138568Ssam return error; 961138568Ssam if (ic->ic_sta == NULL) 962138568Ssam return EINVAL; 963138568Ssam ni = ieee80211_find_node(ic->ic_sta, wpaie.wpa_macaddr); 964138568Ssam if (ni == NULL) 965138568Ssam return EINVAL; /* XXX */ 966138568Ssam memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie)); 967138568Ssam if (ni->ni_wpa_ie != NULL) { 968138568Ssam int ielen = ni->ni_wpa_ie[1] + 2; 969138568Ssam if (ielen > sizeof(wpaie.wpa_ie)) 970138568Ssam ielen = sizeof(wpaie.wpa_ie); 971138568Ssam memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen); 972138568Ssam } 973138568Ssam ieee80211_free_node(ni); 974138568Ssam if (ireq->i_len > sizeof(wpaie)) 975138568Ssam ireq->i_len = sizeof(wpaie); 976138568Ssam return copyout(&wpaie, ireq->i_data, ireq->i_len); 977138568Ssam} 978138568Ssam 979138568Ssamstatic int 980138568Ssamieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq) 981138568Ssam{ 982138568Ssam struct ieee80211_node *ni; 983138568Ssam u_int8_t macaddr[IEEE80211_ADDR_LEN]; 984138568Ssam const int off = __offsetof(struct ieee80211req_sta_stats, is_stats); 985138568Ssam int error; 986138568Ssam 987138568Ssam if (ireq->i_len < off) 988138568Ssam return EINVAL; 989138568Ssam error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); 990138568Ssam if (error != 0) 991138568Ssam return error; 992138568Ssam if (ic->ic_sta == NULL) 993138568Ssam return EINVAL; 994138568Ssam ni = ieee80211_find_node(ic->ic_sta, macaddr); 995138568Ssam if (ni == NULL) 996138568Ssam return EINVAL; /* XXX */ 997138568Ssam if (ireq->i_len > sizeof(struct ieee80211req_sta_stats)) 998138568Ssam ireq->i_len = sizeof(struct ieee80211req_sta_stats); 999138568Ssam /* NB: copy out only the statistics */ 1000138568Ssam error = copyout(&ni->ni_stats, (u_int8_t *) ireq->i_data + off, 1001138568Ssam ireq->i_len - off); 1002138568Ssam ieee80211_free_node(ni); 1003138568Ssam return error; 1004138568Ssam} 1005138568Ssam 1006138568Ssamstatic void 1007138568Ssamget_scan_result(struct ieee80211req_scan_result *sr, 1008138568Ssam const struct ieee80211_node *ni) 1009138568Ssam{ 1010138568Ssam struct ieee80211com *ic = ni->ni_ic; 1011138568Ssam 1012138568Ssam memset(sr, 0, sizeof(*sr)); 1013138568Ssam sr->isr_ssid_len = ni->ni_esslen; 1014138568Ssam if (ni->ni_wpa_ie != NULL) 1015138568Ssam sr->isr_ie_len += 2+ni->ni_wpa_ie[1]; 1016138568Ssam if (ni->ni_wme_ie != NULL) 1017138568Ssam sr->isr_ie_len += 2+ni->ni_wme_ie[1]; 1018138568Ssam sr->isr_len = sizeof(*sr) + sr->isr_ssid_len + sr->isr_ie_len; 1019138568Ssam sr->isr_len = roundup(sr->isr_len, sizeof(u_int32_t)); 1020138568Ssam if (ni->ni_chan != IEEE80211_CHAN_ANYC) { 1021138568Ssam sr->isr_freq = ni->ni_chan->ic_freq; 1022138568Ssam sr->isr_flags = ni->ni_chan->ic_flags; 1023138568Ssam } 1024138568Ssam sr->isr_rssi = ic->ic_node_getrssi(ni); 1025138568Ssam sr->isr_intval = ni->ni_intval; 1026138568Ssam sr->isr_capinfo = ni->ni_capinfo; 1027138568Ssam sr->isr_erp = ni->ni_erp; 1028138568Ssam IEEE80211_ADDR_COPY(sr->isr_bssid, ni->ni_bssid); 1029138568Ssam sr->isr_nrates = ni->ni_rates.rs_nrates; 1030138568Ssam if (sr->isr_nrates > 15) 1031138568Ssam sr->isr_nrates = 15; 1032138568Ssam memcpy(sr->isr_rates, ni->ni_rates.rs_rates, sr->isr_nrates); 1033138568Ssam} 1034138568Ssam 1035138568Ssamstatic int 1036138568Ssamieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) 1037138568Ssam{ 1038138568Ssam union { 1039138568Ssam struct ieee80211req_scan_result res; 1040138568Ssam char data[512]; /* XXX shrink? */ 1041138568Ssam } u; 1042138568Ssam struct ieee80211req_scan_result *sr = &u.res; 1043138568Ssam struct ieee80211_node_table *nt; 1044138568Ssam struct ieee80211_node *ni; 1045138568Ssam int error, space; 1046138568Ssam u_int8_t *p, *cp; 1047138568Ssam 1048138568Ssam p = ireq->i_data; 1049138568Ssam space = ireq->i_len; 1050138568Ssam error = 0; 1051138568Ssam /* XXX locking */ 1052138568Ssam nt = &ic->ic_scan; 1053138568Ssam TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { 1054138568Ssam /* NB: skip pre-scan node state */ 1055138568Ssam if (ni->ni_chan == IEEE80211_CHAN_ANYC) 1056138568Ssam continue; 1057138568Ssam get_scan_result(sr, ni); 1058138568Ssam if (sr->isr_len > sizeof(u)) 1059138568Ssam continue; /* XXX */ 1060138568Ssam if (space < sr->isr_len) 1061138568Ssam break; 1062138568Ssam cp = (u_int8_t *)(sr+1); 1063138568Ssam memcpy(cp, ni->ni_essid, ni->ni_esslen); 1064138568Ssam cp += ni->ni_esslen; 1065138568Ssam if (ni->ni_wpa_ie != NULL) { 1066138568Ssam memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); 1067138568Ssam cp += 2+ni->ni_wpa_ie[1]; 1068138568Ssam } 1069138568Ssam if (ni->ni_wme_ie != NULL) { 1070138568Ssam memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); 1071138568Ssam cp += 2+ni->ni_wme_ie[1]; 1072138568Ssam } 1073138568Ssam error = copyout(sr, p, sr->isr_len); 1074138568Ssam if (error) 1075138568Ssam break; 1076138568Ssam p += sr->isr_len; 1077138568Ssam space -= sr->isr_len; 1078138568Ssam } 1079138568Ssam ireq->i_len -= space; 1080138568Ssam return error; 1081138568Ssam} 1082138568Ssam 1083138568Ssamstatic void 1084138568Ssamget_sta_info(struct ieee80211req_sta_info *si, const struct ieee80211_node *ni) 1085138568Ssam{ 1086138568Ssam struct ieee80211com *ic = ni->ni_ic; 1087138568Ssam 1088138568Ssam si->isi_ie_len = 0; 1089138568Ssam if (ni->ni_wpa_ie != NULL) 1090138568Ssam si->isi_ie_len += 2+ni->ni_wpa_ie[1]; 1091138568Ssam if (ni->ni_wme_ie != NULL) 1092138568Ssam si->isi_ie_len += 2+ni->ni_wme_ie[1]; 1093138568Ssam si->isi_len = sizeof(*si) + si->isi_ie_len, sizeof(u_int32_t); 1094138568Ssam si->isi_len = roundup(si->isi_len, sizeof(u_int32_t)); 1095138568Ssam si->isi_freq = ni->ni_chan->ic_freq; 1096138568Ssam si->isi_flags = ni->ni_chan->ic_flags; 1097138568Ssam si->isi_state = ni->ni_flags; 1098138568Ssam si->isi_authmode = ni->ni_authmode; 1099138568Ssam si->isi_rssi = ic->ic_node_getrssi(ni); 1100138568Ssam si->isi_capinfo = ni->ni_capinfo; 1101138568Ssam si->isi_erp = ni->ni_erp; 1102138568Ssam IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr); 1103138568Ssam si->isi_nrates = ni->ni_rates.rs_nrates; 1104138568Ssam if (si->isi_nrates > 15) 1105138568Ssam si->isi_nrates = 15; 1106138568Ssam memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates); 1107138568Ssam si->isi_txrate = ni->ni_txrate; 1108138568Ssam si->isi_associd = ni->ni_associd; 1109138568Ssam si->isi_txpower = ni->ni_txpower; 1110138568Ssam si->isi_vlan = ni->ni_vlan; 1111138568Ssam if (ni->ni_flags & IEEE80211_NODE_QOS) { 1112138568Ssam memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs)); 1113138568Ssam memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs)); 1114138568Ssam } else { 1115138568Ssam si->isi_txseqs[0] = ni->ni_txseqs[0]; 1116138568Ssam si->isi_rxseqs[0] = ni->ni_rxseqs[0]; 1117138568Ssam } 1118138568Ssam if (ic->ic_opmode == IEEE80211_M_IBSS || ni->ni_associd != 0) 1119138568Ssam si->isi_inact = ic->ic_inact_run; 1120138568Ssam else if (ieee80211_node_is_authorized(ni)) 1121138568Ssam si->isi_inact = ic->ic_inact_auth; 1122138568Ssam else 1123138568Ssam si->isi_inact = ic->ic_inact_init; 1124138568Ssam si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT; 1125138568Ssam} 1126138568Ssam 1127138568Ssamstatic int 1128138568Ssamieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) 1129138568Ssam{ 1130138568Ssam union { 1131138568Ssam struct ieee80211req_sta_info info; 1132138568Ssam char data[512]; /* XXX shrink? */ 1133138568Ssam } u; 1134138568Ssam struct ieee80211req_sta_info *si = &u.info; 1135138568Ssam struct ieee80211_node_table *nt; 1136138568Ssam struct ieee80211_node *ni; 1137138568Ssam int error, space; 1138138568Ssam u_int8_t *p, *cp; 1139138568Ssam 1140138568Ssam nt = ic->ic_sta; 1141138568Ssam if (nt == NULL) 1142138568Ssam return EINVAL; 1143138568Ssam p = ireq->i_data; 1144138568Ssam space = ireq->i_len; 1145138568Ssam error = 0; 1146138568Ssam /* XXX locking */ 1147138568Ssam TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { 1148138568Ssam get_sta_info(si, ni); 1149138568Ssam if (si->isi_len > sizeof(u)) 1150138568Ssam continue; /* XXX */ 1151138568Ssam if (space < si->isi_len) 1152138568Ssam break; 1153138568Ssam cp = (u_int8_t *)(si+1); 1154138568Ssam if (ni->ni_wpa_ie != NULL) { 1155138568Ssam memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); 1156138568Ssam cp += 2+ni->ni_wpa_ie[1]; 1157138568Ssam } 1158138568Ssam if (ni->ni_wme_ie != NULL) { 1159138568Ssam memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); 1160138568Ssam cp += 2+ni->ni_wme_ie[1]; 1161138568Ssam } 1162138568Ssam error = copyout(si, p, si->isi_len); 1163138568Ssam if (error) 1164138568Ssam break; 1165138568Ssam p += si->isi_len; 1166138568Ssam space -= si->isi_len; 1167138568Ssam } 1168138568Ssam ireq->i_len -= space; 1169138568Ssam return error; 1170138568Ssam} 1171138568Ssam 1172138568Ssamstatic int 1173138568Ssamieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) 1174138568Ssam{ 1175138568Ssam struct ieee80211_node *ni; 1176138568Ssam struct ieee80211req_sta_txpow txpow; 1177138568Ssam int error; 1178138568Ssam 1179138568Ssam if (ireq->i_len != sizeof(txpow)) 1180138568Ssam return EINVAL; 1181138568Ssam error = copyin(ireq->i_data, &txpow, sizeof(txpow)); 1182138568Ssam if (error != 0) 1183138568Ssam return error; 1184138568Ssam if (ic->ic_sta == NULL) 1185138568Ssam return EINVAL; 1186138568Ssam ni = ieee80211_find_node(ic->ic_sta, txpow.it_macaddr); 1187138568Ssam if (ni == NULL) 1188138568Ssam return EINVAL; /* XXX */ 1189138568Ssam txpow.it_txpow = ni->ni_txpower; 1190138568Ssam error = copyout(&txpow, ireq->i_data, sizeof(txpow)); 1191138568Ssam ieee80211_free_node(ni); 1192138568Ssam return error; 1193138568Ssam} 1194138568Ssam 1195138568Ssamstatic int 1196138568Ssamieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) 1197138568Ssam{ 1198138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 1199138568Ssam struct wmeParams *wmep; 1200138568Ssam int ac; 1201138568Ssam 1202138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 1203138568Ssam return EINVAL; 1204138568Ssam 1205138568Ssam ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); 1206138568Ssam if (ac >= WME_NUM_AC) 1207138568Ssam ac = WME_AC_BE; 1208138568Ssam if (ireq->i_len & IEEE80211_WMEPARAM_BSS) 1209138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; 1210138568Ssam else 1211138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; 1212138568Ssam switch (ireq->i_type) { 1213138568Ssam case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 1214138568Ssam ireq->i_val = wmep->wmep_logcwmin; 1215138568Ssam break; 1216138568Ssam case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 1217138568Ssam ireq->i_val = wmep->wmep_logcwmax; 1218138568Ssam break; 1219138568Ssam case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 1220138568Ssam ireq->i_val = wmep->wmep_aifsn; 1221138568Ssam break; 1222138568Ssam case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 1223138568Ssam ireq->i_val = wmep->wmep_txopLimit; 1224138568Ssam break; 1225138568Ssam case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 1226138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; 1227138568Ssam ireq->i_val = wmep->wmep_acm; 1228138568Ssam break; 1229138568Ssam case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ 1230138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; 1231138568Ssam ireq->i_val = !wmep->wmep_noackPolicy; 1232138568Ssam break; 1233138568Ssam } 1234138568Ssam return 0; 1235138568Ssam} 1236138568Ssam 1237138568Ssamstatic int 1238138568Ssamieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) 1239138568Ssam{ 1240138568Ssam const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; 1241116742Ssam int error = 0; 1242138568Ssam u_int kid, len, m; 1243116742Ssam u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; 1244116742Ssam char tmpssid[IEEE80211_NWID_LEN]; 1245116742Ssam 1246138568Ssam switch (ireq->i_type) { 1247138568Ssam case IEEE80211_IOC_SSID: 1248138568Ssam switch (ic->ic_state) { 1249138568Ssam case IEEE80211_S_INIT: 1250138568Ssam case IEEE80211_S_SCAN: 1251138568Ssam ireq->i_len = ic->ic_des_esslen; 1252138568Ssam memcpy(tmpssid, ic->ic_des_essid, ireq->i_len); 1253138568Ssam break; 1254138568Ssam default: 1255138568Ssam ireq->i_len = ic->ic_bss->ni_esslen; 1256138568Ssam memcpy(tmpssid, ic->ic_bss->ni_essid, 1257138568Ssam ireq->i_len); 1258138568Ssam break; 1259138568Ssam } 1260138568Ssam error = copyout(tmpssid, ireq->i_data, ireq->i_len); 1261116742Ssam break; 1262138568Ssam case IEEE80211_IOC_NUMSSIDS: 1263138568Ssam ireq->i_val = 1; 1264138568Ssam break; 1265138568Ssam case IEEE80211_IOC_WEP: 1266138568Ssam if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) 1267138568Ssam ireq->i_val = IEEE80211_WEP_OFF; 1268138568Ssam else if (ic->ic_flags & IEEE80211_F_DROPUNENC) 1269138568Ssam ireq->i_val = IEEE80211_WEP_ON; 1270138568Ssam else 1271138568Ssam ireq->i_val = IEEE80211_WEP_MIXED; 1272138568Ssam break; 1273138568Ssam case IEEE80211_IOC_WEPKEY: 1274138568Ssam kid = (u_int) ireq->i_val; 1275138568Ssam if (kid >= IEEE80211_WEP_NKID) 1276138568Ssam return EINVAL; 1277138568Ssam len = (u_int) ic->ic_nw_keys[kid].wk_keylen; 1278138568Ssam /* NB: only root can read WEP keys */ 1279138568Ssam if (suser(curthread) == 0) { 1280138568Ssam bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len); 1281138568Ssam } else { 1282138568Ssam bzero(tmpkey, len); 1283138568Ssam } 1284138568Ssam ireq->i_len = len; 1285138568Ssam error = copyout(tmpkey, ireq->i_data, len); 1286138568Ssam break; 1287138568Ssam case IEEE80211_IOC_NUMWEPKEYS: 1288138568Ssam ireq->i_val = IEEE80211_WEP_NKID; 1289138568Ssam break; 1290138568Ssam case IEEE80211_IOC_WEPTXKEY: 1291138568Ssam ireq->i_val = ic->ic_def_txkey; 1292138568Ssam break; 1293138568Ssam case IEEE80211_IOC_AUTHMODE: 1294138568Ssam if (ic->ic_flags & IEEE80211_F_WPA) 1295138568Ssam ireq->i_val = IEEE80211_AUTH_WPA; 1296138568Ssam else 1297138568Ssam ireq->i_val = ic->ic_bss->ni_authmode; 1298138568Ssam break; 1299138568Ssam case IEEE80211_IOC_CHANNEL: 1300138568Ssam ireq->i_val = ieee80211_chan2ieee(ic, getcurchan(ic)); 1301138568Ssam break; 1302138568Ssam case IEEE80211_IOC_POWERSAVE: 1303138568Ssam if (ic->ic_flags & IEEE80211_F_PMGTON) 1304138568Ssam ireq->i_val = IEEE80211_POWERSAVE_ON; 1305138568Ssam else 1306138568Ssam ireq->i_val = IEEE80211_POWERSAVE_OFF; 1307138568Ssam break; 1308138568Ssam case IEEE80211_IOC_POWERSAVESLEEP: 1309138568Ssam ireq->i_val = ic->ic_lintval; 1310138568Ssam break; 1311138568Ssam case IEEE80211_IOC_RTSTHRESHOLD: 1312138568Ssam ireq->i_val = ic->ic_rtsthreshold; 1313138568Ssam break; 1314138568Ssam case IEEE80211_IOC_PROTMODE: 1315138568Ssam ireq->i_val = ic->ic_protmode; 1316138568Ssam break; 1317138568Ssam case IEEE80211_IOC_TXPOWER: 1318138568Ssam if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) 1319138568Ssam return EINVAL; 1320138568Ssam ireq->i_val = ic->ic_txpowlimit; 1321138568Ssam break; 1322138568Ssam case IEEE80211_IOC_MCASTCIPHER: 1323138568Ssam ireq->i_val = rsn->rsn_mcastcipher; 1324138568Ssam break; 1325138568Ssam case IEEE80211_IOC_MCASTKEYLEN: 1326138568Ssam ireq->i_val = rsn->rsn_mcastkeylen; 1327138568Ssam break; 1328138568Ssam case IEEE80211_IOC_UCASTCIPHERS: 1329138568Ssam ireq->i_val = 0; 1330138568Ssam for (m = 0x1; m != 0; m <<= 1) 1331138568Ssam if (rsn->rsn_ucastcipherset & m) 1332138568Ssam ireq->i_val |= 1<<cap2cipher(m); 1333138568Ssam break; 1334138568Ssam case IEEE80211_IOC_UCASTCIPHER: 1335138568Ssam ireq->i_val = rsn->rsn_ucastcipher; 1336138568Ssam break; 1337138568Ssam case IEEE80211_IOC_UCASTKEYLEN: 1338138568Ssam ireq->i_val = rsn->rsn_ucastkeylen; 1339138568Ssam break; 1340138568Ssam case IEEE80211_IOC_KEYMGTALGS: 1341138568Ssam ireq->i_val = rsn->rsn_keymgmtset; 1342138568Ssam break; 1343138568Ssam case IEEE80211_IOC_RSNCAPS: 1344138568Ssam ireq->i_val = rsn->rsn_caps; 1345138568Ssam break; 1346138568Ssam case IEEE80211_IOC_WPA: 1347138568Ssam switch (ic->ic_flags & IEEE80211_F_WPA) { 1348138568Ssam case IEEE80211_F_WPA1: 1349116742Ssam ireq->i_val = 1; 1350116742Ssam break; 1351138568Ssam case IEEE80211_F_WPA2: 1352138568Ssam ireq->i_val = 2; 1353116742Ssam break; 1354138568Ssam case IEEE80211_F_WPA1 | IEEE80211_F_WPA2: 1355138568Ssam ireq->i_val = 3; 1356116742Ssam break; 1357138568Ssam default: 1358138568Ssam ireq->i_val = 0; 1359116742Ssam break; 1360138568Ssam } 1361138568Ssam break; 1362138568Ssam case IEEE80211_IOC_CHANLIST: 1363138568Ssam error = ieee80211_ioctl_getchanlist(ic, ireq); 1364138568Ssam break; 1365138568Ssam case IEEE80211_IOC_ROAMING: 1366138568Ssam ireq->i_val = ic->ic_roaming; 1367138568Ssam break; 1368138568Ssam case IEEE80211_IOC_PRIVACY: 1369138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0; 1370138568Ssam break; 1371138568Ssam case IEEE80211_IOC_DROPUNENCRYPTED: 1372138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0; 1373138568Ssam break; 1374138568Ssam case IEEE80211_IOC_COUNTERMEASURES: 1375138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0; 1376138568Ssam break; 1377138568Ssam case IEEE80211_IOC_DRIVER_CAPS: 1378138568Ssam ireq->i_val = ic->ic_caps>>16; 1379138568Ssam ireq->i_len = ic->ic_caps&0xffff; 1380138568Ssam break; 1381138568Ssam case IEEE80211_IOC_WME: 1382138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0; 1383138568Ssam break; 1384138568Ssam case IEEE80211_IOC_HIDESSID: 1385138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0; 1386138568Ssam break; 1387138568Ssam case IEEE80211_IOC_APBRIDGE: 1388138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0; 1389138568Ssam break; 1390138568Ssam case IEEE80211_IOC_OPTIE: 1391138568Ssam if (ic->ic_opt_ie == NULL) 1392138568Ssam return EINVAL; 1393138568Ssam /* NB: truncate, caller can check length */ 1394138568Ssam if (ireq->i_len > ic->ic_opt_ie_len) 1395138568Ssam ireq->i_len = ic->ic_opt_ie_len; 1396138568Ssam error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len); 1397138568Ssam break; 1398138568Ssam case IEEE80211_IOC_WPAKEY: 1399138568Ssam error = ieee80211_ioctl_getkey(ic, ireq); 1400138568Ssam break; 1401138568Ssam case IEEE80211_IOC_CHANINFO: 1402138568Ssam error = ieee80211_ioctl_getchaninfo(ic, ireq); 1403138568Ssam break; 1404138568Ssam case IEEE80211_IOC_BSSID: 1405138568Ssam if (ireq->i_len != IEEE80211_ADDR_LEN) 1406138568Ssam return EINVAL; 1407138568Ssam error = copyout(ic->ic_state == IEEE80211_S_RUN ? 1408138568Ssam ic->ic_bss->ni_bssid : 1409138568Ssam ic->ic_des_bssid, 1410138568Ssam ireq->i_data, ireq->i_len); 1411138568Ssam break; 1412138568Ssam case IEEE80211_IOC_WPAIE: 1413138568Ssam error = ieee80211_ioctl_getwpaie(ic, ireq); 1414138568Ssam break; 1415138568Ssam case IEEE80211_IOC_SCAN_RESULTS: 1416138568Ssam error = ieee80211_ioctl_getscanresults(ic, ireq); 1417138568Ssam break; 1418138568Ssam case IEEE80211_IOC_STA_STATS: 1419138568Ssam error = ieee80211_ioctl_getstastats(ic, ireq); 1420138568Ssam break; 1421138568Ssam case IEEE80211_IOC_TXPOWMAX: 1422138568Ssam ireq->i_val = ic->ic_bss->ni_txpower; 1423138568Ssam break; 1424138568Ssam case IEEE80211_IOC_STA_TXPOW: 1425138568Ssam error = ieee80211_ioctl_getstatxpow(ic, ireq); 1426138568Ssam break; 1427138568Ssam case IEEE80211_IOC_STA_INFO: 1428138568Ssam error = ieee80211_ioctl_getstainfo(ic, ireq); 1429138568Ssam break; 1430138568Ssam case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 1431138568Ssam case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 1432138568Ssam case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 1433138568Ssam case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 1434138568Ssam case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 1435138568Ssam case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ 1436138568Ssam error = ieee80211_ioctl_getwmeparam(ic, ireq); 1437138568Ssam break; 1438138568Ssam case IEEE80211_IOC_DTIM_PERIOD: 1439138568Ssam ireq->i_val = ic->ic_dtim_period; 1440138568Ssam break; 1441138568Ssam case IEEE80211_IOC_BEACON_INTERVAL: 1442138568Ssam /* NB: get from ic_bss for station mode */ 1443138568Ssam ireq->i_val = ic->ic_bss->ni_intval; 1444138568Ssam break; 1445138568Ssam default: 1446138568Ssam error = EINVAL; 1447138568Ssam break; 1448138568Ssam } 1449138568Ssam return error; 1450138568Ssam} 1451138568Ssam 1452138568Ssamstatic int 1453138568Ssamieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq) 1454138568Ssam{ 1455138568Ssam int error; 1456138568Ssam void *ie; 1457138568Ssam 1458138568Ssam /* 1459138568Ssam * NB: Doing this for ap operation could be useful (e.g. for 1460138568Ssam * WPA and/or WME) except that it typically is worthless 1461138568Ssam * without being able to intervene when processing 1462138568Ssam * association response frames--so disallow it for now. 1463138568Ssam */ 1464138568Ssam if (ic->ic_opmode != IEEE80211_M_STA) 1465138568Ssam return EINVAL; 1466138568Ssam if (ireq->i_len > IEEE80211_MAX_OPT_IE) 1467138568Ssam return EINVAL; 1468138568Ssam /* NB: data.length is validated by the wireless extensions code */ 1469138568Ssam MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_WAITOK); 1470138568Ssam if (ie == NULL) 1471138568Ssam return ENOMEM; 1472138568Ssam error = copyin(ireq->i_data, ie, ireq->i_len); 1473138568Ssam /* XXX sanity check data? */ 1474138568Ssam if (ic->ic_opt_ie != NULL) 1475138568Ssam FREE(ic->ic_opt_ie, M_DEVBUF); 1476138568Ssam ic->ic_opt_ie = ie; 1477138568Ssam ic->ic_opt_ie_len = ireq->i_len; 1478138568Ssam return 0; 1479138568Ssam} 1480138568Ssam 1481138568Ssamstatic int 1482138568Ssamieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq) 1483138568Ssam{ 1484138568Ssam struct ieee80211req_key ik; 1485138568Ssam struct ieee80211_node *ni; 1486138568Ssam struct ieee80211_key *wk; 1487138568Ssam u_int16_t kid; 1488138568Ssam int error; 1489138568Ssam 1490138568Ssam if (ireq->i_len != sizeof(ik)) 1491138568Ssam return EINVAL; 1492138568Ssam error = copyin(ireq->i_data, &ik, sizeof(ik)); 1493138568Ssam if (error) 1494138568Ssam return error; 1495138568Ssam /* NB: cipher support is verified by ieee80211_crypt_newkey */ 1496138568Ssam /* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */ 1497138568Ssam if (ik.ik_keylen > sizeof(ik.ik_keydata)) 1498138568Ssam return E2BIG; 1499138568Ssam kid = ik.ik_keyix; 1500138568Ssam if (kid == IEEE80211_KEYIX_NONE) { 1501138568Ssam /* XXX unicast keys currently must be tx/rx */ 1502138568Ssam if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)) 1503138568Ssam return EINVAL; 1504138568Ssam if (ic->ic_opmode == IEEE80211_M_STA) { 1505138568Ssam ni = ic->ic_bss; 1506138568Ssam if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) 1507138568Ssam return EADDRNOTAVAIL; 1508138568Ssam } else { 1509138568Ssam if (ic->ic_sta == NULL) 1510138568Ssam return EINVAL; 1511138568Ssam ni = ieee80211_find_node(ic->ic_sta, ik.ik_macaddr); 1512138568Ssam if (ni == NULL) 1513138568Ssam return ENOENT; 1514138568Ssam } 1515138568Ssam wk = &ni->ni_ucastkey; 1516138568Ssam } else { 1517138568Ssam if (kid >= IEEE80211_WEP_NKID) 1518138568Ssam return EINVAL; 1519138568Ssam wk = &ic->ic_nw_keys[kid]; 1520138568Ssam ni = NULL; 1521138568Ssam } 1522138568Ssam error = 0; 1523138568Ssam ieee80211_key_update_begin(ic); 1524138568Ssam if (ieee80211_crypto_newkey(ic, ik.ik_type, wk)) { 1525138568Ssam wk->wk_keylen = ik.ik_keylen; 1526138568Ssam /* NB: MIC presence is implied by cipher type */ 1527138568Ssam if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE) 1528138568Ssam wk->wk_keylen = IEEE80211_KEYBUF_SIZE; 1529138568Ssam wk->wk_keyrsc = ik.ik_keyrsc; 1530138568Ssam wk->wk_keytsc = 0; /* new key, reset */ 1531138568Ssam wk->wk_flags |= 1532138568Ssam ik.ik_flags & (IEEE80211_KEY_XMIT|IEEE80211_KEY_RECV); 1533138568Ssam memset(wk->wk_key, 0, sizeof(wk->wk_key)); 1534138568Ssam memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen); 1535138568Ssam if (!ieee80211_crypto_setkey(ic, wk, 1536138568Ssam ni != NULL ? ni->ni_macaddr : ik.ik_macaddr)) 1537138568Ssam error = EIO; 1538138568Ssam else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT)) 1539138568Ssam ic->ic_def_txkey = kid; 1540138568Ssam } else 1541138568Ssam error = ENXIO; 1542138568Ssam ieee80211_key_update_end(ic); 1543138568Ssam if (ni != NULL) 1544138568Ssam ieee80211_free_node(ni); 1545138568Ssam return error; 1546138568Ssam} 1547138568Ssam 1548138568Ssamstatic int 1549138568Ssamieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq) 1550138568Ssam{ 1551138568Ssam struct ieee80211req_del_key dk; 1552138568Ssam int kid, error; 1553138568Ssam 1554138568Ssam if (ireq->i_len != sizeof(dk)) 1555138568Ssam return EINVAL; 1556138568Ssam error = copyin(ireq->i_data, &dk, sizeof(dk)); 1557138568Ssam if (error) 1558138568Ssam return error; 1559138568Ssam kid = dk.idk_keyix; 1560138568Ssam /* XXX u_int8_t -> u_int16_t */ 1561138568Ssam if (dk.idk_keyix == (u_int8_t) IEEE80211_KEYIX_NONE) { 1562138568Ssam struct ieee80211_node *ni; 1563138568Ssam 1564138568Ssam if (ic->ic_sta == NULL) 1565138568Ssam return EINVAL; 1566138568Ssam ni = ieee80211_find_node(ic->ic_sta, dk.idk_macaddr); 1567138568Ssam if (ni == NULL) 1568138568Ssam return EINVAL; /* XXX */ 1569138568Ssam /* XXX error return */ 1570138568Ssam ieee80211_crypto_delkey(ic, &ni->ni_ucastkey); 1571138568Ssam ieee80211_free_node(ni); 1572138568Ssam } else { 1573138568Ssam if (kid >= IEEE80211_WEP_NKID) 1574138568Ssam return EINVAL; 1575138568Ssam /* XXX error return */ 1576138568Ssam ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]); 1577138568Ssam } 1578138568Ssam return 0; 1579138568Ssam} 1580138568Ssam 1581138568Ssamstatic void 1582138568Ssamdomlme(void *arg, struct ieee80211_node *ni) 1583138568Ssam{ 1584138568Ssam struct ieee80211com *ic = ni->ni_ic; 1585138568Ssam struct ieee80211req_mlme *mlme = arg; 1586138568Ssam 1587138568Ssam if (ni->ni_associd != 0) { 1588138568Ssam IEEE80211_SEND_MGMT(ic, ni, 1589138568Ssam mlme->im_op == IEEE80211_MLME_DEAUTH ? 1590138568Ssam IEEE80211_FC0_SUBTYPE_DEAUTH : 1591138568Ssam IEEE80211_FC0_SUBTYPE_DISASSOC, 1592138568Ssam mlme->im_reason); 1593138568Ssam } 1594138568Ssam ieee80211_node_leave(ic, ni); 1595138568Ssam} 1596138568Ssam 1597138568Ssamstatic int 1598138568Ssamieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) 1599138568Ssam{ 1600138568Ssam struct ieee80211req_mlme mlme; 1601138568Ssam struct ieee80211_node *ni; 1602138568Ssam int error; 1603138568Ssam 1604138568Ssam if (ireq->i_len != sizeof(mlme)) 1605138568Ssam return EINVAL; 1606138568Ssam error = copyin(ireq->i_data, &mlme, sizeof(mlme)); 1607138568Ssam if (error) 1608138568Ssam return error; 1609138568Ssam switch (mlme.im_op) { 1610138568Ssam case IEEE80211_MLME_ASSOC: 1611138568Ssam if (ic->ic_opmode != IEEE80211_M_STA) 1612138568Ssam return EINVAL; 1613138568Ssam /* XXX must be in S_SCAN state? */ 1614138568Ssam 1615138568Ssam if (ic->ic_des_esslen != 0) { 1616138568Ssam /* 1617138568Ssam * Desired ssid specified; must match both bssid and 1618138568Ssam * ssid to distinguish ap advertising multiple ssid's. 1619138568Ssam */ 1620138568Ssam ni = ieee80211_find_node_with_ssid(&ic->ic_scan, 1621138568Ssam mlme.im_macaddr, 1622138568Ssam ic->ic_des_esslen, ic->ic_des_essid); 1623138568Ssam } else { 1624138568Ssam /* 1625138568Ssam * Normal case; just match bssid. 1626138568Ssam */ 1627138568Ssam ni = ieee80211_find_node(&ic->ic_scan, mlme.im_macaddr); 1628138568Ssam } 1629138568Ssam if (ni == NULL) 1630138568Ssam return EINVAL; 1631138568Ssam if (!ieee80211_sta_join(ic, ni)) { 1632138568Ssam ieee80211_free_node(ni); 1633138568Ssam return EINVAL; 1634138568Ssam } 1635138568Ssam break; 1636138568Ssam case IEEE80211_MLME_DISASSOC: 1637138568Ssam case IEEE80211_MLME_DEAUTH: 1638138568Ssam switch (ic->ic_opmode) { 1639138568Ssam case IEEE80211_M_STA: 1640138568Ssam /* XXX not quite right */ 1641138568Ssam ieee80211_new_state(ic, IEEE80211_S_INIT, 1642138568Ssam mlme.im_reason); 1643116742Ssam break; 1644138568Ssam case IEEE80211_M_HOSTAP: 1645138568Ssam /* NB: the broadcast address means do 'em all */ 1646138568Ssam if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) { 1647138568Ssam if (ic->ic_sta == NULL || 1648138568Ssam (ni = ieee80211_find_node(ic->ic_sta, 1649138568Ssam mlme.im_macaddr)) == NULL) 1650138568Ssam return EINVAL; 1651138568Ssam domlme(&mlme, ni); 1652138568Ssam ieee80211_free_node(ni); 1653138568Ssam } else { 1654138568Ssam if (ic->ic_sta != NULL) 1655138568Ssam ieee80211_iterate_nodes(ic->ic_sta, 1656138568Ssam domlme, &mlme); 1657138568Ssam } 1658116742Ssam break; 1659138568Ssam default: 1660138568Ssam return EINVAL; 1661138568Ssam } 1662138568Ssam break; 1663138568Ssam case IEEE80211_MLME_AUTHORIZE: 1664138568Ssam case IEEE80211_MLME_UNAUTHORIZE: 1665138568Ssam if (ic->ic_opmode != IEEE80211_M_HOSTAP) 1666138568Ssam return EINVAL; 1667138568Ssam if (ic->ic_sta == NULL) 1668138568Ssam return EINVAL; 1669138568Ssam ni = ieee80211_find_node(ic->ic_sta, mlme.im_macaddr); 1670138568Ssam if (ni == NULL) 1671138568Ssam return EINVAL; 1672138568Ssam if (mlme.im_op == IEEE80211_MLME_AUTHORIZE) 1673138568Ssam ieee80211_node_authorize(ic, ni); 1674138568Ssam else 1675138568Ssam ieee80211_node_unauthorize(ic, ni); 1676138568Ssam ieee80211_free_node(ni); 1677138568Ssam break; 1678138568Ssam default: 1679138568Ssam return EINVAL; 1680138568Ssam } 1681138568Ssam return 0; 1682138568Ssam} 1683138568Ssam 1684138568Ssamstatic int 1685138568Ssamieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq) 1686138568Ssam{ 1687138568Ssam u_int8_t mac[IEEE80211_ADDR_LEN]; 1688138568Ssam const struct ieee80211_aclator *acl = ic->ic_acl; 1689138568Ssam int error; 1690138568Ssam 1691138568Ssam if (ireq->i_len != sizeof(mac)) 1692138568Ssam return EINVAL; 1693138568Ssam error = copyin(ireq->i_data, mac, ireq->i_len); 1694138568Ssam if (error) 1695138568Ssam return error; 1696138568Ssam if (acl == NULL) { 1697138568Ssam acl = ieee80211_aclator_get("mac"); 1698138568Ssam if (acl == NULL || !acl->iac_attach(ic)) 1699138568Ssam return EINVAL; 1700138568Ssam ic->ic_acl = acl; 1701138568Ssam } 1702138568Ssam if (ireq->i_type == IEEE80211_IOC_ADDMAC) 1703138568Ssam acl->iac_add(ic, mac); 1704138568Ssam else 1705138568Ssam acl->iac_remove(ic, mac); 1706138568Ssam return 0; 1707138568Ssam} 1708138568Ssam 1709138568Ssamstatic int 1710138568Ssamieee80211_ioctl_maccmd(struct ieee80211com *ic, struct ieee80211req *ireq) 1711138568Ssam{ 1712138568Ssam const struct ieee80211_aclator *acl = ic->ic_acl; 1713138568Ssam 1714138568Ssam switch (ireq->i_val) { 1715138568Ssam case IEEE80211_MACCMD_POLICY_OPEN: 1716138568Ssam case IEEE80211_MACCMD_POLICY_ALLOW: 1717138568Ssam case IEEE80211_MACCMD_POLICY_DENY: 1718138568Ssam if (acl == NULL) { 1719138568Ssam acl = ieee80211_aclator_get("mac"); 1720138568Ssam if (acl == NULL || !acl->iac_attach(ic)) 1721138568Ssam return EINVAL; 1722138568Ssam ic->ic_acl = acl; 1723138568Ssam } 1724138568Ssam acl->iac_setpolicy(ic, ireq->i_val); 1725138568Ssam break; 1726138568Ssam case IEEE80211_MACCMD_FLUSH: 1727138568Ssam if (acl != NULL) 1728138568Ssam acl->iac_flush(ic); 1729138568Ssam /* NB: silently ignore when not in use */ 1730138568Ssam break; 1731138568Ssam case IEEE80211_MACCMD_DETACH: 1732138568Ssam if (acl != NULL) { 1733138568Ssam ic->ic_acl = NULL; 1734138568Ssam acl->iac_detach(ic); 1735138568Ssam } 1736138568Ssam break; 1737138568Ssam default: 1738138568Ssam return EINVAL; 1739138568Ssam } 1740138568Ssam return 0; 1741138568Ssam} 1742138568Ssam 1743138568Ssamstatic int 1744138568Ssamieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) 1745138568Ssam{ 1746138568Ssam struct ieee80211req_chanlist list; 1747138568Ssam u_char chanlist[IEEE80211_CHAN_BYTES]; 1748138568Ssam int i, j, error; 1749138568Ssam 1750138568Ssam if (ireq->i_len != sizeof(list)) 1751138568Ssam return EINVAL; 1752138568Ssam error = copyin(ireq->i_data, &list, sizeof(list)); 1753138568Ssam if (error) 1754138568Ssam return error; 1755138568Ssam memset(chanlist, 0, sizeof(chanlist)); 1756138568Ssam /* 1757138568Ssam * Since channel 0 is not available for DS, channel 1 1758138568Ssam * is assigned to LSB on WaveLAN. 1759138568Ssam */ 1760138568Ssam if (ic->ic_phytype == IEEE80211_T_DS) 1761138568Ssam i = 1; 1762138568Ssam else 1763138568Ssam i = 0; 1764138568Ssam for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { 1765138568Ssam /* 1766138568Ssam * NB: silently discard unavailable channels so users 1767138568Ssam * can specify 1-255 to get all available channels. 1768138568Ssam */ 1769138568Ssam if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) 1770138568Ssam setbit(chanlist, i); 1771138568Ssam } 1772138568Ssam if (ic->ic_ibss_chan == NULL || 1773138568Ssam isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { 1774138568Ssam for (i = 0; i <= IEEE80211_CHAN_MAX; i++) 1775138568Ssam if (isset(chanlist, i)) { 1776138568Ssam ic->ic_ibss_chan = &ic->ic_channels[i]; 1777138568Ssam goto found; 1778116742Ssam } 1779138568Ssam return EINVAL; /* no active channels */ 1780138568Ssamfound: 1781138568Ssam ; 1782138568Ssam } 1783138568Ssam memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); 1784138568Ssam if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC || 1785138568Ssam isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan))) 1786138568Ssam ic->ic_bss->ni_chan = ic->ic_ibss_chan; 1787138568Ssam return IS_UP_AUTO(ic) ? ENETRESET : 0; 1788138568Ssam} 1789138568Ssam 1790138568Ssamstatic int 1791138568Ssamieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) 1792138568Ssam{ 1793138568Ssam struct ieee80211_node *ni; 1794138568Ssam struct ieee80211req_sta_txpow txpow; 1795138568Ssam int error; 1796138568Ssam 1797138568Ssam if (ireq->i_len != sizeof(txpow)) 1798138568Ssam return EINVAL; 1799138568Ssam error = copyin(ireq->i_data, &txpow, sizeof(txpow)); 1800138568Ssam if (error != 0) 1801138568Ssam return error; 1802138568Ssam if (ic->ic_sta == NULL) 1803138568Ssam return EINVAL; 1804138568Ssam ni = ieee80211_find_node(ic->ic_sta, txpow.it_macaddr); 1805138568Ssam if (ni == NULL) 1806138568Ssam return EINVAL; /* XXX */ 1807138568Ssam ni->ni_txpower = txpow.it_txpow; 1808138568Ssam ieee80211_free_node(ni); 1809138568Ssam return error; 1810138568Ssam} 1811138568Ssam 1812138568Ssamstatic int 1813138568Ssamieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) 1814138568Ssam{ 1815138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 1816138568Ssam struct wmeParams *wmep, *chanp; 1817138568Ssam int isbss, ac; 1818138568Ssam 1819138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 1820138568Ssam return EINVAL; 1821138568Ssam 1822138568Ssam isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS); 1823138568Ssam ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); 1824138568Ssam if (ac >= WME_NUM_AC) 1825138568Ssam ac = WME_AC_BE; 1826138568Ssam if (isbss) { 1827138568Ssam chanp = &wme->wme_bssChanParams.cap_wmeParams[ac]; 1828138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; 1829138568Ssam } else { 1830138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[ac]; 1831138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; 1832138568Ssam } 1833138568Ssam switch (ireq->i_type) { 1834138568Ssam case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 1835138568Ssam if (isbss) { 1836138568Ssam wmep->wmep_logcwmin = ireq->i_val; 1837138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1838138568Ssam chanp->wmep_logcwmin = ireq->i_val; 1839138568Ssam } else { 1840138568Ssam wmep->wmep_logcwmin = chanp->wmep_logcwmin = 1841138568Ssam ireq->i_val; 1842138568Ssam } 1843138568Ssam break; 1844138568Ssam case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 1845138568Ssam if (isbss) { 1846138568Ssam wmep->wmep_logcwmax = ireq->i_val; 1847138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1848138568Ssam chanp->wmep_logcwmax = ireq->i_val; 1849138568Ssam } else { 1850138568Ssam wmep->wmep_logcwmax = chanp->wmep_logcwmax = 1851138568Ssam ireq->i_val; 1852138568Ssam } 1853138568Ssam break; 1854138568Ssam case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 1855138568Ssam if (isbss) { 1856138568Ssam wmep->wmep_aifsn = ireq->i_val; 1857138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1858138568Ssam chanp->wmep_aifsn = ireq->i_val; 1859138568Ssam } else { 1860138568Ssam wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val; 1861138568Ssam } 1862138568Ssam break; 1863138568Ssam case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 1864138568Ssam if (isbss) { 1865138568Ssam wmep->wmep_txopLimit = ireq->i_val; 1866138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1867138568Ssam chanp->wmep_txopLimit = ireq->i_val; 1868138568Ssam } else { 1869138568Ssam wmep->wmep_txopLimit = chanp->wmep_txopLimit = 1870138568Ssam ireq->i_val; 1871138568Ssam } 1872138568Ssam break; 1873138568Ssam case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 1874138568Ssam wmep->wmep_acm = ireq->i_val; 1875138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1876138568Ssam chanp->wmep_acm = ireq->i_val; 1877138568Ssam break; 1878138568Ssam case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ 1879138568Ssam wmep->wmep_noackPolicy = chanp->wmep_noackPolicy = 1880138568Ssam (ireq->i_val) == 0; 1881138568Ssam break; 1882138568Ssam } 1883138568Ssam ieee80211_wme_updateparams(ic); 1884138568Ssam return 0; 1885138568Ssam} 1886138568Ssam 1887138568Ssamstatic int 1888138568Ssamcipher2cap(int cipher) 1889138568Ssam{ 1890138568Ssam switch (cipher) { 1891138568Ssam case IEEE80211_CIPHER_WEP: return IEEE80211_C_WEP; 1892138568Ssam case IEEE80211_CIPHER_AES_OCB: return IEEE80211_C_AES; 1893138568Ssam case IEEE80211_CIPHER_AES_CCM: return IEEE80211_C_AES_CCM; 1894138568Ssam case IEEE80211_CIPHER_CKIP: return IEEE80211_C_CKIP; 1895138568Ssam case IEEE80211_CIPHER_TKIP: return IEEE80211_C_TKIP; 1896138568Ssam } 1897138568Ssam return 0; 1898138568Ssam} 1899138568Ssam 1900138568Ssamstatic int 1901138568Ssamieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) 1902138568Ssam{ 1903138568Ssam static const u_int8_t zerobssid[IEEE80211_ADDR_LEN]; 1904138568Ssam struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; 1905138568Ssam int error; 1906138568Ssam const struct ieee80211_authenticator *auth; 1907138568Ssam u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; 1908138568Ssam char tmpssid[IEEE80211_NWID_LEN]; 1909138568Ssam u_int8_t tmpbssid[IEEE80211_ADDR_LEN]; 1910138568Ssam struct ieee80211_key *k; 1911138568Ssam int j, caps; 1912138568Ssam u_int kid; 1913138568Ssam 1914138568Ssam error = 0; 1915138568Ssam switch (ireq->i_type) { 1916138568Ssam case IEEE80211_IOC_SSID: 1917138568Ssam if (ireq->i_val != 0 || 1918138568Ssam ireq->i_len > IEEE80211_NWID_LEN) 1919138568Ssam return EINVAL; 1920138568Ssam error = copyin(ireq->i_data, tmpssid, ireq->i_len); 1921138568Ssam if (error) 1922116742Ssam break; 1923138568Ssam memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); 1924138568Ssam ic->ic_des_esslen = ireq->i_len; 1925138568Ssam memcpy(ic->ic_des_essid, tmpssid, ireq->i_len); 1926138568Ssam error = ENETRESET; 1927138568Ssam break; 1928138568Ssam case IEEE80211_IOC_WEP: 1929138568Ssam switch (ireq->i_val) { 1930138568Ssam case IEEE80211_WEP_OFF: 1931138568Ssam ic->ic_flags &= ~IEEE80211_F_PRIVACY; 1932138568Ssam ic->ic_flags &= ~IEEE80211_F_DROPUNENC; 1933116742Ssam break; 1934138568Ssam case IEEE80211_WEP_ON: 1935138568Ssam ic->ic_flags |= IEEE80211_F_PRIVACY; 1936138568Ssam ic->ic_flags |= IEEE80211_F_DROPUNENC; 1937116742Ssam break; 1938138568Ssam case IEEE80211_WEP_MIXED: 1939138568Ssam ic->ic_flags |= IEEE80211_F_PRIVACY; 1940138568Ssam ic->ic_flags &= ~IEEE80211_F_DROPUNENC; 1941116742Ssam break; 1942138568Ssam } 1943138568Ssam error = ENETRESET; 1944138568Ssam break; 1945138568Ssam case IEEE80211_IOC_WEPKEY: 1946138568Ssam kid = (u_int) ireq->i_val; 1947138568Ssam if (kid >= IEEE80211_WEP_NKID) 1948138568Ssam return EINVAL; 1949138568Ssam k = &ic->ic_nw_keys[kid]; 1950138568Ssam if (ireq->i_len == 0) { 1951138568Ssam /* zero-len =>'s delete any existing key */ 1952138568Ssam (void) ieee80211_crypto_delkey(ic, k); 1953127648Ssam break; 1954138568Ssam } 1955138568Ssam if (ireq->i_len > sizeof(tmpkey)) 1956138568Ssam return EINVAL; 1957138568Ssam memset(tmpkey, 0, sizeof(tmpkey)); 1958138568Ssam error = copyin(ireq->i_data, tmpkey, ireq->i_len); 1959138568Ssam if (error) 1960138568Ssam break; 1961138568Ssam ieee80211_key_update_begin(ic); 1962138568Ssam if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP, k)) { 1963138568Ssam k->wk_keylen = ireq->i_len; 1964138568Ssam k->wk_flags |= 1965138568Ssam IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; 1966138568Ssam memcpy(k->wk_key, tmpkey, sizeof(tmpkey)); 1967138568Ssam if (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr)) 1968127648Ssam error = EINVAL; 1969138568Ssam } else 1970138568Ssam error = EINVAL; 1971138568Ssam ieee80211_key_update_end(ic); 1972138568Ssam break; 1973138568Ssam case IEEE80211_IOC_WEPTXKEY: 1974138568Ssam kid = (u_int) ireq->i_val; 1975138568Ssam if (kid >= IEEE80211_WEP_NKID) 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