ieee80211_ioctl.c revision 170530
1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3170360Ssam * Copyright (c) 2002-2007 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. 14116742Ssam * 15116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25116742Ssam */ 26116742Ssam 27116742Ssam#include <sys/cdefs.h> 28116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ioctl.c 170530 2007-06-11 03:36:55Z sam $"); 29116742Ssam 30162659Sru#include "opt_compat.h" 31162659Sru 32116742Ssam/* 33116742Ssam * IEEE 802.11 ioctl support (FreeBSD-specific) 34116742Ssam */ 35116742Ssam 36127646Ssam#include "opt_inet.h" 37127646Ssam#include "opt_ipx.h" 38127646Ssam 39116742Ssam#include <sys/endian.h> 40116742Ssam#include <sys/param.h> 41116742Ssam#include <sys/kernel.h> 42164033Srwatson#include <sys/priv.h> 43116742Ssam#include <sys/socket.h> 44116742Ssam#include <sys/sockio.h> 45116742Ssam#include <sys/systm.h> 46116742Ssam 47116742Ssam#include <net/if.h> 48152315Sru#include <net/if_dl.h> 49116742Ssam#include <net/if_media.h> 50116742Ssam#include <net/ethernet.h> 51116742Ssam 52127646Ssam#ifdef INET 53127646Ssam#include <netinet/in.h> 54127646Ssam#include <netinet/if_ether.h> 55127646Ssam#endif 56127646Ssam 57127646Ssam#ifdef IPX 58127646Ssam#include <netipx/ipx.h> 59127646Ssam#include <netipx/ipx_if.h> 60127646Ssam#endif 61127646Ssam 62116742Ssam#include <net80211/ieee80211_var.h> 63116742Ssam#include <net80211/ieee80211_ioctl.h> 64116742Ssam 65138568Ssam#define IS_UP(_ic) \ 66148887Srwatson (((_ic)->ic_ifp->if_flags & IFF_UP) && \ 67148887Srwatson ((_ic)->ic_ifp->if_drv_flags & IFF_DRV_RUNNING)) 68138568Ssam#define IS_UP_AUTO(_ic) \ 69138568Ssam (IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO) 70170530Ssam#define RESCAN 1 71138568Ssam 72170530Ssamstatic struct ieee80211_channel *findchannel(struct ieee80211com *, 73170530Ssam int ieee, int mode); 74116742Ssam 75116742Ssamstatic int 76138568Ssamcap2cipher(int flag) 77138568Ssam{ 78138568Ssam switch (flag) { 79138568Ssam case IEEE80211_C_WEP: return IEEE80211_CIPHER_WEP; 80138568Ssam case IEEE80211_C_AES: return IEEE80211_CIPHER_AES_OCB; 81138568Ssam case IEEE80211_C_AES_CCM: return IEEE80211_CIPHER_AES_CCM; 82138568Ssam case IEEE80211_C_CKIP: return IEEE80211_CIPHER_CKIP; 83138568Ssam case IEEE80211_C_TKIP: return IEEE80211_CIPHER_TKIP; 84138568Ssam } 85138568Ssam return -1; 86138568Ssam} 87138568Ssam 88138568Ssamstatic int 89138568Ssamieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq) 90138568Ssam{ 91138568Ssam struct ieee80211_node *ni; 92138568Ssam struct ieee80211req_key ik; 93138568Ssam struct ieee80211_key *wk; 94138568Ssam const struct ieee80211_cipher *cip; 95138568Ssam u_int kid; 96138568Ssam int error; 97138568Ssam 98138568Ssam if (ireq->i_len != sizeof(ik)) 99138568Ssam return EINVAL; 100138568Ssam error = copyin(ireq->i_data, &ik, sizeof(ik)); 101138568Ssam if (error) 102138568Ssam return error; 103138568Ssam kid = ik.ik_keyix; 104138568Ssam if (kid == IEEE80211_KEYIX_NONE) { 105140753Ssam ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr); 106138568Ssam if (ni == NULL) 107138568Ssam return EINVAL; /* XXX */ 108138568Ssam wk = &ni->ni_ucastkey; 109138568Ssam } else { 110138568Ssam if (kid >= IEEE80211_WEP_NKID) 111138568Ssam return EINVAL; 112138568Ssam wk = &ic->ic_nw_keys[kid]; 113138568Ssam IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr); 114138568Ssam ni = NULL; 115138568Ssam } 116138568Ssam cip = wk->wk_cipher; 117138568Ssam ik.ik_type = cip->ic_cipher; 118138568Ssam ik.ik_keylen = wk->wk_keylen; 119138568Ssam ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV); 120138568Ssam if (wk->wk_keyix == ic->ic_def_txkey) 121138568Ssam ik.ik_flags |= IEEE80211_KEY_DEFAULT; 122164033Srwatson if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) { 123138568Ssam /* NB: only root can read key data */ 124138568Ssam ik.ik_keyrsc = wk->wk_keyrsc; 125138568Ssam ik.ik_keytsc = wk->wk_keytsc; 126138568Ssam memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen); 127138568Ssam if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) { 128138568Ssam memcpy(ik.ik_keydata+wk->wk_keylen, 129138568Ssam wk->wk_key + IEEE80211_KEYBUF_SIZE, 130138568Ssam IEEE80211_MICBUF_SIZE); 131138568Ssam ik.ik_keylen += IEEE80211_MICBUF_SIZE; 132138568Ssam } 133138568Ssam } else { 134138568Ssam ik.ik_keyrsc = 0; 135138568Ssam ik.ik_keytsc = 0; 136138568Ssam memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata)); 137138568Ssam } 138138568Ssam if (ni != NULL) 139138568Ssam ieee80211_free_node(ni); 140138568Ssam return copyout(&ik, ireq->i_data, sizeof(ik)); 141138568Ssam} 142138568Ssam 143138568Ssamstatic int 144138568Ssamieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) 145138568Ssam{ 146138568Ssam 147153344Ssam if (sizeof(ic->ic_chan_active) < ireq->i_len) 148138568Ssam ireq->i_len = sizeof(ic->ic_chan_active); 149138568Ssam return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len); 150138568Ssam} 151138568Ssam 152138568Ssamstatic int 153138568Ssamieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq) 154138568Ssam{ 155170530Ssam int space; 156138568Ssam 157138568Ssam space = __offsetof(struct ieee80211req_chaninfo, 158170530Ssam ic_chans[ic->ic_nchans]); 159138568Ssam if (space > ireq->i_len) 160138568Ssam space = ireq->i_len; 161170530Ssam /* XXX assumes compatible layout */ 162170530Ssam return copyout(&ic->ic_nchans, ireq->i_data, space); 163138568Ssam} 164138568Ssam 165138568Ssamstatic int 166170530Ssamieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int req) 167138568Ssam{ 168138568Ssam struct ieee80211_node *ni; 169170530Ssam struct ieee80211req_wpaie2 wpaie; 170138568Ssam int error; 171138568Ssam 172138568Ssam if (ireq->i_len < IEEE80211_ADDR_LEN) 173138568Ssam return EINVAL; 174138568Ssam error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN); 175138568Ssam if (error != 0) 176138568Ssam return error; 177140753Ssam ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr); 178138568Ssam if (ni == NULL) 179170530Ssam return ENOENT; /* XXX */ 180138568Ssam memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie)); 181138568Ssam if (ni->ni_wpa_ie != NULL) { 182138568Ssam int ielen = ni->ni_wpa_ie[1] + 2; 183138568Ssam if (ielen > sizeof(wpaie.wpa_ie)) 184138568Ssam ielen = sizeof(wpaie.wpa_ie); 185138568Ssam memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen); 186138568Ssam } 187170530Ssam if (req == IEEE80211_IOC_WPAIE2) { 188170530Ssam memset(wpaie.rsn_ie, 0, sizeof(wpaie.rsn_ie)); 189170530Ssam if (ni->ni_rsn_ie != NULL) { 190170530Ssam int ielen = ni->ni_rsn_ie[1] + 2; 191170530Ssam if (ielen > sizeof(wpaie.rsn_ie)) 192170530Ssam ielen = sizeof(wpaie.rsn_ie); 193170530Ssam memcpy(wpaie.rsn_ie, ni->ni_rsn_ie, ielen); 194170530Ssam } 195170530Ssam if (ireq->i_len > sizeof(struct ieee80211req_wpaie2)) 196170530Ssam ireq->i_len = sizeof(struct ieee80211req_wpaie2); 197170530Ssam } else { 198170530Ssam /* compatibility op, may overwrite wpa ie */ 199170530Ssam /* XXX check ic_flags? */ 200170530Ssam if (ni->ni_rsn_ie != NULL) { 201170530Ssam int ielen = ni->ni_rsn_ie[1] + 2; 202170530Ssam if (ielen > sizeof(wpaie.wpa_ie)) 203170530Ssam ielen = sizeof(wpaie.wpa_ie); 204170530Ssam memcpy(wpaie.wpa_ie, ni->ni_rsn_ie, ielen); 205170530Ssam } 206170530Ssam if (ireq->i_len > sizeof(struct ieee80211req_wpaie)) 207170530Ssam ireq->i_len = sizeof(struct ieee80211req_wpaie); 208170530Ssam } 209138568Ssam ieee80211_free_node(ni); 210138568Ssam return copyout(&wpaie, ireq->i_data, ireq->i_len); 211138568Ssam} 212138568Ssam 213138568Ssamstatic int 214138568Ssamieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq) 215138568Ssam{ 216138568Ssam struct ieee80211_node *ni; 217170530Ssam uint8_t macaddr[IEEE80211_ADDR_LEN]; 218138568Ssam const int off = __offsetof(struct ieee80211req_sta_stats, is_stats); 219138568Ssam int error; 220138568Ssam 221138568Ssam if (ireq->i_len < off) 222138568Ssam return EINVAL; 223138568Ssam error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); 224138568Ssam if (error != 0) 225138568Ssam return error; 226140753Ssam ni = ieee80211_find_node(&ic->ic_sta, macaddr); 227170530Ssam if (ni == NULL) 228170530Ssam return EINVAL; 229138568Ssam if (ireq->i_len > sizeof(struct ieee80211req_sta_stats)) 230138568Ssam ireq->i_len = sizeof(struct ieee80211req_sta_stats); 231138568Ssam /* NB: copy out only the statistics */ 232170530Ssam error = copyout(&ni->ni_stats, (uint8_t *) ireq->i_data + off, 233138568Ssam ireq->i_len - off); 234138568Ssam ieee80211_free_node(ni); 235138568Ssam return error; 236138568Ssam} 237138568Ssam 238170530Ssamstatic __inline uint8_t * 239170530Ssamcopyie(uint8_t *cp, const uint8_t *ie) 240170530Ssam{ 241170530Ssam if (ie != NULL) { 242170530Ssam memcpy(cp, ie, 2+ie[1]); 243170530Ssam cp += 2+ie[1]; 244170530Ssam } 245170530Ssam return cp; 246170530Ssam} 247170530Ssam 248154541Ssam#ifdef COMPAT_FREEBSD6 249154541Ssam#define IEEE80211_IOC_SCAN_RESULTS_OLD 24 250154541Ssam 251154541Ssamstruct scan_result_old { 252170530Ssam uint16_t isr_len; /* length (mult of 4) */ 253170530Ssam uint16_t isr_freq; /* MHz */ 254170530Ssam uint16_t isr_flags; /* channel flags */ 255170530Ssam uint8_t isr_noise; 256170530Ssam uint8_t isr_rssi; 257170530Ssam uint8_t isr_intval; /* beacon interval */ 258170530Ssam uint8_t isr_capinfo; /* capabilities */ 259170530Ssam uint8_t isr_erp; /* ERP element */ 260170530Ssam uint8_t isr_bssid[IEEE80211_ADDR_LEN]; 261170530Ssam uint8_t isr_nrates; 262170530Ssam uint8_t isr_rates[IEEE80211_RATE_MAXSIZE]; 263170530Ssam uint8_t isr_ssid_len; /* SSID length */ 264170530Ssam uint8_t isr_ie_len; /* IE length */ 265170530Ssam uint8_t isr_pad[5]; 266154541Ssam /* variable length SSID followed by IE data */ 267154541Ssam}; 268154541Ssam 269170530Ssamstruct oscanreq { 270170530Ssam struct scan_result_old *sr; 271170530Ssam size_t space; 272170530Ssam}; 273170530Ssam 274170530Ssamstatic size_t 275170530Ssamold_scan_space(const struct ieee80211_scan_entry *se, int *ielen) 276170530Ssam{ 277170530Ssam size_t len; 278170530Ssam 279170530Ssam *ielen = 0; 280170530Ssam if (se->se_wpa_ie != NULL) 281170530Ssam *ielen += 2+se->se_wpa_ie[1]; 282170530Ssam if (se->se_wme_ie != NULL) 283170530Ssam *ielen += 2+se->se_wme_ie[1]; 284170530Ssam /* 285170530Ssam * NB: ie's can be no more than 255 bytes and the max 802.11 286170530Ssam * packet is <3Kbytes so we are sure this doesn't overflow 287170530Ssam * 16-bits; if this is a concern we can drop the ie's. 288170530Ssam */ 289170530Ssam len = sizeof(struct scan_result_old) + se->se_ssid[1] + *ielen; 290170530Ssam return roundup(len, sizeof(uint32_t)); 291170530Ssam} 292170530Ssam 293138568Ssamstatic void 294170530Ssamold_get_scan_space(void *arg, const struct ieee80211_scan_entry *se) 295138568Ssam{ 296170530Ssam struct oscanreq *req = arg; 297170530Ssam int ielen; 298138568Ssam 299170530Ssam req->space += old_scan_space(se, &ielen); 300170530Ssam} 301170530Ssam 302170530Ssamstatic void 303170530Ssamold_get_scan_result(void *arg, const struct ieee80211_scan_entry *se) 304170530Ssam{ 305170530Ssam struct oscanreq *req = arg; 306170530Ssam struct scan_result_old *sr; 307170530Ssam int ielen, len, nr, nxr; 308170530Ssam uint8_t *cp; 309170530Ssam 310170530Ssam len = old_scan_space(se, &ielen); 311170530Ssam if (len > req->space) 312170530Ssam return; 313170530Ssam 314170530Ssam sr = req->sr; 315138568Ssam memset(sr, 0, sizeof(*sr)); 316170530Ssam sr->isr_ssid_len = se->se_ssid[1]; 317154541Ssam /* NB: beware of overflow, isr_ie_len is 8 bits */ 318154541Ssam sr->isr_ie_len = (ielen > 255 ? 0 : ielen); 319170530Ssam sr->isr_len = len; 320170530Ssam sr->isr_freq = se->se_chan->ic_freq; 321170530Ssam sr->isr_flags = se->se_chan->ic_flags; 322170530Ssam sr->isr_rssi = se->se_rssi; 323170530Ssam sr->isr_noise = se->se_noise; 324170530Ssam sr->isr_intval = se->se_intval; 325170530Ssam sr->isr_capinfo = se->se_capinfo; 326170530Ssam sr->isr_erp = se->se_erp; 327170530Ssam IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid); 328170530Ssam nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE); 329170530Ssam memcpy(sr->isr_rates, se->se_rates+2, nr); 330170530Ssam nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr); 331170530Ssam memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr); 332170530Ssam sr->isr_nrates = nr + nxr; 333170530Ssam 334170530Ssam cp = (uint8_t *)(sr+1); 335170530Ssam memcpy(cp, se->se_ssid+2, sr->isr_ssid_len); 336170530Ssam cp += sr->isr_ssid_len; 337170530Ssam if (sr->isr_ie_len) { 338170530Ssam cp = copyie(cp, se->se_wpa_ie); 339170530Ssam cp = copyie(cp, se->se_wme_ie); 340138568Ssam } 341170530Ssam 342170530Ssam req->space -= len; 343170530Ssam req->sr = (struct scan_result_old *)(((uint8_t *)sr) + len); 344138568Ssam} 345138568Ssam 346138568Ssamstatic int 347154541Ssamold_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) 348138568Ssam{ 349170530Ssam struct oscanreq req; 350170530Ssam int error; 351138568Ssam 352170530Ssam if (ireq->i_len < sizeof(struct scan_result_old)) 353170530Ssam return EFAULT; 354170530Ssam 355138568Ssam error = 0; 356170530Ssam req.space = 0; 357170530Ssam ieee80211_scan_iterate(ic, old_get_scan_space, &req); 358170530Ssam if (req.space > ireq->i_len) 359170530Ssam req.space = ireq->i_len; 360170530Ssam if (req.space > 0) { 361170530Ssam size_t space; 362170530Ssam void *p; 363170530Ssam 364170530Ssam space = req.space; 365170530Ssam /* XXX M_WAITOK after driver lock released */ 366170530Ssam MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO); 367170530Ssam if (p == NULL) 368170530Ssam return ENOMEM; 369170530Ssam req.sr = p; 370170530Ssam ieee80211_scan_iterate(ic, old_get_scan_result, &req); 371170530Ssam ireq->i_len = space - req.space; 372170530Ssam error = copyout(p, ireq->i_data, ireq->i_len); 373170530Ssam FREE(p, M_TEMP); 374170530Ssam } else 375170530Ssam ireq->i_len = 0; 376170530Ssam 377138568Ssam return error; 378138568Ssam} 379154541Ssam#endif /* COMPAT_FREEBSD6 */ 380138568Ssam 381170530Ssamstruct scanreq { 382154541Ssam struct ieee80211req_scan_result *sr; 383170530Ssam size_t space; 384154541Ssam}; 385154541Ssam 386154541Ssamstatic size_t 387170530Ssamscan_space(const struct ieee80211_scan_entry *se, int *ielen) 388154541Ssam{ 389154541Ssam size_t len; 390154541Ssam 391154541Ssam *ielen = 0; 392170530Ssam if (se->se_wpa_ie != NULL) 393170530Ssam *ielen += 2+se->se_wpa_ie[1]; 394170530Ssam if (se->se_rsn_ie != NULL) 395170530Ssam *ielen += 2+se->se_rsn_ie[1]; 396170530Ssam if (se->se_wme_ie != NULL) 397170530Ssam *ielen += 2+se->se_wme_ie[1]; 398170530Ssam if (se->se_ath_ie != NULL) 399170530Ssam *ielen += 2+se->se_ath_ie[1]; 400154541Ssam /* 401154541Ssam * NB: ie's can be no more than 255 bytes and the max 802.11 402154541Ssam * packet is <3Kbytes so we are sure this doesn't overflow 403154541Ssam * 16-bits; if this is a concern we can drop the ie's. 404154541Ssam */ 405170530Ssam len = sizeof(struct ieee80211req_scan_result) + se->se_ssid[1] + *ielen; 406170530Ssam return roundup(len, sizeof(uint32_t)); 407154541Ssam} 408154541Ssam 409154541Ssamstatic void 410170530Ssamget_scan_space(void *arg, const struct ieee80211_scan_entry *se) 411154541Ssam{ 412170530Ssam struct scanreq *req = arg; 413170530Ssam int ielen; 414154541Ssam 415170530Ssam req->space += scan_space(se, &ielen); 416154541Ssam} 417154541Ssam 418154541Ssamstatic void 419170530Ssamget_scan_result(void *arg, const struct ieee80211_scan_entry *se) 420154541Ssam{ 421170530Ssam struct scanreq *req = arg; 422154541Ssam struct ieee80211req_scan_result *sr; 423170530Ssam int ielen, len, nr, nxr; 424170530Ssam uint8_t *cp; 425154541Ssam 426170530Ssam len = scan_space(se, &ielen); 427154541Ssam if (len > req->space) 428154541Ssam return; 429170530Ssam 430154541Ssam sr = req->sr; 431154541Ssam KASSERT(len <= 65535 && ielen <= 65535, 432170530Ssam ("len %u ssid %u ie %u", len, se->se_ssid[1], ielen)); 433170530Ssam sr->isr_ie_off = sizeof(struct ieee80211req_scan_result); 434170530Ssam sr->isr_ie_len = ielen; 435154541Ssam sr->isr_len = len; 436170530Ssam sr->isr_freq = se->se_chan->ic_freq; 437170530Ssam sr->isr_flags = se->se_chan->ic_flags; 438170530Ssam sr->isr_rssi = se->se_rssi; 439170530Ssam sr->isr_noise = se->se_noise; 440170530Ssam sr->isr_intval = se->se_intval; 441170530Ssam sr->isr_capinfo = se->se_capinfo; 442170530Ssam sr->isr_erp = se->se_erp; 443170530Ssam IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid); 444170530Ssam nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE); 445170530Ssam memcpy(sr->isr_rates, se->se_rates+2, nr); 446170530Ssam nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr); 447170530Ssam memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr); 448170530Ssam sr->isr_nrates = nr + nxr; 449170530Ssam 450170530Ssam sr->isr_ssid_len = se->se_ssid[1]; 451170530Ssam cp = ((uint8_t *)sr) + sr->isr_ie_off; 452170530Ssam memcpy(cp, se->se_ssid+2, sr->isr_ssid_len); 453170530Ssam 454170530Ssam if (ielen) { 455170530Ssam cp += sr->isr_ssid_len; 456170530Ssam cp = copyie(cp, se->se_wpa_ie); 457170530Ssam cp = copyie(cp, se->se_rsn_ie); 458170530Ssam cp = copyie(cp, se->se_wme_ie); 459170530Ssam cp = copyie(cp, se->se_ath_ie); 460170530Ssam cp = copyie(cp, se->se_htcap_ie); 461154541Ssam } 462154541Ssam 463154541Ssam req->space -= len; 464170530Ssam req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len); 465154541Ssam} 466154541Ssam 467154541Ssamstatic int 468154541Ssamieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) 469154541Ssam{ 470170530Ssam struct scanreq req; 471154541Ssam int error; 472154541Ssam 473167245Ssam if (ireq->i_len < sizeof(struct ieee80211req_scan_result)) 474154541Ssam return EFAULT; 475154541Ssam 476154541Ssam error = 0; 477154541Ssam req.space = 0; 478170530Ssam ieee80211_scan_iterate(ic, get_scan_space, &req); 479154541Ssam if (req.space > ireq->i_len) 480154541Ssam req.space = ireq->i_len; 481154541Ssam if (req.space > 0) { 482154541Ssam size_t space; 483154541Ssam void *p; 484154541Ssam 485154541Ssam space = req.space; 486154541Ssam /* XXX M_WAITOK after driver lock released */ 487154541Ssam MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO); 488154541Ssam if (p == NULL) 489154541Ssam return ENOMEM; 490154541Ssam req.sr = p; 491170530Ssam ieee80211_scan_iterate(ic, get_scan_result, &req); 492154541Ssam ireq->i_len = space - req.space; 493154541Ssam error = copyout(p, ireq->i_data, ireq->i_len); 494154541Ssam FREE(p, M_TEMP); 495154541Ssam } else 496154541Ssam ireq->i_len = 0; 497154541Ssam 498154541Ssam return error; 499154541Ssam} 500154541Ssam 501148845Ssamstruct stainforeq { 502148845Ssam struct ieee80211com *ic; 503148845Ssam struct ieee80211req_sta_info *si; 504148845Ssam size_t space; 505148845Ssam}; 506148845Ssam 507148845Ssamstatic size_t 508148845Ssamsta_space(const struct ieee80211_node *ni, size_t *ielen) 509148845Ssam{ 510148845Ssam *ielen = 0; 511148845Ssam if (ni->ni_wpa_ie != NULL) 512148845Ssam *ielen += 2+ni->ni_wpa_ie[1]; 513170530Ssam if (ni->ni_rsn_ie != NULL) 514170530Ssam *ielen += 2+ni->ni_rsn_ie[1]; 515148845Ssam if (ni->ni_wme_ie != NULL) 516148845Ssam *ielen += 2+ni->ni_wme_ie[1]; 517170530Ssam if (ni->ni_ath_ie != NULL) 518170530Ssam *ielen += 2+ni->ni_ath_ie[1]; 519148845Ssam return roundup(sizeof(struct ieee80211req_sta_info) + *ielen, 520170530Ssam sizeof(uint32_t)); 521148845Ssam} 522148845Ssam 523138568Ssamstatic void 524148845Ssamget_sta_space(void *arg, struct ieee80211_node *ni) 525138568Ssam{ 526148845Ssam struct stainforeq *req = arg; 527138568Ssam struct ieee80211com *ic = ni->ni_ic; 528148845Ssam size_t ielen; 529138568Ssam 530148845Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && 531148845Ssam ni->ni_associd == 0) /* only associated stations */ 532148845Ssam return; 533148845Ssam req->space += sta_space(ni, &ielen); 534148845Ssam} 535148845Ssam 536148845Ssamstatic void 537148845Ssamget_sta_info(void *arg, struct ieee80211_node *ni) 538148845Ssam{ 539148845Ssam struct stainforeq *req = arg; 540148845Ssam struct ieee80211com *ic = ni->ni_ic; 541148845Ssam struct ieee80211req_sta_info *si; 542148845Ssam size_t ielen, len; 543170530Ssam uint8_t *cp; 544148845Ssam 545148845Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && 546148845Ssam ni->ni_associd == 0) /* only associated stations */ 547148845Ssam return; 548148845Ssam if (ni->ni_chan == IEEE80211_CHAN_ANYC) /* XXX bogus entry */ 549148845Ssam return; 550148845Ssam len = sta_space(ni, &ielen); 551148845Ssam if (len > req->space) 552148845Ssam return; 553148845Ssam si = req->si; 554148845Ssam si->isi_len = len; 555170530Ssam si->isi_ie_off = sizeof(struct ieee80211req_sta_info); 556148845Ssam si->isi_ie_len = ielen; 557138568Ssam si->isi_freq = ni->ni_chan->ic_freq; 558138568Ssam si->isi_flags = ni->ni_chan->ic_flags; 559138568Ssam si->isi_state = ni->ni_flags; 560138568Ssam si->isi_authmode = ni->ni_authmode; 561170530Ssam ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise); 562161146Ssam si->isi_noise = 0; /* XXX */ 563138568Ssam si->isi_capinfo = ni->ni_capinfo; 564138568Ssam si->isi_erp = ni->ni_erp; 565138568Ssam IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr); 566138568Ssam si->isi_nrates = ni->ni_rates.rs_nrates; 567138568Ssam if (si->isi_nrates > 15) 568138568Ssam si->isi_nrates = 15; 569138568Ssam memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates); 570138568Ssam si->isi_txrate = ni->ni_txrate; 571170530Ssam si->isi_ie_len = ielen; 572138568Ssam si->isi_associd = ni->ni_associd; 573138568Ssam si->isi_txpower = ni->ni_txpower; 574138568Ssam si->isi_vlan = ni->ni_vlan; 575138568Ssam if (ni->ni_flags & IEEE80211_NODE_QOS) { 576138568Ssam memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs)); 577138568Ssam memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs)); 578138568Ssam } else { 579167439Ssam si->isi_txseqs[0] = ni->ni_txseqs[IEEE80211_NONQOS_TID]; 580167439Ssam si->isi_rxseqs[0] = ni->ni_rxseqs[IEEE80211_NONQOS_TID]; 581138568Ssam } 582148845Ssam /* NB: leave all cases in case we relax ni_associd == 0 check */ 583148845Ssam if (ieee80211_node_is_authorized(ni)) 584138568Ssam si->isi_inact = ic->ic_inact_run; 585148845Ssam else if (ni->ni_associd != 0) 586138568Ssam si->isi_inact = ic->ic_inact_auth; 587138568Ssam else 588138568Ssam si->isi_inact = ic->ic_inact_init; 589138568Ssam si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT; 590148845Ssam 591170530Ssam if (ielen) { 592170530Ssam cp = ((uint8_t *)si) + si->isi_ie_off; 593170530Ssam cp = copyie(cp, ni->ni_wpa_ie); 594170530Ssam cp = copyie(cp, ni->ni_rsn_ie); 595170530Ssam cp = copyie(cp, ni->ni_wme_ie); 596170530Ssam cp = copyie(cp, ni->ni_ath_ie); 597148845Ssam } 598148845Ssam 599170530Ssam req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len); 600148845Ssam req->space -= len; 601138568Ssam} 602138568Ssam 603138568Ssamstatic int 604161146Ssamgetstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq, 605161146Ssam struct ieee80211_node *ni, int off) 606138568Ssam{ 607148845Ssam struct stainforeq req; 608161146Ssam size_t space; 609161146Ssam void *p; 610148845Ssam int error; 611138568Ssam 612138568Ssam error = 0; 613148845Ssam req.space = 0; 614161146Ssam if (ni == NULL) 615161146Ssam ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req); 616161146Ssam else 617161146Ssam get_sta_space(&req, ni); 618148845Ssam if (req.space > ireq->i_len) 619148845Ssam req.space = ireq->i_len; 620148845Ssam if (req.space > 0) { 621148845Ssam space = req.space; 622148845Ssam /* XXX M_WAITOK after driver lock released */ 623148845Ssam MALLOC(p, void *, space, M_TEMP, M_NOWAIT); 624161146Ssam if (p == NULL) { 625161146Ssam error = ENOMEM; 626161146Ssam goto bad; 627161146Ssam } 628148845Ssam req.si = p; 629161146Ssam if (ni == NULL) 630161146Ssam ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req); 631161146Ssam else 632161146Ssam get_sta_info(&req, ni); 633148845Ssam ireq->i_len = space - req.space; 634170530Ssam error = copyout(p, (uint8_t *) ireq->i_data+off, ireq->i_len); 635148845Ssam FREE(p, M_TEMP); 636148845Ssam } else 637148845Ssam ireq->i_len = 0; 638161146Ssambad: 639161146Ssam if (ni != NULL) 640161146Ssam ieee80211_free_node(ni); 641138568Ssam return error; 642138568Ssam} 643138568Ssam 644138568Ssamstatic int 645161146Ssamieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) 646161146Ssam{ 647170530Ssam uint8_t macaddr[IEEE80211_ADDR_LEN]; 648161146Ssam const int off = __offsetof(struct ieee80211req_sta_req, info); 649161146Ssam struct ieee80211_node *ni; 650161146Ssam int error; 651161146Ssam 652161146Ssam if (ireq->i_len < sizeof(struct ieee80211req_sta_req)) 653161146Ssam return EFAULT; 654161146Ssam error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); 655161146Ssam if (error != 0) 656161146Ssam return error; 657161146Ssam if (IEEE80211_ADDR_EQ(macaddr, ic->ic_ifp->if_broadcastaddr)) { 658161146Ssam ni = NULL; 659161146Ssam } else { 660161146Ssam ni = ieee80211_find_node(&ic->ic_sta, macaddr); 661170530Ssam if (ni == NULL) 662170530Ssam return EINVAL; 663161146Ssam } 664161146Ssam return getstainfo_common(ic, ireq, ni, off); 665161146Ssam} 666161146Ssam 667161146Ssam#ifdef COMPAT_FREEBSD6 668161146Ssam#define IEEE80211_IOC_STA_INFO_OLD 45 669161146Ssam 670161146Ssamstatic int 671161146Ssamold_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) 672161146Ssam{ 673161146Ssam if (ireq->i_len < sizeof(struct ieee80211req_sta_info)) 674161146Ssam return EFAULT; 675161146Ssam return getstainfo_common(ic, ireq, NULL, 0); 676161146Ssam} 677161146Ssam#endif /* COMPAT_FREEBSD6 */ 678161146Ssam 679161146Ssamstatic int 680138568Ssamieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) 681138568Ssam{ 682138568Ssam struct ieee80211_node *ni; 683138568Ssam struct ieee80211req_sta_txpow txpow; 684138568Ssam int error; 685138568Ssam 686138568Ssam if (ireq->i_len != sizeof(txpow)) 687138568Ssam return EINVAL; 688138568Ssam error = copyin(ireq->i_data, &txpow, sizeof(txpow)); 689138568Ssam if (error != 0) 690138568Ssam return error; 691140753Ssam ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr); 692138568Ssam if (ni == NULL) 693138568Ssam return EINVAL; /* XXX */ 694138568Ssam txpow.it_txpow = ni->ni_txpower; 695138568Ssam error = copyout(&txpow, ireq->i_data, sizeof(txpow)); 696138568Ssam ieee80211_free_node(ni); 697138568Ssam return error; 698138568Ssam} 699138568Ssam 700138568Ssamstatic int 701138568Ssamieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) 702138568Ssam{ 703138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 704138568Ssam struct wmeParams *wmep; 705138568Ssam int ac; 706138568Ssam 707138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 708138568Ssam return EINVAL; 709138568Ssam 710138568Ssam ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); 711138568Ssam if (ac >= WME_NUM_AC) 712138568Ssam ac = WME_AC_BE; 713138568Ssam if (ireq->i_len & IEEE80211_WMEPARAM_BSS) 714138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; 715138568Ssam else 716138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; 717138568Ssam switch (ireq->i_type) { 718138568Ssam case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 719138568Ssam ireq->i_val = wmep->wmep_logcwmin; 720138568Ssam break; 721138568Ssam case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 722138568Ssam ireq->i_val = wmep->wmep_logcwmax; 723138568Ssam break; 724138568Ssam case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 725138568Ssam ireq->i_val = wmep->wmep_aifsn; 726138568Ssam break; 727138568Ssam case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 728138568Ssam ireq->i_val = wmep->wmep_txopLimit; 729138568Ssam break; 730138568Ssam case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 731138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; 732138568Ssam ireq->i_val = wmep->wmep_acm; 733138568Ssam break; 734138568Ssam case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ 735138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; 736138568Ssam ireq->i_val = !wmep->wmep_noackPolicy; 737138568Ssam break; 738138568Ssam } 739138568Ssam return 0; 740138568Ssam} 741138568Ssam 742149028Ssamstatic int 743149028Ssamieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq) 744149028Ssam{ 745149028Ssam const struct ieee80211_aclator *acl = ic->ic_acl; 746149028Ssam 747149028Ssam return (acl == NULL ? EINVAL : acl->iac_getioctl(ic, ireq)); 748149028Ssam} 749149028Ssam 750143110Swpaul/* 751170530Ssam * Return the current ``state'' of an Atheros capbility. 752170530Ssam * If associated in station mode report the negotiated 753170530Ssam * setting. Otherwise report the current setting. 754170530Ssam */ 755170530Ssamstatic int 756170530Ssamgetathcap(struct ieee80211com *ic, int cap) 757170530Ssam{ 758170530Ssam if (ic->ic_opmode == IEEE80211_M_STA && ic->ic_state == IEEE80211_S_RUN) 759170530Ssam return IEEE80211_ATH_CAP(ic, ic->ic_bss, cap) != 0; 760170530Ssam else 761170530Ssam return (ic->ic_flags & cap) != 0; 762170530Ssam} 763170530Ssam 764170530Ssamstatic int 765170530Ssamieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq) 766170530Ssam{ 767170530Ssam if (ireq->i_len != sizeof(struct ieee80211_channel)) 768170530Ssam return EINVAL; 769170530Ssam return copyout(ic->ic_curchan, ireq->i_data, sizeof(*ic->ic_curchan)); 770170530Ssam} 771170530Ssam 772170530Ssam/* 773143110Swpaul * When building the kernel with -O2 on the i386 architecture, gcc 774143110Swpaul * seems to want to inline this function into ieee80211_ioctl() 775143110Swpaul * (which is the only routine that calls it). When this happens, 776143110Swpaul * ieee80211_ioctl() ends up consuming an additional 2K of stack 777143110Swpaul * space. (Exactly why it needs so much is unclear.) The problem 778143110Swpaul * is that it's possible for ieee80211_ioctl() to invoke other 779143110Swpaul * routines (including driver init functions) which could then find 780143110Swpaul * themselves perilously close to exhausting the stack. 781143110Swpaul * 782143110Swpaul * To avoid this, we deliberately prevent gcc from inlining this 783143110Swpaul * routine. Another way to avoid this is to use less agressive 784143110Swpaul * optimization when compiling this file (i.e. -O instead of -O2) 785143110Swpaul * but special-casing the compilation of this one module in the 786143110Swpaul * build system would be awkward. 787143110Swpaul */ 788143110Swpaul#ifdef __GNUC__ 789143110Swpaul__attribute__ ((noinline)) 790143110Swpaul#endif 791138568Ssamstatic int 792138568Ssamieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) 793138568Ssam{ 794138568Ssam const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; 795116742Ssam int error = 0; 796138568Ssam u_int kid, len, m; 797170530Ssam uint8_t tmpkey[IEEE80211_KEYBUF_SIZE]; 798116742Ssam char tmpssid[IEEE80211_NWID_LEN]; 799116742Ssam 800138568Ssam switch (ireq->i_type) { 801138568Ssam case IEEE80211_IOC_SSID: 802138568Ssam switch (ic->ic_state) { 803138568Ssam case IEEE80211_S_INIT: 804138568Ssam case IEEE80211_S_SCAN: 805170530Ssam ireq->i_len = ic->ic_des_ssid[0].len; 806170530Ssam memcpy(tmpssid, ic->ic_des_ssid[0].ssid, ireq->i_len); 807138568Ssam break; 808138568Ssam default: 809138568Ssam ireq->i_len = ic->ic_bss->ni_esslen; 810138568Ssam memcpy(tmpssid, ic->ic_bss->ni_essid, 811138568Ssam ireq->i_len); 812138568Ssam break; 813138568Ssam } 814138568Ssam error = copyout(tmpssid, ireq->i_data, ireq->i_len); 815116742Ssam break; 816138568Ssam case IEEE80211_IOC_NUMSSIDS: 817138568Ssam ireq->i_val = 1; 818138568Ssam break; 819138568Ssam case IEEE80211_IOC_WEP: 820138568Ssam if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) 821138568Ssam ireq->i_val = IEEE80211_WEP_OFF; 822138568Ssam else if (ic->ic_flags & IEEE80211_F_DROPUNENC) 823138568Ssam ireq->i_val = IEEE80211_WEP_ON; 824138568Ssam else 825138568Ssam ireq->i_val = IEEE80211_WEP_MIXED; 826138568Ssam break; 827138568Ssam case IEEE80211_IOC_WEPKEY: 828138568Ssam kid = (u_int) ireq->i_val; 829138568Ssam if (kid >= IEEE80211_WEP_NKID) 830138568Ssam return EINVAL; 831138568Ssam len = (u_int) ic->ic_nw_keys[kid].wk_keylen; 832138568Ssam /* NB: only root can read WEP keys */ 833164033Srwatson if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) { 834138568Ssam bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len); 835138568Ssam } else { 836138568Ssam bzero(tmpkey, len); 837138568Ssam } 838138568Ssam ireq->i_len = len; 839138568Ssam error = copyout(tmpkey, ireq->i_data, len); 840138568Ssam break; 841138568Ssam case IEEE80211_IOC_NUMWEPKEYS: 842138568Ssam ireq->i_val = IEEE80211_WEP_NKID; 843138568Ssam break; 844138568Ssam case IEEE80211_IOC_WEPTXKEY: 845138568Ssam ireq->i_val = ic->ic_def_txkey; 846138568Ssam break; 847138568Ssam case IEEE80211_IOC_AUTHMODE: 848138568Ssam if (ic->ic_flags & IEEE80211_F_WPA) 849138568Ssam ireq->i_val = IEEE80211_AUTH_WPA; 850138568Ssam else 851138568Ssam ireq->i_val = ic->ic_bss->ni_authmode; 852138568Ssam break; 853138568Ssam case IEEE80211_IOC_CHANNEL: 854148936Ssam ireq->i_val = ieee80211_chan2ieee(ic, ic->ic_curchan); 855138568Ssam break; 856138568Ssam case IEEE80211_IOC_POWERSAVE: 857138568Ssam if (ic->ic_flags & IEEE80211_F_PMGTON) 858138568Ssam ireq->i_val = IEEE80211_POWERSAVE_ON; 859138568Ssam else 860138568Ssam ireq->i_val = IEEE80211_POWERSAVE_OFF; 861138568Ssam break; 862138568Ssam case IEEE80211_IOC_POWERSAVESLEEP: 863138568Ssam ireq->i_val = ic->ic_lintval; 864138568Ssam break; 865138568Ssam case IEEE80211_IOC_RTSTHRESHOLD: 866138568Ssam ireq->i_val = ic->ic_rtsthreshold; 867138568Ssam break; 868138568Ssam case IEEE80211_IOC_PROTMODE: 869138568Ssam ireq->i_val = ic->ic_protmode; 870138568Ssam break; 871138568Ssam case IEEE80211_IOC_TXPOWER: 872138568Ssam if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) 873138568Ssam return EINVAL; 874138568Ssam ireq->i_val = ic->ic_txpowlimit; 875138568Ssam break; 876138568Ssam case IEEE80211_IOC_MCASTCIPHER: 877138568Ssam ireq->i_val = rsn->rsn_mcastcipher; 878138568Ssam break; 879138568Ssam case IEEE80211_IOC_MCASTKEYLEN: 880138568Ssam ireq->i_val = rsn->rsn_mcastkeylen; 881138568Ssam break; 882138568Ssam case IEEE80211_IOC_UCASTCIPHERS: 883138568Ssam ireq->i_val = 0; 884138568Ssam for (m = 0x1; m != 0; m <<= 1) 885138568Ssam if (rsn->rsn_ucastcipherset & m) 886138568Ssam ireq->i_val |= 1<<cap2cipher(m); 887138568Ssam break; 888138568Ssam case IEEE80211_IOC_UCASTCIPHER: 889138568Ssam ireq->i_val = rsn->rsn_ucastcipher; 890138568Ssam break; 891138568Ssam case IEEE80211_IOC_UCASTKEYLEN: 892138568Ssam ireq->i_val = rsn->rsn_ucastkeylen; 893138568Ssam break; 894138568Ssam case IEEE80211_IOC_KEYMGTALGS: 895138568Ssam ireq->i_val = rsn->rsn_keymgmtset; 896138568Ssam break; 897138568Ssam case IEEE80211_IOC_RSNCAPS: 898138568Ssam ireq->i_val = rsn->rsn_caps; 899138568Ssam break; 900138568Ssam case IEEE80211_IOC_WPA: 901138568Ssam switch (ic->ic_flags & IEEE80211_F_WPA) { 902138568Ssam case IEEE80211_F_WPA1: 903116742Ssam ireq->i_val = 1; 904116742Ssam break; 905138568Ssam case IEEE80211_F_WPA2: 906138568Ssam ireq->i_val = 2; 907116742Ssam break; 908138568Ssam case IEEE80211_F_WPA1 | IEEE80211_F_WPA2: 909138568Ssam ireq->i_val = 3; 910116742Ssam break; 911138568Ssam default: 912138568Ssam ireq->i_val = 0; 913116742Ssam break; 914138568Ssam } 915138568Ssam break; 916138568Ssam case IEEE80211_IOC_CHANLIST: 917138568Ssam error = ieee80211_ioctl_getchanlist(ic, ireq); 918138568Ssam break; 919138568Ssam case IEEE80211_IOC_ROAMING: 920138568Ssam ireq->i_val = ic->ic_roaming; 921138568Ssam break; 922138568Ssam case IEEE80211_IOC_PRIVACY: 923138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0; 924138568Ssam break; 925138568Ssam case IEEE80211_IOC_DROPUNENCRYPTED: 926138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0; 927138568Ssam break; 928138568Ssam case IEEE80211_IOC_COUNTERMEASURES: 929138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0; 930138568Ssam break; 931138568Ssam case IEEE80211_IOC_DRIVER_CAPS: 932138568Ssam ireq->i_val = ic->ic_caps>>16; 933138568Ssam ireq->i_len = ic->ic_caps&0xffff; 934138568Ssam break; 935138568Ssam case IEEE80211_IOC_WME: 936138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0; 937138568Ssam break; 938138568Ssam case IEEE80211_IOC_HIDESSID: 939138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0; 940138568Ssam break; 941138568Ssam case IEEE80211_IOC_APBRIDGE: 942138568Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0; 943138568Ssam break; 944138568Ssam case IEEE80211_IOC_OPTIE: 945138568Ssam if (ic->ic_opt_ie == NULL) 946138568Ssam return EINVAL; 947138568Ssam /* NB: truncate, caller can check length */ 948138568Ssam if (ireq->i_len > ic->ic_opt_ie_len) 949138568Ssam ireq->i_len = ic->ic_opt_ie_len; 950138568Ssam error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len); 951138568Ssam break; 952138568Ssam case IEEE80211_IOC_WPAKEY: 953138568Ssam error = ieee80211_ioctl_getkey(ic, ireq); 954138568Ssam break; 955138568Ssam case IEEE80211_IOC_CHANINFO: 956138568Ssam error = ieee80211_ioctl_getchaninfo(ic, ireq); 957138568Ssam break; 958138568Ssam case IEEE80211_IOC_BSSID: 959138568Ssam if (ireq->i_len != IEEE80211_ADDR_LEN) 960138568Ssam return EINVAL; 961138568Ssam error = copyout(ic->ic_state == IEEE80211_S_RUN ? 962138568Ssam ic->ic_bss->ni_bssid : 963138568Ssam ic->ic_des_bssid, 964138568Ssam ireq->i_data, ireq->i_len); 965138568Ssam break; 966138568Ssam case IEEE80211_IOC_WPAIE: 967170530Ssam error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type); 968138568Ssam break; 969170530Ssam case IEEE80211_IOC_WPAIE2: 970170530Ssam error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type); 971170530Ssam break; 972154541Ssam#ifdef COMPAT_FREEBSD6 973154541Ssam case IEEE80211_IOC_SCAN_RESULTS_OLD: 974154541Ssam error = old_getscanresults(ic, ireq); 975154541Ssam break; 976154541Ssam#endif 977138568Ssam case IEEE80211_IOC_SCAN_RESULTS: 978138568Ssam error = ieee80211_ioctl_getscanresults(ic, ireq); 979138568Ssam break; 980138568Ssam case IEEE80211_IOC_STA_STATS: 981138568Ssam error = ieee80211_ioctl_getstastats(ic, ireq); 982138568Ssam break; 983138568Ssam case IEEE80211_IOC_TXPOWMAX: 984138568Ssam ireq->i_val = ic->ic_bss->ni_txpower; 985138568Ssam break; 986138568Ssam case IEEE80211_IOC_STA_TXPOW: 987138568Ssam error = ieee80211_ioctl_getstatxpow(ic, ireq); 988138568Ssam break; 989161146Ssam#ifdef COMPAT_FREEBSD6 990161146Ssam case IEEE80211_IOC_STA_INFO_OLD: 991161146Ssam error = old_getstainfo(ic, ireq); 992161146Ssam break; 993161146Ssam#endif 994138568Ssam case IEEE80211_IOC_STA_INFO: 995138568Ssam error = ieee80211_ioctl_getstainfo(ic, ireq); 996138568Ssam break; 997138568Ssam case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 998138568Ssam case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 999138568Ssam case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 1000138568Ssam case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 1001138568Ssam case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 1002138568Ssam case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ 1003138568Ssam error = ieee80211_ioctl_getwmeparam(ic, ireq); 1004138568Ssam break; 1005138568Ssam case IEEE80211_IOC_DTIM_PERIOD: 1006138568Ssam ireq->i_val = ic->ic_dtim_period; 1007138568Ssam break; 1008138568Ssam case IEEE80211_IOC_BEACON_INTERVAL: 1009138568Ssam /* NB: get from ic_bss for station mode */ 1010138568Ssam ireq->i_val = ic->ic_bss->ni_intval; 1011138568Ssam break; 1012147794Ssam case IEEE80211_IOC_PUREG: 1013147794Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0; 1014147794Ssam break; 1015170530Ssam case IEEE80211_IOC_FF: 1016170530Ssam ireq->i_val = getathcap(ic, IEEE80211_F_FF); 1017170530Ssam break; 1018170530Ssam case IEEE80211_IOC_TURBOP: 1019170530Ssam ireq->i_val = getathcap(ic, IEEE80211_F_TURBOP); 1020170530Ssam break; 1021170530Ssam case IEEE80211_IOC_BGSCAN: 1022170530Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_BGSCAN) != 0; 1023170530Ssam break; 1024170530Ssam case IEEE80211_IOC_BGSCAN_IDLE: 1025170530Ssam ireq->i_val = ic->ic_bgscanidle*hz/1000; /* ms */ 1026170530Ssam break; 1027170530Ssam case IEEE80211_IOC_BGSCAN_INTERVAL: 1028170530Ssam ireq->i_val = ic->ic_bgscanintvl/hz; /* seconds */ 1029170530Ssam break; 1030170530Ssam case IEEE80211_IOC_SCANVALID: 1031170530Ssam ireq->i_val = ic->ic_scanvalid/hz; /* seconds */ 1032170530Ssam break; 1033170530Ssam case IEEE80211_IOC_ROAM_RSSI_11A: 1034170530Ssam ireq->i_val = ic->ic_roam.rssi11a; 1035170530Ssam break; 1036170530Ssam case IEEE80211_IOC_ROAM_RSSI_11B: 1037170530Ssam ireq->i_val = ic->ic_roam.rssi11bOnly; 1038170530Ssam break; 1039170530Ssam case IEEE80211_IOC_ROAM_RSSI_11G: 1040170530Ssam ireq->i_val = ic->ic_roam.rssi11b; 1041170530Ssam break; 1042170530Ssam case IEEE80211_IOC_ROAM_RATE_11A: 1043170530Ssam ireq->i_val = ic->ic_roam.rate11a; 1044170530Ssam break; 1045170530Ssam case IEEE80211_IOC_ROAM_RATE_11B: 1046170530Ssam ireq->i_val = ic->ic_roam.rate11bOnly; 1047170530Ssam break; 1048170530Ssam case IEEE80211_IOC_ROAM_RATE_11G: 1049170530Ssam ireq->i_val = ic->ic_roam.rate11b; 1050170530Ssam break; 1051153346Ssam case IEEE80211_IOC_MCAST_RATE: 1052153346Ssam ireq->i_val = ic->ic_mcast_rate; 1053153346Ssam break; 1054148292Ssam case IEEE80211_IOC_FRAGTHRESHOLD: 1055148292Ssam ireq->i_val = ic->ic_fragthreshold; 1056148292Ssam break; 1057149028Ssam case IEEE80211_IOC_MACCMD: 1058149028Ssam error = ieee80211_ioctl_getmaccmd(ic, ireq); 1059149028Ssam break; 1060153421Ssam case IEEE80211_IOC_BURST: 1061153421Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_BURST) != 0; 1062153421Ssam break; 1063160686Ssam case IEEE80211_IOC_BMISSTHRESHOLD: 1064160686Ssam ireq->i_val = ic->ic_bmissthreshold; 1065160686Ssam break; 1066170530Ssam case IEEE80211_IOC_CURCHAN: 1067170530Ssam error = ieee80211_ioctl_getcurchan(ic, ireq); 1068170530Ssam break; 1069170530Ssam case IEEE80211_IOC_SHORTGI: 1070170530Ssam ireq->i_val = 0; 1071170530Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) 1072170530Ssam ireq->i_val |= IEEE80211_HTCAP_SHORTGI20; 1073170530Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) 1074170530Ssam ireq->i_val |= IEEE80211_HTCAP_SHORTGI40; 1075170530Ssam break; 1076170530Ssam case IEEE80211_IOC_AMPDU: 1077170530Ssam ireq->i_val = 0; 1078170530Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX) 1079170530Ssam ireq->i_val |= 1; 1080170530Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX) 1081170530Ssam ireq->i_val |= 2; 1082170530Ssam break; 1083170530Ssam case IEEE80211_IOC_AMPDU_LIMIT: 1084170530Ssam ireq->i_val = ic->ic_ampdu_limit; /* XXX truncation? */ 1085170530Ssam break; 1086170530Ssam case IEEE80211_IOC_AMPDU_DENSITY: 1087170530Ssam ireq->i_val = ic->ic_ampdu_density; 1088170530Ssam break; 1089170530Ssam case IEEE80211_IOC_AMSDU: 1090170530Ssam ireq->i_val = 0; 1091170530Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_TX) 1092170530Ssam ireq->i_val |= 1; 1093170530Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_RX) 1094170530Ssam ireq->i_val |= 2; 1095170530Ssam break; 1096170530Ssam case IEEE80211_IOC_AMSDU_LIMIT: 1097170530Ssam ireq->i_val = ic->ic_amsdu_limit; /* XXX truncation? */ 1098170530Ssam break; 1099170530Ssam case IEEE80211_IOC_PUREN: 1100170530Ssam ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) != 0; 1101170530Ssam break; 1102170530Ssam case IEEE80211_IOC_DOTH: 1103170530Ssam ireq->i_val = (ic->ic_flags & IEEE80211_F_DOTH) != 0; 1104170530Ssam break; 1105170530Ssam case IEEE80211_IOC_HTCOMPAT: 1106170530Ssam ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0; 1107170530Ssam break; 1108138568Ssam default: 1109138568Ssam error = EINVAL; 1110138568Ssam break; 1111138568Ssam } 1112138568Ssam return error; 1113138568Ssam} 1114138568Ssam 1115138568Ssamstatic int 1116138568Ssamieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq) 1117138568Ssam{ 1118138568Ssam int error; 1119155862Ssam void *ie, *oie; 1120138568Ssam 1121138568Ssam /* 1122138568Ssam * NB: Doing this for ap operation could be useful (e.g. for 1123138568Ssam * WPA and/or WME) except that it typically is worthless 1124138568Ssam * without being able to intervene when processing 1125138568Ssam * association response frames--so disallow it for now. 1126138568Ssam */ 1127138568Ssam if (ic->ic_opmode != IEEE80211_M_STA) 1128138568Ssam return EINVAL; 1129138568Ssam if (ireq->i_len > IEEE80211_MAX_OPT_IE) 1130138568Ssam return EINVAL; 1131170530Ssam /* NB: data.length is validated by the wireless extensions code */ 1132170530Ssam /* XXX M_WAITOK after driver lock released */ 1133155862Ssam if (ireq->i_len > 0) { 1134155862Ssam MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_NOWAIT); 1135155862Ssam if (ie == NULL) 1136155862Ssam return ENOMEM; 1137155862Ssam error = copyin(ireq->i_data, ie, ireq->i_len); 1138155862Ssam if (error) { 1139155862Ssam FREE(ie, M_DEVBUF); 1140155862Ssam return error; 1141155862Ssam } 1142155862Ssam } else { 1143155862Ssam ie = NULL; 1144155862Ssam ireq->i_len = 0; 1145155862Ssam } 1146138568Ssam /* XXX sanity check data? */ 1147155862Ssam oie = ic->ic_opt_ie; 1148138568Ssam ic->ic_opt_ie = ie; 1149138568Ssam ic->ic_opt_ie_len = ireq->i_len; 1150155862Ssam if (oie != NULL) 1151155862Ssam FREE(oie, M_DEVBUF); 1152138568Ssam return 0; 1153138568Ssam} 1154138568Ssam 1155138568Ssamstatic int 1156138568Ssamieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq) 1157138568Ssam{ 1158138568Ssam struct ieee80211req_key ik; 1159138568Ssam struct ieee80211_node *ni; 1160138568Ssam struct ieee80211_key *wk; 1161170530Ssam uint16_t kid; 1162138568Ssam int error; 1163138568Ssam 1164138568Ssam if (ireq->i_len != sizeof(ik)) 1165138568Ssam return EINVAL; 1166138568Ssam error = copyin(ireq->i_data, &ik, sizeof(ik)); 1167138568Ssam if (error) 1168138568Ssam return error; 1169138568Ssam /* NB: cipher support is verified by ieee80211_crypt_newkey */ 1170138568Ssam /* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */ 1171138568Ssam if (ik.ik_keylen > sizeof(ik.ik_keydata)) 1172138568Ssam return E2BIG; 1173138568Ssam kid = ik.ik_keyix; 1174138568Ssam if (kid == IEEE80211_KEYIX_NONE) { 1175138568Ssam /* XXX unicast keys currently must be tx/rx */ 1176138568Ssam if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)) 1177138568Ssam return EINVAL; 1178138568Ssam if (ic->ic_opmode == IEEE80211_M_STA) { 1179147775Ssam ni = ieee80211_ref_node(ic->ic_bss); 1180147775Ssam if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) { 1181147775Ssam ieee80211_free_node(ni); 1182138568Ssam return EADDRNOTAVAIL; 1183147775Ssam } 1184138568Ssam } else { 1185140753Ssam ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr); 1186138568Ssam if (ni == NULL) 1187138568Ssam return ENOENT; 1188138568Ssam } 1189138568Ssam wk = &ni->ni_ucastkey; 1190138568Ssam } else { 1191138568Ssam if (kid >= IEEE80211_WEP_NKID) 1192138568Ssam return EINVAL; 1193138568Ssam wk = &ic->ic_nw_keys[kid]; 1194155885Ssam /* 1195155885Ssam * Global slots start off w/o any assigned key index. 1196155885Ssam * Force one here for consistency with IEEE80211_IOC_WEPKEY. 1197155885Ssam */ 1198155885Ssam if (wk->wk_keyix == IEEE80211_KEYIX_NONE) 1199155885Ssam wk->wk_keyix = kid; 1200138568Ssam ni = NULL; 1201138568Ssam } 1202138568Ssam error = 0; 1203138568Ssam ieee80211_key_update_begin(ic); 1204144960Ssam if (ieee80211_crypto_newkey(ic, ik.ik_type, ik.ik_flags, wk)) { 1205138568Ssam wk->wk_keylen = ik.ik_keylen; 1206138568Ssam /* NB: MIC presence is implied by cipher type */ 1207138568Ssam if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE) 1208138568Ssam wk->wk_keylen = IEEE80211_KEYBUF_SIZE; 1209138568Ssam wk->wk_keyrsc = ik.ik_keyrsc; 1210138568Ssam wk->wk_keytsc = 0; /* new key, reset */ 1211138568Ssam memset(wk->wk_key, 0, sizeof(wk->wk_key)); 1212138568Ssam memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen); 1213138568Ssam if (!ieee80211_crypto_setkey(ic, wk, 1214138568Ssam ni != NULL ? ni->ni_macaddr : ik.ik_macaddr)) 1215138568Ssam error = EIO; 1216138568Ssam else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT)) 1217138568Ssam ic->ic_def_txkey = kid; 1218138568Ssam } else 1219138568Ssam error = ENXIO; 1220138568Ssam ieee80211_key_update_end(ic); 1221138568Ssam if (ni != NULL) 1222138568Ssam ieee80211_free_node(ni); 1223138568Ssam return error; 1224138568Ssam} 1225138568Ssam 1226138568Ssamstatic int 1227138568Ssamieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq) 1228138568Ssam{ 1229138568Ssam struct ieee80211req_del_key dk; 1230138568Ssam int kid, error; 1231138568Ssam 1232138568Ssam if (ireq->i_len != sizeof(dk)) 1233138568Ssam return EINVAL; 1234138568Ssam error = copyin(ireq->i_data, &dk, sizeof(dk)); 1235138568Ssam if (error) 1236138568Ssam return error; 1237138568Ssam kid = dk.idk_keyix; 1238170530Ssam /* XXX uint8_t -> uint16_t */ 1239170530Ssam if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) { 1240138568Ssam struct ieee80211_node *ni; 1241138568Ssam 1242147775Ssam if (ic->ic_opmode == IEEE80211_M_STA) { 1243147775Ssam ni = ieee80211_ref_node(ic->ic_bss); 1244147775Ssam if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) { 1245147775Ssam ieee80211_free_node(ni); 1246147775Ssam return EADDRNOTAVAIL; 1247147775Ssam } 1248147775Ssam } else { 1249147775Ssam ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr); 1250147775Ssam if (ni == NULL) 1251147775Ssam return ENOENT; 1252147775Ssam } 1253138568Ssam /* XXX error return */ 1254148863Ssam ieee80211_node_delucastkey(ni); 1255138568Ssam ieee80211_free_node(ni); 1256138568Ssam } else { 1257138568Ssam if (kid >= IEEE80211_WEP_NKID) 1258138568Ssam return EINVAL; 1259138568Ssam /* XXX error return */ 1260138568Ssam ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]); 1261138568Ssam } 1262138568Ssam return 0; 1263138568Ssam} 1264138568Ssam 1265138568Ssamstatic void 1266138568Ssamdomlme(void *arg, struct ieee80211_node *ni) 1267138568Ssam{ 1268138568Ssam struct ieee80211com *ic = ni->ni_ic; 1269138568Ssam struct ieee80211req_mlme *mlme = arg; 1270138568Ssam 1271138568Ssam if (ni->ni_associd != 0) { 1272138568Ssam IEEE80211_SEND_MGMT(ic, ni, 1273138568Ssam mlme->im_op == IEEE80211_MLME_DEAUTH ? 1274138568Ssam IEEE80211_FC0_SUBTYPE_DEAUTH : 1275138568Ssam IEEE80211_FC0_SUBTYPE_DISASSOC, 1276138568Ssam mlme->im_reason); 1277138568Ssam } 1278138568Ssam ieee80211_node_leave(ic, ni); 1279138568Ssam} 1280138568Ssam 1281170530Ssamstruct scanlookup { 1282170530Ssam const uint8_t *mac; 1283170530Ssam int esslen; 1284170530Ssam const uint8_t *essid; 1285170530Ssam const struct ieee80211_scan_entry *se; 1286170530Ssam}; 1287170530Ssam 1288170530Ssam/* 1289170530Ssam * Match mac address and any ssid. 1290170530Ssam */ 1291170530Ssamstatic void 1292170530Ssammlmelookup(void *arg, const struct ieee80211_scan_entry *se) 1293170530Ssam{ 1294170530Ssam struct scanlookup *look = arg; 1295170530Ssam 1296170530Ssam if (!IEEE80211_ADDR_EQ(look->mac, se->se_macaddr)) 1297170530Ssam return; 1298170530Ssam if (look->esslen != 0) { 1299170530Ssam if (se->se_ssid[1] != look->esslen) 1300170530Ssam return; 1301170530Ssam if (memcmp(look->essid, se->se_ssid+2, look->esslen)) 1302170530Ssam return; 1303170530Ssam } 1304170530Ssam look->se = se; 1305170530Ssam} 1306170530Ssam 1307138568Ssamstatic int 1308138568Ssamieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) 1309138568Ssam{ 1310138568Ssam struct ieee80211req_mlme mlme; 1311138568Ssam struct ieee80211_node *ni; 1312138568Ssam int error; 1313138568Ssam 1314138568Ssam if (ireq->i_len != sizeof(mlme)) 1315138568Ssam return EINVAL; 1316138568Ssam error = copyin(ireq->i_data, &mlme, sizeof(mlme)); 1317138568Ssam if (error) 1318138568Ssam return error; 1319138568Ssam switch (mlme.im_op) { 1320138568Ssam case IEEE80211_MLME_ASSOC: 1321170530Ssam /* XXX ibss/ahdemo */ 1322170530Ssam if (ic->ic_opmode == IEEE80211_M_STA) { 1323170530Ssam struct scanlookup lookup; 1324138568Ssam 1325170530Ssam lookup.se = NULL; 1326170530Ssam lookup.mac = mlme.im_macaddr; 1327170530Ssam /* XXX use revised api w/ explicit ssid */ 1328170530Ssam lookup.esslen = ic->ic_des_ssid[0].len; 1329170530Ssam lookup.essid = ic->ic_des_ssid[0].ssid; 1330170530Ssam ieee80211_scan_iterate(ic, mlmelookup, &lookup); 1331170530Ssam if (lookup.se != NULL && 1332170530Ssam ieee80211_sta_join(ic, lookup.se)) 1333170530Ssam return 0; 1334138568Ssam } 1335170530Ssam return EINVAL; 1336138568Ssam case IEEE80211_MLME_DISASSOC: 1337138568Ssam case IEEE80211_MLME_DEAUTH: 1338138568Ssam switch (ic->ic_opmode) { 1339138568Ssam case IEEE80211_M_STA: 1340138568Ssam /* XXX not quite right */ 1341138568Ssam ieee80211_new_state(ic, IEEE80211_S_INIT, 1342138568Ssam mlme.im_reason); 1343116742Ssam break; 1344138568Ssam case IEEE80211_M_HOSTAP: 1345138568Ssam /* NB: the broadcast address means do 'em all */ 1346138568Ssam if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) { 1347140753Ssam if ((ni = ieee80211_find_node(&ic->ic_sta, 1348138568Ssam mlme.im_macaddr)) == NULL) 1349138568Ssam return EINVAL; 1350138568Ssam domlme(&mlme, ni); 1351138568Ssam ieee80211_free_node(ni); 1352138568Ssam } else { 1353140753Ssam ieee80211_iterate_nodes(&ic->ic_sta, 1354138568Ssam domlme, &mlme); 1355138568Ssam } 1356116742Ssam break; 1357138568Ssam default: 1358138568Ssam return EINVAL; 1359138568Ssam } 1360138568Ssam break; 1361138568Ssam case IEEE80211_MLME_AUTHORIZE: 1362138568Ssam case IEEE80211_MLME_UNAUTHORIZE: 1363138568Ssam if (ic->ic_opmode != IEEE80211_M_HOSTAP) 1364138568Ssam return EINVAL; 1365140753Ssam ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr); 1366138568Ssam if (ni == NULL) 1367138568Ssam return EINVAL; 1368138568Ssam if (mlme.im_op == IEEE80211_MLME_AUTHORIZE) 1369148302Ssam ieee80211_node_authorize(ni); 1370138568Ssam else 1371148302Ssam ieee80211_node_unauthorize(ni); 1372138568Ssam ieee80211_free_node(ni); 1373138568Ssam break; 1374138568Ssam default: 1375138568Ssam return EINVAL; 1376138568Ssam } 1377138568Ssam return 0; 1378138568Ssam} 1379138568Ssam 1380138568Ssamstatic int 1381138568Ssamieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq) 1382138568Ssam{ 1383170530Ssam uint8_t mac[IEEE80211_ADDR_LEN]; 1384138568Ssam const struct ieee80211_aclator *acl = ic->ic_acl; 1385138568Ssam int error; 1386138568Ssam 1387138568Ssam if (ireq->i_len != sizeof(mac)) 1388138568Ssam return EINVAL; 1389138568Ssam error = copyin(ireq->i_data, mac, ireq->i_len); 1390138568Ssam if (error) 1391138568Ssam return error; 1392138568Ssam if (acl == NULL) { 1393138568Ssam acl = ieee80211_aclator_get("mac"); 1394138568Ssam if (acl == NULL || !acl->iac_attach(ic)) 1395138568Ssam return EINVAL; 1396138568Ssam ic->ic_acl = acl; 1397138568Ssam } 1398138568Ssam if (ireq->i_type == IEEE80211_IOC_ADDMAC) 1399138568Ssam acl->iac_add(ic, mac); 1400138568Ssam else 1401138568Ssam acl->iac_remove(ic, mac); 1402138568Ssam return 0; 1403138568Ssam} 1404138568Ssam 1405138568Ssamstatic int 1406149028Ssamieee80211_ioctl_setmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq) 1407138568Ssam{ 1408138568Ssam const struct ieee80211_aclator *acl = ic->ic_acl; 1409138568Ssam 1410138568Ssam switch (ireq->i_val) { 1411138568Ssam case IEEE80211_MACCMD_POLICY_OPEN: 1412138568Ssam case IEEE80211_MACCMD_POLICY_ALLOW: 1413138568Ssam case IEEE80211_MACCMD_POLICY_DENY: 1414138568Ssam if (acl == NULL) { 1415138568Ssam acl = ieee80211_aclator_get("mac"); 1416138568Ssam if (acl == NULL || !acl->iac_attach(ic)) 1417138568Ssam return EINVAL; 1418138568Ssam ic->ic_acl = acl; 1419138568Ssam } 1420138568Ssam acl->iac_setpolicy(ic, ireq->i_val); 1421138568Ssam break; 1422138568Ssam case IEEE80211_MACCMD_FLUSH: 1423138568Ssam if (acl != NULL) 1424138568Ssam acl->iac_flush(ic); 1425138568Ssam /* NB: silently ignore when not in use */ 1426138568Ssam break; 1427138568Ssam case IEEE80211_MACCMD_DETACH: 1428138568Ssam if (acl != NULL) { 1429138568Ssam ic->ic_acl = NULL; 1430138568Ssam acl->iac_detach(ic); 1431138568Ssam } 1432138568Ssam break; 1433138568Ssam default: 1434149028Ssam if (acl == NULL) 1435149028Ssam return EINVAL; 1436149028Ssam else 1437149028Ssam return acl->iac_setioctl(ic, ireq); 1438138568Ssam } 1439138568Ssam return 0; 1440138568Ssam} 1441138568Ssam 1442138568Ssamstatic int 1443138568Ssamieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) 1444138568Ssam{ 1445138568Ssam struct ieee80211req_chanlist list; 1446138568Ssam u_char chanlist[IEEE80211_CHAN_BYTES]; 1447170530Ssam int i, j, nchan, error; 1448138568Ssam 1449138568Ssam if (ireq->i_len != sizeof(list)) 1450138568Ssam return EINVAL; 1451138568Ssam error = copyin(ireq->i_data, &list, sizeof(list)); 1452138568Ssam if (error) 1453138568Ssam return error; 1454138568Ssam memset(chanlist, 0, sizeof(chanlist)); 1455138568Ssam /* 1456138568Ssam * Since channel 0 is not available for DS, channel 1 1457138568Ssam * is assigned to LSB on WaveLAN. 1458138568Ssam */ 1459138568Ssam if (ic->ic_phytype == IEEE80211_T_DS) 1460138568Ssam i = 1; 1461138568Ssam else 1462138568Ssam i = 0; 1463170530Ssam nchan = 0; 1464138568Ssam for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { 1465138568Ssam /* 1466138568Ssam * NB: silently discard unavailable channels so users 1467138568Ssam * can specify 1-255 to get all available channels. 1468138568Ssam */ 1469170530Ssam if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) { 1470138568Ssam setbit(chanlist, i); 1471170530Ssam nchan++; 1472170530Ssam } 1473138568Ssam } 1474170530Ssam if (nchan == 0) 1475170530Ssam return EINVAL; 1476170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && /* XXX */ 1477170530Ssam isclr(chanlist, ic->ic_bsschan->ic_ieee)) 1478170530Ssam ic->ic_bsschan = IEEE80211_CHAN_ANYC; 1479138568Ssam memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); 1480170530Ssam return IS_UP_AUTO(ic) ? ieee80211_init(ic, RESCAN) : 0; 1481138568Ssam} 1482138568Ssam 1483138568Ssamstatic int 1484157172Ssamieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq) 1485157172Ssam{ 1486157172Ssam struct ieee80211_node *ni; 1487170530Ssam uint8_t macaddr[IEEE80211_ADDR_LEN]; 1488157172Ssam int error; 1489157172Ssam 1490157172Ssam /* 1491157172Ssam * NB: we could copyin ieee80211req_sta_stats so apps 1492157172Ssam * could make selective changes but that's overkill; 1493157172Ssam * just clear all stats for now. 1494157172Ssam */ 1495157172Ssam if (ireq->i_len < IEEE80211_ADDR_LEN) 1496157172Ssam return EINVAL; 1497157172Ssam error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); 1498157172Ssam if (error != 0) 1499157172Ssam return error; 1500157172Ssam ni = ieee80211_find_node(&ic->ic_sta, macaddr); 1501157172Ssam if (ni == NULL) 1502157172Ssam return EINVAL; /* XXX */ 1503157172Ssam memset(&ni->ni_stats, 0, sizeof(ni->ni_stats)); 1504157172Ssam ieee80211_free_node(ni); 1505157172Ssam return 0; 1506157172Ssam} 1507157172Ssam 1508157172Ssamstatic int 1509138568Ssamieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) 1510138568Ssam{ 1511138568Ssam struct ieee80211_node *ni; 1512138568Ssam struct ieee80211req_sta_txpow txpow; 1513138568Ssam int error; 1514138568Ssam 1515138568Ssam if (ireq->i_len != sizeof(txpow)) 1516138568Ssam return EINVAL; 1517138568Ssam error = copyin(ireq->i_data, &txpow, sizeof(txpow)); 1518138568Ssam if (error != 0) 1519138568Ssam return error; 1520140753Ssam ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr); 1521138568Ssam if (ni == NULL) 1522138568Ssam return EINVAL; /* XXX */ 1523138568Ssam ni->ni_txpower = txpow.it_txpow; 1524138568Ssam ieee80211_free_node(ni); 1525138568Ssam return error; 1526138568Ssam} 1527138568Ssam 1528138568Ssamstatic int 1529138568Ssamieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) 1530138568Ssam{ 1531138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 1532138568Ssam struct wmeParams *wmep, *chanp; 1533138568Ssam int isbss, ac; 1534138568Ssam 1535138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 1536138568Ssam return EINVAL; 1537138568Ssam 1538138568Ssam isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS); 1539138568Ssam ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); 1540138568Ssam if (ac >= WME_NUM_AC) 1541138568Ssam ac = WME_AC_BE; 1542138568Ssam if (isbss) { 1543138568Ssam chanp = &wme->wme_bssChanParams.cap_wmeParams[ac]; 1544138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; 1545138568Ssam } else { 1546138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[ac]; 1547138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; 1548138568Ssam } 1549138568Ssam switch (ireq->i_type) { 1550138568Ssam case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 1551138568Ssam if (isbss) { 1552138568Ssam wmep->wmep_logcwmin = ireq->i_val; 1553138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1554138568Ssam chanp->wmep_logcwmin = ireq->i_val; 1555138568Ssam } else { 1556138568Ssam wmep->wmep_logcwmin = chanp->wmep_logcwmin = 1557138568Ssam ireq->i_val; 1558138568Ssam } 1559138568Ssam break; 1560138568Ssam case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 1561138568Ssam if (isbss) { 1562138568Ssam wmep->wmep_logcwmax = ireq->i_val; 1563138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1564138568Ssam chanp->wmep_logcwmax = ireq->i_val; 1565138568Ssam } else { 1566138568Ssam wmep->wmep_logcwmax = chanp->wmep_logcwmax = 1567138568Ssam ireq->i_val; 1568138568Ssam } 1569138568Ssam break; 1570138568Ssam case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 1571138568Ssam if (isbss) { 1572138568Ssam wmep->wmep_aifsn = ireq->i_val; 1573138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1574138568Ssam chanp->wmep_aifsn = ireq->i_val; 1575138568Ssam } else { 1576138568Ssam wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val; 1577138568Ssam } 1578138568Ssam break; 1579138568Ssam case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 1580138568Ssam if (isbss) { 1581138568Ssam wmep->wmep_txopLimit = ireq->i_val; 1582138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1583138568Ssam chanp->wmep_txopLimit = ireq->i_val; 1584138568Ssam } else { 1585138568Ssam wmep->wmep_txopLimit = chanp->wmep_txopLimit = 1586138568Ssam ireq->i_val; 1587138568Ssam } 1588138568Ssam break; 1589138568Ssam case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 1590138568Ssam wmep->wmep_acm = ireq->i_val; 1591138568Ssam if ((wme->wme_flags & WME_F_AGGRMODE) == 0) 1592138568Ssam chanp->wmep_acm = ireq->i_val; 1593138568Ssam break; 1594138568Ssam case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ 1595138568Ssam wmep->wmep_noackPolicy = chanp->wmep_noackPolicy = 1596138568Ssam (ireq->i_val) == 0; 1597138568Ssam break; 1598138568Ssam } 1599138568Ssam ieee80211_wme_updateparams(ic); 1600138568Ssam return 0; 1601138568Ssam} 1602138568Ssam 1603138568Ssamstatic int 1604138568Ssamcipher2cap(int cipher) 1605138568Ssam{ 1606138568Ssam switch (cipher) { 1607138568Ssam case IEEE80211_CIPHER_WEP: return IEEE80211_C_WEP; 1608138568Ssam case IEEE80211_CIPHER_AES_OCB: return IEEE80211_C_AES; 1609138568Ssam case IEEE80211_CIPHER_AES_CCM: return IEEE80211_C_AES_CCM; 1610138568Ssam case IEEE80211_CIPHER_CKIP: return IEEE80211_C_CKIP; 1611138568Ssam case IEEE80211_CIPHER_TKIP: return IEEE80211_C_TKIP; 1612138568Ssam } 1613138568Ssam return 0; 1614138568Ssam} 1615138568Ssam 1616138568Ssamstatic int 1617170530Ssamfind11gchannel(struct ieee80211com *ic, int start, int freq) 1618170530Ssam{ 1619170530Ssam const struct ieee80211_channel *c; 1620170530Ssam int i; 1621170530Ssam 1622170530Ssam for (i = start+1; i < ic->ic_nchans; i++) { 1623170530Ssam c = &ic->ic_channels[i]; 1624170530Ssam if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) 1625170530Ssam return 1; 1626170530Ssam } 1627170530Ssam /* NB: should not be needed but in case things are mis-sorted */ 1628170530Ssam for (i = 0; i < start; i++) { 1629170530Ssam c = &ic->ic_channels[i]; 1630170530Ssam if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) 1631170530Ssam return 1; 1632170530Ssam } 1633170530Ssam return 0; 1634170530Ssam} 1635170530Ssam 1636170530Ssamstatic struct ieee80211_channel * 1637170530Ssamfindchannel(struct ieee80211com *ic, int ieee, int mode) 1638170530Ssam{ 1639170530Ssam static const u_int chanflags[IEEE80211_MODE_MAX] = { 1640170530Ssam 0, /* IEEE80211_MODE_AUTO */ 1641170530Ssam IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ 1642170530Ssam IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ 1643170530Ssam IEEE80211_CHAN_G, /* IEEE80211_MODE_11G */ 1644170530Ssam IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ 1645170530Ssam IEEE80211_CHAN_108A, /* IEEE80211_MODE_TURBO_A */ 1646170530Ssam IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ 1647170530Ssam IEEE80211_CHAN_STURBO, /* IEEE80211_MODE_STURBO_A */ 1648170530Ssam /* NB: handled specially below */ 1649170530Ssam IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA */ 1650170530Ssam IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG */ 1651170530Ssam }; 1652170530Ssam u_int modeflags; 1653170530Ssam int i; 1654170530Ssam 1655170530Ssam KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode)); 1656170530Ssam modeflags = chanflags[mode]; 1657170530Ssam KASSERT(modeflags != 0 || mode == IEEE80211_MODE_AUTO, 1658170530Ssam ("no chanflags for mode %u", mode)); 1659170530Ssam for (i = 0; i < ic->ic_nchans; i++) { 1660170530Ssam struct ieee80211_channel *c = &ic->ic_channels[i]; 1661170530Ssam 1662170530Ssam if (c->ic_ieee != ieee) 1663170530Ssam continue; 1664170530Ssam if (mode == IEEE80211_MODE_AUTO) { 1665170530Ssam /* ignore turbo channels for autoselect */ 1666170530Ssam if (IEEE80211_IS_CHAN_TURBO(c)) 1667170530Ssam continue; 1668170530Ssam /* 1669170530Ssam * XXX special-case 11b/g channels so we 1670170530Ssam * always select the g channel if both 1671170530Ssam * are present. 1672170530Ssam * XXX prefer HT to non-HT? 1673170530Ssam */ 1674170530Ssam if (!IEEE80211_IS_CHAN_B(c) || 1675170530Ssam !find11gchannel(ic, i, c->ic_freq)) 1676170530Ssam return c; 1677170530Ssam } else { 1678170530Ssam /* must check HT specially */ 1679170530Ssam if ((mode == IEEE80211_MODE_11NA || 1680170530Ssam mode == IEEE80211_MODE_11NG) && 1681170530Ssam !IEEE80211_IS_CHAN_HT(c)) 1682170530Ssam continue; 1683170530Ssam if ((c->ic_flags & modeflags) == modeflags) 1684170530Ssam return c; 1685170530Ssam } 1686170530Ssam } 1687170530Ssam return NULL; 1688170530Ssam} 1689170530Ssam 1690170530Ssam/* 1691170530Ssam * Check the specified against any desired mode (aka netband). 1692170530Ssam * This is only used (presently) when operating in hostap mode 1693170530Ssam * to enforce consistency. 1694170530Ssam */ 1695170530Ssamstatic int 1696170530Ssamcheck_mode_consistency(const struct ieee80211_channel *c, int mode) 1697170530Ssam{ 1698170530Ssam KASSERT(c != IEEE80211_CHAN_ANYC, ("oops, no channel")); 1699170530Ssam 1700170530Ssam switch (mode) { 1701170530Ssam case IEEE80211_MODE_11B: 1702170530Ssam return (IEEE80211_IS_CHAN_B(c)); 1703170530Ssam case IEEE80211_MODE_11G: 1704170530Ssam return (IEEE80211_IS_CHAN_ANYG(c) && !IEEE80211_IS_CHAN_HT(c)); 1705170530Ssam case IEEE80211_MODE_11A: 1706170530Ssam return (IEEE80211_IS_CHAN_A(c) && !IEEE80211_IS_CHAN_HT(c)); 1707170530Ssam case IEEE80211_MODE_STURBO_A: 1708170530Ssam return (IEEE80211_IS_CHAN_STURBO(c)); 1709170530Ssam case IEEE80211_MODE_11NA: 1710170530Ssam return (IEEE80211_IS_CHAN_HTA(c)); 1711170530Ssam case IEEE80211_MODE_11NG: 1712170530Ssam return (IEEE80211_IS_CHAN_HTG(c)); 1713170530Ssam } 1714170530Ssam return 1; 1715170530Ssam 1716170530Ssam} 1717170530Ssam 1718170530Ssam/* 1719170530Ssam * Common code to set the current channel. If the device 1720170530Ssam * is up and running this may result in an immediate channel 1721170530Ssam * change or a kick of the state machine. 1722170530Ssam */ 1723170530Ssamstatic int 1724170530Ssamsetcurchan(struct ieee80211com *ic, struct ieee80211_channel *c) 1725170530Ssam{ 1726170530Ssam int error; 1727170530Ssam 1728170530Ssam if (c != IEEE80211_CHAN_ANYC) { 1729170530Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && 1730170530Ssam !check_mode_consistency(c, ic->ic_des_mode)) 1731170530Ssam return EINVAL; 1732170530Ssam if (ic->ic_state == IEEE80211_S_RUN && c == ic->ic_curchan) 1733170530Ssam return 0; /* NB: nothing to do */ 1734170530Ssam } 1735170530Ssam ic->ic_des_chan = c; 1736170530Ssam 1737170530Ssam error = 0; 1738170530Ssam if ((ic->ic_opmode == IEEE80211_M_MONITOR || 1739170530Ssam ic->ic_opmode == IEEE80211_M_WDS) && 1740170530Ssam ic->ic_des_chan != IEEE80211_CHAN_ANYC) { 1741170530Ssam /* 1742170530Ssam * Monitor and wds modes can switch directly. 1743170530Ssam */ 1744170530Ssam ic->ic_curchan = ic->ic_des_chan; 1745170530Ssam if (ic->ic_state == IEEE80211_S_RUN) 1746170530Ssam ic->ic_set_channel(ic); 1747170530Ssam } else { 1748170530Ssam /* 1749170530Ssam * Need to go through the state machine in case we 1750170530Ssam * need to reassociate or the like. The state machine 1751170530Ssam * will pickup the desired channel and avoid scanning. 1752170530Ssam */ 1753170530Ssam if (IS_UP_AUTO(ic)) 1754170530Ssam error = ieee80211_init(ic, RESCAN); 1755170530Ssam else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) { 1756170530Ssam /* 1757170530Ssam * When not up+running and a real channel has 1758170530Ssam * been specified fix the current channel so 1759170530Ssam * there is immediate feedback; e.g. via ifconfig. 1760170530Ssam */ 1761170530Ssam ic->ic_curchan = ic->ic_des_chan; 1762170530Ssam } 1763170530Ssam } 1764170530Ssam return error; 1765170530Ssam} 1766170530Ssam 1767170530Ssam/* 1768170530Ssam * Old api for setting the current channel; this is 1769170530Ssam * deprecated because channel numbers are ambiguous. 1770170530Ssam */ 1771170530Ssamstatic int 1772170530Ssamieee80211_ioctl_setchannel(struct ieee80211com *ic, 1773170530Ssam const struct ieee80211req *ireq) 1774170530Ssam{ 1775170530Ssam struct ieee80211_channel *c; 1776170530Ssam 1777170530Ssam /* XXX 0xffff overflows 16-bit signed */ 1778170530Ssam if (ireq->i_val == 0 || 1779170530Ssam ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) { 1780170530Ssam c = IEEE80211_CHAN_ANYC; 1781170530Ssam } else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX) { 1782170530Ssam return EINVAL; 1783170530Ssam } else { 1784170530Ssam struct ieee80211_channel *c2; 1785170530Ssam 1786170530Ssam c = findchannel(ic, ireq->i_val, ic->ic_des_mode); 1787170530Ssam if (c == NULL) { 1788170530Ssam c = findchannel(ic, ireq->i_val, 1789170530Ssam IEEE80211_MODE_AUTO); 1790170530Ssam if (c == NULL) 1791170530Ssam return EINVAL; 1792170530Ssam } 1793170530Ssam /* 1794170530Ssam * Fine tune channel selection based on desired mode: 1795170530Ssam * if 11b is requested, find the 11b version of any 1796170530Ssam * 11g channel returned, 1797170530Ssam * if static turbo, find the turbo version of any 1798170530Ssam * 11a channel return, 1799170530Ssam * if 11na is requested, find the ht version of any 1800170530Ssam * 11a channel returned, 1801170530Ssam * if 11ng is requested, find the ht version of any 1802170530Ssam * 11g channel returned, 1803170530Ssam * otherwise we should be ok with what we've got. 1804170530Ssam */ 1805170530Ssam switch (ic->ic_des_mode) { 1806170530Ssam case IEEE80211_MODE_11B: 1807170530Ssam if (IEEE80211_IS_CHAN_ANYG(c)) { 1808170530Ssam c2 = findchannel(ic, ireq->i_val, 1809170530Ssam IEEE80211_MODE_11B); 1810170530Ssam /* NB: should not happen, =>'s 11g w/o 11b */ 1811170530Ssam if (c2 != NULL) 1812170530Ssam c = c2; 1813170530Ssam } 1814170530Ssam break; 1815170530Ssam case IEEE80211_MODE_TURBO_A: 1816170530Ssam if (IEEE80211_IS_CHAN_A(c)) { 1817170530Ssam c2 = findchannel(ic, ireq->i_val, 1818170530Ssam IEEE80211_MODE_TURBO_A); 1819170530Ssam if (c2 != NULL) 1820170530Ssam c = c2; 1821170530Ssam } 1822170530Ssam break; 1823170530Ssam case IEEE80211_MODE_11NA: 1824170530Ssam if (IEEE80211_IS_CHAN_A(c)) { 1825170530Ssam c2 = findchannel(ic, ireq->i_val, 1826170530Ssam IEEE80211_MODE_11NA); 1827170530Ssam if (c2 != NULL) 1828170530Ssam c = c2; 1829170530Ssam } 1830170530Ssam break; 1831170530Ssam case IEEE80211_MODE_11NG: 1832170530Ssam if (IEEE80211_IS_CHAN_ANYG(c)) { 1833170530Ssam c2 = findchannel(ic, ireq->i_val, 1834170530Ssam IEEE80211_MODE_11NG); 1835170530Ssam if (c2 != NULL) 1836170530Ssam c = c2; 1837170530Ssam } 1838170530Ssam break; 1839170530Ssam default: /* NB: no static turboG */ 1840170530Ssam break; 1841170530Ssam } 1842170530Ssam } 1843170530Ssam return setcurchan(ic, c); 1844170530Ssam} 1845170530Ssam 1846170530Ssam/* 1847170530Ssam * New/current api for setting the current channel; a complete 1848170530Ssam * channel description is provide so there is no ambiguity in 1849170530Ssam * identifying the channel. 1850170530Ssam */ 1851170530Ssamstatic int 1852170530Ssamieee80211_ioctl_setcurchan(struct ieee80211com *ic, 1853170530Ssam const struct ieee80211req *ireq) 1854170530Ssam{ 1855170530Ssam struct ieee80211_channel chan, *c; 1856170530Ssam int error; 1857170530Ssam 1858170530Ssam if (ireq->i_len != sizeof(chan)) 1859170530Ssam return EINVAL; 1860170530Ssam error = copyin(ireq->i_data, &chan, sizeof(chan)); 1861170530Ssam if (error != 0) 1862170530Ssam return error; 1863170530Ssam /* XXX 0xffff overflows 16-bit signed */ 1864170530Ssam if (chan.ic_freq == 0 || chan.ic_freq == IEEE80211_CHAN_ANY) { 1865170530Ssam c = IEEE80211_CHAN_ANYC; 1866170530Ssam } else { 1867170530Ssam c = ieee80211_find_channel(ic, chan.ic_freq, chan.ic_flags); 1868170530Ssam if (c == NULL) 1869170530Ssam return EINVAL; 1870170530Ssam } 1871170530Ssam return setcurchan(ic, c); 1872170530Ssam} 1873170530Ssam 1874170530Ssamstatic int 1875138568Ssamieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) 1876138568Ssam{ 1877170530Ssam static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; 1878138568Ssam struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; 1879138568Ssam int error; 1880138568Ssam const struct ieee80211_authenticator *auth; 1881170530Ssam uint8_t tmpkey[IEEE80211_KEYBUF_SIZE]; 1882138568Ssam char tmpssid[IEEE80211_NWID_LEN]; 1883170530Ssam uint8_t tmpbssid[IEEE80211_ADDR_LEN]; 1884138568Ssam struct ieee80211_key *k; 1885138568Ssam int j, caps; 1886138568Ssam u_int kid; 1887138568Ssam 1888138568Ssam error = 0; 1889138568Ssam switch (ireq->i_type) { 1890138568Ssam case IEEE80211_IOC_SSID: 1891138568Ssam if (ireq->i_val != 0 || 1892138568Ssam ireq->i_len > IEEE80211_NWID_LEN) 1893138568Ssam return EINVAL; 1894138568Ssam error = copyin(ireq->i_data, tmpssid, ireq->i_len); 1895138568Ssam if (error) 1896116742Ssam break; 1897170530Ssam memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN); 1898170530Ssam ic->ic_des_ssid[0].len = ireq->i_len; 1899170530Ssam memcpy(ic->ic_des_ssid[0].ssid, tmpssid, ireq->i_len); 1900170530Ssam ic->ic_des_nssid = (ireq->i_len > 0); 1901170530Ssam if (IS_UP_AUTO(ic)) 1902170530Ssam error = ieee80211_init(ic, RESCAN); 1903138568Ssam break; 1904138568Ssam case IEEE80211_IOC_WEP: 1905138568Ssam switch (ireq->i_val) { 1906138568Ssam case IEEE80211_WEP_OFF: 1907138568Ssam ic->ic_flags &= ~IEEE80211_F_PRIVACY; 1908138568Ssam ic->ic_flags &= ~IEEE80211_F_DROPUNENC; 1909116742Ssam break; 1910138568Ssam case IEEE80211_WEP_ON: 1911138568Ssam ic->ic_flags |= IEEE80211_F_PRIVACY; 1912138568Ssam ic->ic_flags |= IEEE80211_F_DROPUNENC; 1913116742Ssam break; 1914138568Ssam case IEEE80211_WEP_MIXED: 1915138568Ssam ic->ic_flags |= IEEE80211_F_PRIVACY; 1916138568Ssam ic->ic_flags &= ~IEEE80211_F_DROPUNENC; 1917116742Ssam break; 1918138568Ssam } 1919170530Ssam if (IS_UP_AUTO(ic)) 1920170530Ssam error = ieee80211_init(ic, RESCAN); 1921138568Ssam break; 1922138568Ssam case IEEE80211_IOC_WEPKEY: 1923138568Ssam kid = (u_int) ireq->i_val; 1924138568Ssam if (kid >= IEEE80211_WEP_NKID) 1925138568Ssam return EINVAL; 1926138568Ssam k = &ic->ic_nw_keys[kid]; 1927138568Ssam if (ireq->i_len == 0) { 1928138568Ssam /* zero-len =>'s delete any existing key */ 1929138568Ssam (void) ieee80211_crypto_delkey(ic, k); 1930127648Ssam break; 1931138568Ssam } 1932138568Ssam if (ireq->i_len > sizeof(tmpkey)) 1933138568Ssam return EINVAL; 1934138568Ssam memset(tmpkey, 0, sizeof(tmpkey)); 1935138568Ssam error = copyin(ireq->i_data, tmpkey, ireq->i_len); 1936138568Ssam if (error) 1937138568Ssam break; 1938138568Ssam ieee80211_key_update_begin(ic); 1939144960Ssam k->wk_keyix = kid; /* NB: force fixed key id */ 1940144960Ssam if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP, 1941144960Ssam IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) { 1942138568Ssam k->wk_keylen = ireq->i_len; 1943138568Ssam memcpy(k->wk_key, tmpkey, sizeof(tmpkey)); 1944138568Ssam if (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr)) 1945127648Ssam error = EINVAL; 1946138568Ssam } else 1947138568Ssam error = EINVAL; 1948138568Ssam ieee80211_key_update_end(ic); 1949138568Ssam break; 1950138568Ssam case IEEE80211_IOC_WEPTXKEY: 1951138568Ssam kid = (u_int) ireq->i_val; 1952139519Ssam if (kid >= IEEE80211_WEP_NKID && 1953170530Ssam (uint16_t) kid != IEEE80211_KEYIX_NONE) 1954138568Ssam return EINVAL; 1955138568Ssam ic->ic_def_txkey = kid; 1956138568Ssam break; 1957138568Ssam case IEEE80211_IOC_AUTHMODE: 1958138568Ssam switch (ireq->i_val) { 1959138568Ssam case IEEE80211_AUTH_WPA: 1960138568Ssam case IEEE80211_AUTH_8021X: /* 802.1x */ 1961138568Ssam case IEEE80211_AUTH_OPEN: /* open */ 1962138568Ssam case IEEE80211_AUTH_SHARED: /* shared-key */ 1963138568Ssam case IEEE80211_AUTH_AUTO: /* auto */ 1964138568Ssam auth = ieee80211_authenticator_get(ireq->i_val); 1965138568Ssam if (auth == NULL) 1966138568Ssam return EINVAL; 1967127648Ssam break; 1968116742Ssam default: 1969138568Ssam return EINVAL; 1970138568Ssam } 1971138568Ssam switch (ireq->i_val) { 1972138568Ssam case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */ 1973138568Ssam ic->ic_flags |= IEEE80211_F_PRIVACY; 1974138568Ssam ireq->i_val = IEEE80211_AUTH_8021X; 1975127646Ssam break; 1976138568Ssam case IEEE80211_AUTH_OPEN: /* open */ 1977138568Ssam ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY); 1978138568Ssam break; 1979138568Ssam case IEEE80211_AUTH_SHARED: /* shared-key */ 1980138568Ssam case IEEE80211_AUTH_8021X: /* 802.1x */ 1981138568Ssam ic->ic_flags &= ~IEEE80211_F_WPA; 1982138568Ssam /* both require a key so mark the PRIVACY capability */ 1983138568Ssam ic->ic_flags |= IEEE80211_F_PRIVACY; 1984138568Ssam break; 1985138568Ssam case IEEE80211_AUTH_AUTO: /* auto */ 1986138568Ssam ic->ic_flags &= ~IEEE80211_F_WPA; 1987138568Ssam /* XXX PRIVACY handling? */ 1988138568Ssam /* XXX what's the right way to do this? */ 1989138568Ssam break; 1990116742Ssam } 1991138568Ssam /* NB: authenticator attach/detach happens on state change */ 1992138568Ssam ic->ic_bss->ni_authmode = ireq->i_val; 1993138568Ssam /* XXX mixed/mode/usage? */ 1994138568Ssam ic->ic_auth = auth; 1995170530Ssam if (IS_UP_AUTO(ic)) 1996170530Ssam error = ieee80211_init(ic, RESCAN); 1997116742Ssam break; 1998138568Ssam case IEEE80211_IOC_CHANNEL: 1999170530Ssam error = ieee80211_ioctl_setchannel(ic, ireq); 2000138568Ssam break; 2001138568Ssam case IEEE80211_IOC_POWERSAVE: 2002138568Ssam switch (ireq->i_val) { 2003138568Ssam case IEEE80211_POWERSAVE_OFF: 2004138568Ssam if (ic->ic_flags & IEEE80211_F_PMGTON) { 2005138568Ssam ic->ic_flags &= ~IEEE80211_F_PMGTON; 2006138568Ssam error = ENETRESET; 2007116742Ssam } 2008116742Ssam break; 2009138568Ssam case IEEE80211_POWERSAVE_ON: 2010138568Ssam if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) 2011116742Ssam error = EINVAL; 2012138568Ssam else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { 2013138568Ssam ic->ic_flags |= IEEE80211_F_PMGTON; 2014116742Ssam error = ENETRESET; 2015116742Ssam } 2016116742Ssam break; 2017138568Ssam default: 2018138568Ssam error = EINVAL; 2019116742Ssam break; 2020138568Ssam } 2021160407Ssam if (error == ENETRESET) { 2022160407Ssam /* 2023160407Ssam * Switching in+out of power save mode 2024160407Ssam * should not require a state change. 2025160407Ssam */ 2026160407Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2027160407Ssam } 2028138568Ssam break; 2029138568Ssam case IEEE80211_IOC_POWERSAVESLEEP: 2030138568Ssam if (ireq->i_val < 0) 2031138568Ssam return EINVAL; 2032138568Ssam ic->ic_lintval = ireq->i_val; 2033138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2034138568Ssam break; 2035138568Ssam case IEEE80211_IOC_RTSTHRESHOLD: 2036148292Ssam if (!(IEEE80211_RTS_MIN <= ireq->i_val && 2037148292Ssam ireq->i_val <= IEEE80211_RTS_MAX)) 2038138568Ssam return EINVAL; 2039138568Ssam ic->ic_rtsthreshold = ireq->i_val; 2040138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2041138568Ssam break; 2042138568Ssam case IEEE80211_IOC_PROTMODE: 2043138568Ssam if (ireq->i_val > IEEE80211_PROT_RTSCTS) 2044138568Ssam return EINVAL; 2045138568Ssam ic->ic_protmode = ireq->i_val; 2046138568Ssam /* NB: if not operating in 11g this can wait */ 2047170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && 2048170530Ssam IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) 2049138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2050138568Ssam break; 2051138568Ssam case IEEE80211_IOC_TXPOWER: 2052138568Ssam if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) 2053138568Ssam return EINVAL; 2054170530Ssam if (!(IEEE80211_TXPOWER_MIN <= ireq->i_val && 2055170530Ssam ireq->i_val <= IEEE80211_TXPOWER_MAX)) 2056138568Ssam return EINVAL; 2057138568Ssam ic->ic_txpowlimit = ireq->i_val; 2058138568Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2059138568Ssam break; 2060138568Ssam case IEEE80211_IOC_ROAMING: 2061138568Ssam if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val && 2062138568Ssam ireq->i_val <= IEEE80211_ROAMING_MANUAL)) 2063138568Ssam return EINVAL; 2064138568Ssam ic->ic_roaming = ireq->i_val; 2065138568Ssam /* XXXX reset? */ 2066138568Ssam break; 2067138568Ssam case IEEE80211_IOC_PRIVACY: 2068138568Ssam if (ireq->i_val) { 2069138568Ssam /* XXX check for key state? */ 2070138568Ssam ic->ic_flags |= IEEE80211_F_PRIVACY; 2071138568Ssam } else 2072138568Ssam ic->ic_flags &= ~IEEE80211_F_PRIVACY; 2073138568Ssam break; 2074138568Ssam case IEEE80211_IOC_DROPUNENCRYPTED: 2075138568Ssam if (ireq->i_val) 2076138568Ssam ic->ic_flags |= IEEE80211_F_DROPUNENC; 2077138568Ssam else 2078138568Ssam ic->ic_flags &= ~IEEE80211_F_DROPUNENC; 2079138568Ssam break; 2080138568Ssam case IEEE80211_IOC_WPAKEY: 2081138568Ssam error = ieee80211_ioctl_setkey(ic, ireq); 2082138568Ssam break; 2083138568Ssam case IEEE80211_IOC_DELKEY: 2084138568Ssam error = ieee80211_ioctl_delkey(ic, ireq); 2085138568Ssam break; 2086138568Ssam case IEEE80211_IOC_MLME: 2087138568Ssam error = ieee80211_ioctl_setmlme(ic, ireq); 2088138568Ssam break; 2089138568Ssam case IEEE80211_IOC_OPTIE: 2090138568Ssam error = ieee80211_ioctl_setoptie(ic, ireq); 2091138568Ssam break; 2092138568Ssam case IEEE80211_IOC_COUNTERMEASURES: 2093138568Ssam if (ireq->i_val) { 2094138568Ssam if ((ic->ic_flags & IEEE80211_F_WPA) == 0) 2095138568Ssam return EINVAL; 2096138568Ssam ic->ic_flags |= IEEE80211_F_COUNTERM; 2097138568Ssam } else 2098138568Ssam ic->ic_flags &= ~IEEE80211_F_COUNTERM; 2099138568Ssam break; 2100138568Ssam case IEEE80211_IOC_WPA: 2101138568Ssam if (ireq->i_val > 3) 2102138568Ssam return EINVAL; 2103138568Ssam /* XXX verify ciphers available */ 2104138568Ssam ic->ic_flags &= ~IEEE80211_F_WPA; 2105138568Ssam switch (ireq->i_val) { 2106138568Ssam case 1: 2107138568Ssam ic->ic_flags |= IEEE80211_F_WPA1; 2108116742Ssam break; 2109138568Ssam case 2: 2110138568Ssam ic->ic_flags |= IEEE80211_F_WPA2; 2111116742Ssam break; 2112138568Ssam case 3: 2113138568Ssam ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2; 2114127648Ssam break; 2115138568Ssam } 2116170530Ssam error = ENETRESET; 2117138568Ssam break; 2118138568Ssam case IEEE80211_IOC_WME: 2119138568Ssam if (ireq->i_val) { 2120138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 2121138568Ssam return EINVAL; 2122138568Ssam ic->ic_flags |= IEEE80211_F_WME; 2123138568Ssam } else 2124138568Ssam ic->ic_flags &= ~IEEE80211_F_WME; 2125170530Ssam if (IS_UP_AUTO(ic)) 2126170530Ssam error = ieee80211_init(ic, 0); 2127138568Ssam break; 2128138568Ssam case IEEE80211_IOC_HIDESSID: 2129138568Ssam if (ireq->i_val) 2130138568Ssam ic->ic_flags |= IEEE80211_F_HIDESSID; 2131138568Ssam else 2132138568Ssam ic->ic_flags &= ~IEEE80211_F_HIDESSID; 2133138568Ssam error = ENETRESET; 2134138568Ssam break; 2135138568Ssam case IEEE80211_IOC_APBRIDGE: 2136138568Ssam if (ireq->i_val == 0) 2137138568Ssam ic->ic_flags |= IEEE80211_F_NOBRIDGE; 2138138568Ssam else 2139138568Ssam ic->ic_flags &= ~IEEE80211_F_NOBRIDGE; 2140138568Ssam break; 2141138568Ssam case IEEE80211_IOC_MCASTCIPHER: 2142138568Ssam if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 && 2143138568Ssam !ieee80211_crypto_available(ireq->i_val)) 2144138568Ssam return EINVAL; 2145138568Ssam rsn->rsn_mcastcipher = ireq->i_val; 2146138568Ssam error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 2147138568Ssam break; 2148138568Ssam case IEEE80211_IOC_MCASTKEYLEN: 2149138568Ssam if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE)) 2150138568Ssam return EINVAL; 2151138568Ssam /* XXX no way to verify driver capability */ 2152138568Ssam rsn->rsn_mcastkeylen = ireq->i_val; 2153138568Ssam error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 2154138568Ssam break; 2155138568Ssam case IEEE80211_IOC_UCASTCIPHERS: 2156138568Ssam /* 2157138568Ssam * Convert user-specified cipher set to the set 2158138568Ssam * we can support (via hardware or software). 2159138568Ssam * NB: this logic intentionally ignores unknown and 2160138568Ssam * unsupported ciphers so folks can specify 0xff or 2161138568Ssam * similar and get all available ciphers. 2162138568Ssam */ 2163138568Ssam caps = 0; 2164138568Ssam for (j = 1; j < 32; j++) /* NB: skip WEP */ 2165138568Ssam if ((ireq->i_val & (1<<j)) && 2166138568Ssam ((ic->ic_caps & cipher2cap(j)) || 2167138568Ssam ieee80211_crypto_available(j))) 2168138568Ssam caps |= 1<<j; 2169138568Ssam if (caps == 0) /* nothing available */ 2170138568Ssam return EINVAL; 2171138568Ssam /* XXX verify ciphers ok for unicast use? */ 2172138568Ssam /* XXX disallow if running as it'll have no effect */ 2173138568Ssam rsn->rsn_ucastcipherset = caps; 2174138568Ssam error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 2175138568Ssam break; 2176138568Ssam case IEEE80211_IOC_UCASTCIPHER: 2177138568Ssam if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0) 2178138568Ssam return EINVAL; 2179138568Ssam rsn->rsn_ucastcipher = ireq->i_val; 2180138568Ssam break; 2181138568Ssam case IEEE80211_IOC_UCASTKEYLEN: 2182138568Ssam if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE)) 2183138568Ssam return EINVAL; 2184138568Ssam /* XXX no way to verify driver capability */ 2185138568Ssam rsn->rsn_ucastkeylen = ireq->i_val; 2186138568Ssam break; 2187138568Ssam case IEEE80211_IOC_DRIVER_CAPS: 2188138568Ssam /* NB: for testing */ 2189170530Ssam ic->ic_caps = (((uint16_t) ireq->i_val) << 16) | 2190170530Ssam ((uint16_t) ireq->i_len); 2191138568Ssam break; 2192138568Ssam case IEEE80211_IOC_KEYMGTALGS: 2193138568Ssam /* XXX check */ 2194138568Ssam rsn->rsn_keymgmtset = ireq->i_val; 2195138568Ssam error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 2196138568Ssam break; 2197138568Ssam case IEEE80211_IOC_RSNCAPS: 2198138568Ssam /* XXX check */ 2199138568Ssam rsn->rsn_caps = ireq->i_val; 2200138568Ssam error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; 2201138568Ssam break; 2202138568Ssam case IEEE80211_IOC_BSSID: 2203138568Ssam if (ireq->i_len != sizeof(tmpbssid)) 2204138568Ssam return EINVAL; 2205138568Ssam error = copyin(ireq->i_data, tmpbssid, ireq->i_len); 2206138568Ssam if (error) 2207127648Ssam break; 2208138568Ssam IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid); 2209138568Ssam if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid)) 2210138568Ssam ic->ic_flags &= ~IEEE80211_F_DESBSSID; 2211138568Ssam else 2212138568Ssam ic->ic_flags |= IEEE80211_F_DESBSSID; 2213170530Ssam if (IS_UP_AUTO(ic)) 2214170530Ssam error = ieee80211_init(ic, RESCAN); 2215138568Ssam break; 2216138568Ssam case IEEE80211_IOC_CHANLIST: 2217138568Ssam error = ieee80211_ioctl_setchanlist(ic, ireq); 2218138568Ssam break; 2219138568Ssam case IEEE80211_IOC_SCAN_REQ: 2220170530Ssam if (!IS_UP(ic)) 2221170530Ssam return EINVAL; 2222170530Ssam (void) ieee80211_start_scan(ic, 2223170530Ssam IEEE80211_SCAN_ACTIVE | 2224170530Ssam IEEE80211_SCAN_NOPICK | 2225170530Ssam IEEE80211_SCAN_ONCE, IEEE80211_SCAN_FOREVER, 2226170530Ssam /* XXX use ioctl params */ 2227170530Ssam ic->ic_des_nssid, ic->ic_des_ssid); 2228116742Ssam break; 2229138568Ssam case IEEE80211_IOC_ADDMAC: 2230138568Ssam case IEEE80211_IOC_DELMAC: 2231138568Ssam error = ieee80211_ioctl_macmac(ic, ireq); 2232138568Ssam break; 2233138568Ssam case IEEE80211_IOC_MACCMD: 2234149028Ssam error = ieee80211_ioctl_setmaccmd(ic, ireq); 2235138568Ssam break; 2236157172Ssam case IEEE80211_IOC_STA_STATS: 2237157172Ssam error = ieee80211_ioctl_setstastats(ic, ireq); 2238157172Ssam break; 2239138568Ssam case IEEE80211_IOC_STA_TXPOW: 2240138568Ssam error = ieee80211_ioctl_setstatxpow(ic, ireq); 2241138568Ssam break; 2242138568Ssam case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ 2243138568Ssam case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ 2244138568Ssam case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ 2245138568Ssam case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ 2246138568Ssam case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ 2247138568Ssam case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ 2248138568Ssam error = ieee80211_ioctl_setwmeparam(ic, ireq); 2249138568Ssam break; 2250138568Ssam case IEEE80211_IOC_DTIM_PERIOD: 2251138568Ssam if (ic->ic_opmode != IEEE80211_M_HOSTAP && 2252138568Ssam ic->ic_opmode != IEEE80211_M_IBSS) 2253138568Ssam return EINVAL; 2254138568Ssam if (IEEE80211_DTIM_MIN <= ireq->i_val && 2255138568Ssam ireq->i_val <= IEEE80211_DTIM_MAX) { 2256138568Ssam ic->ic_dtim_period = ireq->i_val; 2257138568Ssam error = ENETRESET; /* requires restart */ 2258138568Ssam } else 2259138568Ssam error = EINVAL; 2260138568Ssam break; 2261138568Ssam case IEEE80211_IOC_BEACON_INTERVAL: 2262138568Ssam if (ic->ic_opmode != IEEE80211_M_HOSTAP && 2263138568Ssam ic->ic_opmode != IEEE80211_M_IBSS) 2264138568Ssam return EINVAL; 2265138568Ssam if (IEEE80211_BINTVAL_MIN <= ireq->i_val && 2266138568Ssam ireq->i_val <= IEEE80211_BINTVAL_MAX) { 2267148843Ssam ic->ic_bintval = ireq->i_val; 2268138568Ssam error = ENETRESET; /* requires restart */ 2269138568Ssam } else 2270138568Ssam error = EINVAL; 2271138568Ssam break; 2272147794Ssam case IEEE80211_IOC_PUREG: 2273147794Ssam if (ireq->i_val) 2274147794Ssam ic->ic_flags |= IEEE80211_F_PUREG; 2275147794Ssam else 2276147794Ssam ic->ic_flags &= ~IEEE80211_F_PUREG; 2277147794Ssam /* NB: reset only if we're operating on an 11g channel */ 2278170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && 2279170530Ssam IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) 2280147794Ssam error = ENETRESET; 2281147794Ssam break; 2282170530Ssam case IEEE80211_IOC_FF: 2283170530Ssam if (ireq->i_val) { 2284170530Ssam if ((ic->ic_caps & IEEE80211_C_FF) == 0) 2285170530Ssam return EINVAL; 2286170530Ssam ic->ic_flags |= IEEE80211_F_FF; 2287170530Ssam } else 2288170530Ssam ic->ic_flags &= ~IEEE80211_F_FF; 2289170530Ssam error = ENETRESET; 2290170530Ssam break; 2291170530Ssam case IEEE80211_IOC_TURBOP: 2292170530Ssam if (ireq->i_val) { 2293170530Ssam if ((ic->ic_caps & IEEE80211_C_TURBOP) == 0) 2294170530Ssam return EINVAL; 2295170530Ssam ic->ic_flags |= IEEE80211_F_TURBOP; 2296170530Ssam } else 2297170530Ssam ic->ic_flags &= ~IEEE80211_F_TURBOP; 2298170530Ssam error = ENETRESET; 2299170530Ssam break; 2300170530Ssam case IEEE80211_IOC_BGSCAN: 2301170530Ssam if (ireq->i_val) { 2302170530Ssam if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0) 2303170530Ssam return EINVAL; 2304170530Ssam ic->ic_flags |= IEEE80211_F_BGSCAN; 2305170530Ssam } else 2306170530Ssam ic->ic_flags &= ~IEEE80211_F_BGSCAN; 2307170530Ssam break; 2308170530Ssam case IEEE80211_IOC_BGSCAN_IDLE: 2309170530Ssam if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN) 2310170530Ssam ic->ic_bgscanidle = ireq->i_val*hz/1000; 2311170530Ssam else 2312170530Ssam error = EINVAL; 2313170530Ssam break; 2314170530Ssam case IEEE80211_IOC_BGSCAN_INTERVAL: 2315170530Ssam if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN) 2316170530Ssam ic->ic_bgscanintvl = ireq->i_val*hz; 2317170530Ssam else 2318170530Ssam error = EINVAL; 2319170530Ssam break; 2320170530Ssam case IEEE80211_IOC_SCANVALID: 2321170530Ssam if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN) 2322170530Ssam ic->ic_scanvalid = ireq->i_val*hz; 2323170530Ssam else 2324170530Ssam error = EINVAL; 2325170530Ssam break; 2326170530Ssam case IEEE80211_IOC_ROAM_RSSI_11A: 2327170530Ssam ic->ic_roam.rssi11a = ireq->i_val; 2328170530Ssam break; 2329170530Ssam case IEEE80211_IOC_ROAM_RSSI_11B: 2330170530Ssam ic->ic_roam.rssi11bOnly = ireq->i_val; 2331170530Ssam break; 2332170530Ssam case IEEE80211_IOC_ROAM_RSSI_11G: 2333170530Ssam ic->ic_roam.rssi11b = ireq->i_val; 2334170530Ssam break; 2335170530Ssam case IEEE80211_IOC_ROAM_RATE_11A: 2336170530Ssam ic->ic_roam.rate11a = ireq->i_val & IEEE80211_RATE_VAL; 2337170530Ssam break; 2338170530Ssam case IEEE80211_IOC_ROAM_RATE_11B: 2339170530Ssam ic->ic_roam.rate11bOnly = ireq->i_val & IEEE80211_RATE_VAL; 2340170530Ssam break; 2341170530Ssam case IEEE80211_IOC_ROAM_RATE_11G: 2342170530Ssam ic->ic_roam.rate11b = ireq->i_val & IEEE80211_RATE_VAL; 2343170530Ssam break; 2344153346Ssam case IEEE80211_IOC_MCAST_RATE: 2345153346Ssam ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL; 2346153346Ssam break; 2347148292Ssam case IEEE80211_IOC_FRAGTHRESHOLD: 2348148292Ssam if ((ic->ic_caps & IEEE80211_C_TXFRAG) == 0 && 2349148292Ssam ireq->i_val != IEEE80211_FRAG_MAX) 2350148292Ssam return EINVAL; 2351148292Ssam if (!(IEEE80211_FRAG_MIN <= ireq->i_val && 2352148292Ssam ireq->i_val <= IEEE80211_FRAG_MAX)) 2353148292Ssam return EINVAL; 2354148292Ssam ic->ic_fragthreshold = ireq->i_val; 2355148292Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2356148292Ssam break; 2357153421Ssam case IEEE80211_IOC_BURST: 2358153421Ssam if (ireq->i_val) { 2359153421Ssam if ((ic->ic_caps & IEEE80211_C_BURST) == 0) 2360153421Ssam return EINVAL; 2361153421Ssam ic->ic_flags |= IEEE80211_F_BURST; 2362153421Ssam } else 2363153421Ssam ic->ic_flags &= ~IEEE80211_F_BURST; 2364153421Ssam error = ENETRESET; /* XXX maybe not for station? */ 2365153421Ssam break; 2366160686Ssam case IEEE80211_IOC_BMISSTHRESHOLD: 2367160686Ssam if (!(IEEE80211_HWBMISS_MIN <= ireq->i_val && 2368160686Ssam ireq->i_val <= IEEE80211_HWBMISS_MAX)) 2369160686Ssam return EINVAL; 2370160686Ssam ic->ic_bmissthreshold = ireq->i_val; 2371160686Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2372160686Ssam break; 2373170530Ssam case IEEE80211_IOC_CURCHAN: 2374170530Ssam error = ieee80211_ioctl_setcurchan(ic, ireq); 2375170530Ssam break; 2376170530Ssam case IEEE80211_IOC_SHORTGI: 2377170530Ssam if (ireq->i_val) { 2378170530Ssam#define IEEE80211_HTCAP_SHORTGI \ 2379170530Ssam (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) 2380170530Ssam if (((ireq->i_val ^ ic->ic_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0) 2381170530Ssam return EINVAL; 2382170530Ssam if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20) 2383170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20; 2384170530Ssam if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40) 2385170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40; 2386170530Ssam#undef IEEE80211_HTCAP_SHORTGI 2387170530Ssam } else 2388170530Ssam ic->ic_flags_ext &= 2389170530Ssam ~(IEEE80211_FEXT_SHORTGI20 | IEEE80211_FEXT_SHORTGI40); 2390170530Ssam /* XXX kick state machine? */ 2391170530Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2392170530Ssam break; 2393170530Ssam case IEEE80211_IOC_AMPDU: 2394170530Ssam if (ireq->i_val) { 2395170530Ssam if ((ic->ic_htcaps & IEEE80211_HTC_AMPDU) == 0) 2396170530Ssam return EINVAL; 2397170530Ssam if (ireq->i_val & 1) 2398170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX; 2399170530Ssam if (ireq->i_val & 2) 2400170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX; 2401170530Ssam } else 2402170530Ssam ic->ic_flags_ext &= 2403170530Ssam ~(IEEE80211_FEXT_AMPDU_TX|IEEE80211_FEXT_AMPDU_RX); 2404170530Ssam /* NB: reset only if we're operating on an 11n channel */ 2405170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && 2406170530Ssam IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) 2407170530Ssam error = ENETRESET; 2408170530Ssam break; 2409170530Ssam case IEEE80211_IOC_AMPDU_LIMIT: 2410170530Ssam /* XXX validate */ 2411170530Ssam ic->ic_ampdu_limit = ireq->i_val; 2412170530Ssam break; 2413170530Ssam case IEEE80211_IOC_AMPDU_DENSITY: 2414170530Ssam /* XXX validate */ 2415170530Ssam ic->ic_ampdu_density = ireq->i_val; 2416170530Ssam break; 2417170530Ssam case IEEE80211_IOC_AMSDU: 2418170530Ssam if (ireq->i_val) { 2419170530Ssam if ((ic->ic_htcaps & IEEE80211_HTC_AMSDU) == 0) 2420170530Ssam return EINVAL; 2421170530Ssam if (ireq->i_val & 1) 2422170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX; 2423170530Ssam if (ireq->i_val & 2) 2424170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX; 2425170530Ssam } else 2426170530Ssam ic->ic_flags_ext &= 2427170530Ssam ~(IEEE80211_FEXT_AMSDU_TX|IEEE80211_FEXT_AMSDU_RX); 2428170530Ssam /* NB: reset only if we're operating on an 11n channel */ 2429170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && 2430170530Ssam IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) 2431170530Ssam error = ENETRESET; 2432170530Ssam break; 2433170530Ssam case IEEE80211_IOC_AMSDU_LIMIT: 2434170530Ssam /* XXX validate */ 2435170530Ssam ic->ic_amsdu_limit = ireq->i_val; /* XXX truncation? */ 2436170530Ssam break; 2437170530Ssam case IEEE80211_IOC_PUREN: 2438170530Ssam if (ireq->i_val) { 2439170530Ssam if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0) 2440170530Ssam return EINVAL; 2441170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_PUREN; 2442170530Ssam } else 2443170530Ssam ic->ic_flags_ext &= ~IEEE80211_FEXT_PUREN; 2444170530Ssam /* NB: reset only if we're operating on an 11n channel */ 2445170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && 2446170530Ssam IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) 2447170530Ssam error = ENETRESET; 2448170530Ssam break; 2449170530Ssam case IEEE80211_IOC_DOTH: 2450170530Ssam if (ireq->i_val) { 2451170530Ssam#if 0 2452170530Ssam /* XXX no capability */ 2453170530Ssam if ((ic->ic_caps & IEEE80211_C_DOTH) == 0) 2454170530Ssam return EINVAL; 2455170530Ssam#endif 2456170530Ssam ic->ic_flags |= IEEE80211_F_DOTH; 2457170530Ssam } else 2458170530Ssam ic->ic_flags &= ~IEEE80211_F_DOTH; 2459170530Ssam error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; 2460170530Ssam break; 2461170530Ssam case IEEE80211_IOC_HTCOMPAT: 2462170530Ssam if (ireq->i_val) { 2463170530Ssam if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0) 2464170530Ssam return EINVAL; 2465170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_HTCOMPAT; 2466170530Ssam } else 2467170530Ssam ic->ic_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT; 2468170530Ssam /* NB: reset only if we're operating on an 11n channel */ 2469170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && 2470170530Ssam IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) 2471170530Ssam error = ENETRESET; 2472170530Ssam break; 2473138568Ssam default: 2474138568Ssam error = EINVAL; 2475138568Ssam break; 2476138568Ssam } 2477170530Ssam if (error == ENETRESET) 2478170530Ssam error = IS_UP_AUTO(ic) ? ieee80211_init(ic, 0) : 0; 2479138568Ssam return error; 2480138568Ssam} 2481138568Ssam 2482138568Ssamint 2483138568Ssamieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data) 2484138568Ssam{ 2485138568Ssam struct ifnet *ifp = ic->ic_ifp; 2486138568Ssam int error = 0; 2487138568Ssam struct ifreq *ifr; 2488138568Ssam struct ifaddr *ifa; /* XXX */ 2489138568Ssam 2490138568Ssam switch (cmd) { 2491138568Ssam case SIOCSIFMEDIA: 2492138568Ssam case SIOCGIFMEDIA: 2493138568Ssam error = ifmedia_ioctl(ifp, (struct ifreq *) data, 2494138568Ssam &ic->ic_media, cmd); 2495138568Ssam break; 2496138568Ssam case SIOCG80211: 2497138568Ssam error = ieee80211_ioctl_get80211(ic, cmd, 2498138568Ssam (struct ieee80211req *) data); 2499138568Ssam break; 2500138568Ssam case SIOCS80211: 2501164033Srwatson error = priv_check(curthread, PRIV_NET80211_MANAGE); 2502138568Ssam if (error == 0) 2503138568Ssam error = ieee80211_ioctl_set80211(ic, cmd, 2504138568Ssam (struct ieee80211req *) data); 2505138568Ssam break; 2506121180Ssam case SIOCG80211STATS: 2507121180Ssam ifr = (struct ifreq *)data; 2508121180Ssam copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats)); 2509121180Ssam break; 2510124457Ssam case SIOCSIFMTU: 2511124457Ssam ifr = (struct ifreq *)data; 2512124457Ssam if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu && 2513124457Ssam ifr->ifr_mtu <= IEEE80211_MTU_MAX)) 2514124457Ssam error = EINVAL; 2515124457Ssam else 2516124457Ssam ifp->if_mtu = ifr->ifr_mtu; 2517124457Ssam break; 2518127646Ssam case SIOCSIFADDR: 2519127646Ssam /* 2520127646Ssam * XXX Handle this directly so we can supress if_init calls. 2521127646Ssam * XXX This should be done in ether_ioctl but for the moment 2522127646Ssam * XXX there are too many other parts of the system that 2523127646Ssam * XXX set IFF_UP and so supress if_init being called when 2524127646Ssam * XXX it should be. 2525127646Ssam */ 2526127646Ssam ifa = (struct ifaddr *) data; 2527127646Ssam switch (ifa->ifa_addr->sa_family) { 2528127646Ssam#ifdef INET 2529127646Ssam case AF_INET: 2530127646Ssam if ((ifp->if_flags & IFF_UP) == 0) { 2531127646Ssam ifp->if_flags |= IFF_UP; 2532127646Ssam ifp->if_init(ifp->if_softc); 2533127646Ssam } 2534127646Ssam arp_ifinit(ifp, ifa); 2535127646Ssam break; 2536127646Ssam#endif 2537127646Ssam#ifdef IPX 2538127646Ssam /* 2539127646Ssam * XXX - This code is probably wrong, 2540127646Ssam * but has been copied many times. 2541127646Ssam */ 2542127646Ssam case AF_IPX: { 2543127646Ssam struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); 2544127646Ssam 2545127646Ssam if (ipx_nullhost(*ina)) 2546147256Sbrooks ina->x_host = *(union ipx_host *) 2547152315Sru IF_LLADDR(ifp); 2548127646Ssam else 2549127646Ssam bcopy((caddr_t) ina->x_host.c_host, 2550152315Sru (caddr_t) IF_LLADDR(ifp), 2551147256Sbrooks ETHER_ADDR_LEN); 2552127646Ssam /* fall thru... */ 2553127646Ssam } 2554127646Ssam#endif 2555127646Ssam default: 2556127646Ssam if ((ifp->if_flags & IFF_UP) == 0) { 2557127646Ssam ifp->if_flags |= IFF_UP; 2558127646Ssam ifp->if_init(ifp->if_softc); 2559127646Ssam } 2560127646Ssam break; 2561127646Ssam } 2562127646Ssam break; 2563116742Ssam default: 2564116742Ssam error = ether_ioctl(ifp, cmd, data); 2565116742Ssam break; 2566116742Ssam } 2567116742Ssam return error; 2568116742Ssam} 2569