diff options
Diffstat (limited to 'sys/net80211/ieee80211_ioctl.c')
-rw-r--r-- | sys/net80211/ieee80211_ioctl.c | 1902 |
1 files changed, 852 insertions, 1050 deletions
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c index 17befd2..5929edd 100644 --- a/sys/net80211/ieee80211_ioctl.c +++ b/sys/net80211/ieee80211_ioctl.c @@ -62,752 +62,15 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_ioctl.h> -#include <dev/wi/if_wavelan_ieee.h> - #define IS_UP(_ic) \ (((_ic)->ic_ifp->if_flags & IFF_UP) && \ ((_ic)->ic_ifp->if_drv_flags & IFF_DRV_RUNNING)) #define IS_UP_AUTO(_ic) \ (IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO) +#define RESCAN 1 -/* - * XXX - * Wireless LAN specific configuration interface, which is compatible - * with wicontrol(8). - */ - -struct wi_read_ap_args { - int i; /* result count */ - struct wi_apinfo *ap; /* current entry in result buffer */ - caddr_t max; /* result buffer bound */ -}; - -static void -wi_read_ap_result(void *arg, struct ieee80211_node *ni) -{ - struct ieee80211com *ic = ni->ni_ic; - struct wi_read_ap_args *sa = arg; - struct wi_apinfo *ap = sa->ap; - struct ieee80211_rateset *rs; - int j; - - if ((caddr_t)(ap + 1) > sa->max) - return; - memset(ap, 0, sizeof(struct wi_apinfo)); - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr); - ap->namelen = ic->ic_des_esslen; - if (ic->ic_des_esslen) - memcpy(ap->name, ic->ic_des_essid, - ic->ic_des_esslen); - } else { - IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid); - ap->namelen = ni->ni_esslen; - if (ni->ni_esslen) - memcpy(ap->name, ni->ni_essid, - ni->ni_esslen); - } - ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan); - ap->signal = ic->ic_node_getrssi(ni); - ap->capinfo = ni->ni_capinfo; - ap->interval = ni->ni_intval; - rs = &ni->ni_rates; - for (j = 0; j < rs->rs_nrates; j++) { - if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) { - ap->rate = (rs->rs_rates[j] & - IEEE80211_RATE_VAL) * 5; /* XXX */ - } - } - sa->i++; - sa->ap++; -} - -struct wi_read_prism2_args { - int i; /* result count */ - struct wi_scan_res *res;/* current entry in result buffer */ - caddr_t max; /* result buffer bound */ -}; - -static void -wi_read_prism2_result(void *arg, struct ieee80211_node *ni) -{ - struct ieee80211com *ic = ni->ni_ic; - struct wi_read_prism2_args *sa = arg; - struct wi_scan_res *res = sa->res; - - if ((caddr_t)(res + 1) > sa->max) - return; - res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan); - res->wi_noise = 0; - res->wi_signal = ic->ic_node_getrssi(ni); - IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid); - res->wi_interval = ni->ni_intval; - res->wi_capinfo = ni->ni_capinfo; - res->wi_ssid_len = ni->ni_esslen; - memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN); - /* NB: assumes wi_srates holds <= ni->ni_rates */ - memcpy(res->wi_srates, ni->ni_rates.rs_rates, - sizeof(res->wi_srates)); - if (ni->ni_rates.rs_nrates < 10) - res->wi_srates[ni->ni_rates.rs_nrates] = 0; - res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate]; - res->wi_rsvd = 0; - - sa->i++; - sa->res++; -} - -struct wi_read_sigcache_args { - int i; /* result count */ - struct wi_sigcache *wsc;/* current entry in result buffer */ - caddr_t max; /* result buffer bound */ -}; - -static void -wi_read_sigcache(void *arg, struct ieee80211_node *ni) -{ - struct ieee80211com *ic = ni->ni_ic; - struct wi_read_sigcache_args *sa = arg; - struct wi_sigcache *wsc = sa->wsc; - - if ((caddr_t)(wsc + 1) > sa->max) - return; - memset(wsc, 0, sizeof(struct wi_sigcache)); - IEEE80211_ADDR_COPY(wsc->macsrc, ni->ni_macaddr); - wsc->signal = ic->ic_node_getrssi(ni); - - sa->wsc++; - sa->i++; -} - -int -ieee80211_cfgget(struct ieee80211com *ic, u_long cmd, caddr_t data) -{ - struct ifnet *ifp = ic->ic_ifp; - int i, j, error; - struct ifreq *ifr = (struct ifreq *)data; - struct wi_req wreq; - struct wi_ltv_keys *keys; - - error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); - if (error) - return error; - wreq.wi_len = 0; - switch (wreq.wi_type) { - case WI_RID_SERIALNO: - /* nothing appropriate */ - break; - case WI_RID_NODENAME: - strcpy((char *)&wreq.wi_val[1], hostname); - wreq.wi_val[0] = htole16(strlen(hostname)); - wreq.wi_len = (1 + strlen(hostname) + 1) / 2; - break; - case WI_RID_CURRENT_SSID: - if (ic->ic_state != IEEE80211_S_RUN) { - wreq.wi_val[0] = 0; - wreq.wi_len = 1; - break; - } - wreq.wi_val[0] = htole16(ic->ic_bss->ni_esslen); - memcpy(&wreq.wi_val[1], ic->ic_bss->ni_essid, - ic->ic_bss->ni_esslen); - wreq.wi_len = (1 + ic->ic_bss->ni_esslen + 1) / 2; - break; - case WI_RID_OWN_SSID: - case WI_RID_DESIRED_SSID: - wreq.wi_val[0] = htole16(ic->ic_des_esslen); - memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen); - wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2; - break; - case WI_RID_CURRENT_BSSID: - if (ic->ic_state == IEEE80211_S_RUN) - IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss->ni_bssid); - else - memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN); - wreq.wi_len = IEEE80211_ADDR_LEN / 2; - break; - case WI_RID_CHANNEL_LIST: - memset(wreq.wi_val, 0, sizeof(wreq.wi_val)); - /* - * Since channel 0 is not available for DS, channel 1 - * is assigned to LSB on WaveLAN. - */ - if (ic->ic_phytype == IEEE80211_T_DS) - i = 1; - else - i = 0; - for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) - if (isset(ic->ic_chan_active, i)) { - setbit((u_int8_t *)wreq.wi_val, j); - wreq.wi_len = j / 16 + 1; - } - break; - case WI_RID_OWN_CHNL: - wreq.wi_val[0] = htole16( - ieee80211_chan2ieee(ic, ic->ic_ibss_chan)); - wreq.wi_len = 1; - break; - case WI_RID_CURRENT_CHAN: - wreq.wi_val[0] = htole16( - ieee80211_chan2ieee(ic, ic->ic_curchan)); - wreq.wi_len = 1; - break; - case WI_RID_COMMS_QUALITY: - wreq.wi_val[0] = 0; /* quality */ - wreq.wi_val[1] = htole16(ic->ic_node_getrssi(ic->ic_bss)); - wreq.wi_val[2] = 0; /* noise */ - wreq.wi_len = 3; - break; - case WI_RID_PROMISC: - wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0); - wreq.wi_len = 1; - break; - case WI_RID_PORTTYPE: - wreq.wi_val[0] = htole16(ic->ic_opmode); - wreq.wi_len = 1; - break; - case WI_RID_MAC_NODE: - IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr); - wreq.wi_len = IEEE80211_ADDR_LEN / 2; - break; - case WI_RID_TX_RATE: - if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) - wreq.wi_val[0] = 0; /* auto */ - else - wreq.wi_val[0] = htole16( - (ic->ic_sup_rates[ic->ic_curmode].rs_rates[ic->ic_fixed_rate] & - IEEE80211_RATE_VAL) / 2); - wreq.wi_len = 1; - break; - case WI_RID_CUR_TX_RATE: - wreq.wi_val[0] = htole16( - (ic->ic_bss->ni_rates.rs_rates[ic->ic_bss->ni_txrate] & - IEEE80211_RATE_VAL) / 2); - wreq.wi_len = 1; - break; - case WI_RID_RTS_THRESH: - wreq.wi_val[0] = htole16(ic->ic_rtsthreshold); - wreq.wi_len = 1; - break; - case WI_RID_CREATE_IBSS: - wreq.wi_val[0] = - htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0); - wreq.wi_len = 1; - break; - case WI_RID_MICROWAVE_OVEN: - wreq.wi_val[0] = 0; /* no ... not supported */ - wreq.wi_len = 1; - break; - case WI_RID_ROAMING_MODE: - wreq.wi_val[0] = htole16(ic->ic_roaming); /* XXX map */ - wreq.wi_len = 1; - break; - case WI_RID_SYSTEM_SCALE: - wreq.wi_val[0] = htole16(1); /* low density ... not supp */ - wreq.wi_len = 1; - break; - case WI_RID_PM_ENABLED: - wreq.wi_val[0] = - htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0); - wreq.wi_len = 1; - break; - case WI_RID_MAX_SLEEP: - wreq.wi_val[0] = htole16(ic->ic_lintval); - wreq.wi_len = 1; - break; - case WI_RID_CUR_BEACON_INT: - wreq.wi_val[0] = htole16(ic->ic_bss->ni_intval); - wreq.wi_len = 1; - break; - case WI_RID_WEP_AVAIL: - wreq.wi_val[0] = htole16(1); /* always available */ - wreq.wi_len = 1; - break; - case WI_RID_CNFAUTHMODE: - wreq.wi_val[0] = htole16(1); /* TODO: open system only */ - wreq.wi_len = 1; - break; - case WI_RID_ENCRYPTION: - wreq.wi_val[0] = - htole16((ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0); - wreq.wi_len = 1; - break; - case WI_RID_TX_CRYPT_KEY: - wreq.wi_val[0] = htole16(ic->ic_def_txkey); - wreq.wi_len = 1; - break; - case WI_RID_DEFLT_CRYPT_KEYS: - keys = (struct wi_ltv_keys *)&wreq; - /* do not show keys to non-root user */ - error = priv_check(curthread, PRIV_NET80211_GETKEY); - if (error) { - memset(keys, 0, sizeof(*keys)); - error = 0; - break; - } - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - keys->wi_keys[i].wi_keylen = - htole16(ic->ic_nw_keys[i].wk_keylen); - memcpy(keys->wi_keys[i].wi_keydat, - ic->ic_nw_keys[i].wk_key, - ic->ic_nw_keys[i].wk_keylen); - } - wreq.wi_len = sizeof(*keys) / 2; - break; - case WI_RID_MAX_DATALEN: - wreq.wi_val[0] = htole16(ic->ic_fragthreshold); - wreq.wi_len = 1; - break; - case WI_RID_IFACE_STATS: - /* XXX: should be implemented in lower drivers */ - break; - case WI_RID_READ_APS: - /* - * Don't return results until active scan completes. - */ - if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) { - struct wi_read_ap_args args; - - args.i = 0; - args.ap = (void *)((char *)wreq.wi_val + sizeof(i)); - args.max = (void *)(&wreq + 1); - ieee80211_iterate_nodes(&ic->ic_scan, - wi_read_ap_result, &args); - memcpy(wreq.wi_val, &args.i, sizeof(args.i)); - wreq.wi_len = (sizeof(int) + - sizeof(struct wi_apinfo) * args.i) / 2; - } else - error = EINPROGRESS; - break; - case WI_RID_PRISM2: - /* NB: we lie so WI_RID_SCAN_RES can include rates */ - wreq.wi_val[0] = 1; - wreq.wi_len = sizeof(u_int16_t) / 2; - break; - case WI_RID_SCAN_RES: /* compatibility interface */ - if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) { - struct wi_read_prism2_args args; - struct wi_scan_p2_hdr *p2; - - /* NB: use Prism2 format so we can include rate info */ - p2 = (struct wi_scan_p2_hdr *)wreq.wi_val; - args.i = 0; - args.res = (void *)&p2[1]; - args.max = (void *)(&wreq + 1); - ieee80211_iterate_nodes(&ic->ic_scan, - wi_read_prism2_result, &args); - p2->wi_rsvd = 0; - p2->wi_reason = args.i; - wreq.wi_len = (sizeof(*p2) + - sizeof(struct wi_scan_res) * args.i) / 2; - } else - error = EINPROGRESS; - break; - case WI_RID_READ_CACHE: { - struct wi_read_sigcache_args args; - args.i = 0; - args.wsc = (struct wi_sigcache *) wreq.wi_val; - args.max = (void *)(&wreq + 1); - ieee80211_iterate_nodes(&ic->ic_scan, wi_read_sigcache, &args); - wreq.wi_len = sizeof(struct wi_sigcache) * args.i / 2; - break; - } - default: - error = EINVAL; - break; - } - if (error == 0) { - wreq.wi_len++; - error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); - } - return error; -} - -static int -findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) -{ -#define IEEERATE(_ic,_m,_i) \ - ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) - int i, nrates = ic->ic_sup_rates[mode].rs_nrates; - for (i = 0; i < nrates; i++) - if (IEEERATE(ic, mode, i) == rate) - return i; - return -1; -#undef IEEERATE -} - -/* - * Prepare to do a user-initiated scan for AP's. If no - * current/default channel is setup or the current channel - * is invalid then pick the first available channel from - * the active list as the place to start the scan. - */ -static int -ieee80211_setupscan(struct ieee80211com *ic, const u_int8_t chanlist[]) -{ - - /* - * XXX don't permit a scan to be started unless we - * know the device is ready. For the moment this means - * the device is marked up as this is the required to - * initialize the hardware. It would be better to permit - * scanning prior to being up but that'll require some - * changes to the infrastructure. - */ - if (!IS_UP(ic)) - return EINVAL; - memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); - /* - * We force the state to INIT before calling ieee80211_new_state - * to get ieee80211_begin_scan called. We really want to scan w/o - * altering the current state but that's not possible right now. - */ - /* XXX handle proberequest case */ - ic->ic_state = IEEE80211_S_INIT; /* XXX bypass state machine */ - return 0; -} - -int -ieee80211_cfgset(struct ieee80211com *ic, u_long cmd, caddr_t data) -{ - struct ifnet *ifp = ic->ic_ifp; - int i, j, len, error, rate; - struct ifreq *ifr = (struct ifreq *)data; - struct wi_ltv_keys *keys; - struct wi_req wreq; - u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)]; - - error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); - if (error) - return error; - len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0; - switch (wreq.wi_type) { - case WI_RID_SERIALNO: - case WI_RID_NODENAME: - return EPERM; - case WI_RID_CURRENT_SSID: - return EPERM; - case WI_RID_OWN_SSID: - case WI_RID_DESIRED_SSID: - if (le16toh(wreq.wi_val[0]) * 2 > len || - le16toh(wreq.wi_val[0]) > IEEE80211_NWID_LEN) { - error = ENOSPC; - break; - } - memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid)); - ic->ic_des_esslen = le16toh(wreq.wi_val[0]) * 2; - memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen); - error = ENETRESET; - break; - case WI_RID_CURRENT_BSSID: - return EPERM; - case WI_RID_OWN_CHNL: - if (len != 2) - return EINVAL; - i = le16toh(wreq.wi_val[0]); - if (i < 0 || - i > IEEE80211_CHAN_MAX || - isclr(ic->ic_chan_active, i)) - return EINVAL; - ic->ic_ibss_chan = &ic->ic_channels[i]; - if (ic->ic_opmode == IEEE80211_M_MONITOR) - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - else - error = ENETRESET; - break; - case WI_RID_CURRENT_CHAN: - return EPERM; - case WI_RID_COMMS_QUALITY: - return EPERM; - case WI_RID_PROMISC: - if (len != 2) - return EINVAL; - if (ifp->if_flags & IFF_PROMISC) { - if (wreq.wi_val[0] == 0) { - ifp->if_flags &= ~IFF_PROMISC; - error = ENETRESET; - } - } else { - if (wreq.wi_val[0] != 0) { - ifp->if_flags |= IFF_PROMISC; - error = ENETRESET; - } - } - break; - case WI_RID_PORTTYPE: - if (len != 2) - return EINVAL; - switch (le16toh(wreq.wi_val[0])) { - case IEEE80211_M_STA: - break; - case IEEE80211_M_IBSS: - if (!(ic->ic_caps & IEEE80211_C_IBSS)) - return EINVAL; - break; - case IEEE80211_M_AHDEMO: - if (ic->ic_phytype != IEEE80211_T_DS || - !(ic->ic_caps & IEEE80211_C_AHDEMO)) - return EINVAL; - break; - case IEEE80211_M_HOSTAP: - if (!(ic->ic_caps & IEEE80211_C_HOSTAP)) - return EINVAL; - break; - default: - return EINVAL; - } - if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) { - ic->ic_opmode = le16toh(wreq.wi_val[0]); - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - } - break; -#if 0 - case WI_RID_MAC_NODE: - if (len != IEEE80211_ADDR_LEN) - return EINVAL; - IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val); - /* if_init will copy lladdr into ic_myaddr */ - error = ENETRESET; - break; -#endif - case WI_RID_TX_RATE: - if (len != 2) - return EINVAL; - if (wreq.wi_val[0] == 0) { - /* auto */ - ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; - break; - } - rate = 2 * le16toh(wreq.wi_val[0]); - if (ic->ic_curmode == IEEE80211_MODE_AUTO) { - /* - * In autoselect mode search for the rate. We take - * the first instance which may not be right, but we - * are limited by the interface. Note that we also - * lock the mode to insure the rate is meaningful - * when it is used. - */ - for (j = IEEE80211_MODE_11A; - j < IEEE80211_MODE_MAX; j++) { - if (isclr(ic->ic_modecaps, j)) - continue; - i = findrate(ic, j, rate); - if (i != -1) { - /* lock mode too */ - ic->ic_curmode = j; - goto setrate; - } - } - } else { - i = findrate(ic, ic->ic_curmode, rate); - if (i != -1) - goto setrate; - } - return EINVAL; - setrate: - ic->ic_fixed_rate = i; - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - break; - case WI_RID_CUR_TX_RATE: - return EPERM; - case WI_RID_RTS_THRESH: - if (len != 2) - return EINVAL; - if (le16toh(wreq.wi_val[0]) != IEEE80211_MAX_LEN) - return EINVAL; /* TODO: RTS */ - break; - case WI_RID_CREATE_IBSS: - if (len != 2) - return EINVAL; - if (wreq.wi_val[0] != 0) { - if ((ic->ic_caps & IEEE80211_C_IBSS) == 0) - return EINVAL; - if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) { - ic->ic_flags |= IEEE80211_F_IBSSON; - if (ic->ic_opmode == IEEE80211_M_IBSS && - ic->ic_state == IEEE80211_S_SCAN) - error = IS_UP_AUTO(ic) ? ENETRESET : 0; - } - } else { - if (ic->ic_flags & IEEE80211_F_IBSSON) { - ic->ic_flags &= ~IEEE80211_F_IBSSON; - if (ic->ic_flags & IEEE80211_F_SIBSS) { - ic->ic_flags &= ~IEEE80211_F_SIBSS; - error = IS_UP_AUTO(ic) ? ENETRESET : 0; - } - } - } - break; - case WI_RID_MICROWAVE_OVEN: - if (len != 2) - return EINVAL; - if (wreq.wi_val[0] != 0) - return EINVAL; /* not supported */ - break; - case WI_RID_ROAMING_MODE: - if (len != 2) - return EINVAL; - i = le16toh(wreq.wi_val[0]); - if (i > IEEE80211_ROAMING_MANUAL) - return EINVAL; /* not supported */ - ic->ic_roaming = i; - break; - case WI_RID_SYSTEM_SCALE: - if (len != 2) - return EINVAL; - if (le16toh(wreq.wi_val[0]) != 1) - return EINVAL; /* not supported */ - break; - case WI_RID_PM_ENABLED: - if (len != 2) - return EINVAL; - if (wreq.wi_val[0] != 0) { - if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) - return EINVAL; - if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { - ic->ic_flags |= IEEE80211_F_PMGTON; - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - } - } else { - if (ic->ic_flags & IEEE80211_F_PMGTON) { - ic->ic_flags &= ~IEEE80211_F_PMGTON; - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - } - } - break; - case WI_RID_MAX_SLEEP: - if (len != 2) - return EINVAL; - ic->ic_lintval = le16toh(wreq.wi_val[0]); - if (ic->ic_flags & IEEE80211_F_PMGTON) - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - break; - case WI_RID_CUR_BEACON_INT: - return EPERM; - case WI_RID_WEP_AVAIL: - return EPERM; - case WI_RID_CNFAUTHMODE: - if (len != 2) - return EINVAL; - i = le16toh(wreq.wi_val[0]); - if (i > IEEE80211_AUTH_WPA) - return EINVAL; - ic->ic_bss->ni_authmode = i; /* XXX ENETRESET? */ - error = ENETRESET; - break; - case WI_RID_ENCRYPTION: - if (len != 2) - return EINVAL; - if (wreq.wi_val[0] != 0) { - if ((ic->ic_caps & IEEE80211_C_WEP) == 0) - return EINVAL; - if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { - ic->ic_flags |= IEEE80211_F_PRIVACY; - error = ENETRESET; - } - } else { - if (ic->ic_flags & IEEE80211_F_PRIVACY) { - ic->ic_flags &= ~IEEE80211_F_PRIVACY; - error = ENETRESET; - } - } - break; - case WI_RID_TX_CRYPT_KEY: - if (len != 2) - return EINVAL; - i = le16toh(wreq.wi_val[0]); - if (i >= IEEE80211_WEP_NKID) - return EINVAL; - ic->ic_def_txkey = i; - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - break; - case WI_RID_DEFLT_CRYPT_KEYS: - if (len != sizeof(struct wi_ltv_keys)) - return EINVAL; - keys = (struct wi_ltv_keys *)&wreq; - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - len = le16toh(keys->wi_keys[i].wi_keylen); - if (len != 0 && len < IEEE80211_WEP_KEYLEN) - return EINVAL; - if (len > IEEE80211_KEYBUF_SIZE) - return EINVAL; - } - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - struct ieee80211_key *k = &ic->ic_nw_keys[i]; - - len = le16toh(keys->wi_keys[i].wi_keylen); - k->wk_keylen = len; - k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; - memset(k->wk_key, 0, sizeof(k->wk_key)); - memcpy(k->wk_key, keys->wi_keys[i].wi_keydat, len); -#if 0 - k->wk_type = IEEE80211_CIPHER_WEP; -#endif - } - error = ENETRESET; - break; - case WI_RID_MAX_DATALEN: - if (len != 2) - return EINVAL; - len = le16toh(wreq.wi_val[0]); - if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN) - return EINVAL; - ic->ic_fragthreshold = len; - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - break; - case WI_RID_IFACE_STATS: - error = EPERM; - break; - case WI_RID_SCAN_REQ: /* XXX wicontrol */ - if (ic->ic_opmode == IEEE80211_M_HOSTAP) - break; - error = ieee80211_setupscan(ic, ic->ic_chan_avail); - if (error == 0) - error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); - break; - case WI_RID_SCAN_APS: - if (ic->ic_opmode == IEEE80211_M_HOSTAP) - break; - len--; /* XXX: tx rate? */ - /* FALLTHRU */ - case WI_RID_CHANNEL_LIST: - memset(chanlist, 0, sizeof(chanlist)); - /* - * Since channel 0 is not available for DS, channel 1 - * is assigned to LSB on WaveLAN. - */ - if (ic->ic_phytype == IEEE80211_T_DS) - i = 1; - else - i = 0; - for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { - if ((j / 8) >= len) - break; - if (isclr((u_int8_t *)wreq.wi_val, j)) - continue; - if (isclr(ic->ic_chan_active, i)) { - if (wreq.wi_type != WI_RID_CHANNEL_LIST) - continue; - if (isclr(ic->ic_chan_avail, i)) - return EPERM; - } - setbit(chanlist, i); - } - error = ieee80211_setupscan(ic, chanlist); - if (wreq.wi_type == WI_RID_CHANNEL_LIST) { - /* NB: ignore error from ieee80211_setupscan */ - error = ENETRESET; - } else if (error == 0) - error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); - break; - default: - error = EINVAL; - break; - } - if (error == ENETRESET && !IS_UP_AUTO(ic)) - error = 0; - return error; -} +static struct ieee80211_channel *findchannel(struct ieee80211com *, + int ieee, int mode); static int cap2cipher(int flag) @@ -889,37 +152,21 @@ ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) static int ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq) { - struct ieee80211req_chaninfo chans; /* XXX off stack? */ - int i, space; + int space; - /* - * Since channel 0 is not available for DS, channel 1 - * is assigned to LSB on WaveLAN. - */ - if (ic->ic_phytype == IEEE80211_T_DS) - i = 1; - else - i = 0; - memset(&chans, 0, sizeof(chans)); - for (; i <= IEEE80211_CHAN_MAX; i++) - if (isset(ic->ic_chan_avail, i)) { - struct ieee80211_channel *c = &ic->ic_channels[i]; - chans.ic_chans[chans.ic_nchans].ic_freq = c->ic_freq; - chans.ic_chans[chans.ic_nchans].ic_flags = c->ic_flags; - chans.ic_nchans++; - } space = __offsetof(struct ieee80211req_chaninfo, - ic_chans[chans.ic_nchans]); + ic_chans[ic->ic_nchans]); if (space > ireq->i_len) space = ireq->i_len; - return copyout(&chans, ireq->i_data, space); + /* XXX assumes compatible layout */ + return copyout(&ic->ic_nchans, ireq->i_data, space); } static int -ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq) +ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int req) { struct ieee80211_node *ni; - struct ieee80211req_wpaie wpaie; + struct ieee80211req_wpaie2 wpaie; int error; if (ireq->i_len < IEEE80211_ADDR_LEN) @@ -929,7 +176,7 @@ ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq) return error; ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr); if (ni == NULL) - return EINVAL; /* XXX */ + return ENOENT; /* XXX */ memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie)); if (ni->ni_wpa_ie != NULL) { int ielen = ni->ni_wpa_ie[1] + 2; @@ -937,9 +184,29 @@ ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq) ielen = sizeof(wpaie.wpa_ie); memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen); } + if (req == IEEE80211_IOC_WPAIE2) { + memset(wpaie.rsn_ie, 0, sizeof(wpaie.rsn_ie)); + if (ni->ni_rsn_ie != NULL) { + int ielen = ni->ni_rsn_ie[1] + 2; + if (ielen > sizeof(wpaie.rsn_ie)) + ielen = sizeof(wpaie.rsn_ie); + memcpy(wpaie.rsn_ie, ni->ni_rsn_ie, ielen); + } + if (ireq->i_len > sizeof(struct ieee80211req_wpaie2)) + ireq->i_len = sizeof(struct ieee80211req_wpaie2); + } else { + /* compatibility op, may overwrite wpa ie */ + /* XXX check ic_flags? */ + if (ni->ni_rsn_ie != NULL) { + int ielen = ni->ni_rsn_ie[1] + 2; + if (ielen > sizeof(wpaie.wpa_ie)) + ielen = sizeof(wpaie.wpa_ie); + memcpy(wpaie.wpa_ie, ni->ni_rsn_ie, ielen); + } + if (ireq->i_len > sizeof(struct ieee80211req_wpaie)) + ireq->i_len = sizeof(struct ieee80211req_wpaie); + } ieee80211_free_node(ni); - if (ireq->i_len > sizeof(wpaie)) - ireq->i_len = sizeof(wpaie); return copyout(&wpaie, ireq->i_data, ireq->i_len); } @@ -947,7 +214,7 @@ static int ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211_node *ni; - u_int8_t macaddr[IEEE80211_ADDR_LEN]; + uint8_t macaddr[IEEE80211_ADDR_LEN]; const int off = __offsetof(struct ieee80211req_sta_stats, is_stats); int error; @@ -957,213 +224,250 @@ ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq) if (error != 0) return error; ni = ieee80211_find_node(&ic->ic_sta, macaddr); - if (ni == NULL) { - /* XXX special-case sta-mode until bss is node in ic_sta */ - if (ic->ic_opmode != IEEE80211_M_STA) - return ENOENT; - ni = ieee80211_ref_node(ic->ic_bss); - } + if (ni == NULL) + return EINVAL; if (ireq->i_len > sizeof(struct ieee80211req_sta_stats)) ireq->i_len = sizeof(struct ieee80211req_sta_stats); /* NB: copy out only the statistics */ - error = copyout(&ni->ni_stats, (u_int8_t *) ireq->i_data + off, + error = copyout(&ni->ni_stats, (uint8_t *) ireq->i_data + off, ireq->i_len - off); ieee80211_free_node(ni); return error; } +static __inline uint8_t * +copyie(uint8_t *cp, const uint8_t *ie) +{ + if (ie != NULL) { + memcpy(cp, ie, 2+ie[1]); + cp += 2+ie[1]; + } + return cp; +} + #ifdef COMPAT_FREEBSD6 #define IEEE80211_IOC_SCAN_RESULTS_OLD 24 struct scan_result_old { - u_int16_t isr_len; /* length (mult of 4) */ - u_int16_t isr_freq; /* MHz */ - u_int16_t isr_flags; /* channel flags */ - u_int8_t isr_noise; - u_int8_t isr_rssi; - u_int8_t isr_intval; /* beacon interval */ - u_int8_t isr_capinfo; /* capabilities */ - u_int8_t isr_erp; /* ERP element */ - u_int8_t isr_bssid[IEEE80211_ADDR_LEN]; - u_int8_t isr_nrates; - u_int8_t isr_rates[IEEE80211_RATE_MAXSIZE]; - u_int8_t isr_ssid_len; /* SSID length */ - u_int8_t isr_ie_len; /* IE length */ - u_int8_t isr_pad[5]; + uint16_t isr_len; /* length (mult of 4) */ + uint16_t isr_freq; /* MHz */ + uint16_t isr_flags; /* channel flags */ + uint8_t isr_noise; + uint8_t isr_rssi; + uint8_t isr_intval; /* beacon interval */ + uint8_t isr_capinfo; /* capabilities */ + uint8_t isr_erp; /* ERP element */ + uint8_t isr_bssid[IEEE80211_ADDR_LEN]; + uint8_t isr_nrates; + uint8_t isr_rates[IEEE80211_RATE_MAXSIZE]; + uint8_t isr_ssid_len; /* SSID length */ + uint8_t isr_ie_len; /* IE length */ + uint8_t isr_pad[5]; /* variable length SSID followed by IE data */ }; +struct oscanreq { + struct scan_result_old *sr; + size_t space; +}; + +static size_t +old_scan_space(const struct ieee80211_scan_entry *se, int *ielen) +{ + size_t len; + + *ielen = 0; + if (se->se_wpa_ie != NULL) + *ielen += 2+se->se_wpa_ie[1]; + if (se->se_wme_ie != NULL) + *ielen += 2+se->se_wme_ie[1]; + /* + * NB: ie's can be no more than 255 bytes and the max 802.11 + * packet is <3Kbytes so we are sure this doesn't overflow + * 16-bits; if this is a concern we can drop the ie's. + */ + len = sizeof(struct scan_result_old) + se->se_ssid[1] + *ielen; + return roundup(len, sizeof(uint32_t)); +} + static void -old_get_scan_result(struct scan_result_old *sr, - const struct ieee80211_node *ni) +old_get_scan_space(void *arg, const struct ieee80211_scan_entry *se) { - struct ieee80211com *ic = ni->ni_ic; - u_int ielen; + struct oscanreq *req = arg; + int ielen; + + req->space += old_scan_space(se, &ielen); +} + +static void +old_get_scan_result(void *arg, const struct ieee80211_scan_entry *se) +{ + struct oscanreq *req = arg; + struct scan_result_old *sr; + int ielen, len, nr, nxr; + uint8_t *cp; + + len = old_scan_space(se, &ielen); + if (len > req->space) + return; + sr = req->sr; memset(sr, 0, sizeof(*sr)); - sr->isr_ssid_len = ni->ni_esslen; - ielen = 0; - if (ni->ni_wpa_ie != NULL) - ielen += 2+ni->ni_wpa_ie[1]; - if (ni->ni_wme_ie != NULL) - ielen += 2+ni->ni_wme_ie[1]; + sr->isr_ssid_len = se->se_ssid[1]; /* NB: beware of overflow, isr_ie_len is 8 bits */ sr->isr_ie_len = (ielen > 255 ? 0 : ielen); - sr->isr_len = sizeof(*sr) + sr->isr_ssid_len + sr->isr_ie_len; - sr->isr_len = roundup(sr->isr_len, sizeof(u_int32_t)); - if (ni->ni_chan != IEEE80211_CHAN_ANYC) { - sr->isr_freq = ni->ni_chan->ic_freq; - sr->isr_flags = ni->ni_chan->ic_flags; + sr->isr_len = len; + sr->isr_freq = se->se_chan->ic_freq; + sr->isr_flags = se->se_chan->ic_flags; + sr->isr_rssi = se->se_rssi; + sr->isr_noise = se->se_noise; + sr->isr_intval = se->se_intval; + sr->isr_capinfo = se->se_capinfo; + sr->isr_erp = se->se_erp; + IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid); + nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE); + memcpy(sr->isr_rates, se->se_rates+2, nr); + nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr); + memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr); + sr->isr_nrates = nr + nxr; + + cp = (uint8_t *)(sr+1); + memcpy(cp, se->se_ssid+2, sr->isr_ssid_len); + cp += sr->isr_ssid_len; + if (sr->isr_ie_len) { + cp = copyie(cp, se->se_wpa_ie); + cp = copyie(cp, se->se_wme_ie); } - sr->isr_rssi = ic->ic_node_getrssi(ni); - sr->isr_intval = ni->ni_intval; - sr->isr_capinfo = ni->ni_capinfo; - sr->isr_erp = ni->ni_erp; - IEEE80211_ADDR_COPY(sr->isr_bssid, ni->ni_bssid); - sr->isr_nrates = ni->ni_rates.rs_nrates; - if (sr->isr_nrates > 15) - sr->isr_nrates = 15; - memcpy(sr->isr_rates, ni->ni_rates.rs_rates, sr->isr_nrates); + + req->space -= len; + req->sr = (struct scan_result_old *)(((uint8_t *)sr) + len); } static int old_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) { - union { - struct scan_result_old res; - char data[512]; /* XXX shrink? */ - } u; - struct scan_result_old *sr = &u.res; - struct ieee80211_node_table *nt; - struct ieee80211_node *ni; - int error, space; - u_int8_t *p, *cp; + struct oscanreq req; + int error; + + if (ireq->i_len < sizeof(struct scan_result_old)) + return EFAULT; - p = ireq->i_data; - space = ireq->i_len; error = 0; - /* XXX locking */ - nt = &ic->ic_scan; - TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { - /* NB: skip pre-scan node state */ - if (ni->ni_chan == IEEE80211_CHAN_ANYC) - continue; - old_get_scan_result(sr, ni); - if (sr->isr_len > sizeof(u)) - continue; /* XXX */ - if (space < sr->isr_len) - break; - cp = (u_int8_t *)(sr+1); - memcpy(cp, ni->ni_essid, ni->ni_esslen); - cp += ni->ni_esslen; - if (sr->isr_ie_len) { - if (ni->ni_wpa_ie != NULL) { - memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); - cp += 2+ni->ni_wpa_ie[1]; - } - if (ni->ni_wme_ie != NULL) { - memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); - cp += 2+ni->ni_wme_ie[1]; - } - } - error = copyout(sr, p, sr->isr_len); - if (error) - break; - p += sr->isr_len; - space -= sr->isr_len; - } - ireq->i_len -= space; + req.space = 0; + ieee80211_scan_iterate(ic, old_get_scan_space, &req); + if (req.space > ireq->i_len) + req.space = ireq->i_len; + if (req.space > 0) { + size_t space; + void *p; + + space = req.space; + /* XXX M_WAITOK after driver lock released */ + MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO); + if (p == NULL) + return ENOMEM; + req.sr = p; + ieee80211_scan_iterate(ic, old_get_scan_result, &req); + ireq->i_len = space - req.space; + error = copyout(p, ireq->i_data, ireq->i_len); + FREE(p, M_TEMP); + } else + ireq->i_len = 0; + return error; } #endif /* COMPAT_FREEBSD6 */ -struct scanresultsreq { +struct scanreq { struct ieee80211req_scan_result *sr; - size_t space; + size_t space; }; static size_t -scan_space(const struct ieee80211_node *ni, size_t *ielen) +scan_space(const struct ieee80211_scan_entry *se, int *ielen) { size_t len; *ielen = 0; - if (ni->ni_wpa_ie != NULL) - *ielen += 2+ni->ni_wpa_ie[1]; - if (ni->ni_wme_ie != NULL) - *ielen += 2+ni->ni_wme_ie[1]; + if (se->se_wpa_ie != NULL) + *ielen += 2+se->se_wpa_ie[1]; + if (se->se_rsn_ie != NULL) + *ielen += 2+se->se_rsn_ie[1]; + if (se->se_wme_ie != NULL) + *ielen += 2+se->se_wme_ie[1]; + if (se->se_ath_ie != NULL) + *ielen += 2+se->se_ath_ie[1]; /* * NB: ie's can be no more than 255 bytes and the max 802.11 * packet is <3Kbytes so we are sure this doesn't overflow * 16-bits; if this is a concern we can drop the ie's. */ - len = sizeof(struct ieee80211req_scan_result) + ni->ni_esslen + *ielen; - return roundup(len, sizeof(u_int32_t)); + len = sizeof(struct ieee80211req_scan_result) + se->se_ssid[1] + *ielen; + return roundup(len, sizeof(uint32_t)); } static void -get_scan_space(void *arg, struct ieee80211_node *ni) +get_scan_space(void *arg, const struct ieee80211_scan_entry *se) { - struct scanresultsreq *req = arg; - size_t ielen; + struct scanreq *req = arg; + int ielen; - req->space += scan_space(ni, &ielen); + req->space += scan_space(se, &ielen); } static void -get_scan_result(void *arg, struct ieee80211_node *ni) +get_scan_result(void *arg, const struct ieee80211_scan_entry *se) { - struct scanresultsreq *req = arg; - struct ieee80211com *ic = ni->ni_ic; + struct scanreq *req = arg; struct ieee80211req_scan_result *sr; - size_t ielen, len; - u_int8_t *cp; + int ielen, len, nr, nxr; + uint8_t *cp; - len = scan_space(ni, &ielen); + len = scan_space(se, &ielen); if (len > req->space) return; + sr = req->sr; KASSERT(len <= 65535 && ielen <= 65535, - ("len %zu ssid %u ie %zu", len, ni->ni_esslen, ielen)); - sr->isr_len = len; - sr->isr_ssid_len = ni->ni_esslen; + ("len %u ssid %u ie %u", len, se->se_ssid[1], ielen)); + sr->isr_ie_off = sizeof(struct ieee80211req_scan_result); sr->isr_ie_len = ielen; - if (ni->ni_chan != IEEE80211_CHAN_ANYC) { - sr->isr_freq = ni->ni_chan->ic_freq; - sr->isr_flags = ni->ni_chan->ic_flags; - } - /* XXX need to rev driver apis for signal data */ - sr->isr_rssi = (int8_t) ic->ic_node_getrssi(ni); - sr->isr_intval = ni->ni_intval; - sr->isr_capinfo = ni->ni_capinfo; - sr->isr_erp = ni->ni_erp; - IEEE80211_ADDR_COPY(sr->isr_bssid, ni->ni_bssid); - sr->isr_nrates = ni->ni_rates.rs_nrates; - if (sr->isr_nrates > 15) - sr->isr_nrates = 15; - memcpy(sr->isr_rates, ni->ni_rates.rs_rates, sr->isr_nrates); - cp = (u_int8_t *)(sr+1); - memcpy(cp, ni->ni_essid, ni->ni_esslen); - cp += ni->ni_esslen; - if (sr->isr_ie_len) { - if (ni->ni_wpa_ie != NULL) { - memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); - cp += 2+ni->ni_wpa_ie[1]; - } - if (ni->ni_wme_ie != NULL) { - memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); - cp += 2+ni->ni_wme_ie[1]; - } + sr->isr_len = len; + sr->isr_freq = se->se_chan->ic_freq; + sr->isr_flags = se->se_chan->ic_flags; + sr->isr_rssi = se->se_rssi; + sr->isr_noise = se->se_noise; + sr->isr_intval = se->se_intval; + sr->isr_capinfo = se->se_capinfo; + sr->isr_erp = se->se_erp; + IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid); + nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE); + memcpy(sr->isr_rates, se->se_rates+2, nr); + nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr); + memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr); + sr->isr_nrates = nr + nxr; + + sr->isr_ssid_len = se->se_ssid[1]; + cp = ((uint8_t *)sr) + sr->isr_ie_off; + memcpy(cp, se->se_ssid+2, sr->isr_ssid_len); + + if (ielen) { + cp += sr->isr_ssid_len; + cp = copyie(cp, se->se_wpa_ie); + cp = copyie(cp, se->se_rsn_ie); + cp = copyie(cp, se->se_wme_ie); + cp = copyie(cp, se->se_ath_ie); + cp = copyie(cp, se->se_htcap_ie); } - req->sr = (struct ieee80211req_scan_result *)(((u_int8_t *)sr) + len); req->space -= len; + req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len); } static int ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) { - struct scanresultsreq req; + struct scanreq req; int error; if (ireq->i_len < sizeof(struct ieee80211req_scan_result)) @@ -1171,7 +475,7 @@ ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ire error = 0; req.space = 0; - ieee80211_iterate_nodes(&ic->ic_scan, get_scan_space, &req); + ieee80211_scan_iterate(ic, get_scan_space, &req); if (req.space > ireq->i_len) req.space = ireq->i_len; if (req.space > 0) { @@ -1184,7 +488,7 @@ ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ire if (p == NULL) return ENOMEM; req.sr = p; - ieee80211_iterate_nodes(&ic->ic_scan, get_scan_result, &req); + ieee80211_scan_iterate(ic, get_scan_result, &req); ireq->i_len = space - req.space; error = copyout(p, ireq->i_data, ireq->i_len); FREE(p, M_TEMP); @@ -1206,10 +510,14 @@ sta_space(const struct ieee80211_node *ni, size_t *ielen) *ielen = 0; if (ni->ni_wpa_ie != NULL) *ielen += 2+ni->ni_wpa_ie[1]; + if (ni->ni_rsn_ie != NULL) + *ielen += 2+ni->ni_rsn_ie[1]; if (ni->ni_wme_ie != NULL) *ielen += 2+ni->ni_wme_ie[1]; + if (ni->ni_ath_ie != NULL) + *ielen += 2+ni->ni_ath_ie[1]; return roundup(sizeof(struct ieee80211req_sta_info) + *ielen, - sizeof(u_int32_t)); + sizeof(uint32_t)); } static void @@ -1232,7 +540,7 @@ get_sta_info(void *arg, struct ieee80211_node *ni) struct ieee80211com *ic = ni->ni_ic; struct ieee80211req_sta_info *si; size_t ielen, len; - u_int8_t *cp; + uint8_t *cp; if (ic->ic_opmode == IEEE80211_M_HOSTAP && ni->ni_associd == 0) /* only associated stations */ @@ -1243,14 +551,14 @@ get_sta_info(void *arg, struct ieee80211_node *ni) if (len > req->space) return; si = req->si; - KASSERT(len <= 65535 && ielen <= 65535, ("len %zu ie %zu", len, ielen)); si->isi_len = len; + si->isi_ie_off = sizeof(struct ieee80211req_sta_info); si->isi_ie_len = ielen; si->isi_freq = ni->ni_chan->ic_freq; si->isi_flags = ni->ni_chan->ic_flags; si->isi_state = ni->ni_flags; si->isi_authmode = ni->ni_authmode; - si->isi_rssi = ic->ic_node_getrssi(ni); + ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise); si->isi_noise = 0; /* XXX */ si->isi_capinfo = ni->ni_capinfo; si->isi_erp = ni->ni_erp; @@ -1260,6 +568,7 @@ get_sta_info(void *arg, struct ieee80211_node *ni) si->isi_nrates = 15; memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates); si->isi_txrate = ni->ni_txrate; + si->isi_ie_len = ielen; si->isi_associd = ni->ni_associd; si->isi_txpower = ni->ni_txpower; si->isi_vlan = ni->ni_vlan; @@ -1279,17 +588,15 @@ get_sta_info(void *arg, struct ieee80211_node *ni) si->isi_inact = ic->ic_inact_init; si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT; - cp = (u_int8_t *)(si+1); - if (ni->ni_wpa_ie != NULL) { - memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); - cp += 2+ni->ni_wpa_ie[1]; - } - if (ni->ni_wme_ie != NULL) { - memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); - cp += 2+ni->ni_wme_ie[1]; + if (ielen) { + cp = ((uint8_t *)si) + si->isi_ie_off; + cp = copyie(cp, ni->ni_wpa_ie); + cp = copyie(cp, ni->ni_rsn_ie); + cp = copyie(cp, ni->ni_wme_ie); + cp = copyie(cp, ni->ni_ath_ie); } - req->si = (struct ieee80211req_sta_info *)(((u_int8_t *)si) + len); + req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len); req->space -= len; } @@ -1324,7 +631,7 @@ getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq, else get_sta_info(&req, ni); ireq->i_len = space - req.space; - error = copyout(p, (u_int8_t *) ireq->i_data+off, ireq->i_len); + error = copyout(p, (uint8_t *) ireq->i_data+off, ireq->i_len); FREE(p, M_TEMP); } else ireq->i_len = 0; @@ -1337,7 +644,7 @@ bad: static int ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) { - u_int8_t macaddr[IEEE80211_ADDR_LEN]; + uint8_t macaddr[IEEE80211_ADDR_LEN]; const int off = __offsetof(struct ieee80211req_sta_req, info); struct ieee80211_node *ni; int error; @@ -1351,12 +658,8 @@ ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) ni = NULL; } else { ni = ieee80211_find_node(&ic->ic_sta, macaddr); - if (ni == NULL) { - /* XXX special-case sta-mode until bss is in ic_sta */ - if (ic->ic_opmode != IEEE80211_M_STA) - return EINVAL; /* XXX */ - ni = ieee80211_ref_node(ic->ic_bss); - } + if (ni == NULL) + return EINVAL; } return getstainfo_common(ic, ireq, ni, off); } @@ -1445,6 +748,28 @@ ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq) } /* + * Return the current ``state'' of an Atheros capbility. + * If associated in station mode report the negotiated + * setting. Otherwise report the current setting. + */ +static int +getathcap(struct ieee80211com *ic, int cap) +{ + if (ic->ic_opmode == IEEE80211_M_STA && ic->ic_state == IEEE80211_S_RUN) + return IEEE80211_ATH_CAP(ic, ic->ic_bss, cap) != 0; + else + return (ic->ic_flags & cap) != 0; +} + +static int +ieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + if (ireq->i_len != sizeof(struct ieee80211_channel)) + return EINVAL; + return copyout(ic->ic_curchan, ireq->i_data, sizeof(*ic->ic_curchan)); +} + +/* * When building the kernel with -O2 on the i386 architecture, gcc * seems to want to inline this function into ieee80211_ioctl() * (which is the only routine that calls it). When this happens, @@ -1469,7 +794,7 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; int error = 0; u_int kid, len, m; - u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; + uint8_t tmpkey[IEEE80211_KEYBUF_SIZE]; char tmpssid[IEEE80211_NWID_LEN]; switch (ireq->i_type) { @@ -1477,8 +802,8 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re switch (ic->ic_state) { case IEEE80211_S_INIT: case IEEE80211_S_SCAN: - ireq->i_len = ic->ic_des_esslen; - memcpy(tmpssid, ic->ic_des_essid, ireq->i_len); + ireq->i_len = ic->ic_des_ssid[0].len; + memcpy(tmpssid, ic->ic_des_ssid[0].ssid, ireq->i_len); break; default: ireq->i_len = ic->ic_bss->ni_esslen; @@ -1639,7 +964,10 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ireq->i_data, ireq->i_len); break; case IEEE80211_IOC_WPAIE: - error = ieee80211_ioctl_getwpaie(ic, ireq); + error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type); + break; + case IEEE80211_IOC_WPAIE2: + error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type); break; #ifdef COMPAT_FREEBSD6 case IEEE80211_IOC_SCAN_RESULTS_OLD: @@ -1684,6 +1012,42 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re case IEEE80211_IOC_PUREG: ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0; break; + case IEEE80211_IOC_FF: + ireq->i_val = getathcap(ic, IEEE80211_F_FF); + break; + case IEEE80211_IOC_TURBOP: + ireq->i_val = getathcap(ic, IEEE80211_F_TURBOP); + break; + case IEEE80211_IOC_BGSCAN: + ireq->i_val = (ic->ic_flags & IEEE80211_F_BGSCAN) != 0; + break; + case IEEE80211_IOC_BGSCAN_IDLE: + ireq->i_val = ic->ic_bgscanidle*hz/1000; /* ms */ + break; + case IEEE80211_IOC_BGSCAN_INTERVAL: + ireq->i_val = ic->ic_bgscanintvl/hz; /* seconds */ + break; + case IEEE80211_IOC_SCANVALID: + ireq->i_val = ic->ic_scanvalid/hz; /* seconds */ + break; + case IEEE80211_IOC_ROAM_RSSI_11A: + ireq->i_val = ic->ic_roam.rssi11a; + break; + case IEEE80211_IOC_ROAM_RSSI_11B: + ireq->i_val = ic->ic_roam.rssi11bOnly; + break; + case IEEE80211_IOC_ROAM_RSSI_11G: + ireq->i_val = ic->ic_roam.rssi11b; + break; + case IEEE80211_IOC_ROAM_RATE_11A: + ireq->i_val = ic->ic_roam.rate11a; + break; + case IEEE80211_IOC_ROAM_RATE_11B: + ireq->i_val = ic->ic_roam.rate11bOnly; + break; + case IEEE80211_IOC_ROAM_RATE_11G: + ireq->i_val = ic->ic_roam.rate11b; + break; case IEEE80211_IOC_MCAST_RATE: ireq->i_val = ic->ic_mcast_rate; break; @@ -1699,6 +1063,48 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re case IEEE80211_IOC_BMISSTHRESHOLD: ireq->i_val = ic->ic_bmissthreshold; break; + case IEEE80211_IOC_CURCHAN: + error = ieee80211_ioctl_getcurchan(ic, ireq); + break; + case IEEE80211_IOC_SHORTGI: + ireq->i_val = 0; + if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) + ireq->i_val |= IEEE80211_HTCAP_SHORTGI20; + if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) + ireq->i_val |= IEEE80211_HTCAP_SHORTGI40; + break; + case IEEE80211_IOC_AMPDU: + ireq->i_val = 0; + if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX) + ireq->i_val |= 1; + if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX) + ireq->i_val |= 2; + break; + case IEEE80211_IOC_AMPDU_LIMIT: + ireq->i_val = ic->ic_ampdu_limit; /* XXX truncation? */ + break; + case IEEE80211_IOC_AMPDU_DENSITY: + ireq->i_val = ic->ic_ampdu_density; + break; + case IEEE80211_IOC_AMSDU: + ireq->i_val = 0; + if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_TX) + ireq->i_val |= 1; + if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_RX) + ireq->i_val |= 2; + break; + case IEEE80211_IOC_AMSDU_LIMIT: + ireq->i_val = ic->ic_amsdu_limit; /* XXX truncation? */ + break; + case IEEE80211_IOC_PUREN: + ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) != 0; + break; + case IEEE80211_IOC_DOTH: + ireq->i_val = (ic->ic_flags & IEEE80211_F_DOTH) != 0; + break; + case IEEE80211_IOC_HTCOMPAT: + ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0; + break; default: error = EINVAL; break; @@ -1722,6 +1128,8 @@ ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq) return EINVAL; if (ireq->i_len > IEEE80211_MAX_OPT_IE) return EINVAL; + /* NB: data.length is validated by the wireless extensions code */ + /* XXX M_WAITOK after driver lock released */ if (ireq->i_len > 0) { MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_NOWAIT); if (ie == NULL) @@ -1750,7 +1158,7 @@ ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq) struct ieee80211req_key ik; struct ieee80211_node *ni; struct ieee80211_key *wk; - u_int16_t kid; + uint16_t kid; int error; if (ireq->i_len != sizeof(ik)) @@ -1827,8 +1235,8 @@ ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq) if (error) return error; kid = dk.idk_keyix; - /* XXX u_int8_t -> u_int16_t */ - if (dk.idk_keyix == (u_int8_t) IEEE80211_KEYIX_NONE) { + /* XXX uint8_t -> uint16_t */ + if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) { struct ieee80211_node *ni; if (ic->ic_opmode == IEEE80211_M_STA) { @@ -1870,6 +1278,32 @@ domlme(void *arg, struct ieee80211_node *ni) ieee80211_node_leave(ic, ni); } +struct scanlookup { + const uint8_t *mac; + int esslen; + const uint8_t *essid; + const struct ieee80211_scan_entry *se; +}; + +/* + * Match mac address and any ssid. + */ +static void +mlmelookup(void *arg, const struct ieee80211_scan_entry *se) +{ + struct scanlookup *look = arg; + + if (!IEEE80211_ADDR_EQ(look->mac, se->se_macaddr)) + return; + if (look->esslen != 0) { + if (se->se_ssid[1] != look->esslen) + return; + if (memcmp(look->essid, se->se_ssid+2, look->esslen)) + return; + } + look->se = se; +} + static int ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) { @@ -1884,31 +1318,21 @@ ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) return error; switch (mlme.im_op) { case IEEE80211_MLME_ASSOC: - if (ic->ic_opmode != IEEE80211_M_STA) - return EINVAL; - /* XXX must be in S_SCAN state? */ - - if (mlme.im_ssid_len != 0) { - /* - * Desired ssid specified; must match both bssid and - * ssid to distinguish ap advertising multiple ssid's. - */ - ni = ieee80211_find_node_with_ssid(&ic->ic_scan, - mlme.im_macaddr, - mlme.im_ssid_len, mlme.im_ssid); - } else { - /* - * Normal case; just match bssid. - */ - ni = ieee80211_find_node(&ic->ic_scan, mlme.im_macaddr); - } - if (ni == NULL) - return EINVAL; - if (!ieee80211_sta_join(ic, ni)) { - ieee80211_free_node(ni); - return EINVAL; + /* XXX ibss/ahdemo */ + if (ic->ic_opmode == IEEE80211_M_STA) { + struct scanlookup lookup; + + lookup.se = NULL; + lookup.mac = mlme.im_macaddr; + /* XXX use revised api w/ explicit ssid */ + lookup.esslen = ic->ic_des_ssid[0].len; + lookup.essid = ic->ic_des_ssid[0].ssid; + ieee80211_scan_iterate(ic, mlmelookup, &lookup); + if (lookup.se != NULL && + ieee80211_sta_join(ic, lookup.se)) + return 0; } - break; + return EINVAL; case IEEE80211_MLME_DISASSOC: case IEEE80211_MLME_DEAUTH: switch (ic->ic_opmode) { @@ -1956,7 +1380,7 @@ ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) static int ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq) { - u_int8_t mac[IEEE80211_ADDR_LEN]; + uint8_t mac[IEEE80211_ADDR_LEN]; const struct ieee80211_aclator *acl = ic->ic_acl; int error; @@ -2020,7 +1444,7 @@ ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211req_chanlist list; u_char chanlist[IEEE80211_CHAN_BYTES]; - int i, j, error; + int i, j, nchan, error; if (ireq->i_len != sizeof(list)) return EINVAL; @@ -2036,34 +1460,31 @@ ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) i = 1; else i = 0; + nchan = 0; for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { /* * NB: silently discard unavailable channels so users * can specify 1-255 to get all available channels. */ - if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) + if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) { setbit(chanlist, i); + nchan++; + } } - if (ic->ic_ibss_chan == NULL || - isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { - for (i = 0; i <= IEEE80211_CHAN_MAX; i++) - if (isset(chanlist, i)) { - ic->ic_ibss_chan = &ic->ic_channels[i]; - goto found; - } - return EINVAL; /* no active channels */ -found: - ; - } + if (nchan == 0) + return EINVAL; + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && /* XXX */ + isclr(chanlist, ic->ic_bsschan->ic_ieee)) + ic->ic_bsschan = IEEE80211_CHAN_ANYC; memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); - return IS_UP_AUTO(ic) ? ENETRESET : 0; + return IS_UP_AUTO(ic) ? ieee80211_init(ic, RESCAN) : 0; } static int ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq) { struct ieee80211_node *ni; - u_int8_t macaddr[IEEE80211_ADDR_LEN]; + uint8_t macaddr[IEEE80211_ADDR_LEN]; int error; /* @@ -2193,15 +1614,273 @@ cipher2cap(int cipher) } static int +find11gchannel(struct ieee80211com *ic, int start, int freq) +{ + const struct ieee80211_channel *c; + int i; + + for (i = start+1; i < ic->ic_nchans; i++) { + c = &ic->ic_channels[i]; + if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) + return 1; + } + /* NB: should not be needed but in case things are mis-sorted */ + for (i = 0; i < start; i++) { + c = &ic->ic_channels[i]; + if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) + return 1; + } + return 0; +} + +static struct ieee80211_channel * +findchannel(struct ieee80211com *ic, int ieee, int mode) +{ + static const u_int chanflags[IEEE80211_MODE_MAX] = { + 0, /* IEEE80211_MODE_AUTO */ + IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ + IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ + IEEE80211_CHAN_G, /* IEEE80211_MODE_11G */ + IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ + IEEE80211_CHAN_108A, /* IEEE80211_MODE_TURBO_A */ + IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ + IEEE80211_CHAN_STURBO, /* IEEE80211_MODE_STURBO_A */ + /* NB: handled specially below */ + IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA */ + IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG */ + }; + u_int modeflags; + int i; + + KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode)); + modeflags = chanflags[mode]; + KASSERT(modeflags != 0 || mode == IEEE80211_MODE_AUTO, + ("no chanflags for mode %u", mode)); + for (i = 0; i < ic->ic_nchans; i++) { + struct ieee80211_channel *c = &ic->ic_channels[i]; + + if (c->ic_ieee != ieee) + continue; + if (mode == IEEE80211_MODE_AUTO) { + /* ignore turbo channels for autoselect */ + if (IEEE80211_IS_CHAN_TURBO(c)) + continue; + /* + * XXX special-case 11b/g channels so we + * always select the g channel if both + * are present. + * XXX prefer HT to non-HT? + */ + if (!IEEE80211_IS_CHAN_B(c) || + !find11gchannel(ic, i, c->ic_freq)) + return c; + } else { + /* must check HT specially */ + if ((mode == IEEE80211_MODE_11NA || + mode == IEEE80211_MODE_11NG) && + !IEEE80211_IS_CHAN_HT(c)) + continue; + if ((c->ic_flags & modeflags) == modeflags) + return c; + } + } + return NULL; +} + +/* + * Check the specified against any desired mode (aka netband). + * This is only used (presently) when operating in hostap mode + * to enforce consistency. + */ +static int +check_mode_consistency(const struct ieee80211_channel *c, int mode) +{ + KASSERT(c != IEEE80211_CHAN_ANYC, ("oops, no channel")); + + switch (mode) { + case IEEE80211_MODE_11B: + return (IEEE80211_IS_CHAN_B(c)); + case IEEE80211_MODE_11G: + return (IEEE80211_IS_CHAN_ANYG(c) && !IEEE80211_IS_CHAN_HT(c)); + case IEEE80211_MODE_11A: + return (IEEE80211_IS_CHAN_A(c) && !IEEE80211_IS_CHAN_HT(c)); + case IEEE80211_MODE_STURBO_A: + return (IEEE80211_IS_CHAN_STURBO(c)); + case IEEE80211_MODE_11NA: + return (IEEE80211_IS_CHAN_HTA(c)); + case IEEE80211_MODE_11NG: + return (IEEE80211_IS_CHAN_HTG(c)); + } + return 1; + +} + +/* + * Common code to set the current channel. If the device + * is up and running this may result in an immediate channel + * change or a kick of the state machine. + */ +static int +setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c) +{ + int error; + + if (c != IEEE80211_CHAN_ANYC) { + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + !check_mode_consistency(c, ic->ic_des_mode)) + return EINVAL; + if (ic->ic_state == IEEE80211_S_RUN && c == ic->ic_curchan) + return 0; /* NB: nothing to do */ + } + ic->ic_des_chan = c; + + error = 0; + if ((ic->ic_opmode == IEEE80211_M_MONITOR || + ic->ic_opmode == IEEE80211_M_WDS) && + ic->ic_des_chan != IEEE80211_CHAN_ANYC) { + /* + * Monitor and wds modes can switch directly. + */ + ic->ic_curchan = ic->ic_des_chan; + if (ic->ic_state == IEEE80211_S_RUN) + ic->ic_set_channel(ic); + } else { + /* + * Need to go through the state machine in case we + * need to reassociate or the like. The state machine + * will pickup the desired channel and avoid scanning. + */ + if (IS_UP_AUTO(ic)) + error = ieee80211_init(ic, RESCAN); + else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) { + /* + * When not up+running and a real channel has + * been specified fix the current channel so + * there is immediate feedback; e.g. via ifconfig. + */ + ic->ic_curchan = ic->ic_des_chan; + } + } + return error; +} + +/* + * Old api for setting the current channel; this is + * deprecated because channel numbers are ambiguous. + */ +static int +ieee80211_ioctl_setchannel(struct ieee80211com *ic, + const struct ieee80211req *ireq) +{ + struct ieee80211_channel *c; + + /* XXX 0xffff overflows 16-bit signed */ + if (ireq->i_val == 0 || + ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) { + c = IEEE80211_CHAN_ANYC; + } else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX) { + return EINVAL; + } else { + struct ieee80211_channel *c2; + + c = findchannel(ic, ireq->i_val, ic->ic_des_mode); + if (c == NULL) { + c = findchannel(ic, ireq->i_val, + IEEE80211_MODE_AUTO); + if (c == NULL) + return EINVAL; + } + /* + * Fine tune channel selection based on desired mode: + * if 11b is requested, find the 11b version of any + * 11g channel returned, + * if static turbo, find the turbo version of any + * 11a channel return, + * if 11na is requested, find the ht version of any + * 11a channel returned, + * if 11ng is requested, find the ht version of any + * 11g channel returned, + * otherwise we should be ok with what we've got. + */ + switch (ic->ic_des_mode) { + case IEEE80211_MODE_11B: + if (IEEE80211_IS_CHAN_ANYG(c)) { + c2 = findchannel(ic, ireq->i_val, + IEEE80211_MODE_11B); + /* NB: should not happen, =>'s 11g w/o 11b */ + if (c2 != NULL) + c = c2; + } + break; + case IEEE80211_MODE_TURBO_A: + if (IEEE80211_IS_CHAN_A(c)) { + c2 = findchannel(ic, ireq->i_val, + IEEE80211_MODE_TURBO_A); + if (c2 != NULL) + c = c2; + } + break; + case IEEE80211_MODE_11NA: + if (IEEE80211_IS_CHAN_A(c)) { + c2 = findchannel(ic, ireq->i_val, + IEEE80211_MODE_11NA); + if (c2 != NULL) + c = c2; + } + break; + case IEEE80211_MODE_11NG: + if (IEEE80211_IS_CHAN_ANYG(c)) { + c2 = findchannel(ic, ireq->i_val, + IEEE80211_MODE_11NG); + if (c2 != NULL) + c = c2; + } + break; + default: /* NB: no static turboG */ + break; + } + } + return setcurchan(ic, c); +} + +/* + * New/current api for setting the current channel; a complete + * channel description is provide so there is no ambiguity in + * identifying the channel. + */ +static int +ieee80211_ioctl_setcurchan(struct ieee80211com *ic, + const struct ieee80211req *ireq) +{ + struct ieee80211_channel chan, *c; + int error; + + if (ireq->i_len != sizeof(chan)) + return EINVAL; + error = copyin(ireq->i_data, &chan, sizeof(chan)); + if (error != 0) + return error; + /* XXX 0xffff overflows 16-bit signed */ + if (chan.ic_freq == 0 || chan.ic_freq == IEEE80211_CHAN_ANY) { + c = IEEE80211_CHAN_ANYC; + } else { + c = ieee80211_find_channel(ic, chan.ic_freq, chan.ic_flags); + if (c == NULL) + return EINVAL; + } + return setcurchan(ic, c); +} + +static int ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) { - static const u_int8_t zerobssid[IEEE80211_ADDR_LEN]; + static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; int error; const struct ieee80211_authenticator *auth; - u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; + uint8_t tmpkey[IEEE80211_KEYBUF_SIZE]; char tmpssid[IEEE80211_NWID_LEN]; - u_int8_t tmpbssid[IEEE80211_ADDR_LEN]; + uint8_t tmpbssid[IEEE80211_ADDR_LEN]; struct ieee80211_key *k; int j, caps; u_int kid; @@ -2215,10 +1894,12 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re error = copyin(ireq->i_data, tmpssid, ireq->i_len); if (error) break; - memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); - ic->ic_des_esslen = ireq->i_len; - memcpy(ic->ic_des_essid, tmpssid, ireq->i_len); - error = ENETRESET; + memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN); + ic->ic_des_ssid[0].len = ireq->i_len; + memcpy(ic->ic_des_ssid[0].ssid, tmpssid, ireq->i_len); + ic->ic_des_nssid = (ireq->i_len > 0); + if (IS_UP_AUTO(ic)) + error = ieee80211_init(ic, RESCAN); break; case IEEE80211_IOC_WEP: switch (ireq->i_val) { @@ -2235,7 +1916,8 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ic->ic_flags &= ~IEEE80211_F_DROPUNENC; break; } - error = ENETRESET; + if (IS_UP_AUTO(ic)) + error = ieee80211_init(ic, RESCAN); break; case IEEE80211_IOC_WEPKEY: kid = (u_int) ireq->i_val; @@ -2264,16 +1946,13 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re } else error = EINVAL; ieee80211_key_update_end(ic); - if (!error) /* NB: for compatibility */ - error = ENETRESET; break; case IEEE80211_IOC_WEPTXKEY: kid = (u_int) ireq->i_val; if (kid >= IEEE80211_WEP_NKID && - (u_int16_t) kid != IEEE80211_KEYIX_NONE) + (uint16_t) kid != IEEE80211_KEYIX_NONE) return EINVAL; ic->ic_def_txkey = kid; - error = ENETRESET; /* push to hardware */ break; case IEEE80211_IOC_AUTHMODE: switch (ireq->i_val) { @@ -2313,48 +1992,11 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ic->ic_bss->ni_authmode = ireq->i_val; /* XXX mixed/mode/usage? */ ic->ic_auth = auth; - error = ENETRESET; + if (IS_UP_AUTO(ic)) + error = ieee80211_init(ic, RESCAN); break; case IEEE80211_IOC_CHANNEL: - /* XXX 0xffff overflows 16-bit signed */ - if (ireq->i_val == 0 || - ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) - ic->ic_des_chan = IEEE80211_CHAN_ANYC; - else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX || - isclr(ic->ic_chan_active, ireq->i_val)) { - return EINVAL; - } else - ic->ic_ibss_chan = ic->ic_des_chan = - &ic->ic_channels[ireq->i_val]; - switch (ic->ic_state) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - error = ENETRESET; - break; - default: - /* - * If the desired channel has changed (to something - * other than any) and we're not already scanning, - * then kick the state machine. - */ - if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && - ic->ic_bss->ni_chan != ic->ic_des_chan && - (ic->ic_flags & IEEE80211_F_SCAN) == 0) - error = ENETRESET; - break; - } - if (error == ENETRESET && - ic->ic_opmode == IEEE80211_M_MONITOR) { - if (IS_UP(ic)) { - /* - * Monitor mode can switch directly. - */ - if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) - ic->ic_curchan = ic->ic_des_chan; - error = ic->ic_reset(ic->ic_ifp); - } else - error = 0; - } + error = ieee80211_ioctl_setchannel(ic, ireq); break; case IEEE80211_IOC_POWERSAVE: switch (ireq->i_val) { @@ -2402,14 +2044,15 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re return EINVAL; ic->ic_protmode = ireq->i_val; /* NB: if not operating in 11g this can wait */ - if (ic->ic_curmode == IEEE80211_MODE_11G) + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case IEEE80211_IOC_TXPOWER: if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) return EINVAL; - if (!(IEEE80211_TXPOWER_MIN < ireq->i_val && - ireq->i_val < IEEE80211_TXPOWER_MAX)) + if (!(IEEE80211_TXPOWER_MIN <= ireq->i_val && + ireq->i_val <= IEEE80211_TXPOWER_MAX)) return EINVAL; ic->ic_txpowlimit = ireq->i_val; error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; @@ -2470,7 +2113,7 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2; break; } - error = ENETRESET; /* XXX? */ + error = ENETRESET; break; case IEEE80211_IOC_WME: if (ireq->i_val) { @@ -2479,7 +2122,8 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ic->ic_flags |= IEEE80211_F_WME; } else ic->ic_flags &= ~IEEE80211_F_WME; - error = ENETRESET; /* XXX maybe not for station? */ + if (IS_UP_AUTO(ic)) + error = ieee80211_init(ic, 0); break; case IEEE80211_IOC_HIDESSID: if (ireq->i_val) @@ -2542,8 +2186,8 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re break; case IEEE80211_IOC_DRIVER_CAPS: /* NB: for testing */ - ic->ic_caps = (((u_int16_t) ireq->i_val) << 16) | - ((u_int16_t) ireq->i_len); + ic->ic_caps = (((uint16_t) ireq->i_val) << 16) | + ((uint16_t) ireq->i_len); break; case IEEE80211_IOC_KEYMGTALGS: /* XXX check */ @@ -2566,17 +2210,21 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ic->ic_flags &= ~IEEE80211_F_DESBSSID; else ic->ic_flags |= IEEE80211_F_DESBSSID; - error = ENETRESET; + if (IS_UP_AUTO(ic)) + error = ieee80211_init(ic, RESCAN); break; case IEEE80211_IOC_CHANLIST: error = ieee80211_ioctl_setchanlist(ic, ireq); break; case IEEE80211_IOC_SCAN_REQ: - if (ic->ic_opmode == IEEE80211_M_HOSTAP) /* XXX ignore */ - break; - error = ieee80211_setupscan(ic, ic->ic_chan_avail); - if (error == 0) /* XXX background scan */ - error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + if (!IS_UP(ic)) + return EINVAL; + (void) ieee80211_start_scan(ic, + IEEE80211_SCAN_ACTIVE | + IEEE80211_SCAN_NOPICK | + IEEE80211_SCAN_ONCE, IEEE80211_SCAN_FOREVER, + /* XXX use ioctl params */ + ic->ic_des_nssid, ic->ic_des_ssid); break; case IEEE80211_IOC_ADDMAC: case IEEE80211_IOC_DELMAC: @@ -2627,9 +2275,72 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re else ic->ic_flags &= ~IEEE80211_F_PUREG; /* NB: reset only if we're operating on an 11g channel */ - if (ic->ic_curmode == IEEE80211_MODE_11G) + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) error = ENETRESET; break; + case IEEE80211_IOC_FF: + if (ireq->i_val) { + if ((ic->ic_caps & IEEE80211_C_FF) == 0) + return EINVAL; + ic->ic_flags |= IEEE80211_F_FF; + } else + ic->ic_flags &= ~IEEE80211_F_FF; + error = ENETRESET; + break; + case IEEE80211_IOC_TURBOP: + if (ireq->i_val) { + if ((ic->ic_caps & IEEE80211_C_TURBOP) == 0) + return EINVAL; + ic->ic_flags |= IEEE80211_F_TURBOP; + } else + ic->ic_flags &= ~IEEE80211_F_TURBOP; + error = ENETRESET; + break; + case IEEE80211_IOC_BGSCAN: + if (ireq->i_val) { + if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0) + return EINVAL; + ic->ic_flags |= IEEE80211_F_BGSCAN; + } else + ic->ic_flags &= ~IEEE80211_F_BGSCAN; + break; + case IEEE80211_IOC_BGSCAN_IDLE: + if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN) + ic->ic_bgscanidle = ireq->i_val*hz/1000; + else + error = EINVAL; + break; + case IEEE80211_IOC_BGSCAN_INTERVAL: + if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN) + ic->ic_bgscanintvl = ireq->i_val*hz; + else + error = EINVAL; + break; + case IEEE80211_IOC_SCANVALID: + if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN) + ic->ic_scanvalid = ireq->i_val*hz; + else + error = EINVAL; + break; + case IEEE80211_IOC_ROAM_RSSI_11A: + ic->ic_roam.rssi11a = ireq->i_val; + break; + case IEEE80211_IOC_ROAM_RSSI_11B: + ic->ic_roam.rssi11bOnly = ireq->i_val; + break; + case IEEE80211_IOC_ROAM_RSSI_11G: + ic->ic_roam.rssi11b = ireq->i_val; + break; + case IEEE80211_IOC_ROAM_RATE_11A: + ic->ic_roam.rate11a = ireq->i_val & IEEE80211_RATE_VAL; + break; + case IEEE80211_IOC_ROAM_RATE_11B: + ic->ic_roam.rate11bOnly = ireq->i_val & IEEE80211_RATE_VAL; + break; + case IEEE80211_IOC_ROAM_RATE_11G: + ic->ic_roam.rate11b = ireq->i_val & IEEE80211_RATE_VAL; + break; case IEEE80211_IOC_MCAST_RATE: ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL; break; @@ -2659,12 +2370,112 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ic->ic_bmissthreshold = ireq->i_val; error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; + case IEEE80211_IOC_CURCHAN: + error = ieee80211_ioctl_setcurchan(ic, ireq); + break; + case IEEE80211_IOC_SHORTGI: + if (ireq->i_val) { +#define IEEE80211_HTCAP_SHORTGI \ + (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) + if (((ireq->i_val ^ ic->ic_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0) + return EINVAL; + if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20) + ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20; + if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40) + ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40; +#undef IEEE80211_HTCAP_SHORTGI + } else + ic->ic_flags_ext &= + ~(IEEE80211_FEXT_SHORTGI20 | IEEE80211_FEXT_SHORTGI40); + /* XXX kick state machine? */ + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_AMPDU: + if (ireq->i_val) { + if ((ic->ic_htcaps & IEEE80211_HTC_AMPDU) == 0) + return EINVAL; + if (ireq->i_val & 1) + ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX; + if (ireq->i_val & 2) + ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX; + } else + ic->ic_flags_ext &= + ~(IEEE80211_FEXT_AMPDU_TX|IEEE80211_FEXT_AMPDU_RX); + /* NB: reset only if we're operating on an 11n channel */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) + error = ENETRESET; + break; + case IEEE80211_IOC_AMPDU_LIMIT: + /* XXX validate */ + ic->ic_ampdu_limit = ireq->i_val; + break; + case IEEE80211_IOC_AMPDU_DENSITY: + /* XXX validate */ + ic->ic_ampdu_density = ireq->i_val; + break; + case IEEE80211_IOC_AMSDU: + if (ireq->i_val) { + if ((ic->ic_htcaps & IEEE80211_HTC_AMSDU) == 0) + return EINVAL; + if (ireq->i_val & 1) + ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX; + if (ireq->i_val & 2) + ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX; + } else + ic->ic_flags_ext &= + ~(IEEE80211_FEXT_AMSDU_TX|IEEE80211_FEXT_AMSDU_RX); + /* NB: reset only if we're operating on an 11n channel */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) + error = ENETRESET; + break; + case IEEE80211_IOC_AMSDU_LIMIT: + /* XXX validate */ + ic->ic_amsdu_limit = ireq->i_val; /* XXX truncation? */ + break; + case IEEE80211_IOC_PUREN: + if (ireq->i_val) { + if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0) + return EINVAL; + ic->ic_flags_ext |= IEEE80211_FEXT_PUREN; + } else + ic->ic_flags_ext &= ~IEEE80211_FEXT_PUREN; + /* NB: reset only if we're operating on an 11n channel */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) + error = ENETRESET; + break; + case IEEE80211_IOC_DOTH: + if (ireq->i_val) { +#if 0 + /* XXX no capability */ + if ((ic->ic_caps & IEEE80211_C_DOTH) == 0) + return EINVAL; +#endif + ic->ic_flags |= IEEE80211_F_DOTH; + } else + ic->ic_flags &= ~IEEE80211_F_DOTH; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_HTCOMPAT: + if (ireq->i_val) { + if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0) + return EINVAL; + ic->ic_flags_ext |= IEEE80211_FEXT_HTCOMPAT; + } else + ic->ic_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT; + /* NB: reset only if we're operating on an 11n channel */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) + error = ENETRESET; + break; default: error = EINVAL; break; } - if (error == ENETRESET && !IS_UP_AUTO(ic)) - error = 0; + if (error == ENETRESET) + error = IS_UP_AUTO(ic) ? ieee80211_init(ic, 0) : 0; return error; } @@ -2692,15 +2503,6 @@ ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data) error = ieee80211_ioctl_set80211(ic, cmd, (struct ieee80211req *) data); break; - case SIOCGIFGENERIC: - error = ieee80211_cfgget(ic, cmd, data); - break; - case SIOCSIFGENERIC: - error = priv_check(curthread, PRIV_NET80211_MANAGE); - if (error) - break; - error = ieee80211_cfgset(ic, cmd, data); - break; case SIOCG80211STATS: ifr = (struct ifreq *)data; copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats)); |