ieee80211_ioctl.c revision 124457
1/*-
2 * Copyright (c) 2001 Atsushi Onoe
3 * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * Alternatively, this software may be distributed under the terms of the
18 * GNU General Public License ("GPL") version 2 as published by the Free
19 * Software Foundation.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ioctl.c 124457 2004-01-13 06:22:55Z sam $");
35
36/*
37 * IEEE 802.11 ioctl support (FreeBSD-specific)
38 */
39
40#include <sys/endian.h>
41#include <sys/param.h>
42#include <sys/kernel.h>
43#include <sys/socket.h>
44#include <sys/sockio.h>
45#include <sys/systm.h>
46
47#include <net/if.h>
48#include <net/if_arp.h>
49#include <net/if_media.h>
50#include <net/ethernet.h>
51
52#include <net80211/ieee80211_var.h>
53#include <net80211/ieee80211_ioctl.h>
54
55#include <dev/wi/if_wavelan_ieee.h>
56
57/*
58 * XXX
59 * Wireless LAN specific configuration interface, which is compatible
60 * with wicontrol(8).
61 */
62
63int
64ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
65{
66	struct ieee80211com *ic = (void *)ifp;
67	int i, j, error;
68	struct ifreq *ifr = (struct ifreq *)data;
69	struct wi_req wreq;
70	struct wi_ltv_keys *keys;
71	struct wi_apinfo *ap;
72	struct ieee80211_node *ni;
73	struct ieee80211_rateset *rs;
74	struct wi_sigcache wsc;
75	struct wi_scan_p2_hdr *p2;
76	struct wi_scan_res *res;
77
78	error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
79	if (error)
80		return error;
81	wreq.wi_len = 0;
82	switch (wreq.wi_type) {
83	case WI_RID_SERIALNO:
84		/* nothing appropriate */
85		break;
86	case WI_RID_NODENAME:
87		strcpy((char *)&wreq.wi_val[1], hostname);
88		wreq.wi_val[0] = htole16(strlen(hostname));
89		wreq.wi_len = (1 + strlen(hostname) + 1) / 2;
90		break;
91	case WI_RID_CURRENT_SSID:
92		if (ic->ic_state != IEEE80211_S_RUN) {
93			wreq.wi_val[0] = 0;
94			wreq.wi_len = 1;
95			break;
96		}
97		wreq.wi_val[0] = htole16(ic->ic_bss->ni_esslen);
98		memcpy(&wreq.wi_val[1], ic->ic_bss->ni_essid,
99		    ic->ic_bss->ni_esslen);
100		wreq.wi_len = (1 + ic->ic_bss->ni_esslen + 1) / 2;
101		break;
102	case WI_RID_OWN_SSID:
103	case WI_RID_DESIRED_SSID:
104		wreq.wi_val[0] = htole16(ic->ic_des_esslen);
105		memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen);
106		wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2;
107		break;
108	case WI_RID_CURRENT_BSSID:
109		if (ic->ic_state == IEEE80211_S_RUN)
110			IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss->ni_bssid);
111		else
112			memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN);
113		wreq.wi_len = IEEE80211_ADDR_LEN / 2;
114		break;
115	case WI_RID_CHANNEL_LIST:
116		memset(wreq.wi_val, 0, sizeof(wreq.wi_val));
117		/*
118		 * Since channel 0 is not available for DS, channel 1
119		 * is assigned to LSB on WaveLAN.
120		 */
121		if (ic->ic_phytype == IEEE80211_T_DS)
122			i = 1;
123		else
124			i = 0;
125		for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++)
126			if (isset(ic->ic_chan_active, i)) {
127				setbit((u_int8_t *)wreq.wi_val, j);
128				wreq.wi_len = j / 16 + 1;
129			}
130		break;
131	case WI_RID_OWN_CHNL:
132		wreq.wi_val[0] = htole16(
133			ieee80211_chan2ieee(ic, ic->ic_ibss_chan));
134		wreq.wi_len = 1;
135		break;
136	case WI_RID_CURRENT_CHAN:
137		wreq.wi_val[0] = htole16(
138			ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan));
139		wreq.wi_len = 1;
140		break;
141	case WI_RID_COMMS_QUALITY:
142		wreq.wi_val[0] = 0;				/* quality */
143		wreq.wi_val[1] =
144			htole16((*ic->ic_node_getrssi)(ic, ic->ic_bss));
145		wreq.wi_val[2] = 0;				/* noise */
146		wreq.wi_len = 3;
147		break;
148	case WI_RID_PROMISC:
149		wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0);
150		wreq.wi_len = 1;
151		break;
152	case WI_RID_PORTTYPE:
153		wreq.wi_val[0] = htole16(ic->ic_opmode);
154		wreq.wi_len = 1;
155		break;
156	case WI_RID_MAC_NODE:
157		IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr);
158		wreq.wi_len = IEEE80211_ADDR_LEN / 2;
159		break;
160	case WI_RID_TX_RATE:
161		if (ic->ic_fixed_rate == -1)
162			wreq.wi_val[0] = 0;	/* auto */
163		else
164			wreq.wi_val[0] = htole16(
165			    (ic->ic_sup_rates[ic->ic_curmode].rs_rates[ic->ic_fixed_rate] &
166			    IEEE80211_RATE_VAL) / 2);
167		wreq.wi_len = 1;
168		break;
169	case WI_RID_CUR_TX_RATE:
170		wreq.wi_val[0] = htole16(
171		    (ic->ic_bss->ni_rates.rs_rates[ic->ic_bss->ni_txrate] &
172		    IEEE80211_RATE_VAL) / 2);
173		wreq.wi_len = 1;
174		break;
175	case WI_RID_RTS_THRESH:
176		wreq.wi_val[0] = htole16(ic->ic_rtsthreshold);
177		wreq.wi_len = 1;
178		break;
179	case WI_RID_CREATE_IBSS:
180		wreq.wi_val[0] =
181		    htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0);
182		wreq.wi_len = 1;
183		break;
184	case WI_RID_MICROWAVE_OVEN:
185		wreq.wi_val[0] = 0;	/* no ... not supported */
186		wreq.wi_len = 1;
187		break;
188	case WI_RID_ROAMING_MODE:
189		wreq.wi_val[0] = htole16(1);	/* enabled ... not supported */
190		wreq.wi_len = 1;
191		break;
192	case WI_RID_SYSTEM_SCALE:
193		wreq.wi_val[0] = htole16(1);	/* low density ... not supp */
194		wreq.wi_len = 1;
195		break;
196	case WI_RID_PM_ENABLED:
197		wreq.wi_val[0] =
198		    htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0);
199		wreq.wi_len = 1;
200		break;
201	case WI_RID_MAX_SLEEP:
202		wreq.wi_val[0] = htole16(ic->ic_lintval);
203		wreq.wi_len = 1;
204		break;
205	case WI_RID_CUR_BEACON_INT:
206		wreq.wi_val[0] = htole16(ic->ic_bss->ni_intval);
207		wreq.wi_len = 1;
208		break;
209	case WI_RID_WEP_AVAIL:
210		wreq.wi_val[0] =
211		    htole16((ic->ic_caps & IEEE80211_C_WEP) ? 1 : 0);
212		wreq.wi_len = 1;
213		break;
214	case WI_RID_CNFAUTHMODE:
215		wreq.wi_val[0] = htole16(1);	/* TODO: open system only */
216		wreq.wi_len = 1;
217		break;
218	case WI_RID_ENCRYPTION:
219		wreq.wi_val[0] =
220		    htole16((ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0);
221		wreq.wi_len = 1;
222		break;
223	case WI_RID_TX_CRYPT_KEY:
224		wreq.wi_val[0] = htole16(ic->ic_wep_txkey);
225		wreq.wi_len = 1;
226		break;
227	case WI_RID_DEFLT_CRYPT_KEYS:
228		keys = (struct wi_ltv_keys *)&wreq;
229		/* do not show keys to non-root user */
230		error = suser(curthread);
231		if (error) {
232			memset(keys, 0, sizeof(*keys));
233			error = 0;
234			break;
235		}
236		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
237			keys->wi_keys[i].wi_keylen =
238			    htole16(ic->ic_nw_keys[i].wk_len);
239			memcpy(keys->wi_keys[i].wi_keydat,
240			    ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len);
241		}
242		wreq.wi_len = sizeof(*keys) / 2;
243		break;
244	case WI_RID_MAX_DATALEN:
245		wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN);	/* TODO: frag */
246		wreq.wi_len = 1;
247		break;
248	case WI_RID_IFACE_STATS:
249		/* XXX: should be implemented in lower drivers */
250		break;
251	case WI_RID_READ_APS:
252		if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
253			/*
254			 * Don't return results until active scan completes.
255			 */
256			if (ic->ic_state == IEEE80211_S_SCAN &&
257			    (ic->ic_flags & IEEE80211_F_ASCAN)) {
258				error = EINPROGRESS;
259				break;
260			}
261		}
262		i = 0;
263		ap = (void *)((char *)wreq.wi_val + sizeof(i));
264		TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
265			if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1))
266				break;
267			memset(ap, 0, sizeof(*ap));
268			if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
269				IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr);
270				ap->namelen = ic->ic_des_esslen;
271				if (ic->ic_des_esslen)
272					memcpy(ap->name, ic->ic_des_essid,
273					    ic->ic_des_esslen);
274			} else {
275				IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid);
276				ap->namelen = ni->ni_esslen;
277				if (ni->ni_esslen)
278					memcpy(ap->name, ni->ni_essid,
279					    ni->ni_esslen);
280			}
281			ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan);
282			ap->signal = (*ic->ic_node_getrssi)(ic, ni);
283			ap->capinfo = ni->ni_capinfo;
284			ap->interval = ni->ni_intval;
285			rs = &ni->ni_rates;
286			for (j = 0; j < rs->rs_nrates; j++) {
287				if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) {
288					ap->rate = (rs->rs_rates[j] &
289					    IEEE80211_RATE_VAL) * 5; /* XXX */
290				}
291			}
292			i++;
293			ap++;
294		}
295		memcpy(wreq.wi_val, &i, sizeof(i));
296		wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2;
297		break;
298	case WI_RID_PRISM2:
299		wreq.wi_val[0] = 1;	/* XXX lie so SCAN_RES can give rates */
300		wreq.wi_len = sizeof(u_int16_t) / 2;
301		break;
302	case WI_RID_SCAN_RES:			/* compatibility interface */
303		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
304		    ic->ic_state == IEEE80211_S_SCAN) {
305			error = EINPROGRESS;
306			break;
307		}
308		/* NB: we use the Prism2 format so we can return rate info */
309		p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
310		res = (void *)&p2[1];
311		i = 0;
312		TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
313			if ((caddr_t)(res + 1) > (caddr_t)(&wreq + 1))
314				break;
315			res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan);
316			res->wi_noise = 0;
317			res->wi_signal = (*ic->ic_node_getrssi)(ic, ni);
318			IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid);
319			res->wi_interval = ni->ni_intval;
320			res->wi_capinfo = ni->ni_capinfo;
321			res->wi_ssid_len = ni->ni_esslen;
322			memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN);
323			/* NB: assumes wi_srates holds <= ni->ni_rates */
324			memcpy(res->wi_srates, ni->ni_rates.rs_rates,
325				sizeof(res->wi_srates));
326			if (ni->ni_rates.rs_nrates < 10)
327				res->wi_srates[ni->ni_rates.rs_nrates] = 0;
328			res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate];
329			res->wi_rsvd = 0;
330			res++, i++;
331		}
332		p2->wi_rsvd = 0;
333		p2->wi_reason = i;
334		wreq.wi_len = (sizeof(*p2) + sizeof(*res) * i) / 2;
335		break;
336	case WI_RID_READ_CACHE:
337		i = 0;
338		TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
339			if (i == (WI_MAX_DATALEN/sizeof(struct wi_sigcache))-1)
340				break;
341			IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr);
342			memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc));
343			wsc.signal = (*ic->ic_node_getrssi)(ic, ni);
344			wsc.noise = 0;
345			wsc.quality = 0;
346			memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i,
347			    &wsc, sizeof(wsc));
348			i++;
349		}
350		wreq.wi_len = sizeof(wsc) * i / 2;
351		break;
352	case WI_RID_SCAN_APS:
353		error = EINVAL;
354		break;
355	default:
356		error = EINVAL;
357		break;
358	}
359	if (error == 0) {
360		wreq.wi_len++;
361		error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
362	}
363	return error;
364}
365
366static int
367findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
368{
369#define	IEEERATE(_ic,_m,_i) \
370	((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
371	int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
372	for (i = 0; i < nrates; i++)
373		if (IEEERATE(ic, mode, i) == rate)
374			return i;
375	return -1;
376#undef IEEERATE
377}
378
379/*
380 * Prepare to do a user-initiated scan for AP's.  If no
381 * current/default channel is setup or the current channel
382 * is invalid then pick the first available channel from
383 * the active list as the place to start the scan.
384 */
385static int
386ieee80211_setupscan(struct ieee80211com *ic)
387{
388	u_char *chanlist = ic->ic_chan_active;
389	int i;
390
391	if (ic->ic_ibss_chan == NULL ||
392	    isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
393		for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
394			if (isset(chanlist, i)) {
395				ic->ic_ibss_chan = &ic->ic_channels[i];
396				goto found;
397			}
398		return EINVAL;			/* no active channels */
399found:
400		;
401	}
402	if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC ||
403	    isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)))
404		ic->ic_bss->ni_chan = ic->ic_ibss_chan;
405	/*
406	 * XXX don't permit a scan to be started unless we
407	 * know the device is ready.  For the moment this means
408	 * the device is marked up as this is the required to
409	 * initialize the hardware.  It would be better to permit
410	 * scanning prior to being up but that'll require some
411	 * changes to the infrastructure.
412	 */
413	return (ic->ic_if.if_flags & IFF_UP) ? 0 : ENETRESET;
414}
415
416int
417ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
418{
419	struct ieee80211com *ic = (void *)ifp;
420	int i, j, len, error, rate;
421	struct ifreq *ifr = (struct ifreq *)data;
422	struct wi_ltv_keys *keys;
423	struct wi_req wreq;
424	u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)];
425
426	error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
427	if (error)
428		return error;
429	len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0;
430	switch (wreq.wi_type) {
431	case WI_RID_SERIALNO:
432	case WI_RID_NODENAME:
433		return EPERM;
434	case WI_RID_CURRENT_SSID:
435		return EPERM;
436	case WI_RID_OWN_SSID:
437	case WI_RID_DESIRED_SSID:
438		if (le16toh(wreq.wi_val[0]) * 2 > len ||
439		    le16toh(wreq.wi_val[0]) > IEEE80211_NWID_LEN) {
440			error = ENOSPC;
441			break;
442		}
443		memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid));
444		ic->ic_des_esslen = le16toh(wreq.wi_val[0]) * 2;
445		memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen);
446		error = ENETRESET;
447		break;
448	case WI_RID_CURRENT_BSSID:
449		return EPERM;
450	case WI_RID_OWN_CHNL:
451		if (len != 2)
452			return EINVAL;
453		i = le16toh(wreq.wi_val[0]);
454		if (i < 0 ||
455		    i > IEEE80211_CHAN_MAX ||
456		    isclr(ic->ic_chan_active, i))
457			return EINVAL;
458		ic->ic_ibss_chan = &ic->ic_channels[i];
459		if (ic->ic_flags & IEEE80211_F_SIBSS)
460			error = ENETRESET;
461		break;
462	case WI_RID_CURRENT_CHAN:
463		return EPERM;
464	case WI_RID_COMMS_QUALITY:
465		return EPERM;
466	case WI_RID_PROMISC:
467		if (len != 2)
468			return EINVAL;
469		if (ifp->if_flags & IFF_PROMISC) {
470			if (wreq.wi_val[0] == 0) {
471				ifp->if_flags &= ~IFF_PROMISC;
472				error = ENETRESET;
473			}
474		} else {
475			if (wreq.wi_val[0] != 0) {
476				ifp->if_flags |= IFF_PROMISC;
477				error = ENETRESET;
478			}
479		}
480		break;
481	case WI_RID_PORTTYPE:
482		if (len != 2)
483			return EINVAL;
484		switch (le16toh(wreq.wi_val[0])) {
485		case IEEE80211_M_STA:
486			break;
487		case IEEE80211_M_IBSS:
488			if (!(ic->ic_caps & IEEE80211_C_IBSS))
489				return EINVAL;
490			break;
491		case IEEE80211_M_AHDEMO:
492			if (ic->ic_phytype != IEEE80211_T_DS ||
493			    !(ic->ic_caps & IEEE80211_C_AHDEMO))
494				return EINVAL;
495			break;
496		case IEEE80211_M_HOSTAP:
497			if (!(ic->ic_caps & IEEE80211_C_HOSTAP))
498				return EINVAL;
499			break;
500		default:
501			return EINVAL;
502		}
503		if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) {
504			ic->ic_opmode = le16toh(wreq.wi_val[0]);
505			error = ENETRESET;
506		}
507		break;
508#if 0
509	case WI_RID_MAC_NODE:
510		if (len != IEEE80211_ADDR_LEN)
511			return EINVAL;
512		IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val);
513		/* if_init will copy lladdr into ic_myaddr */
514		error = ENETRESET;
515		break;
516#endif
517	case WI_RID_TX_RATE:
518		if (len != 2)
519			return EINVAL;
520		if (wreq.wi_val[0] == 0) {
521			/* auto */
522			ic->ic_fixed_rate = -1;
523			break;
524		}
525		rate = 2 * le16toh(wreq.wi_val[0]);
526		if (ic->ic_curmode == IEEE80211_MODE_AUTO) {
527			/*
528			 * In autoselect mode search for the rate.  We take
529			 * the first instance which may not be right, but we
530			 * are limited by the interface.  Note that we also
531			 * lock the mode to insure the rate is meaningful
532			 * when it is used.
533			 */
534			for (j = IEEE80211_MODE_11A;
535			     j < IEEE80211_MODE_MAX; j++) {
536				if ((ic->ic_modecaps & (1<<j)) == 0)
537					continue;
538				i = findrate(ic, j, rate);
539				if (i != -1) {
540					/* lock mode too */
541					ic->ic_curmode = j;
542					goto setrate;
543				}
544			}
545		} else {
546			i = findrate(ic, ic->ic_curmode, rate);
547			if (i != -1)
548				goto setrate;
549		}
550		return EINVAL;
551	setrate:
552		ic->ic_fixed_rate = i;
553		error = ENETRESET;
554		break;
555	case WI_RID_CUR_TX_RATE:
556		return EPERM;
557	case WI_RID_RTS_THRESH:
558		if (len != 2)
559			return EINVAL;
560		if (le16toh(wreq.wi_val[0]) != IEEE80211_MAX_LEN)
561			return EINVAL;		/* TODO: RTS */
562		break;
563	case WI_RID_CREATE_IBSS:
564		if (len != 2)
565			return EINVAL;
566		if (wreq.wi_val[0] != 0) {
567			if ((ic->ic_caps & IEEE80211_C_IBSS) == 0)
568				return EINVAL;
569			if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) {
570				ic->ic_flags |= IEEE80211_F_IBSSON;
571				if (ic->ic_opmode == IEEE80211_M_IBSS &&
572				    ic->ic_state == IEEE80211_S_SCAN)
573					error = ENETRESET;
574			}
575		} else {
576			if (ic->ic_flags & IEEE80211_F_IBSSON) {
577				ic->ic_flags &= ~IEEE80211_F_IBSSON;
578				if (ic->ic_flags & IEEE80211_F_SIBSS) {
579					ic->ic_flags &= ~IEEE80211_F_SIBSS;
580					error = ENETRESET;
581				}
582			}
583		}
584		break;
585	case WI_RID_MICROWAVE_OVEN:
586		if (len != 2)
587			return EINVAL;
588		if (wreq.wi_val[0] != 0)
589			return EINVAL;		/* not supported */
590		break;
591	case WI_RID_ROAMING_MODE:
592		if (len != 2)
593			return EINVAL;
594		if (le16toh(wreq.wi_val[0]) != 1)
595			return EINVAL;		/* not supported */
596		break;
597	case WI_RID_SYSTEM_SCALE:
598		if (len != 2)
599			return EINVAL;
600		if (le16toh(wreq.wi_val[0]) != 1)
601			return EINVAL;		/* not supported */
602		break;
603	case WI_RID_PM_ENABLED:
604		if (len != 2)
605			return EINVAL;
606		if (wreq.wi_val[0] != 0) {
607			if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
608				return EINVAL;
609			if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
610				ic->ic_flags |= IEEE80211_F_PMGTON;
611				error = ENETRESET;
612			}
613		} else {
614			if (ic->ic_flags & IEEE80211_F_PMGTON) {
615				ic->ic_flags &= ~IEEE80211_F_PMGTON;
616				error = ENETRESET;
617			}
618		}
619		break;
620	case WI_RID_MAX_SLEEP:
621		if (len != 2)
622			return EINVAL;
623		ic->ic_lintval = le16toh(wreq.wi_val[0]);
624		if (ic->ic_flags & IEEE80211_F_PMGTON)
625			error = ENETRESET;
626		break;
627	case WI_RID_CUR_BEACON_INT:
628		return EPERM;
629	case WI_RID_WEP_AVAIL:
630		return EPERM;
631	case WI_RID_CNFAUTHMODE:
632		if (len != 2)
633			return EINVAL;
634		if (le16toh(wreq.wi_val[0]) != 1)
635			return EINVAL;		/* TODO: shared key auth */
636		break;
637	case WI_RID_ENCRYPTION:
638		if (len != 2)
639			return EINVAL;
640		if (wreq.wi_val[0] != 0) {
641			if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
642				return EINVAL;
643			if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) {
644				ic->ic_flags |= IEEE80211_F_WEPON;
645				error = ENETRESET;
646			}
647		} else {
648			if (ic->ic_flags & IEEE80211_F_WEPON) {
649				ic->ic_flags &= ~IEEE80211_F_WEPON;
650				error = ENETRESET;
651			}
652		}
653		break;
654	case WI_RID_TX_CRYPT_KEY:
655		if (len != 2)
656			return EINVAL;
657		i = le16toh(wreq.wi_val[0]);
658		if (i >= IEEE80211_WEP_NKID)
659			return EINVAL;
660		ic->ic_wep_txkey = i;
661		break;
662	case WI_RID_DEFLT_CRYPT_KEYS:
663		if (len != sizeof(struct wi_ltv_keys))
664			return EINVAL;
665		keys = (struct wi_ltv_keys *)&wreq;
666		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
667			len = le16toh(keys->wi_keys[i].wi_keylen);
668			if (len != 0 && len < IEEE80211_WEP_KEYLEN)
669				return EINVAL;
670			if (len > sizeof(ic->ic_nw_keys[i].wk_key))
671				return EINVAL;
672		}
673		memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys));
674		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
675			len = le16toh(keys->wi_keys[i].wi_keylen);
676			ic->ic_nw_keys[i].wk_len = len;
677			memcpy(ic->ic_nw_keys[i].wk_key,
678			    keys->wi_keys[i].wi_keydat, len);
679		}
680		error = ENETRESET;
681		break;
682	case WI_RID_MAX_DATALEN:
683		if (len != 2)
684			return EINVAL;
685		len = le16toh(wreq.wi_val[0]);
686		if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN)
687			return EINVAL;
688		if (len != IEEE80211_MAX_LEN)
689			return EINVAL;		/* TODO: fragment */
690		ic->ic_fragthreshold = len;
691		error = ENETRESET;
692		break;
693	case WI_RID_IFACE_STATS:
694		error = EPERM;
695		break;
696	case WI_RID_SCAN_REQ:			/* XXX wicontrol */
697		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
698			break;
699		error = ieee80211_setupscan(ic);
700		if (error == 0)
701			error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
702		break;
703	case WI_RID_SCAN_APS:
704		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
705			break;
706		len--;			/* XXX: tx rate? */
707		/* FALLTHRU */
708	case WI_RID_CHANNEL_LIST:
709		memset(chanlist, 0, sizeof(chanlist));
710		/*
711		 * Since channel 0 is not available for DS, channel 1
712		 * is assigned to LSB on WaveLAN.
713		 */
714		if (ic->ic_phytype == IEEE80211_T_DS)
715			i = 1;
716		else
717			i = 0;
718		for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
719			if ((j / 8) >= len)
720				break;
721			if (isclr((u_int8_t *)wreq.wi_val, j))
722				continue;
723			if (isclr(ic->ic_chan_active, i)) {
724				if (wreq.wi_type != WI_RID_CHANNEL_LIST)
725					continue;
726				if (isclr(ic->ic_chan_avail, i))
727					return EPERM;
728			}
729			setbit(chanlist, i);
730		}
731		memcpy(ic->ic_chan_active, chanlist,
732		    sizeof(ic->ic_chan_active));
733		error = ieee80211_setupscan(ic);
734		if (wreq.wi_type == WI_RID_CHANNEL_LIST) {
735			/* NB: ignore error from ieee80211_setupscan */
736			error = ENETRESET;
737		} else if (error == 0)
738			error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
739		break;
740	default:
741		error = EINVAL;
742		break;
743	}
744	return error;
745}
746
747int
748ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
749{
750	struct ieee80211com *ic = (void *)ifp;
751	int error = 0;
752	u_int kid, len;
753	struct ieee80211req *ireq;
754	struct ifreq *ifr;
755	u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE];
756	char tmpssid[IEEE80211_NWID_LEN];
757	struct ieee80211_channel *chan;
758
759	switch (cmd) {
760	case SIOCSIFMEDIA:
761	case SIOCGIFMEDIA:
762		error = ifmedia_ioctl(ifp, (struct ifreq *) data,
763				&ic->ic_media, cmd);
764		break;
765	case SIOCG80211:
766		ireq = (struct ieee80211req *) data;
767		switch (ireq->i_type) {
768		case IEEE80211_IOC_SSID:
769			switch (ic->ic_state) {
770			case IEEE80211_S_INIT:
771			case IEEE80211_S_SCAN:
772				ireq->i_len = ic->ic_des_esslen;
773				memcpy(tmpssid, ic->ic_des_essid, ireq->i_len);
774				break;
775			default:
776				ireq->i_len = ic->ic_bss->ni_esslen;
777				memcpy(tmpssid, ic->ic_bss->ni_essid,
778					ireq->i_len);
779				break;
780			}
781			error = copyout(tmpssid, ireq->i_data, ireq->i_len);
782			break;
783		case IEEE80211_IOC_NUMSSIDS:
784			ireq->i_val = 1;
785			break;
786		case IEEE80211_IOC_WEP:
787			if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
788				ireq->i_val = IEEE80211_WEP_NOSUP;
789			} else {
790				if (ic->ic_flags & IEEE80211_F_WEPON) {
791					ireq->i_val =
792					    IEEE80211_WEP_MIXED;
793				} else {
794					ireq->i_val =
795					    IEEE80211_WEP_OFF;
796				}
797			}
798			break;
799		case IEEE80211_IOC_WEPKEY:
800			if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
801				error = EINVAL;
802				break;
803			}
804			kid = (u_int) ireq->i_val;
805			if (kid >= IEEE80211_WEP_NKID) {
806				error = EINVAL;
807				break;
808			}
809			len = (u_int) ic->ic_nw_keys[kid].wk_len;
810			/* NB: only root can read WEP keys */
811			if (suser(curthread) == 0) {
812				bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
813			} else {
814				bzero(tmpkey, len);
815			}
816			ireq->i_len = len;
817			error = copyout(tmpkey, ireq->i_data, len);
818			break;
819		case IEEE80211_IOC_NUMWEPKEYS:
820			if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
821				error = EINVAL;
822			else
823				ireq->i_val = IEEE80211_WEP_NKID;
824			break;
825		case IEEE80211_IOC_WEPTXKEY:
826			if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
827				error = EINVAL;
828			else
829				ireq->i_val = ic->ic_wep_txkey;
830			break;
831		case IEEE80211_IOC_AUTHMODE:
832			ireq->i_val = IEEE80211_AUTH_OPEN;
833			break;
834		case IEEE80211_IOC_CHANNEL:
835			switch (ic->ic_state) {
836			case IEEE80211_S_INIT:
837			case IEEE80211_S_SCAN:
838				if (ic->ic_opmode == IEEE80211_M_STA)
839					chan = ic->ic_des_chan;
840				else
841					chan = ic->ic_ibss_chan;
842				break;
843			default:
844				chan = ic->ic_bss->ni_chan;
845				break;
846			}
847			ireq->i_val = ieee80211_chan2ieee(ic, chan);
848			break;
849		case IEEE80211_IOC_POWERSAVE:
850			if (ic->ic_flags & IEEE80211_F_PMGTON)
851				ireq->i_val = IEEE80211_POWERSAVE_ON;
852			else
853				ireq->i_val = IEEE80211_POWERSAVE_OFF;
854			break;
855		case IEEE80211_IOC_POWERSAVESLEEP:
856			ireq->i_val = ic->ic_lintval;
857			break;
858		case IEEE80211_IOC_RTSTHRESHOLD:
859			ireq->i_val = ic->ic_rtsthreshold;
860			break;
861		default:
862			error = EINVAL;
863		}
864		break;
865	case SIOCS80211:
866		error = suser(curthread);
867		if (error)
868			break;
869		ireq = (struct ieee80211req *) data;
870		switch (ireq->i_type) {
871		case IEEE80211_IOC_SSID:
872			if (ireq->i_val != 0 ||
873			    ireq->i_len > IEEE80211_NWID_LEN) {
874				error = EINVAL;
875				break;
876			}
877			error = copyin(ireq->i_data, tmpssid, ireq->i_len);
878			if (error)
879				break;
880			memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN);
881			ic->ic_des_esslen = ireq->i_len;
882			memcpy(ic->ic_des_essid, tmpssid, ireq->i_len);
883			error = ENETRESET;
884			break;
885		case IEEE80211_IOC_WEP:
886			/*
887			 * These cards only support one mode so
888			 * we just turn wep on if what ever is
889			 * passed in is not OFF.
890			 */
891			if (ireq->i_val == IEEE80211_WEP_OFF) {
892				ic->ic_flags &= ~IEEE80211_F_WEPON;
893			} else {
894				ic->ic_flags |= IEEE80211_F_WEPON;
895			}
896			error = ENETRESET;
897			break;
898		case IEEE80211_IOC_WEPKEY:
899			if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
900				error = EINVAL;
901				break;
902			}
903			kid = (u_int) ireq->i_val;
904			if (kid >= IEEE80211_WEP_NKID) {
905				error = EINVAL;
906				break;
907			}
908			if (ireq->i_len > sizeof(tmpkey)) {
909				error = EINVAL;
910				break;
911			}
912			memset(tmpkey, 0, sizeof(tmpkey));
913			error = copyin(ireq->i_data, tmpkey, ireq->i_len);
914			if (error)
915				break;
916			memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey,
917				sizeof(tmpkey));
918			ic->ic_nw_keys[kid].wk_len = ireq->i_len;
919			error = ENETRESET;
920			break;
921		case IEEE80211_IOC_WEPTXKEY:
922			kid = (u_int) ireq->i_val;
923			if (kid >= IEEE80211_WEP_NKID) {
924				error = EINVAL;
925				break;
926			}
927			ic->ic_wep_txkey = kid;
928			error = ENETRESET;
929			break;
930#if 0
931		case IEEE80211_IOC_AUTHMODE:
932			sc->wi_authmode = ireq->i_val;
933			break;
934#endif
935		case IEEE80211_IOC_CHANNEL:
936			/* XXX 0xffff overflows 16-bit signed */
937			if (ireq->i_val == 0 ||
938			    ireq->i_val == (int16_t) IEEE80211_CHAN_ANY)
939				ic->ic_des_chan = IEEE80211_CHAN_ANYC;
940			else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX ||
941			    isclr(ic->ic_chan_active, ireq->i_val)) {
942				error = EINVAL;
943				break;
944			} else
945				ic->ic_ibss_chan = ic->ic_des_chan =
946					&ic->ic_channels[ireq->i_val];
947			switch (ic->ic_state) {
948			case IEEE80211_S_INIT:
949			case IEEE80211_S_SCAN:
950				error = ENETRESET;
951				break;
952			default:
953				if (ic->ic_opmode == IEEE80211_M_STA) {
954					if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
955					    ic->ic_bss->ni_chan != ic->ic_des_chan)
956						error = ENETRESET;
957				} else {
958					if (ic->ic_bss->ni_chan != ic->ic_ibss_chan)
959						error = ENETRESET;
960				}
961				break;
962			}
963			break;
964		case IEEE80211_IOC_POWERSAVE:
965			switch (ireq->i_val) {
966			case IEEE80211_POWERSAVE_OFF:
967				if (ic->ic_flags & IEEE80211_F_PMGTON) {
968					ic->ic_flags &= ~IEEE80211_F_PMGTON;
969					error = ENETRESET;
970				}
971				break;
972			case IEEE80211_POWERSAVE_ON:
973				if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
974					error = EINVAL;
975				else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
976					ic->ic_flags |= IEEE80211_F_PMGTON;
977					error = ENETRESET;
978				}
979				break;
980			default:
981				error = EINVAL;
982				break;
983			}
984			break;
985		case IEEE80211_IOC_POWERSAVESLEEP:
986			if (ireq->i_val < 0) {
987				error = EINVAL;
988				break;
989			}
990			ic->ic_lintval = ireq->i_val;
991			error = ENETRESET;
992			break;
993		case IEEE80211_IOC_RTSTHRESHOLD:
994			if (!(IEEE80211_RTS_MIN < ireq->i_val &&
995			      ireq->i_val < IEEE80211_RTS_MAX)) {
996				error = EINVAL;
997				break;
998			}
999			ic->ic_rtsthreshold = ireq->i_val;
1000			error = ENETRESET;
1001			break;
1002		default:
1003			error = EINVAL;
1004			break;
1005		}
1006		break;
1007	case SIOCGIFGENERIC:
1008		error = ieee80211_cfgget(ifp, cmd, data);
1009		break;
1010	case SIOCSIFGENERIC:
1011		error = suser(curthread);
1012		if (error)
1013			break;
1014		error = ieee80211_cfgset(ifp, cmd, data);
1015		break;
1016	case SIOCG80211STATS:
1017		ifr = (struct ifreq *)data;
1018		copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats));
1019		break;
1020	case SIOCSIFMTU:
1021		ifr = (struct ifreq *)data;
1022		if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu &&
1023		    ifr->ifr_mtu <= IEEE80211_MTU_MAX))
1024			error = EINVAL;
1025		else
1026			ifp->if_mtu = ifr->ifr_mtu;
1027		break;
1028	default:
1029		error = ether_ioctl(ifp, cmd, data);
1030		break;
1031	}
1032	return error;
1033}
1034