aboutsummaryrefslogtreecommitdiff
path: root/sys/net80211/ieee80211_ioctl.c
diff options
context:
space:
mode:
authorSam Leffler <sam@FreeBSD.org>2004-12-08 17:26:47 +0000
committerSam Leffler <sam@FreeBSD.org>2004-12-08 17:26:47 +0000
commit8a1b9b6ad42cc9836185b817fbbead06a8d03cec (patch)
tree704c955cb68020dd8237664b417e3ee6141f028b /sys/net80211/ieee80211_ioctl.c
parent74c71bfc197138bc8813101a0bdc267f4c6f23ae (diff)
downloadsrc-8a1b9b6ad42cc9836185b817fbbead06a8d03cec.tar.gz
src-8a1b9b6ad42cc9836185b817fbbead06a8d03cec.zip
Update 802.11 support; too much new functionality to fully describe
here but it includes completed 802.11g, WPA, 802.11i, 802.1x, WME/WMM, AP-side power-save, crypto plugin framework, authenticator plugin framework, and access control plugin frameowrk.
Notes
Notes: svn path=/head/; revision=138568
Diffstat (limited to 'sys/net80211/ieee80211_ioctl.c')
-rw-r--r--sys/net80211/ieee80211_ioctl.c2101
1 files changed, 1694 insertions, 407 deletions
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c
index 75995910d253..059d215b86c1 100644
--- a/sys/net80211/ieee80211_ioctl.c
+++ b/sys/net80211/ieee80211_ioctl.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -67,26 +67,129 @@ __FBSDID("$FreeBSD$");
#include <dev/wi/if_wavelan_ieee.h>
+#define IS_UP(_ic) \
+ (((_ic)->ic_ifp->if_flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP))
+#define IS_UP_AUTO(_ic) \
+ (IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO)
+
/*
* 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 ifnet *ifp, u_long cmd, caddr_t data)
+ieee80211_cfgget(struct ieee80211com *ic, u_long cmd, caddr_t data)
{
- struct ieee80211com *ic = (void *)ifp;
+ 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;
- struct wi_apinfo *ap;
- struct ieee80211_node *ni;
- struct ieee80211_rateset *rs;
- struct wi_sigcache wsc;
- struct wi_scan_p2_hdr *p2;
- struct wi_scan_res *res;
error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
if (error)
@@ -153,8 +256,7 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
case WI_RID_COMMS_QUALITY:
wreq.wi_val[0] = 0; /* quality */
- wreq.wi_val[1] =
- htole16((*ic->ic_node_getrssi)(ic, ic->ic_bss));
+ wreq.wi_val[1] = htole16(ic->ic_node_getrssi(ic->ic_bss));
wreq.wi_val[2] = 0; /* noise */
wreq.wi_len = 3;
break;
@@ -199,7 +301,7 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
wreq.wi_len = 1;
break;
case WI_RID_ROAMING_MODE:
- wreq.wi_val[0] = htole16(1); /* enabled ... not supported */
+ wreq.wi_val[0] = htole16(ic->ic_roaming); /* XXX map */
wreq.wi_len = 1;
break;
case WI_RID_SYSTEM_SCALE:
@@ -220,8 +322,7 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
wreq.wi_len = 1;
break;
case WI_RID_WEP_AVAIL:
- wreq.wi_val[0] =
- htole16((ic->ic_caps & IEEE80211_C_WEP) ? 1 : 0);
+ wreq.wi_val[0] = htole16(1); /* always available */
wreq.wi_len = 1;
break;
case WI_RID_CNFAUTHMODE:
@@ -230,11 +331,11 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
case WI_RID_ENCRYPTION:
wreq.wi_val[0] =
- htole16((ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 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_wep_txkey);
+ wreq.wi_val[0] = htole16(ic->ic_def_txkey);
wreq.wi_len = 1;
break;
case WI_RID_DEFLT_CRYPT_KEYS:
@@ -248,124 +349,71 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
}
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
keys->wi_keys[i].wi_keylen =
- htole16(ic->ic_nw_keys[i].wk_len);
+ 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_len);
+ 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(IEEE80211_MAX_LEN); /* TODO: frag */
+ 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:
- if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
- /*
- * Don't return results until active scan completes.
- */
- if (ic->ic_state == IEEE80211_S_SCAN &&
- (ic->ic_flags & IEEE80211_F_ASCAN)) {
- error = EINPROGRESS;
- break;
- }
- }
- i = 0;
- ap = (void *)((char *)wreq.wi_val + sizeof(i));
- TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
- if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1))
- break;
- memset(ap, 0, sizeof(*ap));
- 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)(ic, 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 */
- }
- }
- i++;
- ap++;
- }
- memcpy(wreq.wi_val, &i, sizeof(i));
- wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2;
+ /*
+ * 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:
- wreq.wi_val[0] = 1; /* XXX lie so SCAN_RES can give rates */
+ /* 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_opmode != IEEE80211_M_HOSTAP &&
- ic->ic_state == IEEE80211_S_SCAN &&
- (ic->ic_flags & IEEE80211_F_ASCAN)) {
+ 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;
- }
- /* NB: we use the Prism2 format so we can return rate info */
- p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
- res = (void *)&p2[1];
- i = 0;
- TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
- if ((caddr_t)(res + 1) > (caddr_t)(&wreq + 1))
- break;
- res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan);
- res->wi_noise = 0;
- res->wi_signal = (*ic->ic_node_getrssi)(ic, 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;
- res++, i++;
- }
- p2->wi_rsvd = 0;
- p2->wi_reason = i;
- wreq.wi_len = (sizeof(*p2) + sizeof(*res) * i) / 2;
break;
- case WI_RID_READ_CACHE:
- i = 0;
- TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
- if (i == (WI_MAX_DATALEN/sizeof(struct wi_sigcache))-1)
- break;
- IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr);
- memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc));
- wsc.signal = (*ic->ic_node_getrssi)(ic, ni);
- wsc.noise = 0;
- wsc.quality = 0;
- memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i,
- &wsc, sizeof(wsc));
- i++;
- }
- wreq.wi_len = sizeof(wsc) * i / 2;
- break;
- case WI_RID_SCAN_APS:
- error = EINVAL;
+ 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;
@@ -397,11 +445,20 @@ findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
* the active list as the place to start the scan.
*/
static int
-ieee80211_setupscan(struct ieee80211com *ic)
+ieee80211_setupscan(struct ieee80211com *ic, const u_int8_t chanlist[])
{
- u_char *chanlist = ic->ic_chan_active;
int i;
+ /*
+ * 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;
if (ic->ic_ibss_chan == NULL ||
isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
@@ -416,21 +473,21 @@ found:
if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC ||
isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)))
ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+ memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
/*
- * 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.
+ * 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.
*/
- return (ic->ic_if.if_flags & IFF_UP) ? 0 : ENETRESET;
+ /* XXX handle proberequest case */
+ ic->ic_state = IEEE80211_S_INIT; /* XXX bypass state machine */
+ return 0;
}
int
-ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
+ieee80211_cfgset(struct ieee80211com *ic, u_long cmd, caddr_t data)
{
- struct ieee80211com *ic = (void *)ifp;
+ struct ifnet *ifp = ic->ic_ifp;
int i, j, len, error, rate;
struct ifreq *ifr = (struct ifreq *)data;
struct wi_ltv_keys *keys;
@@ -470,7 +527,9 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
isclr(ic->ic_chan_active, i))
return EINVAL;
ic->ic_ibss_chan = &ic->ic_channels[i];
- if (ic->ic_flags & IEEE80211_F_SIBSS)
+ 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:
@@ -516,7 +575,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
}
if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) {
ic->ic_opmode = le16toh(wreq.wi_val[0]);
- error = ENETRESET;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
}
break;
#if 0
@@ -564,7 +623,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
return EINVAL;
setrate:
ic->ic_fixed_rate = i;
- error = ENETRESET;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
break;
case WI_RID_CUR_TX_RATE:
return EPERM;
@@ -584,14 +643,14 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
ic->ic_flags |= IEEE80211_F_IBSSON;
if (ic->ic_opmode == IEEE80211_M_IBSS &&
ic->ic_state == IEEE80211_S_SCAN)
- error = ENETRESET;
+ 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 = ENETRESET;
+ error = IS_UP_AUTO(ic) ? ENETRESET : 0;
}
}
}
@@ -605,8 +664,10 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
case WI_RID_ROAMING_MODE:
if (len != 2)
return EINVAL;
- if (le16toh(wreq.wi_val[0]) != 1)
+ 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)
@@ -622,12 +683,12 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
return EINVAL;
if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
ic->ic_flags |= IEEE80211_F_PMGTON;
- error = ENETRESET;
+ 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 = ENETRESET;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
}
}
break;
@@ -636,7 +697,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
return EINVAL;
ic->ic_lintval = le16toh(wreq.wi_val[0]);
if (ic->ic_flags & IEEE80211_F_PMGTON)
- error = ENETRESET;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
break;
case WI_RID_CUR_BEACON_INT:
return EPERM;
@@ -645,8 +706,11 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
case WI_RID_CNFAUTHMODE:
if (len != 2)
return EINVAL;
- if (le16toh(wreq.wi_val[0]) != 1)
- return EINVAL; /* TODO: shared key auth */
+ 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)
@@ -654,13 +718,13 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
if (wreq.wi_val[0] != 0) {
if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
return EINVAL;
- if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) {
- ic->ic_flags |= IEEE80211_F_WEPON;
+ if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
+ ic->ic_flags |= IEEE80211_F_PRIVACY;
error = ENETRESET;
}
} else {
- if (ic->ic_flags & IEEE80211_F_WEPON) {
- ic->ic_flags &= ~IEEE80211_F_WEPON;
+ if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ ic->ic_flags &= ~IEEE80211_F_PRIVACY;
error = ENETRESET;
}
}
@@ -671,7 +735,8 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
i = le16toh(wreq.wi_val[0]);
if (i >= IEEE80211_WEP_NKID)
return EINVAL;
- ic->ic_wep_txkey = i;
+ 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))
@@ -681,15 +746,20 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
len = le16toh(keys->wi_keys[i].wi_keylen);
if (len != 0 && len < IEEE80211_WEP_KEYLEN)
return EINVAL;
- if (len > sizeof(ic->ic_nw_keys[i].wk_key))
+ if (len > IEEE80211_KEYBUF_SIZE)
return EINVAL;
}
- memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys));
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);
- ic->ic_nw_keys[i].wk_len = len;
- memcpy(ic->ic_nw_keys[i].wk_key,
- keys->wi_keys[i].wi_keydat, len);
+ 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;
@@ -699,10 +769,8 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
len = le16toh(wreq.wi_val[0]);
if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN)
return EINVAL;
- if (len != IEEE80211_MAX_LEN)
- return EINVAL; /* TODO: fragment */
ic->ic_fragthreshold = len;
- error = ENETRESET;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
break;
case WI_RID_IFACE_STATS:
error = EPERM;
@@ -710,7 +778,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
case WI_RID_SCAN_REQ: /* XXX wicontrol */
if (ic->ic_opmode == IEEE80211_M_HOSTAP)
break;
- error = ieee80211_setupscan(ic);
+ error = ieee80211_setupscan(ic, ic->ic_chan_avail);
if (error == 0)
error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
break;
@@ -742,9 +810,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
}
setbit(chanlist, i);
}
- memcpy(ic->ic_chan_active, chanlist,
- sizeof(ic->ic_chan_active));
- error = ieee80211_setupscan(ic);
+ error = ieee80211_setupscan(ic, chanlist);
if (wreq.wi_type == WI_RID_CHANNEL_LIST) {
/* NB: ignore error from ieee80211_setupscan */
error = ENETRESET;
@@ -755,311 +821,1532 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
error = EINVAL;
break;
}
+ if (error == ENETRESET && !IS_UP_AUTO(ic))
+ error = 0;
return error;
}
-int
-ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+static struct ieee80211_channel *
+getcurchan(struct ieee80211com *ic)
+{
+ switch (ic->ic_state) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ return ic->ic_des_chan;
+ default:
+ return ic->ic_ibss_chan;
+ }
+}
+
+static int
+cap2cipher(int flag)
+{
+ switch (flag) {
+ case IEEE80211_C_WEP: return IEEE80211_CIPHER_WEP;
+ case IEEE80211_C_AES: return IEEE80211_CIPHER_AES_OCB;
+ case IEEE80211_C_AES_CCM: return IEEE80211_CIPHER_AES_CCM;
+ case IEEE80211_C_CKIP: return IEEE80211_CIPHER_CKIP;
+ case IEEE80211_C_TKIP: return IEEE80211_CIPHER_TKIP;
+ }
+ return -1;
+}
+
+static int
+ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ struct ieee80211req_key ik;
+ struct ieee80211_key *wk;
+ const struct ieee80211_cipher *cip;
+ u_int kid;
+ int error;
+
+ if (ireq->i_len != sizeof(ik))
+ return EINVAL;
+ error = copyin(ireq->i_data, &ik, sizeof(ik));
+ if (error)
+ return error;
+ kid = ik.ik_keyix;
+ if (kid == IEEE80211_KEYIX_NONE) {
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, ik.ik_macaddr);
+ if (ni == NULL)
+ return EINVAL; /* XXX */
+ wk = &ni->ni_ucastkey;
+ } else {
+ if (kid >= IEEE80211_WEP_NKID)
+ return EINVAL;
+ wk = &ic->ic_nw_keys[kid];
+ IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr);
+ ni = NULL;
+ }
+ cip = wk->wk_cipher;
+ ik.ik_type = cip->ic_cipher;
+ ik.ik_keylen = wk->wk_keylen;
+ ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
+ if (wk->wk_keyix == ic->ic_def_txkey)
+ ik.ik_flags |= IEEE80211_KEY_DEFAULT;
+ if (suser(curthread) == 0) {
+ /* NB: only root can read key data */
+ ik.ik_keyrsc = wk->wk_keyrsc;
+ ik.ik_keytsc = wk->wk_keytsc;
+ memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen);
+ if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) {
+ memcpy(ik.ik_keydata+wk->wk_keylen,
+ wk->wk_key + IEEE80211_KEYBUF_SIZE,
+ IEEE80211_MICBUF_SIZE);
+ ik.ik_keylen += IEEE80211_MICBUF_SIZE;
+ }
+ } else {
+ ik.ik_keyrsc = 0;
+ ik.ik_keytsc = 0;
+ memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata));
+ }
+ if (ni != NULL)
+ ieee80211_free_node(ni);
+ return copyout(&ik, ireq->i_data, sizeof(ik));
+}
+
+static int
+ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+
+ if (sizeof(ic->ic_chan_active) > ireq->i_len)
+ ireq->i_len = sizeof(ic->ic_chan_active);
+ return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len);
+}
+
+static int
+ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211req_chaninfo chans; /* XXX off stack? */
+ int i, 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]);
+ if (space > ireq->i_len)
+ space = ireq->i_len;
+ return copyout(&chans, ireq->i_data, space);
+}
+
+static int
+ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ struct ieee80211req_wpaie wpaie;
+ int error;
+
+ if (ireq->i_len < IEEE80211_ADDR_LEN)
+ return EINVAL;
+ error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN);
+ if (error != 0)
+ return error;
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, wpaie.wpa_macaddr);
+ if (ni == NULL)
+ return EINVAL; /* XXX */
+ memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie));
+ if (ni->ni_wpa_ie != NULL) {
+ int ielen = ni->ni_wpa_ie[1] + 2;
+ if (ielen > sizeof(wpaie.wpa_ie))
+ ielen = sizeof(wpaie.wpa_ie);
+ memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen);
+ }
+ ieee80211_free_node(ni);
+ if (ireq->i_len > sizeof(wpaie))
+ ireq->i_len = sizeof(wpaie);
+ return copyout(&wpaie, ireq->i_data, ireq->i_len);
+}
+
+static int
+ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ u_int8_t macaddr[IEEE80211_ADDR_LEN];
+ const int off = __offsetof(struct ieee80211req_sta_stats, is_stats);
+ int error;
+
+ if (ireq->i_len < off)
+ return EINVAL;
+ error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
+ if (error != 0)
+ return error;
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, macaddr);
+ if (ni == NULL)
+ return EINVAL; /* XXX */
+ 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,
+ ireq->i_len - off);
+ ieee80211_free_node(ni);
+ return error;
+}
+
+static void
+get_scan_result(struct ieee80211req_scan_result *sr,
+ const struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+
+ memset(sr, 0, sizeof(*sr));
+ sr->isr_ssid_len = ni->ni_esslen;
+ if (ni->ni_wpa_ie != NULL)
+ sr->isr_ie_len += 2+ni->ni_wpa_ie[1];
+ if (ni->ni_wme_ie != NULL)
+ sr->isr_ie_len += 2+ni->ni_wme_ie[1];
+ 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_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);
+}
+
+static int
+ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ union {
+ struct ieee80211req_scan_result res;
+ char data[512]; /* XXX shrink? */
+ } u;
+ struct ieee80211req_scan_result *sr = &u.res;
+ struct ieee80211_node_table *nt;
+ struct ieee80211_node *ni;
+ int error, space;
+ u_int8_t *p, *cp;
+
+ 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;
+ 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 (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;
+ return error;
+}
+
+static void
+get_sta_info(struct ieee80211req_sta_info *si, const struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+
+ si->isi_ie_len = 0;
+ if (ni->ni_wpa_ie != NULL)
+ si->isi_ie_len += 2+ni->ni_wpa_ie[1];
+ if (ni->ni_wme_ie != NULL)
+ si->isi_ie_len += 2+ni->ni_wme_ie[1];
+ si->isi_len = sizeof(*si) + si->isi_ie_len, sizeof(u_int32_t);
+ si->isi_len = roundup(si->isi_len, sizeof(u_int32_t));
+ 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);
+ si->isi_capinfo = ni->ni_capinfo;
+ si->isi_erp = ni->ni_erp;
+ IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr);
+ si->isi_nrates = ni->ni_rates.rs_nrates;
+ if (si->isi_nrates > 15)
+ si->isi_nrates = 15;
+ memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates);
+ si->isi_txrate = ni->ni_txrate;
+ si->isi_associd = ni->ni_associd;
+ si->isi_txpower = ni->ni_txpower;
+ si->isi_vlan = ni->ni_vlan;
+ if (ni->ni_flags & IEEE80211_NODE_QOS) {
+ memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs));
+ memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs));
+ } else {
+ si->isi_txseqs[0] = ni->ni_txseqs[0];
+ si->isi_rxseqs[0] = ni->ni_rxseqs[0];
+ }
+ if (ic->ic_opmode == IEEE80211_M_IBSS || ni->ni_associd != 0)
+ si->isi_inact = ic->ic_inact_run;
+ else if (ieee80211_node_is_authorized(ni))
+ si->isi_inact = ic->ic_inact_auth;
+ else
+ si->isi_inact = ic->ic_inact_init;
+ si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
+}
+
+static int
+ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ union {
+ struct ieee80211req_sta_info info;
+ char data[512]; /* XXX shrink? */
+ } u;
+ struct ieee80211req_sta_info *si = &u.info;
+ struct ieee80211_node_table *nt;
+ struct ieee80211_node *ni;
+ int error, space;
+ u_int8_t *p, *cp;
+
+ nt = ic->ic_sta;
+ if (nt == NULL)
+ return EINVAL;
+ p = ireq->i_data;
+ space = ireq->i_len;
+ error = 0;
+ /* XXX locking */
+ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+ get_sta_info(si, ni);
+ if (si->isi_len > sizeof(u))
+ continue; /* XXX */
+ if (space < si->isi_len)
+ break;
+ 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];
+ }
+ error = copyout(si, p, si->isi_len);
+ if (error)
+ break;
+ p += si->isi_len;
+ space -= si->isi_len;
+ }
+ ireq->i_len -= space;
+ return error;
+}
+
+static int
+ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ struct ieee80211req_sta_txpow txpow;
+ int error;
+
+ if (ireq->i_len != sizeof(txpow))
+ return EINVAL;
+ error = copyin(ireq->i_data, &txpow, sizeof(txpow));
+ if (error != 0)
+ return error;
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, txpow.it_macaddr);
+ if (ni == NULL)
+ return EINVAL; /* XXX */
+ txpow.it_txpow = ni->ni_txpower;
+ error = copyout(&txpow, ireq->i_data, sizeof(txpow));
+ ieee80211_free_node(ni);
+ return error;
+}
+
+static int
+ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
{
- struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_wme_state *wme = &ic->ic_wme;
+ struct wmeParams *wmep;
+ int ac;
+
+ if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+ return EINVAL;
+
+ ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
+ if (ac >= WME_NUM_AC)
+ ac = WME_AC_BE;
+ if (ireq->i_len & IEEE80211_WMEPARAM_BSS)
+ wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
+ else
+ wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
+ ireq->i_val = wmep->wmep_logcwmin;
+ break;
+ case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
+ ireq->i_val = wmep->wmep_logcwmax;
+ break;
+ case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */
+ ireq->i_val = wmep->wmep_aifsn;
+ break;
+ case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
+ ireq->i_val = wmep->wmep_txopLimit;
+ break;
+ case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
+ wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
+ ireq->i_val = wmep->wmep_acm;
+ break;
+ case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/
+ wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
+ ireq->i_val = !wmep->wmep_noackPolicy;
+ break;
+ }
+ return 0;
+}
+
+static int
+ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
+{
+ const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
int error = 0;
- u_int kid, len;
- struct ieee80211req *ireq;
- struct ifreq *ifr;
+ u_int kid, len, m;
u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE];
char tmpssid[IEEE80211_NWID_LEN];
- struct ieee80211_channel *chan;
- struct ifaddr *ifa; /* XXX */
- switch (cmd) {
- case SIOCSIFMEDIA:
- case SIOCGIFMEDIA:
- error = ifmedia_ioctl(ifp, (struct ifreq *) data,
- &ic->ic_media, cmd);
- break;
- case SIOCG80211:
- ireq = (struct ieee80211req *) data;
- switch (ireq->i_type) {
- case IEEE80211_IOC_SSID:
- 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);
- break;
- default:
- ireq->i_len = ic->ic_bss->ni_esslen;
- memcpy(tmpssid, ic->ic_bss->ni_essid,
- ireq->i_len);
- break;
- }
- error = copyout(tmpssid, ireq->i_data, ireq->i_len);
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_SSID:
+ 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);
break;
- case IEEE80211_IOC_NUMSSIDS:
+ default:
+ ireq->i_len = ic->ic_bss->ni_esslen;
+ memcpy(tmpssid, ic->ic_bss->ni_essid,
+ ireq->i_len);
+ break;
+ }
+ error = copyout(tmpssid, ireq->i_data, ireq->i_len);
+ break;
+ case IEEE80211_IOC_NUMSSIDS:
+ ireq->i_val = 1;
+ break;
+ case IEEE80211_IOC_WEP:
+ if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
+ ireq->i_val = IEEE80211_WEP_OFF;
+ else if (ic->ic_flags & IEEE80211_F_DROPUNENC)
+ ireq->i_val = IEEE80211_WEP_ON;
+ else
+ ireq->i_val = IEEE80211_WEP_MIXED;
+ break;
+ case IEEE80211_IOC_WEPKEY:
+ kid = (u_int) ireq->i_val;
+ if (kid >= IEEE80211_WEP_NKID)
+ return EINVAL;
+ len = (u_int) ic->ic_nw_keys[kid].wk_keylen;
+ /* NB: only root can read WEP keys */
+ if (suser(curthread) == 0) {
+ bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
+ } else {
+ bzero(tmpkey, len);
+ }
+ ireq->i_len = len;
+ error = copyout(tmpkey, ireq->i_data, len);
+ break;
+ case IEEE80211_IOC_NUMWEPKEYS:
+ ireq->i_val = IEEE80211_WEP_NKID;
+ break;
+ case IEEE80211_IOC_WEPTXKEY:
+ ireq->i_val = ic->ic_def_txkey;
+ break;
+ case IEEE80211_IOC_AUTHMODE:
+ if (ic->ic_flags & IEEE80211_F_WPA)
+ ireq->i_val = IEEE80211_AUTH_WPA;
+ else
+ ireq->i_val = ic->ic_bss->ni_authmode;
+ break;
+ case IEEE80211_IOC_CHANNEL:
+ ireq->i_val = ieee80211_chan2ieee(ic, getcurchan(ic));
+ break;
+ case IEEE80211_IOC_POWERSAVE:
+ if (ic->ic_flags & IEEE80211_F_PMGTON)
+ ireq->i_val = IEEE80211_POWERSAVE_ON;
+ else
+ ireq->i_val = IEEE80211_POWERSAVE_OFF;
+ break;
+ case IEEE80211_IOC_POWERSAVESLEEP:
+ ireq->i_val = ic->ic_lintval;
+ break;
+ case IEEE80211_IOC_RTSTHRESHOLD:
+ ireq->i_val = ic->ic_rtsthreshold;
+ break;
+ case IEEE80211_IOC_PROTMODE:
+ ireq->i_val = ic->ic_protmode;
+ break;
+ case IEEE80211_IOC_TXPOWER:
+ if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
+ return EINVAL;
+ ireq->i_val = ic->ic_txpowlimit;
+ break;
+ case IEEE80211_IOC_MCASTCIPHER:
+ ireq->i_val = rsn->rsn_mcastcipher;
+ break;
+ case IEEE80211_IOC_MCASTKEYLEN:
+ ireq->i_val = rsn->rsn_mcastkeylen;
+ break;
+ case IEEE80211_IOC_UCASTCIPHERS:
+ ireq->i_val = 0;
+ for (m = 0x1; m != 0; m <<= 1)
+ if (rsn->rsn_ucastcipherset & m)
+ ireq->i_val |= 1<<cap2cipher(m);
+ break;
+ case IEEE80211_IOC_UCASTCIPHER:
+ ireq->i_val = rsn->rsn_ucastcipher;
+ break;
+ case IEEE80211_IOC_UCASTKEYLEN:
+ ireq->i_val = rsn->rsn_ucastkeylen;
+ break;
+ case IEEE80211_IOC_KEYMGTALGS:
+ ireq->i_val = rsn->rsn_keymgmtset;
+ break;
+ case IEEE80211_IOC_RSNCAPS:
+ ireq->i_val = rsn->rsn_caps;
+ break;
+ case IEEE80211_IOC_WPA:
+ switch (ic->ic_flags & IEEE80211_F_WPA) {
+ case IEEE80211_F_WPA1:
ireq->i_val = 1;
break;
- case IEEE80211_IOC_WEP:
- if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
- ireq->i_val = IEEE80211_WEP_NOSUP;
- } else {
- if (ic->ic_flags & IEEE80211_F_WEPON) {
- ireq->i_val =
- IEEE80211_WEP_MIXED;
- } else {
- ireq->i_val =
- IEEE80211_WEP_OFF;
- }
- }
+ case IEEE80211_F_WPA2:
+ ireq->i_val = 2;
break;
- case IEEE80211_IOC_WEPKEY:
- if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
- error = EINVAL;
- break;
- }
- kid = (u_int) ireq->i_val;
- if (kid >= IEEE80211_WEP_NKID) {
- error = EINVAL;
- break;
- }
- len = (u_int) ic->ic_nw_keys[kid].wk_len;
- /* NB: only root can read WEP keys */
- if (suser(curthread) == 0) {
- bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
- } else {
- bzero(tmpkey, len);
- }
- ireq->i_len = len;
- error = copyout(tmpkey, ireq->i_data, len);
+ case IEEE80211_F_WPA1 | IEEE80211_F_WPA2:
+ ireq->i_val = 3;
break;
- case IEEE80211_IOC_NUMWEPKEYS:
- if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
- error = EINVAL;
- else
- ireq->i_val = IEEE80211_WEP_NKID;
+ default:
+ ireq->i_val = 0;
break;
- case IEEE80211_IOC_WEPTXKEY:
- if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
- error = EINVAL;
- else
- ireq->i_val = ic->ic_wep_txkey;
+ }
+ break;
+ case IEEE80211_IOC_CHANLIST:
+ error = ieee80211_ioctl_getchanlist(ic, ireq);
+ break;
+ case IEEE80211_IOC_ROAMING:
+ ireq->i_val = ic->ic_roaming;
+ break;
+ case IEEE80211_IOC_PRIVACY:
+ ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0;
+ break;
+ case IEEE80211_IOC_DROPUNENCRYPTED:
+ ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0;
+ break;
+ case IEEE80211_IOC_COUNTERMEASURES:
+ ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0;
+ break;
+ case IEEE80211_IOC_DRIVER_CAPS:
+ ireq->i_val = ic->ic_caps>>16;
+ ireq->i_len = ic->ic_caps&0xffff;
+ break;
+ case IEEE80211_IOC_WME:
+ ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0;
+ break;
+ case IEEE80211_IOC_HIDESSID:
+ ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0;
+ break;
+ case IEEE80211_IOC_APBRIDGE:
+ ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0;
+ break;
+ case IEEE80211_IOC_OPTIE:
+ if (ic->ic_opt_ie == NULL)
+ return EINVAL;
+ /* NB: truncate, caller can check length */
+ if (ireq->i_len > ic->ic_opt_ie_len)
+ ireq->i_len = ic->ic_opt_ie_len;
+ error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len);
+ break;
+ case IEEE80211_IOC_WPAKEY:
+ error = ieee80211_ioctl_getkey(ic, ireq);
+ break;
+ case IEEE80211_IOC_CHANINFO:
+ error = ieee80211_ioctl_getchaninfo(ic, ireq);
+ break;
+ case IEEE80211_IOC_BSSID:
+ if (ireq->i_len != IEEE80211_ADDR_LEN)
+ return EINVAL;
+ error = copyout(ic->ic_state == IEEE80211_S_RUN ?
+ ic->ic_bss->ni_bssid :
+ ic->ic_des_bssid,
+ ireq->i_data, ireq->i_len);
+ break;
+ case IEEE80211_IOC_WPAIE:
+ error = ieee80211_ioctl_getwpaie(ic, ireq);
+ break;
+ case IEEE80211_IOC_SCAN_RESULTS:
+ error = ieee80211_ioctl_getscanresults(ic, ireq);
+ break;
+ case IEEE80211_IOC_STA_STATS:
+ error = ieee80211_ioctl_getstastats(ic, ireq);
+ break;
+ case IEEE80211_IOC_TXPOWMAX:
+ ireq->i_val = ic->ic_bss->ni_txpower;
+ break;
+ case IEEE80211_IOC_STA_TXPOW:
+ error = ieee80211_ioctl_getstatxpow(ic, ireq);
+ break;
+ case IEEE80211_IOC_STA_INFO:
+ error = ieee80211_ioctl_getstainfo(ic, ireq);
+ break;
+ case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
+ case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
+ case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */
+ case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
+ case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
+ case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */
+ error = ieee80211_ioctl_getwmeparam(ic, ireq);
+ break;
+ case IEEE80211_IOC_DTIM_PERIOD:
+ ireq->i_val = ic->ic_dtim_period;
+ break;
+ case IEEE80211_IOC_BEACON_INTERVAL:
+ /* NB: get from ic_bss for station mode */
+ ireq->i_val = ic->ic_bss->ni_intval;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+static int
+ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ int error;
+ void *ie;
+
+ /*
+ * NB: Doing this for ap operation could be useful (e.g. for
+ * WPA and/or WME) except that it typically is worthless
+ * without being able to intervene when processing
+ * association response frames--so disallow it for now.
+ */
+ if (ic->ic_opmode != IEEE80211_M_STA)
+ return EINVAL;
+ if (ireq->i_len > IEEE80211_MAX_OPT_IE)
+ return EINVAL;
+ /* NB: data.length is validated by the wireless extensions code */
+ MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_WAITOK);
+ if (ie == NULL)
+ return ENOMEM;
+ error = copyin(ireq->i_data, ie, ireq->i_len);
+ /* XXX sanity check data? */
+ if (ic->ic_opt_ie != NULL)
+ FREE(ic->ic_opt_ie, M_DEVBUF);
+ ic->ic_opt_ie = ie;
+ ic->ic_opt_ie_len = ireq->i_len;
+ return 0;
+}
+
+static int
+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;
+ int error;
+
+ if (ireq->i_len != sizeof(ik))
+ return EINVAL;
+ error = copyin(ireq->i_data, &ik, sizeof(ik));
+ if (error)
+ return error;
+ /* NB: cipher support is verified by ieee80211_crypt_newkey */
+ /* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */
+ if (ik.ik_keylen > sizeof(ik.ik_keydata))
+ return E2BIG;
+ kid = ik.ik_keyix;
+ if (kid == IEEE80211_KEYIX_NONE) {
+ /* XXX unicast keys currently must be tx/rx */
+ if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
+ return EINVAL;
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ ni = ic->ic_bss;
+ if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid))
+ return EADDRNOTAVAIL;
+ } else {
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, ik.ik_macaddr);
+ if (ni == NULL)
+ return ENOENT;
+ }
+ wk = &ni->ni_ucastkey;
+ } else {
+ if (kid >= IEEE80211_WEP_NKID)
+ return EINVAL;
+ wk = &ic->ic_nw_keys[kid];
+ ni = NULL;
+ }
+ error = 0;
+ ieee80211_key_update_begin(ic);
+ if (ieee80211_crypto_newkey(ic, ik.ik_type, wk)) {
+ wk->wk_keylen = ik.ik_keylen;
+ /* NB: MIC presence is implied by cipher type */
+ if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
+ wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
+ wk->wk_keyrsc = ik.ik_keyrsc;
+ wk->wk_keytsc = 0; /* new key, reset */
+ wk->wk_flags |=
+ ik.ik_flags & (IEEE80211_KEY_XMIT|IEEE80211_KEY_RECV);
+ memset(wk->wk_key, 0, sizeof(wk->wk_key));
+ memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen);
+ if (!ieee80211_crypto_setkey(ic, wk,
+ ni != NULL ? ni->ni_macaddr : ik.ik_macaddr))
+ error = EIO;
+ else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT))
+ ic->ic_def_txkey = kid;
+ } else
+ error = ENXIO;
+ ieee80211_key_update_end(ic);
+ if (ni != NULL)
+ ieee80211_free_node(ni);
+ return error;
+}
+
+static int
+ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211req_del_key dk;
+ int kid, error;
+
+ if (ireq->i_len != sizeof(dk))
+ return EINVAL;
+ error = copyin(ireq->i_data, &dk, sizeof(dk));
+ 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) {
+ struct ieee80211_node *ni;
+
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, dk.idk_macaddr);
+ if (ni == NULL)
+ return EINVAL; /* XXX */
+ /* XXX error return */
+ ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
+ ieee80211_free_node(ni);
+ } else {
+ if (kid >= IEEE80211_WEP_NKID)
+ return EINVAL;
+ /* XXX error return */
+ ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]);
+ }
+ return 0;
+}
+
+static void
+domlme(void *arg, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211req_mlme *mlme = arg;
+
+ if (ni->ni_associd != 0) {
+ IEEE80211_SEND_MGMT(ic, ni,
+ mlme->im_op == IEEE80211_MLME_DEAUTH ?
+ IEEE80211_FC0_SUBTYPE_DEAUTH :
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ mlme->im_reason);
+ }
+ ieee80211_node_leave(ic, ni);
+}
+
+static int
+ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211req_mlme mlme;
+ struct ieee80211_node *ni;
+ int error;
+
+ if (ireq->i_len != sizeof(mlme))
+ return EINVAL;
+ error = copyin(ireq->i_data, &mlme, sizeof(mlme));
+ if (error)
+ 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 (ic->ic_des_esslen != 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,
+ ic->ic_des_esslen, ic->ic_des_essid);
+ } 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;
+ }
+ break;
+ case IEEE80211_MLME_DISASSOC:
+ case IEEE80211_MLME_DEAUTH:
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ /* XXX not quite right */
+ ieee80211_new_state(ic, IEEE80211_S_INIT,
+ mlme.im_reason);
break;
- case IEEE80211_IOC_AUTHMODE:
- ireq->i_val = IEEE80211_AUTH_OPEN;
+ case IEEE80211_M_HOSTAP:
+ /* NB: the broadcast address means do 'em all */
+ if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) {
+ if (ic->ic_sta == NULL ||
+ (ni = ieee80211_find_node(ic->ic_sta,
+ mlme.im_macaddr)) == NULL)
+ return EINVAL;
+ domlme(&mlme, ni);
+ ieee80211_free_node(ni);
+ } else {
+ if (ic->ic_sta != NULL)
+ ieee80211_iterate_nodes(ic->ic_sta,
+ domlme, &mlme);
+ }
break;
- case IEEE80211_IOC_CHANNEL:
- switch (ic->ic_state) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- if (ic->ic_opmode == IEEE80211_M_STA)
- chan = ic->ic_des_chan;
- else
- chan = ic->ic_ibss_chan;
- break;
- default:
- chan = ic->ic_bss->ni_chan;
- break;
+ default:
+ return EINVAL;
+ }
+ break;
+ case IEEE80211_MLME_AUTHORIZE:
+ case IEEE80211_MLME_UNAUTHORIZE:
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+ return EINVAL;
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, mlme.im_macaddr);
+ if (ni == NULL)
+ return EINVAL;
+ if (mlme.im_op == IEEE80211_MLME_AUTHORIZE)
+ ieee80211_node_authorize(ic, ni);
+ else
+ ieee80211_node_unauthorize(ic, ni);
+ ieee80211_free_node(ni);
+ break;
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
+
+static int
+ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ u_int8_t mac[IEEE80211_ADDR_LEN];
+ const struct ieee80211_aclator *acl = ic->ic_acl;
+ int error;
+
+ if (ireq->i_len != sizeof(mac))
+ return EINVAL;
+ error = copyin(ireq->i_data, mac, ireq->i_len);
+ if (error)
+ return error;
+ if (acl == NULL) {
+ acl = ieee80211_aclator_get("mac");
+ if (acl == NULL || !acl->iac_attach(ic))
+ return EINVAL;
+ ic->ic_acl = acl;
+ }
+ if (ireq->i_type == IEEE80211_IOC_ADDMAC)
+ acl->iac_add(ic, mac);
+ else
+ acl->iac_remove(ic, mac);
+ return 0;
+}
+
+static int
+ieee80211_ioctl_maccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ const struct ieee80211_aclator *acl = ic->ic_acl;
+
+ switch (ireq->i_val) {
+ case IEEE80211_MACCMD_POLICY_OPEN:
+ case IEEE80211_MACCMD_POLICY_ALLOW:
+ case IEEE80211_MACCMD_POLICY_DENY:
+ if (acl == NULL) {
+ acl = ieee80211_aclator_get("mac");
+ if (acl == NULL || !acl->iac_attach(ic))
+ return EINVAL;
+ ic->ic_acl = acl;
+ }
+ acl->iac_setpolicy(ic, ireq->i_val);
+ break;
+ case IEEE80211_MACCMD_FLUSH:
+ if (acl != NULL)
+ acl->iac_flush(ic);
+ /* NB: silently ignore when not in use */
+ break;
+ case IEEE80211_MACCMD_DETACH:
+ if (acl != NULL) {
+ ic->ic_acl = NULL;
+ acl->iac_detach(ic);
+ }
+ break;
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
+
+static int
+ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211req_chanlist list;
+ u_char chanlist[IEEE80211_CHAN_BYTES];
+ int i, j, error;
+
+ if (ireq->i_len != sizeof(list))
+ return EINVAL;
+ error = copyin(ireq->i_data, &list, sizeof(list));
+ if (error)
+ return error;
+ 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++) {
+ /*
+ * 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))
+ setbit(chanlist, i);
+ }
+ 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;
}
- ireq->i_val = ieee80211_chan2ieee(ic, chan);
+ return EINVAL; /* no active channels */
+found:
+ ;
+ }
+ memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
+ if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC ||
+ isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)))
+ ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+ return IS_UP_AUTO(ic) ? ENETRESET : 0;
+}
+
+static int
+ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ struct ieee80211req_sta_txpow txpow;
+ int error;
+
+ if (ireq->i_len != sizeof(txpow))
+ return EINVAL;
+ error = copyin(ireq->i_data, &txpow, sizeof(txpow));
+ if (error != 0)
+ return error;
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, txpow.it_macaddr);
+ if (ni == NULL)
+ return EINVAL; /* XXX */
+ ni->ni_txpower = txpow.it_txpow;
+ ieee80211_free_node(ni);
+ return error;
+}
+
+static int
+ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211_wme_state *wme = &ic->ic_wme;
+ struct wmeParams *wmep, *chanp;
+ int isbss, ac;
+
+ if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+ return EINVAL;
+
+ isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS);
+ ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
+ if (ac >= WME_NUM_AC)
+ ac = WME_AC_BE;
+ if (isbss) {
+ chanp = &wme->wme_bssChanParams.cap_wmeParams[ac];
+ wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
+ } else {
+ chanp = &wme->wme_chanParams.cap_wmeParams[ac];
+ wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
+ }
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
+ if (isbss) {
+ wmep->wmep_logcwmin = ireq->i_val;
+ if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+ chanp->wmep_logcwmin = ireq->i_val;
+ } else {
+ wmep->wmep_logcwmin = chanp->wmep_logcwmin =
+ ireq->i_val;
+ }
+ break;
+ case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
+ if (isbss) {
+ wmep->wmep_logcwmax = ireq->i_val;
+ if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+ chanp->wmep_logcwmax = ireq->i_val;
+ } else {
+ wmep->wmep_logcwmax = chanp->wmep_logcwmax =
+ ireq->i_val;
+ }
+ break;
+ case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */
+ if (isbss) {
+ wmep->wmep_aifsn = ireq->i_val;
+ if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+ chanp->wmep_aifsn = ireq->i_val;
+ } else {
+ wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val;
+ }
+ break;
+ case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
+ if (isbss) {
+ wmep->wmep_txopLimit = ireq->i_val;
+ if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+ chanp->wmep_txopLimit = ireq->i_val;
+ } else {
+ wmep->wmep_txopLimit = chanp->wmep_txopLimit =
+ ireq->i_val;
+ }
+ break;
+ case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
+ wmep->wmep_acm = ireq->i_val;
+ if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+ chanp->wmep_acm = ireq->i_val;
+ break;
+ case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/
+ wmep->wmep_noackPolicy = chanp->wmep_noackPolicy =
+ (ireq->i_val) == 0;
+ break;
+ }
+ ieee80211_wme_updateparams(ic);
+ return 0;
+}
+
+static int
+cipher2cap(int cipher)
+{
+ switch (cipher) {
+ case IEEE80211_CIPHER_WEP: return IEEE80211_C_WEP;
+ case IEEE80211_CIPHER_AES_OCB: return IEEE80211_C_AES;
+ case IEEE80211_CIPHER_AES_CCM: return IEEE80211_C_AES_CCM;
+ case IEEE80211_CIPHER_CKIP: return IEEE80211_C_CKIP;
+ case IEEE80211_CIPHER_TKIP: return IEEE80211_C_TKIP;
+ }
+ return 0;
+}
+
+static int
+ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
+{
+ static const u_int8_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];
+ char tmpssid[IEEE80211_NWID_LEN];
+ u_int8_t tmpbssid[IEEE80211_ADDR_LEN];
+ struct ieee80211_key *k;
+ int j, caps;
+ u_int kid;
+
+ error = 0;
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_SSID:
+ if (ireq->i_val != 0 ||
+ ireq->i_len > IEEE80211_NWID_LEN)
+ return EINVAL;
+ error = copyin(ireq->i_data, tmpssid, ireq->i_len);
+ if (error)
break;
- case IEEE80211_IOC_POWERSAVE:
- if (ic->ic_flags & IEEE80211_F_PMGTON)
- ireq->i_val = IEEE80211_POWERSAVE_ON;
- else
- ireq->i_val = IEEE80211_POWERSAVE_OFF;
+ 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;
+ break;
+ case IEEE80211_IOC_WEP:
+ switch (ireq->i_val) {
+ case IEEE80211_WEP_OFF:
+ ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+ ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+ break;
+ case IEEE80211_WEP_ON:
+ ic->ic_flags |= IEEE80211_F_PRIVACY;
+ ic->ic_flags |= IEEE80211_F_DROPUNENC;
break;
- case IEEE80211_IOC_POWERSAVESLEEP:
- ireq->i_val = ic->ic_lintval;
+ case IEEE80211_WEP_MIXED:
+ ic->ic_flags |= IEEE80211_F_PRIVACY;
+ ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
break;
- case IEEE80211_IOC_RTSTHRESHOLD:
- ireq->i_val = ic->ic_rtsthreshold;
+ }
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_WEPKEY:
+ kid = (u_int) ireq->i_val;
+ if (kid >= IEEE80211_WEP_NKID)
+ return EINVAL;
+ k = &ic->ic_nw_keys[kid];
+ if (ireq->i_len == 0) {
+ /* zero-len =>'s delete any existing key */
+ (void) ieee80211_crypto_delkey(ic, k);
break;
- case IEEE80211_IOC_PROTMODE:
- ireq->i_val = ic->ic_protmode;
+ }
+ if (ireq->i_len > sizeof(tmpkey))
+ return EINVAL;
+ memset(tmpkey, 0, sizeof(tmpkey));
+ error = copyin(ireq->i_data, tmpkey, ireq->i_len);
+ if (error)
break;
- case IEEE80211_IOC_TXPOWER:
- if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
+ ieee80211_key_update_begin(ic);
+ if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP, k)) {
+ k->wk_keylen = ireq->i_len;
+ k->wk_flags |=
+ IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
+ memcpy(k->wk_key, tmpkey, sizeof(tmpkey));
+ if (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr))
error = EINVAL;
- else
- ireq->i_val = ic->ic_txpower;
+ } else
+ error = EINVAL;
+ ieee80211_key_update_end(ic);
+ break;
+ case IEEE80211_IOC_WEPTXKEY:
+ kid = (u_int) ireq->i_val;
+ if (kid >= IEEE80211_WEP_NKID)
+ return EINVAL;
+ ic->ic_def_txkey = kid;
+ error = ERESTART; /* push to hardware */
+ break;
+ case IEEE80211_IOC_AUTHMODE:
+ switch (ireq->i_val) {
+ case IEEE80211_AUTH_WPA:
+ case IEEE80211_AUTH_8021X: /* 802.1x */
+ case IEEE80211_AUTH_OPEN: /* open */
+ case IEEE80211_AUTH_SHARED: /* shared-key */
+ case IEEE80211_AUTH_AUTO: /* auto */
+ auth = ieee80211_authenticator_get(ireq->i_val);
+ if (auth == NULL)
+ return EINVAL;
break;
default:
- error = EINVAL;
+ return EINVAL;
+ }
+ switch (ireq->i_val) {
+ case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */
+ ic->ic_flags |= IEEE80211_F_PRIVACY;
+ ireq->i_val = IEEE80211_AUTH_8021X;
+ break;
+ case IEEE80211_AUTH_OPEN: /* open */
+ ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
+ break;
+ case IEEE80211_AUTH_SHARED: /* shared-key */
+ case IEEE80211_AUTH_8021X: /* 802.1x */
+ ic->ic_flags &= ~IEEE80211_F_WPA;
+ /* both require a key so mark the PRIVACY capability */
+ ic->ic_flags |= IEEE80211_F_PRIVACY;
+ break;
+ case IEEE80211_AUTH_AUTO: /* auto */
+ ic->ic_flags &= ~IEEE80211_F_WPA;
+ /* XXX PRIVACY handling? */
+ /* XXX what's the right way to do this? */
break;
}
+ /* NB: authenticator attach/detach happens on state change */
+ ic->ic_bss->ni_authmode = ireq->i_val;
+ /* XXX mixed/mode/usage? */
+ ic->ic_auth = auth;
+ error = ENETRESET;
break;
- case SIOCS80211:
- error = suser(curthread);
- if (error)
- break;
- ireq = (struct ieee80211req *) data;
- switch (ireq->i_type) {
- case IEEE80211_IOC_SSID:
- if (ireq->i_val != 0 ||
- ireq->i_len > IEEE80211_NWID_LEN) {
- error = EINVAL;
- break;
- }
- 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);
+ 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;
- case IEEE80211_IOC_WEP:
+ default:
/*
- * These cards only support one mode so
- * we just turn wep on if what ever is
- * passed in is not OFF.
+ * If the desired channel has changed (to something
+ * other than any) and we're not already scanning,
+ * then kick the state machine.
*/
- if (ireq->i_val == IEEE80211_WEP_OFF) {
- ic->ic_flags &= ~IEEE80211_F_WEPON;
- } else {
- ic->ic_flags |= IEEE80211_F_WEPON;
- }
- error = ENETRESET;
- break;
- case IEEE80211_IOC_WEPKEY:
- if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
- error = EINVAL;
- break;
- }
- kid = (u_int) ireq->i_val;
- if (kid >= IEEE80211_WEP_NKID) {
- error = EINVAL;
- break;
- }
- if (ireq->i_len > sizeof(tmpkey)) {
- error = EINVAL;
- break;
- }
- memset(tmpkey, 0, sizeof(tmpkey));
- error = copyin(ireq->i_data, tmpkey, ireq->i_len);
- if (error)
- break;
- memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey,
- sizeof(tmpkey));
- ic->ic_nw_keys[kid].wk_len = ireq->i_len;
- error = ENETRESET;
+ 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;
- case IEEE80211_IOC_WEPTXKEY:
- kid = (u_int) ireq->i_val;
- if (kid >= IEEE80211_WEP_NKID) {
- error = EINVAL;
- break;
+ }
+ if (error == ENETRESET && ic->ic_opmode == IEEE80211_M_MONITOR)
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ break;
+ case IEEE80211_IOC_POWERSAVE:
+ switch (ireq->i_val) {
+ case IEEE80211_POWERSAVE_OFF:
+ if (ic->ic_flags & IEEE80211_F_PMGTON) {
+ ic->ic_flags &= ~IEEE80211_F_PMGTON;
+ error = ENETRESET;
}
- ic->ic_wep_txkey = kid;
- error = ENETRESET;
- break;
-#if 0
- case IEEE80211_IOC_AUTHMODE:
- sc->wi_authmode = ireq->i_val;
break;
-#endif
- 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)) {
+ case IEEE80211_POWERSAVE_ON:
+ if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
error = EINVAL;
- break;
- } 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:
+ else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
+ ic->ic_flags |= IEEE80211_F_PMGTON;
error = ENETRESET;
- break;
- default:
- if (ic->ic_opmode == IEEE80211_M_STA) {
- if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
- ic->ic_bss->ni_chan != ic->ic_des_chan)
- error = ENETRESET;
- } else {
- if (ic->ic_bss->ni_chan != ic->ic_ibss_chan)
- error = ENETRESET;
- }
- break;
}
break;
- case IEEE80211_IOC_POWERSAVE:
- switch (ireq->i_val) {
- case IEEE80211_POWERSAVE_OFF:
- if (ic->ic_flags & IEEE80211_F_PMGTON) {
- ic->ic_flags &= ~IEEE80211_F_PMGTON;
- error = ENETRESET;
- }
- break;
- case IEEE80211_POWERSAVE_ON:
- if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
- error = EINVAL;
- else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
- ic->ic_flags |= IEEE80211_F_PMGTON;
- error = ENETRESET;
- }
- break;
- default:
- error = EINVAL;
- break;
- }
+ default:
+ error = EINVAL;
break;
- case IEEE80211_IOC_POWERSAVESLEEP:
- if (ireq->i_val < 0) {
- error = EINVAL;
- break;
- }
- ic->ic_lintval = ireq->i_val;
- error = ENETRESET;
+ }
+ break;
+ case IEEE80211_IOC_POWERSAVESLEEP:
+ if (ireq->i_val < 0)
+ return EINVAL;
+ ic->ic_lintval = ireq->i_val;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ break;
+ case IEEE80211_IOC_RTSTHRESHOLD:
+ if (!(IEEE80211_RTS_MIN < ireq->i_val &&
+ ireq->i_val < IEEE80211_RTS_MAX))
+ return EINVAL;
+ ic->ic_rtsthreshold = ireq->i_val;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ break;
+ case IEEE80211_IOC_PROTMODE:
+ if (ireq->i_val > IEEE80211_PROT_RTSCTS)
+ return EINVAL;
+ ic->ic_protmode = ireq->i_val;
+ /* NB: if not operating in 11g this can wait */
+ if (ic->ic_curmode == IEEE80211_MODE_11G)
+ 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))
+ return EINVAL;
+ ic->ic_txpowlimit = ireq->i_val;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ break;
+ case IEEE80211_IOC_ROAMING:
+ if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val &&
+ ireq->i_val <= IEEE80211_ROAMING_MANUAL))
+ return EINVAL;
+ ic->ic_roaming = ireq->i_val;
+ /* XXXX reset? */
+ break;
+ case IEEE80211_IOC_PRIVACY:
+ if (ireq->i_val) {
+ /* XXX check for key state? */
+ ic->ic_flags |= IEEE80211_F_PRIVACY;
+ } else
+ ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+ break;
+ case IEEE80211_IOC_DROPUNENCRYPTED:
+ if (ireq->i_val)
+ ic->ic_flags |= IEEE80211_F_DROPUNENC;
+ else
+ ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+ break;
+ case IEEE80211_IOC_WPAKEY:
+ error = ieee80211_ioctl_setkey(ic, ireq);
+ break;
+ case IEEE80211_IOC_DELKEY:
+ error = ieee80211_ioctl_delkey(ic, ireq);
+ break;
+ case IEEE80211_IOC_MLME:
+ error = ieee80211_ioctl_setmlme(ic, ireq);
+ break;
+ case IEEE80211_IOC_OPTIE:
+ error = ieee80211_ioctl_setoptie(ic, ireq);
+ break;
+ case IEEE80211_IOC_COUNTERMEASURES:
+ if (ireq->i_val) {
+ if ((ic->ic_flags & IEEE80211_F_WPA) == 0)
+ return EINVAL;
+ ic->ic_flags |= IEEE80211_F_COUNTERM;
+ } else
+ ic->ic_flags &= ~IEEE80211_F_COUNTERM;
+ break;
+ case IEEE80211_IOC_WPA:
+ if (ireq->i_val > 3)
+ return EINVAL;
+ /* XXX verify ciphers available */
+ ic->ic_flags &= ~IEEE80211_F_WPA;
+ switch (ireq->i_val) {
+ case 1:
+ ic->ic_flags |= IEEE80211_F_WPA1;
break;
- case IEEE80211_IOC_RTSTHRESHOLD:
- if (!(IEEE80211_RTS_MIN < ireq->i_val &&
- ireq->i_val < IEEE80211_RTS_MAX)) {
- error = EINVAL;
- break;
- }
- ic->ic_rtsthreshold = ireq->i_val;
- error = ENETRESET;
+ case 2:
+ ic->ic_flags |= IEEE80211_F_WPA2;
break;
- case IEEE80211_IOC_PROTMODE:
- if (ireq->i_val > IEEE80211_PROT_RTSCTS) {
- error = EINVAL;
- break;
- }
- ic->ic_protmode = ireq->i_val;
- /* NB: if not operating in 11g this can wait */
- if (ic->ic_curmode == IEEE80211_MODE_11G)
- error = ENETRESET;
+ case 3:
+ ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
break;
- case IEEE80211_IOC_TXPOWER:
- if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) {
- error = EINVAL;
- break;
- }
- if (!(IEEE80211_TXPOWER_MIN < ireq->i_val &&
- ireq->i_val < IEEE80211_TXPOWER_MAX)) {
- error = EINVAL;
- break;
- }
- ic->ic_txpower = ireq->i_val;
- error = ENETRESET;
+ }
+ error = ENETRESET; /* XXX? */
+ break;
+ case IEEE80211_IOC_WME:
+ if (ireq->i_val) {
+ if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+ return EINVAL;
+ ic->ic_flags |= IEEE80211_F_WME;
+ } else
+ ic->ic_flags &= ~IEEE80211_F_WME;
+ error = ENETRESET; /* XXX maybe not for station? */
+ break;
+ case IEEE80211_IOC_HIDESSID:
+ if (ireq->i_val)
+ ic->ic_flags |= IEEE80211_F_HIDESSID;
+ else
+ ic->ic_flags &= ~IEEE80211_F_HIDESSID;
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_APBRIDGE:
+ if (ireq->i_val == 0)
+ ic->ic_flags |= IEEE80211_F_NOBRIDGE;
+ else
+ ic->ic_flags &= ~IEEE80211_F_NOBRIDGE;
+ break;
+ case IEEE80211_IOC_MCASTCIPHER:
+ if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 &&
+ !ieee80211_crypto_available(ireq->i_val))
+ return EINVAL;
+ rsn->rsn_mcastcipher = ireq->i_val;
+ error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+ break;
+ case IEEE80211_IOC_MCASTKEYLEN:
+ if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
+ return EINVAL;
+ /* XXX no way to verify driver capability */
+ rsn->rsn_mcastkeylen = ireq->i_val;
+ error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+ break;
+ case IEEE80211_IOC_UCASTCIPHERS:
+ /*
+ * Convert user-specified cipher set to the set
+ * we can support (via hardware or software).
+ * NB: this logic intentionally ignores unknown and
+ * unsupported ciphers so folks can specify 0xff or
+ * similar and get all available ciphers.
+ */
+ caps = 0;
+ for (j = 1; j < 32; j++) /* NB: skip WEP */
+ if ((ireq->i_val & (1<<j)) &&
+ ((ic->ic_caps & cipher2cap(j)) ||
+ ieee80211_crypto_available(j)))
+ caps |= 1<<j;
+ if (caps == 0) /* nothing available */
+ return EINVAL;
+ /* XXX verify ciphers ok for unicast use? */
+ /* XXX disallow if running as it'll have no effect */
+ rsn->rsn_ucastcipherset = caps;
+ error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+ break;
+ case IEEE80211_IOC_UCASTCIPHER:
+ if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0)
+ return EINVAL;
+ rsn->rsn_ucastcipher = ireq->i_val;
+ break;
+ case IEEE80211_IOC_UCASTKEYLEN:
+ if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
+ return EINVAL;
+ /* XXX no way to verify driver capability */
+ rsn->rsn_ucastkeylen = ireq->i_val;
+ 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);
+ break;
+ case IEEE80211_IOC_KEYMGTALGS:
+ /* XXX check */
+ rsn->rsn_keymgmtset = ireq->i_val;
+ error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+ break;
+ case IEEE80211_IOC_RSNCAPS:
+ /* XXX check */
+ rsn->rsn_caps = ireq->i_val;
+ error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+ break;
+ case IEEE80211_IOC_BSSID:
+ /* NB: should only be set when in STA mode */
+ if (ic->ic_opmode != IEEE80211_M_STA)
+ return EINVAL;
+ if (ireq->i_len != sizeof(tmpbssid))
+ return EINVAL;
+ error = copyin(ireq->i_data, tmpbssid, ireq->i_len);
+ if (error)
break;
- default:
- error = EINVAL;
+ IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid);
+ if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid))
+ ic->ic_flags &= ~IEEE80211_F_DESBSSID;
+ else
+ ic->ic_flags |= IEEE80211_F_DESBSSID;
+ error = ENETRESET;
+ 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);
+ break;
+ case IEEE80211_IOC_ADDMAC:
+ case IEEE80211_IOC_DELMAC:
+ error = ieee80211_ioctl_macmac(ic, ireq);
+ break;
+ case IEEE80211_IOC_MACCMD:
+ error = ieee80211_ioctl_maccmd(ic, ireq);
+ break;
+ case IEEE80211_IOC_STA_TXPOW:
+ error = ieee80211_ioctl_setstatxpow(ic, ireq);
+ break;
+ case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
+ case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
+ case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */
+ case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
+ case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
+ case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */
+ error = ieee80211_ioctl_setwmeparam(ic, ireq);
+ break;
+ case IEEE80211_IOC_DTIM_PERIOD:
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ ic->ic_opmode != IEEE80211_M_IBSS)
+ return EINVAL;
+ if (IEEE80211_DTIM_MIN <= ireq->i_val &&
+ ireq->i_val <= IEEE80211_DTIM_MAX) {
+ ic->ic_dtim_period = ireq->i_val;
+ error = ENETRESET; /* requires restart */
+ } else
+ error = EINVAL;
+ break;
+ case IEEE80211_IOC_BEACON_INTERVAL:
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ ic->ic_opmode != IEEE80211_M_IBSS)
+ return EINVAL;
+ if (IEEE80211_BINTVAL_MIN <= ireq->i_val &&
+ ireq->i_val <= IEEE80211_BINTVAL_MAX) {
+ ic->ic_lintval = ireq->i_val;
+ error = ENETRESET; /* requires restart */
+ } else
+ error = EINVAL;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ if (error == ENETRESET && !IS_UP_AUTO(ic))
+ error = 0;
+ return error;
+}
+
+int
+ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ int error = 0;
+ struct ifreq *ifr;
+ struct ifaddr *ifa; /* XXX */
+
+ switch (cmd) {
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, (struct ifreq *) data,
+ &ic->ic_media, cmd);
+ break;
+ case SIOCG80211:
+ error = ieee80211_ioctl_get80211(ic, cmd,
+ (struct ieee80211req *) data);
+ break;
+ case SIOCS80211:
+ error = suser(curthread);
+ if (error == 0)
+ error = ieee80211_ioctl_set80211(ic, cmd,
+ (struct ieee80211req *) data);
break;
case SIOCGIFGENERIC:
- error = ieee80211_cfgget(ifp, cmd, data);
+ error = ieee80211_cfgget(ic, cmd, data);
break;
case SIOCSIFGENERIC:
error = suser(curthread);
if (error)
break;
- error = ieee80211_cfgset(ifp, cmd, data);
+ error = ieee80211_cfgset(ic, cmd, data);
break;
case SIOCG80211STATS:
ifr = (struct ifreq *)data;