ifieee80211.c revision 175953
1/*
2 * Copyright 2001 The Aerospace Corporation.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. The name of The Aerospace Corporation may not be used to endorse or
13 *    promote products derived from this software.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AEROSPACE CORPORATION BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sbin/ifconfig/ifieee80211.c 175953 2008-02-03 18:29:58Z sam $
28 */
29
30/*-
31 * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
32 * All rights reserved.
33 *
34 * This code is derived from software contributed to The NetBSD Foundation
35 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
36 * NASA Ames Research Center.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 *    notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 *    notice, this list of conditions and the following disclaimer in the
45 *    documentation and/or other materials provided with the distribution.
46 * 3. All advertising materials mentioning features or use of this software
47 *    must display the following acknowledgement:
48 *	This product includes software developed by the NetBSD
49 *	Foundation, Inc. and its contributors.
50 * 4. Neither the name of The NetBSD Foundation nor the names of its
51 *    contributors may be used to endorse or promote products derived
52 *    from this software without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
55 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
56 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
58 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
59 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
60 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
61 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
62 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
63 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
64 * POSSIBILITY OF SUCH DAMAGE.
65 */
66
67#include <sys/param.h>
68#include <sys/ioctl.h>
69#include <sys/socket.h>
70#include <sys/sysctl.h>
71#include <sys/time.h>
72
73#include <net/ethernet.h>
74#include <net/if.h>
75#include <net/if_dl.h>
76#include <net/if_types.h>
77#include <net/if_media.h>
78#include <net/route.h>
79
80#include <net80211/ieee80211.h>
81#include <net80211/ieee80211_crypto.h>
82#include <net80211/ieee80211_ioctl.h>
83
84#include <ctype.h>
85#include <err.h>
86#include <errno.h>
87#include <fcntl.h>
88#include <inttypes.h>
89#include <stdio.h>
90#include <stdlib.h>
91#include <string.h>
92#include <unistd.h>
93#include <stdarg.h>
94#include <stddef.h>		/* NB: for offsetof */
95
96#include "ifconfig.h"
97
98#define	MAXCOL	78
99static	int col;
100static	char spacer;
101
102static void LINE_INIT(char c);
103static void LINE_BREAK(void);
104static void LINE_CHECK(const char *fmt, ...);
105
106/* XXX need max array size */
107static const int htrates[16] = {
108	13,		/* IFM_IEEE80211_MCS0 */
109	26,		/* IFM_IEEE80211_MCS1 */
110	39,		/* IFM_IEEE80211_MCS2 */
111	52,		/* IFM_IEEE80211_MCS3 */
112	78,		/* IFM_IEEE80211_MCS4 */
113	104,		/* IFM_IEEE80211_MCS5 */
114	117,		/* IFM_IEEE80211_MCS6 */
115	130,		/* IFM_IEEE80211_MCS7 */
116	26,		/* IFM_IEEE80211_MCS8 */
117	52,		/* IFM_IEEE80211_MCS9 */
118	78,		/* IFM_IEEE80211_MCS10 */
119	104,		/* IFM_IEEE80211_MCS11 */
120	156,		/* IFM_IEEE80211_MCS12 */
121	208,		/* IFM_IEEE80211_MCS13 */
122	234,		/* IFM_IEEE80211_MCS14 */
123	260,		/* IFM_IEEE80211_MCS15 */
124};
125
126static int get80211(int s, int type, void *data, int len);
127static int get80211len(int s, int type, void *data, int len, int *plen);
128static int get80211val(int s, int type, int *val);
129static void set80211(int s, int type, int val, int len, void *data);
130static const char *get_string(const char *val, const char *sep,
131    u_int8_t *buf, int *lenp);
132static void print_string(const u_int8_t *buf, int len);
133
134static struct ieee80211req_chaninfo chaninfo;
135static struct ifmediareq *ifmr;
136static struct ieee80211_channel curchan;
137static int gotcurchan = 0;
138static int htconf = 0;
139static	int gothtconf = 0;
140
141static void
142gethtconf(int s)
143{
144	if (gothtconf)
145		return;
146	if (get80211val(s, IEEE80211_IOC_HTCONF, &htconf) < 0)
147		warn("unable to get HT configuration information");
148	gothtconf = 1;
149}
150
151/*
152 * Collect channel info from the kernel.  We use this (mostly)
153 * to handle mapping between frequency and IEEE channel number.
154 */
155static void
156getchaninfo(int s)
157{
158	if (chaninfo.ic_nchans != 0)
159		return;
160	if (get80211(s, IEEE80211_IOC_CHANINFO, &chaninfo, sizeof(chaninfo)) < 0)
161		errx(1, "unable to get channel information");
162
163	ifmr = ifmedia_getstate(s);
164	gethtconf(s);
165}
166
167/*
168 * Given the channel at index i with attributes from,
169 * check if there is a channel with attributes to in
170 * the channel table.  With suitable attributes this
171 * allows the caller to look for promotion; e.g. from
172 * 11b > 11g.
173 */
174static int
175canpromote(int i, int from, int to)
176{
177	const struct ieee80211_channel *fc = &chaninfo.ic_chans[i];
178	int j;
179
180	if ((fc->ic_flags & from) != from)
181		return i;
182	/* NB: quick check exploiting ordering of chans w/ same frequency */
183	if (i+1 < chaninfo.ic_nchans &&
184	    chaninfo.ic_chans[i+1].ic_freq == fc->ic_freq &&
185	    (chaninfo.ic_chans[i+1].ic_flags & to) == to)
186		return i+1;
187	/* brute force search in case channel list is not ordered */
188	for (j = 0; j < chaninfo.ic_nchans; j++) {
189		const struct ieee80211_channel *tc = &chaninfo.ic_chans[j];
190		if (j != i &&
191		    tc->ic_freq == fc->ic_freq && (tc->ic_flags & to) == to)
192		return j;
193	}
194	return i;
195}
196
197/*
198 * Handle channel promotion.  When a channel is specified with
199 * only a frequency we want to promote it to the ``best'' channel
200 * available.  The channel list has separate entries for 11b, 11g,
201 * 11a, and 11n[ga] channels so specifying a frequency w/o any
202 * attributes requires we upgrade, e.g. from 11b -> 11g.  This
203 * gets complicated when the channel is specified on the same
204 * command line with a media request that constrains the available
205 * channe list (e.g. mode 11a); we want to honor that to avoid
206 * confusing behaviour.
207 */
208static int
209promote(int i)
210{
211	/*
212	 * Query the current mode of the interface in case it's
213	 * constrained (e.g. to 11a).  We must do this carefully
214	 * as there may be a pending ifmedia request in which case
215	 * asking the kernel will give us the wrong answer.  This
216	 * is an unfortunate side-effect of the way ifconfig is
217	 * structure for modularity (yech).
218	 *
219	 * NB: ifmr is actually setup in getchaninfo (above); we
220	 *     assume it's called coincident with to this call so
221	 *     we have a ``current setting''; otherwise we must pass
222	 *     the socket descriptor down to here so we can make
223	 *     the ifmedia_getstate call ourselves.
224	 */
225	int chanmode = ifmr != NULL ? IFM_MODE(ifmr->ifm_current) : IFM_AUTO;
226
227	/* when ambiguous promote to ``best'' */
228	/* NB: we abitrarily pick HT40+ over HT40- */
229	if (chanmode != IFM_IEEE80211_11B)
230		i = canpromote(i, IEEE80211_CHAN_B, IEEE80211_CHAN_G);
231	if (chanmode != IFM_IEEE80211_11G && (htconf & 1)) {
232		i = canpromote(i, IEEE80211_CHAN_G,
233			IEEE80211_CHAN_G | IEEE80211_CHAN_HT20);
234		if (htconf & 2) {
235			i = canpromote(i, IEEE80211_CHAN_G,
236				IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D);
237			i = canpromote(i, IEEE80211_CHAN_G,
238				IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U);
239		}
240	}
241	if (chanmode != IFM_IEEE80211_11A && (htconf & 1)) {
242		i = canpromote(i, IEEE80211_CHAN_A,
243			IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
244		if (htconf & 2) {
245			i = canpromote(i, IEEE80211_CHAN_A,
246				IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
247			i = canpromote(i, IEEE80211_CHAN_A,
248				IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
249		}
250	}
251	return i;
252}
253
254static void
255mapfreq(struct ieee80211_channel *chan, int freq, int flags)
256{
257	int i;
258
259	for (i = 0; i < chaninfo.ic_nchans; i++) {
260		const struct ieee80211_channel *c = &chaninfo.ic_chans[i];
261
262		if (c->ic_freq == freq && (c->ic_flags & flags) == flags) {
263			if (flags == 0) {
264				/* when ambiguous promote to ``best'' */
265				c = &chaninfo.ic_chans[promote(i)];
266			}
267			*chan = *c;
268			return;
269		}
270	}
271	errx(1, "unknown/undefined frequency %u/0x%x", freq, flags);
272}
273
274static void
275mapchan(struct ieee80211_channel *chan, int ieee, int flags)
276{
277	int i;
278
279	for (i = 0; i < chaninfo.ic_nchans; i++) {
280		const struct ieee80211_channel *c = &chaninfo.ic_chans[i];
281
282		if (c->ic_ieee == ieee && (c->ic_flags & flags) == flags) {
283			if (flags == 0) {
284				/* when ambiguous promote to ``best'' */
285				c = &chaninfo.ic_chans[promote(i)];
286			}
287			*chan = *c;
288			return;
289		}
290	}
291	errx(1, "unknown/undefined channel number %d flags 0x%x", ieee, flags);
292}
293
294static const struct ieee80211_channel *
295getcurchan(int s)
296{
297	if (gotcurchan)
298		return &curchan;
299	if (get80211(s, IEEE80211_IOC_CURCHAN, &curchan, sizeof(curchan)) < 0) {
300		int val;
301		/* fall back to legacy ioctl */
302		if (get80211val(s, IEEE80211_IOC_CHANNEL, &val) < 0)
303			errx(-1, "cannot figure out current channel");
304		getchaninfo(s);
305		mapchan(&curchan, val, 0);
306	}
307	gotcurchan = 1;
308	return &curchan;
309}
310
311static int
312ieee80211_mhz2ieee(int freq, int flags)
313{
314	struct ieee80211_channel chan;
315	mapfreq(&chan, freq, flags);
316	return chan.ic_ieee;
317}
318
319static int
320isanyarg(const char *arg)
321{
322	return (strncmp(arg, "-", 1) == 0 ||
323	    strncasecmp(arg, "any", 3) == 0 || strncasecmp(arg, "off", 3) == 0);
324}
325
326static void
327set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
328{
329	int		ssid;
330	int		len;
331	u_int8_t	data[IEEE80211_NWID_LEN];
332
333	ssid = 0;
334	len = strlen(val);
335	if (len > 2 && isdigit(val[0]) && val[1] == ':') {
336		ssid = atoi(val)-1;
337		val += 2;
338	}
339
340	bzero(data, sizeof(data));
341	len = sizeof(data);
342	if (get_string(val, NULL, data, &len) == NULL)
343		exit(1);
344
345	set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
346}
347
348static void
349set80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
350{
351	int			len;
352	u_int8_t		data[33];
353
354	bzero(data, sizeof(data));
355	len = sizeof(data);
356	get_string(val, NULL, data, &len);
357
358	set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
359}
360
361/*
362 * Parse a channel specification for attributes/flags.
363 * The syntax is:
364 *	freq/xx		channel width (5,10,20,40,40+,40-)
365 *	freq:mode	channel mode (a,b,g,h,n,t,s,d)
366 *
367 * These can be combined in either order; e.g. 2437:ng/40.
368 * Modes are case insensitive.
369 *
370 * The result is not validated here; it's assumed to be
371 * checked against the channel table fetched from the kernel.
372 */
373static int
374getchannelflags(const char *val, int freq)
375{
376#define	_CHAN_HT	0x80000000
377	const char *cp;
378	int flags;
379
380	flags = 0;
381
382	cp = strchr(val, ':');
383	if (cp != NULL) {
384		for (cp++; isalpha((int) *cp); cp++) {
385			/* accept mixed case */
386			int c = *cp;
387			if (isupper(c))
388				c = tolower(c);
389			switch (c) {
390			case 'a':		/* 802.11a */
391				flags |= IEEE80211_CHAN_A;
392				break;
393			case 'b':		/* 802.11b */
394				flags |= IEEE80211_CHAN_B;
395				break;
396			case 'g':		/* 802.11g */
397				flags |= IEEE80211_CHAN_G;
398				break;
399			case 'h':		/* ht = 802.11n */
400			case 'n':		/* 802.11n */
401				flags |= _CHAN_HT;	/* NB: private */
402				break;
403			case 'd':		/* dt = Atheros Dynamic Turbo */
404				flags |= IEEE80211_CHAN_TURBO;
405				break;
406			case 't':		/* ht, dt, st, t */
407				/* dt and unadorned t specify Dynamic Turbo */
408				if ((flags & (IEEE80211_CHAN_STURBO|_CHAN_HT)) == 0)
409					flags |= IEEE80211_CHAN_TURBO;
410				break;
411			case 's':		/* st = Atheros Static Turbo */
412				flags |= IEEE80211_CHAN_STURBO;
413				break;
414			default:
415				errx(-1, "%s: Invalid channel attribute %c\n",
416				    val, *cp);
417			}
418		}
419	}
420	cp = strchr(val, '/');
421	if (cp != NULL) {
422		char *ep;
423		u_long cw = strtoul(cp+1, &ep, 10);
424
425		switch (cw) {
426		case 5:
427			flags |= IEEE80211_CHAN_QUARTER;
428			break;
429		case 10:
430			flags |= IEEE80211_CHAN_HALF;
431			break;
432		case 20:
433			/* NB: this may be removed below */
434			flags |= IEEE80211_CHAN_HT20;
435			break;
436		case 40:
437			if (ep != NULL && *ep == '+')
438				flags |= IEEE80211_CHAN_HT40U;
439			else if (ep != NULL && *ep == '-')
440				flags |= IEEE80211_CHAN_HT40D;
441			break;
442		default:
443			errx(-1, "%s: Invalid channel width\n", val);
444		}
445	}
446	/*
447	 * Cleanup specifications.
448	 */
449	if ((flags & _CHAN_HT) == 0) {
450		/*
451		 * If user specified freq/20 or freq/40 quietly remove
452		 * HT cw attributes depending on channel use.  To give
453		 * an explicit 20/40 width for an HT channel you must
454		 * indicate it is an HT channel since all HT channels
455		 * are also usable for legacy operation; e.g. freq:n/40.
456		 */
457		flags &= ~IEEE80211_CHAN_HT;
458	} else {
459		/*
460		 * Remove private indicator that this is an HT channel
461		 * and if no explicit channel width has been given
462		 * provide the default settings.
463		 */
464		flags &= ~_CHAN_HT;
465		if ((flags & IEEE80211_CHAN_HT) == 0) {
466			struct ieee80211_channel chan;
467			/*
468			 * Consult the channel list to see if we can use
469			 * HT40+ or HT40- (if both the map routines choose).
470			 */
471			if (freq > 255)
472				mapfreq(&chan, freq, 0);
473			else
474				mapchan(&chan, freq, 0);
475			flags |= (chan.ic_flags & IEEE80211_CHAN_HT);
476		}
477	}
478	return flags;
479#undef _CHAN_HT
480}
481
482static void
483set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
484{
485	struct ieee80211_channel chan;
486
487	memset(&chan, 0, sizeof(chan));
488	if (!isanyarg(val)) {
489		int v, flags;
490
491		getchaninfo(s);
492		v = atoi(val);
493		flags = getchannelflags(val, v);
494		if (v > 255) {		/* treat as frequency */
495			mapfreq(&chan, v, flags);
496		} else {
497			mapchan(&chan, v, flags);
498		}
499	} else {
500		chan.ic_freq = IEEE80211_CHAN_ANY;
501	}
502	set80211(s, IEEE80211_IOC_CURCHAN, 0, sizeof(chan), &chan);
503}
504
505static void
506set80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
507{
508	int	mode;
509
510	if (strcasecmp(val, "none") == 0) {
511		mode = IEEE80211_AUTH_NONE;
512	} else if (strcasecmp(val, "open") == 0) {
513		mode = IEEE80211_AUTH_OPEN;
514	} else if (strcasecmp(val, "shared") == 0) {
515		mode = IEEE80211_AUTH_SHARED;
516	} else if (strcasecmp(val, "8021x") == 0) {
517		mode = IEEE80211_AUTH_8021X;
518	} else if (strcasecmp(val, "wpa") == 0) {
519		mode = IEEE80211_AUTH_WPA;
520	} else {
521		errx(1, "unknown authmode");
522	}
523
524	set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
525}
526
527static void
528set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
529{
530	int	mode;
531
532	if (strcasecmp(val, "off") == 0) {
533		mode = IEEE80211_POWERSAVE_OFF;
534	} else if (strcasecmp(val, "on") == 0) {
535		mode = IEEE80211_POWERSAVE_ON;
536	} else if (strcasecmp(val, "cam") == 0) {
537		mode = IEEE80211_POWERSAVE_CAM;
538	} else if (strcasecmp(val, "psp") == 0) {
539		mode = IEEE80211_POWERSAVE_PSP;
540	} else if (strcasecmp(val, "psp-cam") == 0) {
541		mode = IEEE80211_POWERSAVE_PSP_CAM;
542	} else {
543		errx(1, "unknown powersavemode");
544	}
545
546	set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
547}
548
549static void
550set80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
551{
552	if (d == 0)
553		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
554		    0, NULL);
555	else
556		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
557		    0, NULL);
558}
559
560static void
561set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
562{
563	set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
564}
565
566static void
567set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
568{
569	int	mode;
570
571	if (strcasecmp(val, "off") == 0) {
572		mode = IEEE80211_WEP_OFF;
573	} else if (strcasecmp(val, "on") == 0) {
574		mode = IEEE80211_WEP_ON;
575	} else if (strcasecmp(val, "mixed") == 0) {
576		mode = IEEE80211_WEP_MIXED;
577	} else {
578		errx(1, "unknown wep mode");
579	}
580
581	set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
582}
583
584static void
585set80211wep(const char *val, int d, int s, const struct afswtch *rafp)
586{
587	set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
588}
589
590static int
591isundefarg(const char *arg)
592{
593	return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0);
594}
595
596static void
597set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
598{
599	if (isundefarg(val))
600		set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
601	else
602		set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
603}
604
605static void
606set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
607{
608	int		key = 0;
609	int		len;
610	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
611
612	if (isdigit(val[0]) && val[1] == ':') {
613		key = atoi(val)-1;
614		val += 2;
615	}
616
617	bzero(data, sizeof(data));
618	len = sizeof(data);
619	get_string(val, NULL, data, &len);
620
621	set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
622}
623
624/*
625 * This function is purely a NetBSD compatability interface.  The NetBSD
626 * interface is too inflexible, but it's there so we'll support it since
627 * it's not all that hard.
628 */
629static void
630set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
631{
632	int		txkey;
633	int		i, len;
634	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
635
636	set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
637
638	if (isdigit(val[0]) && val[1] == ':') {
639		txkey = val[0]-'0'-1;
640		val += 2;
641
642		for (i = 0; i < 4; i++) {
643			bzero(data, sizeof(data));
644			len = sizeof(data);
645			val = get_string(val, ",", data, &len);
646			if (val == NULL)
647				exit(1);
648
649			set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
650		}
651	} else {
652		bzero(data, sizeof(data));
653		len = sizeof(data);
654		get_string(val, NULL, data, &len);
655		txkey = 0;
656
657		set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
658
659		bzero(data, sizeof(data));
660		for (i = 1; i < 4; i++)
661			set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
662	}
663
664	set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
665}
666
667static void
668set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
669{
670	set80211(s, IEEE80211_IOC_RTSTHRESHOLD,
671		isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL);
672}
673
674static void
675set80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
676{
677	int	mode;
678
679	if (strcasecmp(val, "off") == 0) {
680		mode = IEEE80211_PROTMODE_OFF;
681	} else if (strcasecmp(val, "cts") == 0) {
682		mode = IEEE80211_PROTMODE_CTS;
683	} else if (strncasecmp(val, "rtscts", 3) == 0) {
684		mode = IEEE80211_PROTMODE_RTSCTS;
685	} else {
686		errx(1, "unknown protection mode");
687	}
688
689	set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
690}
691
692static void
693set80211htprotmode(const char *val, int d, int s, const struct afswtch *rafp)
694{
695	int	mode;
696
697	if (strcasecmp(val, "off") == 0) {
698		mode = IEEE80211_PROTMODE_OFF;
699	} else if (strncasecmp(val, "rts", 3) == 0) {
700		mode = IEEE80211_PROTMODE_RTSCTS;
701	} else {
702		errx(1, "unknown protection mode");
703	}
704
705	set80211(s, IEEE80211_IOC_HTPROTMODE, mode, 0, NULL);
706}
707
708static void
709set80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
710{
711	double v = atof(val);
712	int txpow;
713
714	txpow = (int) (2*v);
715	if (txpow != 2*v)
716		errx(-1, "invalid tx power (must be .5 dBm units)");
717	set80211(s, IEEE80211_IOC_TXPOWER, txpow, 0, NULL);
718}
719
720#define	IEEE80211_ROAMING_DEVICE	0
721#define	IEEE80211_ROAMING_AUTO		1
722#define	IEEE80211_ROAMING_MANUAL	2
723
724static void
725set80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
726{
727	int mode;
728
729	if (strcasecmp(val, "device") == 0) {
730		mode = IEEE80211_ROAMING_DEVICE;
731	} else if (strcasecmp(val, "auto") == 0) {
732		mode = IEEE80211_ROAMING_AUTO;
733	} else if (strcasecmp(val, "manual") == 0) {
734		mode = IEEE80211_ROAMING_MANUAL;
735	} else {
736		errx(1, "unknown roaming mode");
737	}
738	set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
739}
740
741static void
742set80211wme(const char *val, int d, int s, const struct afswtch *rafp)
743{
744	set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
745}
746
747static void
748set80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
749{
750	set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
751}
752
753static void
754set80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
755{
756	set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
757}
758
759static void
760set80211fastframes(const char *val, int d, int s, const struct afswtch *rafp)
761{
762	set80211(s, IEEE80211_IOC_FF, d, 0, NULL);
763}
764
765static void
766set80211dturbo(const char *val, int d, int s, const struct afswtch *rafp)
767{
768	set80211(s, IEEE80211_IOC_TURBOP, d, 0, NULL);
769}
770
771static void
772set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
773{
774	struct ieee80211req_chanlist chanlist;
775#define	MAXCHAN	(sizeof(chanlist.ic_channels)*NBBY)
776	char *temp, *cp, *tp;
777
778	temp = malloc(strlen(val) + 1);
779	if (temp == NULL)
780		errx(1, "malloc failed");
781	strcpy(temp, val);
782	memset(&chanlist, 0, sizeof(chanlist));
783	cp = temp;
784	for (;;) {
785		int first, last, f, c;
786
787		tp = strchr(cp, ',');
788		if (tp != NULL)
789			*tp++ = '\0';
790		switch (sscanf(cp, "%u-%u", &first, &last)) {
791		case 1:
792			if (first > MAXCHAN)
793				errx(-1, "channel %u out of range, max %zu",
794					first, MAXCHAN);
795			setbit(chanlist.ic_channels, first);
796			break;
797		case 2:
798			if (first > MAXCHAN)
799				errx(-1, "channel %u out of range, max %zu",
800					first, MAXCHAN);
801			if (last > MAXCHAN)
802				errx(-1, "channel %u out of range, max %zu",
803					last, MAXCHAN);
804			if (first > last)
805				errx(-1, "void channel range, %u > %u",
806					first, last);
807			for (f = first; f <= last; f++)
808				setbit(chanlist.ic_channels, f);
809			break;
810		}
811		if (tp == NULL)
812			break;
813		c = *tp;
814		while (isspace(c))
815			tp++;
816		if (!isdigit(c))
817			break;
818		cp = tp;
819	}
820	set80211(s, IEEE80211_IOC_CHANLIST, 0, sizeof(chanlist), &chanlist);
821#undef MAXCHAN
822}
823
824static void
825set80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
826{
827
828	if (!isanyarg(val)) {
829		char *temp;
830		struct sockaddr_dl sdl;
831
832		temp = malloc(strlen(val) + 2); /* ':' and '\0' */
833		if (temp == NULL)
834			errx(1, "malloc failed");
835		temp[0] = ':';
836		strcpy(temp + 1, val);
837		sdl.sdl_len = sizeof(sdl);
838		link_addr(temp, &sdl);
839		free(temp);
840		if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
841			errx(1, "malformed link-level address");
842		set80211(s, IEEE80211_IOC_BSSID, 0,
843			IEEE80211_ADDR_LEN, LLADDR(&sdl));
844	} else {
845		uint8_t zerobssid[IEEE80211_ADDR_LEN];
846		memset(zerobssid, 0, sizeof(zerobssid));
847		set80211(s, IEEE80211_IOC_BSSID, 0,
848			IEEE80211_ADDR_LEN, zerobssid);
849	}
850}
851
852static int
853getac(const char *ac)
854{
855	if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0)
856		return WME_AC_BE;
857	if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0)
858		return WME_AC_BK;
859	if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0)
860		return WME_AC_VI;
861	if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0)
862		return WME_AC_VO;
863	errx(1, "unknown wme access class %s", ac);
864}
865
866static
867DECL_CMD_FUNC2(set80211cwmin, ac, val)
868{
869	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
870}
871
872static
873DECL_CMD_FUNC2(set80211cwmax, ac, val)
874{
875	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
876}
877
878static
879DECL_CMD_FUNC2(set80211aifs, ac, val)
880{
881	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
882}
883
884static
885DECL_CMD_FUNC2(set80211txoplimit, ac, val)
886{
887	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
888}
889
890static
891DECL_CMD_FUNC(set80211acm, ac, d)
892{
893	set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL);
894}
895static
896DECL_CMD_FUNC(set80211noacm, ac, d)
897{
898	set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL);
899}
900
901static
902DECL_CMD_FUNC(set80211ackpolicy, ac, d)
903{
904	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL);
905}
906static
907DECL_CMD_FUNC(set80211noackpolicy, ac, d)
908{
909	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL);
910}
911
912static
913DECL_CMD_FUNC2(set80211bsscwmin, ac, val)
914{
915	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
916		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
917}
918
919static
920DECL_CMD_FUNC2(set80211bsscwmax, ac, val)
921{
922	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
923		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
924}
925
926static
927DECL_CMD_FUNC2(set80211bssaifs, ac, val)
928{
929	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
930		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
931}
932
933static
934DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
935{
936	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
937		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
938}
939
940static
941DECL_CMD_FUNC(set80211dtimperiod, val, d)
942{
943	set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
944}
945
946static
947DECL_CMD_FUNC(set80211bintval, val, d)
948{
949	set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
950}
951
952static void
953set80211macmac(int s, int op, const char *val)
954{
955	char *temp;
956	struct sockaddr_dl sdl;
957
958	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
959	if (temp == NULL)
960		errx(1, "malloc failed");
961	temp[0] = ':';
962	strcpy(temp + 1, val);
963	sdl.sdl_len = sizeof(sdl);
964	link_addr(temp, &sdl);
965	free(temp);
966	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
967		errx(1, "malformed link-level address");
968	set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
969}
970
971static
972DECL_CMD_FUNC(set80211addmac, val, d)
973{
974	set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
975}
976
977static
978DECL_CMD_FUNC(set80211delmac, val, d)
979{
980	set80211macmac(s, IEEE80211_IOC_DELMAC, val);
981}
982
983static
984DECL_CMD_FUNC(set80211kickmac, val, d)
985{
986	char *temp;
987	struct sockaddr_dl sdl;
988	struct ieee80211req_mlme mlme;
989
990	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
991	if (temp == NULL)
992		errx(1, "malloc failed");
993	temp[0] = ':';
994	strcpy(temp + 1, val);
995	sdl.sdl_len = sizeof(sdl);
996	link_addr(temp, &sdl);
997	free(temp);
998	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
999		errx(1, "malformed link-level address");
1000	memset(&mlme, 0, sizeof(mlme));
1001	mlme.im_op = IEEE80211_MLME_DEAUTH;
1002	mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE;
1003	memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN);
1004	set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), &mlme);
1005}
1006
1007static
1008DECL_CMD_FUNC(set80211maccmd, val, d)
1009{
1010	set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
1011}
1012
1013static void
1014set80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
1015{
1016	set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
1017}
1018
1019static void
1020set80211bgscan(const char *val, int d, int s, const struct afswtch *rafp)
1021{
1022	set80211(s, IEEE80211_IOC_BGSCAN, d, 0, NULL);
1023}
1024
1025static
1026DECL_CMD_FUNC(set80211bgscanidle, val, d)
1027{
1028	set80211(s, IEEE80211_IOC_BGSCAN_IDLE, atoi(val), 0, NULL);
1029}
1030
1031static
1032DECL_CMD_FUNC(set80211bgscanintvl, val, d)
1033{
1034	set80211(s, IEEE80211_IOC_BGSCAN_INTERVAL, atoi(val), 0, NULL);
1035}
1036
1037static
1038DECL_CMD_FUNC(set80211scanvalid, val, d)
1039{
1040	set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL);
1041}
1042
1043static
1044DECL_CMD_FUNC(set80211roamrssi11a, val, d)
1045{
1046	set80211(s, IEEE80211_IOC_ROAM_RSSI_11A, atoi(val), 0, NULL);
1047}
1048
1049static
1050DECL_CMD_FUNC(set80211roamrssi11b, val, d)
1051{
1052	set80211(s, IEEE80211_IOC_ROAM_RSSI_11B, atoi(val), 0, NULL);
1053}
1054
1055static
1056DECL_CMD_FUNC(set80211roamrssi11g, val, d)
1057{
1058	set80211(s, IEEE80211_IOC_ROAM_RSSI_11G, atoi(val), 0, NULL);
1059}
1060
1061static
1062DECL_CMD_FUNC(set80211roamrate11a, val, d)
1063{
1064	set80211(s, IEEE80211_IOC_ROAM_RATE_11A, 2*atoi(val), 0, NULL);
1065}
1066
1067static
1068DECL_CMD_FUNC(set80211roamrate11b, val, d)
1069{
1070	set80211(s, IEEE80211_IOC_ROAM_RATE_11B, 2*atoi(val), 0, NULL);
1071}
1072
1073static
1074DECL_CMD_FUNC(set80211roamrate11g, val, d)
1075{
1076	set80211(s, IEEE80211_IOC_ROAM_RATE_11G, 2*atoi(val), 0, NULL);
1077}
1078
1079static
1080DECL_CMD_FUNC(set80211mcastrate, val, d)
1081{
1082	set80211(s, IEEE80211_IOC_MCAST_RATE, 2*atoi(val), 0, NULL);
1083}
1084
1085static
1086DECL_CMD_FUNC(set80211fragthreshold, val, d)
1087{
1088	set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
1089		isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
1090}
1091
1092static
1093DECL_CMD_FUNC(set80211bmissthreshold, val, d)
1094{
1095	set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
1096		isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
1097}
1098
1099static void
1100set80211burst(const char *val, int d, int s, const struct afswtch *rafp)
1101{
1102	set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
1103}
1104
1105static void
1106set80211doth(const char *val, int d, int s, const struct afswtch *rafp)
1107{
1108	set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL);
1109}
1110
1111static void
1112set80211shortgi(const char *val, int d, int s, const struct afswtch *rafp)
1113{
1114	set80211(s, IEEE80211_IOC_SHORTGI,
1115		d ? (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) : 0,
1116		0, NULL);
1117}
1118
1119static void
1120set80211ampdu(const char *val, int d, int s, const struct afswtch *rafp)
1121{
1122	int ampdu;
1123
1124	if (get80211val(s, IEEE80211_IOC_AMPDU, &ampdu) < 0)
1125		errx(-1, "cannot get AMPDU setting");
1126	if (d < 0) {
1127		d = -d;
1128		ampdu &= ~d;
1129	} else
1130		ampdu |= d;
1131	set80211(s, IEEE80211_IOC_AMPDU, ampdu, 0, NULL);
1132}
1133
1134static
1135DECL_CMD_FUNC(set80211ampdulimit, val, d)
1136{
1137	int v;
1138
1139	switch (atoi(val)) {
1140	case 8:
1141	case 8*1024:
1142		v = IEEE80211_HTCAP_MAXRXAMPDU_8K;
1143		break;
1144	case 16:
1145	case 16*1024:
1146		v = IEEE80211_HTCAP_MAXRXAMPDU_16K;
1147		break;
1148	case 32:
1149	case 32*1024:
1150		v = IEEE80211_HTCAP_MAXRXAMPDU_32K;
1151		break;
1152	case 64:
1153	case 64*1024:
1154		v = IEEE80211_HTCAP_MAXRXAMPDU_64K;
1155		break;
1156	default:
1157		errx(-1, "invalid A-MPDU limit %s", val);
1158	}
1159	set80211(s, IEEE80211_IOC_AMPDU_LIMIT, v, 0, NULL);
1160}
1161
1162static
1163DECL_CMD_FUNC(set80211ampdudensity, val, d)
1164{
1165	int v;
1166
1167	if (isanyarg(val))
1168		v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1169	else switch ((int)(atof(val)*4)) {
1170	case 0:
1171		v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1172		break;
1173	case 1:
1174		v = IEEE80211_HTCAP_MPDUDENSITY_025;
1175		break;
1176	case 2:
1177		v = IEEE80211_HTCAP_MPDUDENSITY_05;
1178		break;
1179	case 4:
1180		v = IEEE80211_HTCAP_MPDUDENSITY_1;
1181		break;
1182	case 8:
1183		v = IEEE80211_HTCAP_MPDUDENSITY_2;
1184		break;
1185	case 16:
1186		v = IEEE80211_HTCAP_MPDUDENSITY_4;
1187		break;
1188	case 32:
1189		v = IEEE80211_HTCAP_MPDUDENSITY_8;
1190		break;
1191	case 64:
1192		v = IEEE80211_HTCAP_MPDUDENSITY_16;
1193		break;
1194	default:
1195		errx(-1, "invalid A-MPDU density %s", val);
1196	}
1197	set80211(s, IEEE80211_IOC_AMPDU_DENSITY, v, 0, NULL);
1198}
1199
1200static void
1201set80211amsdu(const char *val, int d, int s, const struct afswtch *rafp)
1202{
1203	int amsdu;
1204
1205	if (get80211val(s, IEEE80211_IOC_AMSDU, &amsdu) < 0)
1206		errx(-1, "cannot get AMSDU setting");
1207	if (d < 0) {
1208		d = -d;
1209		amsdu &= ~d;
1210	} else
1211		amsdu |= d;
1212	set80211(s, IEEE80211_IOC_AMSDU, amsdu, 0, NULL);
1213}
1214
1215static
1216DECL_CMD_FUNC(set80211amsdulimit, val, d)
1217{
1218	set80211(s, IEEE80211_IOC_AMSDU_LIMIT, atoi(val), 0, NULL);
1219}
1220
1221static void
1222set80211puren(const char *val, int d, int s, const struct afswtch *rafp)
1223{
1224	set80211(s, IEEE80211_IOC_PUREN, d, 0, NULL);
1225}
1226
1227static void
1228set80211htcompat(const char *val, int d, int s, const struct afswtch *rafp)
1229{
1230	set80211(s, IEEE80211_IOC_HTCOMPAT, d, 0, NULL);
1231}
1232
1233static void
1234set80211htconf(const char *val, int d, int s, const struct afswtch *rafp)
1235{
1236	set80211(s, IEEE80211_IOC_HTCONF, d, 0, NULL);
1237	htconf = d;
1238}
1239
1240static void
1241set80211inact(const char *val, int d, int s, const struct afswtch *rafp)
1242{
1243	set80211(s, IEEE80211_IOC_INACTIVITY, d, 0, NULL);
1244}
1245
1246static void
1247LINE_INIT(char c)
1248{
1249	spacer = c;
1250	if (c == '\t')
1251		col = 8;
1252	else
1253		col = 1;
1254}
1255
1256static void
1257LINE_BREAK(void)
1258{
1259	if (spacer != '\t') {
1260		printf("\n");
1261		spacer = '\t';
1262	}
1263	col = 8;		/* 8-col tab */
1264}
1265
1266static void
1267LINE_CHECK(const char *fmt, ...)
1268{
1269	char buf[80];
1270	va_list ap;
1271	int n;
1272
1273	va_start(ap, fmt);
1274	n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
1275	va_end(ap);
1276	col += 1+n;
1277	if (col > MAXCOL) {
1278		LINE_BREAK();
1279		col += n;
1280	}
1281	buf[0] = spacer;
1282	printf("%s", buf);
1283	spacer = ' ';
1284}
1285
1286static int
1287getmaxrate(const uint8_t rates[15], uint8_t nrates)
1288{
1289	int i, maxrate = -1;
1290
1291	for (i = 0; i < nrates; i++) {
1292		int rate = rates[i] & IEEE80211_RATE_VAL;
1293		if (rate > maxrate)
1294			maxrate = rate;
1295	}
1296	return maxrate / 2;
1297}
1298
1299static const char *
1300getcaps(int capinfo)
1301{
1302	static char capstring[32];
1303	char *cp = capstring;
1304
1305	if (capinfo & IEEE80211_CAPINFO_ESS)
1306		*cp++ = 'E';
1307	if (capinfo & IEEE80211_CAPINFO_IBSS)
1308		*cp++ = 'I';
1309	if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
1310		*cp++ = 'c';
1311	if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
1312		*cp++ = 'C';
1313	if (capinfo & IEEE80211_CAPINFO_PRIVACY)
1314		*cp++ = 'P';
1315	if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
1316		*cp++ = 'S';
1317	if (capinfo & IEEE80211_CAPINFO_PBCC)
1318		*cp++ = 'B';
1319	if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
1320		*cp++ = 'A';
1321	if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
1322		*cp++ = 's';
1323	if (capinfo & IEEE80211_CAPINFO_RSN)
1324		*cp++ = 'R';
1325	if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
1326		*cp++ = 'D';
1327	*cp = '\0';
1328	return capstring;
1329}
1330
1331static const char *
1332getflags(int flags)
1333{
1334/* XXX need these publicly defined or similar */
1335#define	IEEE80211_NODE_AUTH	0x0001		/* authorized for data */
1336#define	IEEE80211_NODE_QOS	0x0002		/* QoS enabled */
1337#define	IEEE80211_NODE_ERP	0x0004		/* ERP enabled */
1338#define	IEEE80211_NODE_PWR_MGT	0x0010		/* power save mode enabled */
1339#define	IEEE80211_NODE_HT	0x0040		/* HT enabled */
1340#define	IEEE80211_NODE_HTCOMPAT	0x0080		/* HT setup w/ vendor OUI's */
1341#define	IEEE80211_NODE_WPS	0x0100		/* WPS association */
1342#define	IEEE80211_NODE_TSN	0x0200		/* TSN association */
1343
1344	static char flagstring[32];
1345	char *cp = flagstring;
1346
1347	if (flags & IEEE80211_NODE_AUTH)
1348		*cp++ = 'A';
1349	if (flags & IEEE80211_NODE_QOS)
1350		*cp++ = 'Q';
1351	if (flags & IEEE80211_NODE_ERP)
1352		*cp++ = 'E';
1353	if (flags & IEEE80211_NODE_PWR_MGT)
1354		*cp++ = 'P';
1355	if (flags & IEEE80211_NODE_HT) {
1356		*cp++ = 'H';
1357		if (flags & IEEE80211_NODE_HTCOMPAT)
1358			*cp++ = '+';
1359	}
1360	if (flags & IEEE80211_NODE_WPS)
1361		*cp++ = 'W';
1362	if (flags & IEEE80211_NODE_TSN)
1363		*cp++ = 'T';
1364	*cp = '\0';
1365	return flagstring;
1366#undef IEEE80211_NODE_TSN
1367#undef IEEE80211_NODE_WPS
1368#undef IEEE80211_NODE_HTCOMPAT
1369#undef IEEE80211_NODE_HT
1370#undef IEEE80211_NODE_AUTH
1371#undef IEEE80211_NODE_QOS
1372#undef IEEE80211_NODE_ERP
1373#undef IEEE80211_NODE_PWR_MGT
1374}
1375
1376static void
1377printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
1378{
1379	printf("%s", tag);
1380	if (verbose) {
1381		maxlen -= strlen(tag)+2;
1382		if (2*ielen > maxlen)
1383			maxlen--;
1384		printf("<");
1385		for (; ielen > 0; ie++, ielen--) {
1386			if (maxlen-- <= 0)
1387				break;
1388			printf("%02x", *ie);
1389		}
1390		if (ielen != 0)
1391			printf("-");
1392		printf(">");
1393	}
1394}
1395
1396#define LE_READ_2(p)					\
1397	((u_int16_t)					\
1398	 ((((const u_int8_t *)(p))[0]      ) |		\
1399	  (((const u_int8_t *)(p))[1] <<  8)))
1400#define LE_READ_4(p)					\
1401	((u_int32_t)					\
1402	 ((((const u_int8_t *)(p))[0]      ) |		\
1403	  (((const u_int8_t *)(p))[1] <<  8) |		\
1404	  (((const u_int8_t *)(p))[2] << 16) |		\
1405	  (((const u_int8_t *)(p))[3] << 24)))
1406
1407/*
1408 * NB: The decoding routines assume a properly formatted ie
1409 *     which should be safe as the kernel only retains them
1410 *     if they parse ok.
1411 */
1412
1413static void
1414printwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1415{
1416#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
1417	static const char *acnames[] = { "BE", "BK", "VO", "VI" };
1418	const struct ieee80211_wme_param *wme =
1419	    (const struct ieee80211_wme_param *) ie;
1420	int i;
1421
1422	printf("%s", tag);
1423	if (!verbose)
1424		return;
1425	printf("<qosinfo 0x%x", wme->param_qosInfo);
1426	ie += offsetof(struct ieee80211_wme_param, params_acParams);
1427	for (i = 0; i < WME_NUM_AC; i++) {
1428		const struct ieee80211_wme_acparams *ac =
1429		    &wme->params_acParams[i];
1430
1431		printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]"
1432			, acnames[i]
1433			, MS(ac->acp_aci_aifsn, WME_PARAM_ACM) ? "acm " : ""
1434			, MS(ac->acp_aci_aifsn, WME_PARAM_AIFSN)
1435			, MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMIN)
1436			, MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMAX)
1437			, LE_READ_2(&ac->acp_txop)
1438		);
1439	}
1440	printf(">");
1441#undef MS
1442}
1443
1444static void
1445printwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1446{
1447	printf("%s", tag);
1448	if (verbose) {
1449		const struct ieee80211_wme_info *wme =
1450		    (const struct ieee80211_wme_info *) ie;
1451		printf("<version 0x%x info 0x%x>",
1452		    wme->wme_version, wme->wme_info);
1453	}
1454}
1455
1456static void
1457printhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1458{
1459	printf("%s", tag);
1460	if (verbose) {
1461		const struct ieee80211_ie_htcap *htcap =
1462		    (const struct ieee80211_ie_htcap *) ie;
1463		const char *sep;
1464		int i, j;
1465
1466		printf("<cap 0x%x param 0x%x",
1467		    LE_READ_2(&htcap->hc_cap), htcap->hc_param);
1468		printf(" mcsset[");
1469		sep = "";
1470		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
1471			if (isset(htcap->hc_mcsset, i)) {
1472				for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
1473					if (isclr(htcap->hc_mcsset, j))
1474						break;
1475				j--;
1476				if (i == j)
1477					printf("%s%u", sep, i);
1478				else
1479					printf("%s%u-%u", sep, i, j);
1480				i += j-i;
1481				sep = ",";
1482			}
1483		printf("] extcap 0x%x txbf 0x%x antenna 0x%x>",
1484		    LE_READ_2(&htcap->hc_extcap),
1485		    LE_READ_4(&htcap->hc_txbf),
1486		    htcap->hc_antenna);
1487	}
1488}
1489
1490static void
1491printhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1492{
1493	printf("%s", tag);
1494	if (verbose) {
1495		const struct ieee80211_ie_htinfo *htinfo =
1496		    (const struct ieee80211_ie_htinfo *) ie;
1497		const char *sep;
1498		int i, j;
1499
1500		printf("<ctl %u, %x,%x,%x,%x", htinfo->hi_ctrlchannel,
1501		    htinfo->hi_byte1, htinfo->hi_byte2, htinfo->hi_byte3,
1502		    LE_READ_2(&htinfo->hi_byte45));
1503		printf(" basicmcs[");
1504		sep = "";
1505		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
1506			if (isset(htinfo->hi_basicmcsset, i)) {
1507				for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
1508					if (isclr(htinfo->hi_basicmcsset, j))
1509						break;
1510				j--;
1511				if (i == j)
1512					printf("%s%u", sep, i);
1513				else
1514					printf("%s%u-%u", sep, i, j);
1515				i += j-i;
1516				sep = ",";
1517			}
1518		printf("]>");
1519	}
1520}
1521
1522static void
1523printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1524{
1525
1526	printf("%s", tag);
1527	if (verbose) {
1528		const struct ieee80211_ath_ie *ath =
1529			(const struct ieee80211_ath_ie *)ie;
1530
1531		printf("<");
1532		if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME)
1533			printf("DTURBO,");
1534		if (ath->ath_capability & ATHEROS_CAP_COMPRESSION)
1535			printf("COMP,");
1536		if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME)
1537			printf("FF,");
1538		if (ath->ath_capability & ATHEROS_CAP_XR)
1539			printf("XR,");
1540		if (ath->ath_capability & ATHEROS_CAP_AR)
1541			printf("AR,");
1542		if (ath->ath_capability & ATHEROS_CAP_BURST)
1543			printf("BURST,");
1544		if (ath->ath_capability & ATHEROS_CAP_WME)
1545			printf("WME,");
1546		if (ath->ath_capability & ATHEROS_CAP_BOOST)
1547			printf("BOOST,");
1548		printf("0x%x>", LE_READ_2(ath->ath_defkeyix));
1549	}
1550}
1551
1552static const char *
1553wpa_cipher(const u_int8_t *sel)
1554{
1555#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
1556	u_int32_t w = LE_READ_4(sel);
1557
1558	switch (w) {
1559	case WPA_SEL(WPA_CSE_NULL):
1560		return "NONE";
1561	case WPA_SEL(WPA_CSE_WEP40):
1562		return "WEP40";
1563	case WPA_SEL(WPA_CSE_WEP104):
1564		return "WEP104";
1565	case WPA_SEL(WPA_CSE_TKIP):
1566		return "TKIP";
1567	case WPA_SEL(WPA_CSE_CCMP):
1568		return "AES-CCMP";
1569	}
1570	return "?";		/* NB: so 1<< is discarded */
1571#undef WPA_SEL
1572}
1573
1574static const char *
1575wpa_keymgmt(const u_int8_t *sel)
1576{
1577#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
1578	u_int32_t w = LE_READ_4(sel);
1579
1580	switch (w) {
1581	case WPA_SEL(WPA_ASE_8021X_UNSPEC):
1582		return "8021X-UNSPEC";
1583	case WPA_SEL(WPA_ASE_8021X_PSK):
1584		return "8021X-PSK";
1585	case WPA_SEL(WPA_ASE_NONE):
1586		return "NONE";
1587	}
1588	return "?";
1589#undef WPA_SEL
1590}
1591
1592static void
1593printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1594{
1595	u_int8_t len = ie[1];
1596
1597	printf("%s", tag);
1598	if (verbose) {
1599		const char *sep;
1600		int n;
1601
1602		ie += 6, len -= 4;		/* NB: len is payload only */
1603
1604		printf("<v%u", LE_READ_2(ie));
1605		ie += 2, len -= 2;
1606
1607		printf(" mc:%s", wpa_cipher(ie));
1608		ie += 4, len -= 4;
1609
1610		/* unicast ciphers */
1611		n = LE_READ_2(ie);
1612		ie += 2, len -= 2;
1613		sep = " uc:";
1614		for (; n > 0; n--) {
1615			printf("%s%s", sep, wpa_cipher(ie));
1616			ie += 4, len -= 4;
1617			sep = "+";
1618		}
1619
1620		/* key management algorithms */
1621		n = LE_READ_2(ie);
1622		ie += 2, len -= 2;
1623		sep = " km:";
1624		for (; n > 0; n--) {
1625			printf("%s%s", sep, wpa_keymgmt(ie));
1626			ie += 4, len -= 4;
1627			sep = "+";
1628		}
1629
1630		if (len > 2)		/* optional capabilities */
1631			printf(", caps 0x%x", LE_READ_2(ie));
1632		printf(">");
1633	}
1634}
1635
1636static const char *
1637rsn_cipher(const u_int8_t *sel)
1638{
1639#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
1640	u_int32_t w = LE_READ_4(sel);
1641
1642	switch (w) {
1643	case RSN_SEL(RSN_CSE_NULL):
1644		return "NONE";
1645	case RSN_SEL(RSN_CSE_WEP40):
1646		return "WEP40";
1647	case RSN_SEL(RSN_CSE_WEP104):
1648		return "WEP104";
1649	case RSN_SEL(RSN_CSE_TKIP):
1650		return "TKIP";
1651	case RSN_SEL(RSN_CSE_CCMP):
1652		return "AES-CCMP";
1653	case RSN_SEL(RSN_CSE_WRAP):
1654		return "AES-OCB";
1655	}
1656	return "?";
1657#undef WPA_SEL
1658}
1659
1660static const char *
1661rsn_keymgmt(const u_int8_t *sel)
1662{
1663#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
1664	u_int32_t w = LE_READ_4(sel);
1665
1666	switch (w) {
1667	case RSN_SEL(RSN_ASE_8021X_UNSPEC):
1668		return "8021X-UNSPEC";
1669	case RSN_SEL(RSN_ASE_8021X_PSK):
1670		return "8021X-PSK";
1671	case RSN_SEL(RSN_ASE_NONE):
1672		return "NONE";
1673	}
1674	return "?";
1675#undef RSN_SEL
1676}
1677
1678static void
1679printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1680{
1681	printf("%s", tag);
1682	if (verbose) {
1683		const char *sep;
1684		int n;
1685
1686		ie += 2, ielen -= 2;
1687
1688		printf("<v%u", LE_READ_2(ie));
1689		ie += 2, ielen -= 2;
1690
1691		printf(" mc:%s", rsn_cipher(ie));
1692		ie += 4, ielen -= 4;
1693
1694		/* unicast ciphers */
1695		n = LE_READ_2(ie);
1696		ie += 2, ielen -= 2;
1697		sep = " uc:";
1698		for (; n > 0; n--) {
1699			printf("%s%s", sep, rsn_cipher(ie));
1700			ie += 4, ielen -= 4;
1701			sep = "+";
1702		}
1703
1704		/* key management algorithms */
1705		n = LE_READ_2(ie);
1706		ie += 2, ielen -= 2;
1707		sep = " km:";
1708		for (; n > 0; n--) {
1709			printf("%s%s", sep, rsn_keymgmt(ie));
1710			ie += 4, ielen -= 4;
1711			sep = "+";
1712		}
1713
1714		if (ielen > 2)		/* optional capabilities */
1715			printf(", caps 0x%x", LE_READ_2(ie));
1716		/* XXXPMKID */
1717		printf(">");
1718	}
1719}
1720
1721/*
1722 * Copy the ssid string contents into buf, truncating to fit.  If the
1723 * ssid is entirely printable then just copy intact.  Otherwise convert
1724 * to hexadecimal.  If the result is truncated then replace the last
1725 * three characters with "...".
1726 */
1727static int
1728copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
1729{
1730	const u_int8_t *p;
1731	size_t maxlen;
1732	int i;
1733
1734	if (essid_len > bufsize)
1735		maxlen = bufsize;
1736	else
1737		maxlen = essid_len;
1738	/* determine printable or not */
1739	for (i = 0, p = essid; i < maxlen; i++, p++) {
1740		if (*p < ' ' || *p > 0x7e)
1741			break;
1742	}
1743	if (i != maxlen) {		/* not printable, print as hex */
1744		if (bufsize < 3)
1745			return 0;
1746		strlcpy(buf, "0x", bufsize);
1747		bufsize -= 2;
1748		p = essid;
1749		for (i = 0; i < maxlen && bufsize >= 2; i++) {
1750			sprintf(&buf[2+2*i], "%02x", p[i]);
1751			bufsize -= 2;
1752		}
1753		if (i != essid_len)
1754			memcpy(&buf[2+2*i-3], "...", 3);
1755	} else {			/* printable, truncate as needed */
1756		memcpy(buf, essid, maxlen);
1757		if (maxlen != essid_len)
1758			memcpy(&buf[maxlen-3], "...", 3);
1759	}
1760	return maxlen;
1761}
1762
1763static void
1764printssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1765{
1766	char ssid[2*IEEE80211_NWID_LEN+1];
1767
1768	printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid);
1769}
1770
1771static void
1772printrates(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1773{
1774	const char *sep;
1775	int i;
1776
1777	printf("%s", tag);
1778	sep = "<";
1779	for (i = 2; i < ielen; i++) {
1780		printf("%s%s%d", sep,
1781		    ie[i] & IEEE80211_RATE_BASIC ? "B" : "",
1782		    ie[i] & IEEE80211_RATE_VAL);
1783		sep = ",";
1784	}
1785	printf(">");
1786}
1787
1788static void
1789printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1790{
1791	const struct ieee80211_country_ie *cie =
1792	   (const struct ieee80211_country_ie *) ie;
1793	int i, nbands, schan, nchan;
1794
1795	printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]);
1796	nbands = (cie->len - 3) / sizeof(cie->band[0]);
1797	for (i = 0; i < nbands; i++) {
1798		schan = cie->band[i].schan;
1799		nchan = cie->band[i].nchan;
1800		if (nchan != 1)
1801			printf(" %u-%u,%u", schan, schan + nchan-1,
1802			    cie->band[i].maxtxpwr);
1803		else
1804			printf(" %u,%u", schan, cie->band[i].maxtxpwr);
1805	}
1806	printf(">");
1807}
1808
1809/* unaligned little endian access */
1810#define LE_READ_4(p)					\
1811	((u_int32_t)					\
1812	 ((((const u_int8_t *)(p))[0]      ) |		\
1813	  (((const u_int8_t *)(p))[1] <<  8) |		\
1814	  (((const u_int8_t *)(p))[2] << 16) |		\
1815	  (((const u_int8_t *)(p))[3] << 24)))
1816
1817static int __inline
1818iswpaoui(const u_int8_t *frm)
1819{
1820	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
1821}
1822
1823static int __inline
1824iswmeinfo(const u_int8_t *frm)
1825{
1826	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
1827		frm[6] == WME_INFO_OUI_SUBTYPE;
1828}
1829
1830static int __inline
1831iswmeparam(const u_int8_t *frm)
1832{
1833	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
1834		frm[6] == WME_PARAM_OUI_SUBTYPE;
1835}
1836
1837static int __inline
1838isatherosoui(const u_int8_t *frm)
1839{
1840	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
1841}
1842
1843static const char *
1844iename(int elemid)
1845{
1846	switch (elemid) {
1847	case IEEE80211_ELEMID_FHPARMS:	return " FHPARMS";
1848	case IEEE80211_ELEMID_CFPARMS:	return " CFPARMS";
1849	case IEEE80211_ELEMID_TIM:	return " TIM";
1850	case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS";
1851	case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE";
1852	case IEEE80211_ELEMID_PWRCNSTR:	return " PWRCNSTR";
1853	case IEEE80211_ELEMID_PWRCAP:	return " PWRCAP";
1854	case IEEE80211_ELEMID_TPCREQ:	return " TPCREQ";
1855	case IEEE80211_ELEMID_TPCREP:	return " TPCREP";
1856	case IEEE80211_ELEMID_SUPPCHAN:	return " SUPPCHAN";
1857	case IEEE80211_ELEMID_CHANSWITCHANN:return " CSA";
1858	case IEEE80211_ELEMID_MEASREQ:	return " MEASREQ";
1859	case IEEE80211_ELEMID_MEASREP:	return " MEASREP";
1860	case IEEE80211_ELEMID_QUIET:	return " QUIET";
1861	case IEEE80211_ELEMID_IBSSDFS:	return " IBSSDFS";
1862	case IEEE80211_ELEMID_TPC:	return " TPC";
1863	case IEEE80211_ELEMID_CCKM:	return " CCKM";
1864	}
1865	return " ???";
1866}
1867
1868static void
1869printies(const u_int8_t *vp, int ielen, int maxcols)
1870{
1871	while (ielen > 0) {
1872		switch (vp[0]) {
1873		case IEEE80211_ELEMID_SSID:
1874			if (verbose)
1875				printssid(" SSID", vp, 2+vp[1], maxcols);
1876			break;
1877		case IEEE80211_ELEMID_RATES:
1878		case IEEE80211_ELEMID_XRATES:
1879			if (verbose)
1880				printrates(vp[0] == IEEE80211_ELEMID_RATES ?
1881				    " RATES" : " XRATES", vp, 2+vp[1], maxcols);
1882			break;
1883		case IEEE80211_ELEMID_DSPARMS:
1884			if (verbose)
1885				printf(" DSPARMS<%u>", vp[2]);
1886			break;
1887		case IEEE80211_ELEMID_COUNTRY:
1888			if (verbose)
1889				printcountry(" COUNTRY", vp, 2+vp[1], maxcols);
1890			break;
1891		case IEEE80211_ELEMID_ERP:
1892			if (verbose)
1893				printf(" ERP<0x%x>", vp[2]);
1894			break;
1895		case IEEE80211_ELEMID_VENDOR:
1896			if (iswpaoui(vp))
1897				printwpaie(" WPA", vp, 2+vp[1], maxcols);
1898			else if (iswmeinfo(vp))
1899				printwmeinfo(" WME", vp, 2+vp[1], maxcols);
1900			else if (iswmeparam(vp))
1901				printwmeparam(" WME", vp, 2+vp[1], maxcols);
1902			else if (isatherosoui(vp))
1903				printathie(" ATH", vp, 2+vp[1], maxcols);
1904			else if (verbose)
1905				printie(" VEN", vp, 2+vp[1], maxcols);
1906			break;
1907		case IEEE80211_ELEMID_RSN:
1908			printrsnie(" RSN", vp, 2+vp[1], maxcols);
1909			break;
1910		case IEEE80211_ELEMID_HTCAP:
1911			printhtcap(" HTCAP", vp, 2+vp[1], maxcols);
1912			break;
1913		case IEEE80211_ELEMID_HTINFO:
1914			if (verbose)
1915				printhtinfo(" HTINFO", vp, 2+vp[1], maxcols);
1916			break;
1917		default:
1918			if (verbose)
1919				printie(iename(vp[0]), vp, 2+vp[1], maxcols);
1920			break;
1921		}
1922		ielen -= 2+vp[1];
1923		vp += 2+vp[1];
1924	}
1925}
1926
1927static void
1928list_scan(int s)
1929{
1930	uint8_t buf[24*1024];
1931	char ssid[IEEE80211_NWID_LEN+1];
1932	const uint8_t *cp;
1933	int len, ssidmax;
1934
1935	if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0)
1936		errx(1, "unable to get scan results");
1937	if (len < sizeof(struct ieee80211req_scan_result))
1938		return;
1939
1940	getchaninfo(s);
1941
1942	ssidmax = verbose ? IEEE80211_NWID_LEN : 14;
1943	printf("%-*.*s  %-17.17s  %4s %4s  %-7s  %3s %4s\n"
1944		, ssidmax, ssidmax, "SSID"
1945		, "BSSID"
1946		, "CHAN"
1947		, "RATE"
1948		, " S:N"
1949		, "INT"
1950		, "CAPS"
1951	);
1952	cp = buf;
1953	do {
1954		const struct ieee80211req_scan_result *sr;
1955		const uint8_t *vp;
1956
1957		sr = (const struct ieee80211req_scan_result *) cp;
1958		vp = cp + sr->isr_ie_off;
1959		printf("%-*.*s  %s  %3d  %3dM %3d:%-3d  %3d %-4.4s"
1960			, ssidmax
1961			  , copy_essid(ssid, ssidmax, vp, sr->isr_ssid_len)
1962			  , ssid
1963			, ether_ntoa((const struct ether_addr *) sr->isr_bssid)
1964			, ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
1965			, getmaxrate(sr->isr_rates, sr->isr_nrates)
1966			, (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise
1967			, sr->isr_intval
1968			, getcaps(sr->isr_capinfo)
1969		);
1970		printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);
1971		printf("\n");
1972		cp += sr->isr_len, len -= sr->isr_len;
1973	} while (len >= sizeof(struct ieee80211req_scan_result));
1974}
1975
1976#include <net80211/ieee80211_freebsd.h>
1977
1978static void
1979scan_and_wait(int s)
1980{
1981	struct ieee80211req ireq;
1982	int sroute;
1983
1984	sroute = socket(PF_ROUTE, SOCK_RAW, 0);
1985	if (sroute < 0) {
1986		perror("socket(PF_ROUTE,SOCK_RAW)");
1987		return;
1988	}
1989	(void) memset(&ireq, 0, sizeof(ireq));
1990	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1991	ireq.i_type = IEEE80211_IOC_SCAN_REQ;
1992	/* NB: only root can trigger a scan so ignore errors */
1993	if (ioctl(s, SIOCS80211, &ireq) >= 0) {
1994		char buf[2048];
1995		struct if_announcemsghdr *ifan;
1996		struct rt_msghdr *rtm;
1997
1998		do {
1999			if (read(sroute, buf, sizeof(buf)) < 0) {
2000				perror("read(PF_ROUTE)");
2001				break;
2002			}
2003			rtm = (struct rt_msghdr *) buf;
2004			if (rtm->rtm_version != RTM_VERSION)
2005				break;
2006			ifan = (struct if_announcemsghdr *) rtm;
2007		} while (rtm->rtm_type != RTM_IEEE80211 ||
2008		    ifan->ifan_what != RTM_IEEE80211_SCAN);
2009	}
2010	close(sroute);
2011}
2012
2013static
2014DECL_CMD_FUNC(set80211scan, val, d)
2015{
2016	scan_and_wait(s);
2017	list_scan(s);
2018}
2019
2020static enum ieee80211_opmode get80211opmode(int s);
2021
2022static int
2023gettxseq(const struct ieee80211req_sta_info *si)
2024{
2025#define	IEEE80211_NODE_QOS	0x0002		/* QoS enabled */
2026
2027	int i, txseq;
2028
2029	if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
2030		return si->isi_txseqs[0];
2031	/* XXX not right but usually what folks want */
2032	txseq = 0;
2033	for (i = 0; i < IEEE80211_TID_SIZE; i++)
2034		if (si->isi_txseqs[i] > txseq)
2035			txseq = si->isi_txseqs[i];
2036	return txseq;
2037#undef IEEE80211_NODE_QOS
2038}
2039
2040static int
2041getrxseq(const struct ieee80211req_sta_info *si)
2042{
2043#define	IEEE80211_NODE_QOS	0x0002		/* QoS enabled */
2044
2045	int i, rxseq;
2046
2047	if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
2048		return si->isi_rxseqs[0];
2049	/* XXX not right but usually what folks want */
2050	rxseq = 0;
2051	for (i = 0; i < IEEE80211_TID_SIZE; i++)
2052		if (si->isi_rxseqs[i] > rxseq)
2053			rxseq = si->isi_rxseqs[i];
2054	return rxseq;
2055#undef IEEE80211_NODE_QOS
2056}
2057
2058static int
2059gettxrate(const struct ieee80211req_sta_info *si)
2060{
2061	int txrate = si->isi_txrate;
2062
2063	if (txrate & 0x80) {
2064		txrate = htrates[txrate & 0xf];
2065		/* NB: could bump this more based on short gi */
2066		return si->isi_flags & IEEE80211_CHAN_HT40 ?
2067		    txrate : txrate / 2;
2068	} else
2069		return (si->isi_rates[txrate] & IEEE80211_RATE_VAL) / 2;
2070}
2071
2072static void
2073list_stations(int s)
2074{
2075	union {
2076		struct ieee80211req_sta_req req;
2077		uint8_t buf[24*1024];
2078	} u;
2079	enum ieee80211_opmode opmode = get80211opmode(s);
2080	const uint8_t *cp;
2081	int len;
2082
2083	/* broadcast address =>'s get all stations */
2084	(void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
2085	if (opmode == IEEE80211_M_STA) {
2086		/*
2087		 * Get information about the associated AP.
2088		 */
2089		(void) get80211(s, IEEE80211_IOC_BSSID,
2090		    u.req.is_u.macaddr, IEEE80211_ADDR_LEN);
2091	}
2092	if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0)
2093		errx(1, "unable to get station information");
2094	if (len < sizeof(struct ieee80211req_sta_info))
2095		return;
2096
2097	getchaninfo(s);
2098
2099	printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %4s\n"
2100		, "ADDR"
2101		, "AID"
2102		, "CHAN"
2103		, "RATE"
2104		, "RSSI"
2105		, "IDLE"
2106		, "TXSEQ"
2107		, "RXSEQ"
2108		, "CAPS"
2109		, "FLAG"
2110	);
2111	cp = (const uint8_t *) u.req.info;
2112	do {
2113		const struct ieee80211req_sta_info *si;
2114
2115		si = (const struct ieee80211req_sta_info *) cp;
2116		if (si->isi_len < sizeof(*si))
2117			break;
2118		printf("%s %4u %4d %3dM %3.1f %4d %6d %6d %-4.4s %-4.4s"
2119			, ether_ntoa((const struct ether_addr*) si->isi_macaddr)
2120			, IEEE80211_AID(si->isi_associd)
2121			, ieee80211_mhz2ieee(si->isi_freq, si->isi_flags)
2122			, gettxrate(si)
2123			, si->isi_rssi/2.
2124			, si->isi_inact
2125			, gettxseq(si)
2126			, getrxseq(si)
2127			, getcaps(si->isi_capinfo)
2128			, getflags(si->isi_state)
2129		);
2130		printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
2131		printf("\n");
2132		cp += si->isi_len, len -= si->isi_len;
2133	} while (len >= sizeof(struct ieee80211req_sta_info));
2134}
2135
2136static const char *
2137get_chaninfo(const struct ieee80211_channel *c, int precise,
2138	char buf[], size_t bsize)
2139{
2140	buf[0] = '\0';
2141	if (IEEE80211_IS_CHAN_FHSS(c))
2142		strlcat(buf, " FHSS", bsize);
2143	if (IEEE80211_IS_CHAN_A(c)) {
2144		if (IEEE80211_IS_CHAN_HALF(c))
2145			strlcat(buf, " 11a/10Mhz", bsize);
2146		else if (IEEE80211_IS_CHAN_QUARTER(c))
2147			strlcat(buf, " 11a/5Mhz", bsize);
2148		else
2149			strlcat(buf, " 11a", bsize);
2150	}
2151	if (IEEE80211_IS_CHAN_ANYG(c)) {
2152		if (IEEE80211_IS_CHAN_HALF(c))
2153			strlcat(buf, " 11g/10Mhz", bsize);
2154		else if (IEEE80211_IS_CHAN_QUARTER(c))
2155			strlcat(buf, " 11g/5Mhz", bsize);
2156		else
2157			strlcat(buf, " 11g", bsize);
2158	} else if (IEEE80211_IS_CHAN_B(c))
2159		strlcat(buf, " 11b", bsize);
2160	if (IEEE80211_IS_CHAN_TURBO(c))
2161		strlcat(buf, " Turbo", bsize);
2162	if (precise) {
2163		if (IEEE80211_IS_CHAN_HT20(c))
2164			strlcat(buf, " ht/20", bsize);
2165		else if (IEEE80211_IS_CHAN_HT40D(c))
2166			strlcat(buf, " ht/40-", bsize);
2167		else if (IEEE80211_IS_CHAN_HT40U(c))
2168			strlcat(buf, " ht/40+", bsize);
2169	} else {
2170		if (IEEE80211_IS_CHAN_HT(c))
2171			strlcat(buf, " ht", bsize);
2172	}
2173	return buf;
2174}
2175
2176static void
2177print_chaninfo(const struct ieee80211_channel *c, int verb)
2178{
2179	char buf[14];
2180
2181	printf("Channel %3u : %u%c Mhz%-14.14s",
2182		ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
2183		IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
2184		get_chaninfo(c, verb, buf, sizeof(buf)));
2185}
2186
2187static void
2188print_channels(int s, const struct ieee80211req_chaninfo *chans,
2189	int allchans, int verb)
2190{
2191	struct ieee80211req_chaninfo achans;
2192	uint8_t reported[IEEE80211_CHAN_BYTES];
2193	const struct ieee80211_channel *c;
2194	int i, half;
2195
2196	memset(&achans, 0, sizeof(achans));
2197	memset(reported, 0, sizeof(reported));
2198	if (!allchans) {
2199		struct ieee80211req_chanlist active;
2200
2201		if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0)
2202			errx(1, "unable to get active channel list");
2203		memset(&achans, 0, sizeof(achans));
2204		for (i = 0; i < chans->ic_nchans; i++) {
2205			c = &chans->ic_chans[i];
2206			if (!isset(active.ic_channels, c->ic_ieee))
2207				continue;
2208			/*
2209			 * Suppress compatible duplicates unless
2210			 * verbose.  The kernel gives us it's
2211			 * complete channel list which has separate
2212			 * entries for 11g/11b and 11a/turbo.
2213			 */
2214			if (isset(reported, c->ic_ieee) && !verb) {
2215				/* XXX we assume duplicates are adjacent */
2216				achans.ic_chans[achans.ic_nchans-1] = *c;
2217			} else {
2218				achans.ic_chans[achans.ic_nchans++] = *c;
2219				setbit(reported, c->ic_ieee);
2220			}
2221		}
2222	} else {
2223		for (i = 0; i < chans->ic_nchans; i++) {
2224			c = &chans->ic_chans[i];
2225			/* suppress duplicates as above */
2226			if (isset(reported, c->ic_ieee) && !verb) {
2227				/* XXX we assume duplicates are adjacent */
2228				achans.ic_chans[achans.ic_nchans-1] = *c;
2229			} else {
2230				achans.ic_chans[achans.ic_nchans++] = *c;
2231				setbit(reported, c->ic_ieee);
2232			}
2233		}
2234	}
2235	half = achans.ic_nchans / 2;
2236	if (achans.ic_nchans % 2)
2237		half++;
2238
2239	for (i = 0; i < achans.ic_nchans / 2; i++) {
2240		print_chaninfo(&achans.ic_chans[i], verb);
2241		print_chaninfo(&achans.ic_chans[half+i], verb);
2242		printf("\n");
2243	}
2244	if (achans.ic_nchans % 2) {
2245		print_chaninfo(&achans.ic_chans[i], verb);
2246		printf("\n");
2247	}
2248}
2249
2250static void
2251list_channels(int s, int allchans)
2252{
2253	getchaninfo(s);
2254	print_channels(s, &chaninfo, allchans, verbose);
2255}
2256
2257static void
2258print_txpow(const struct ieee80211_channel *c)
2259{
2260	printf("Channel %3u : %u Mhz %3.1f reg %2d  ",
2261	    c->ic_ieee, c->ic_freq,
2262	    c->ic_maxpower/2., c->ic_maxregpower);
2263}
2264
2265static void
2266print_txpow_verbose(const struct ieee80211_channel *c)
2267{
2268	print_chaninfo(c, 1);
2269	printf("min %4.1f dBm  max %3.1f dBm  reg %2d dBm",
2270	    c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
2271	/* indicate where regulatory cap limits power use */
2272	if (c->ic_maxpower > 2*c->ic_maxregpower)
2273		printf(" <");
2274}
2275
2276static void
2277list_txpow(int s)
2278{
2279	struct ieee80211req_chaninfo achans;
2280	uint8_t reported[IEEE80211_CHAN_BYTES];
2281	struct ieee80211_channel *c, *prev;
2282	int i, half;
2283
2284	getchaninfo(s);
2285	memset(&achans, 0, sizeof(achans));
2286	memset(reported, 0, sizeof(reported));
2287	for (i = 0; i < chaninfo.ic_nchans; i++) {
2288		c = &chaninfo.ic_chans[i];
2289		/* suppress duplicates as above */
2290		if (isset(reported, c->ic_ieee) && !verbose) {
2291			/* XXX we assume duplicates are adjacent */
2292			prev = &achans.ic_chans[achans.ic_nchans-1];
2293			/* display highest power on channel */
2294			if (c->ic_maxpower > prev->ic_maxpower)
2295				*prev = *c;
2296		} else {
2297			achans.ic_chans[achans.ic_nchans++] = *c;
2298			setbit(reported, c->ic_ieee);
2299		}
2300	}
2301	if (!verbose) {
2302		half = achans.ic_nchans / 2;
2303		if (achans.ic_nchans % 2)
2304			half++;
2305
2306		for (i = 0; i < achans.ic_nchans / 2; i++) {
2307			print_txpow(&achans.ic_chans[i]);
2308			print_txpow(&achans.ic_chans[half+i]);
2309			printf("\n");
2310		}
2311		if (achans.ic_nchans % 2) {
2312			print_txpow(&achans.ic_chans[i]);
2313			printf("\n");
2314		}
2315	} else {
2316		for (i = 0; i < achans.ic_nchans; i++) {
2317			print_txpow_verbose(&achans.ic_chans[i]);
2318			printf("\n");
2319		}
2320	}
2321}
2322
2323static void
2324list_keys(int s)
2325{
2326}
2327
2328#define	IEEE80211_C_BITS \
2329"\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\7FF\10TURBOP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \
2330"\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \
2331"\31WPA2\32BURST\33WME\34WDS\36BGSCAN\37TXFRAG"
2332
2333static void
2334list_capabilities(int s)
2335{
2336	struct ieee80211req ireq;
2337	u_int32_t caps;
2338
2339	(void) memset(&ireq, 0, sizeof(ireq));
2340	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2341	ireq.i_type = IEEE80211_IOC_DRIVER_CAPS;
2342	if (ioctl(s, SIOCG80211, &ireq) < 0)
2343		errx(1, "unable to get driver capabilities");
2344	caps = (((u_int16_t) ireq.i_val) << 16) | ((u_int16_t) ireq.i_len);
2345	printb(name, caps, IEEE80211_C_BITS);
2346	putchar('\n');
2347}
2348
2349static int
2350get80211wme(int s, int param, int ac, int *val)
2351{
2352	struct ieee80211req ireq;
2353
2354	(void) memset(&ireq, 0, sizeof(ireq));
2355	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2356	ireq.i_type = param;
2357	ireq.i_len = ac;
2358	if (ioctl(s, SIOCG80211, &ireq) < 0) {
2359		warn("cannot get WME parameter %d, ac %d%s",
2360		    param, ac & IEEE80211_WMEPARAM_VAL,
2361		    ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : "");
2362		return -1;
2363	}
2364	*val = ireq.i_val;
2365	return 0;
2366}
2367
2368static void
2369list_wme(int s)
2370{
2371	static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
2372	int ac, val;
2373
2374	for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
2375again:
2376		if (ac & IEEE80211_WMEPARAM_BSS)
2377			printf("\t%s", "     ");
2378		else
2379			printf("\t%s", acnames[ac]);
2380
2381		/* show WME BSS parameters */
2382		if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1)
2383			printf(" cwmin %2u", val);
2384		if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1)
2385			printf(" cwmax %2u", val);
2386		if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1)
2387			printf(" aifs %2u", val);
2388		if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1)
2389			printf(" txopLimit %3u", val);
2390		if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) {
2391			if (val)
2392				printf(" acm");
2393			else if (verbose)
2394				printf(" -acm");
2395		}
2396		/* !BSS only */
2397		if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
2398			if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) {
2399				if (!val)
2400					printf(" -ack");
2401				else if (verbose)
2402					printf(" ack");
2403			}
2404		}
2405		printf("\n");
2406		if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
2407			ac |= IEEE80211_WMEPARAM_BSS;
2408			goto again;
2409		} else
2410			ac &= ~IEEE80211_WMEPARAM_BSS;
2411	}
2412}
2413
2414static void
2415printpolicy(int policy)
2416{
2417	switch (policy) {
2418	case IEEE80211_MACCMD_POLICY_OPEN:
2419		printf("policy: open\n");
2420		break;
2421	case IEEE80211_MACCMD_POLICY_ALLOW:
2422		printf("policy: allow\n");
2423		break;
2424	case IEEE80211_MACCMD_POLICY_DENY:
2425		printf("policy: deny\n");
2426		break;
2427	default:
2428		printf("policy: unknown (%u)\n", policy);
2429		break;
2430	}
2431}
2432
2433static void
2434list_mac(int s)
2435{
2436	struct ieee80211req ireq;
2437	struct ieee80211req_maclist *acllist;
2438	int i, nacls, policy, len;
2439	uint8_t *data;
2440	char c;
2441
2442	(void) memset(&ireq, 0, sizeof(ireq));
2443	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
2444	ireq.i_type = IEEE80211_IOC_MACCMD;
2445	ireq.i_val = IEEE80211_MACCMD_POLICY;
2446	if (ioctl(s, SIOCG80211, &ireq) < 0) {
2447		if (errno == EINVAL) {
2448			printf("No acl policy loaded\n");
2449			return;
2450		}
2451		err(1, "unable to get mac policy");
2452	}
2453	policy = ireq.i_val;
2454	if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
2455		c = '*';
2456	} else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
2457		c = '+';
2458	} else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
2459		c = '-';
2460	} else {
2461		printf("policy: unknown (%u)\n", policy);
2462		c = '?';
2463	}
2464	if (verbose || c == '?')
2465		printpolicy(policy);
2466
2467	ireq.i_val = IEEE80211_MACCMD_LIST;
2468	ireq.i_len = 0;
2469	if (ioctl(s, SIOCG80211, &ireq) < 0)
2470		err(1, "unable to get mac acl list size");
2471	if (ireq.i_len == 0) {		/* NB: no acls */
2472		if (!(verbose || c == '?'))
2473			printpolicy(policy);
2474		return;
2475	}
2476	len = ireq.i_len;
2477
2478	data = malloc(len);
2479	if (data == NULL)
2480		err(1, "out of memory for acl list");
2481
2482	ireq.i_data = data;
2483	if (ioctl(s, SIOCG80211, &ireq) < 0)
2484		err(1, "unable to get mac acl list");
2485	nacls = len / sizeof(*acllist);
2486	acllist = (struct ieee80211req_maclist *) data;
2487	for (i = 0; i < nacls; i++)
2488		printf("%c%s\n", c, ether_ntoa(
2489			(const struct ether_addr *) acllist[i].ml_macaddr));
2490	free(data);
2491}
2492
2493static
2494DECL_CMD_FUNC(set80211list, arg, d)
2495{
2496#define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
2497
2498	LINE_INIT('\t');
2499
2500	if (iseq(arg, "sta"))
2501		list_stations(s);
2502	else if (iseq(arg, "scan") || iseq(arg, "ap"))
2503		list_scan(s);
2504	else if (iseq(arg, "chan") || iseq(arg, "freq"))
2505		list_channels(s, 1);
2506	else if (iseq(arg, "active"))
2507		list_channels(s, 0);
2508	else if (iseq(arg, "keys"))
2509		list_keys(s);
2510	else if (iseq(arg, "caps"))
2511		list_capabilities(s);
2512	else if (iseq(arg, "wme"))
2513		list_wme(s);
2514	else if (iseq(arg, "mac"))
2515		list_mac(s);
2516	else if (iseq(arg, "txpow"))
2517		list_txpow(s);
2518	else
2519		errx(1, "Don't know how to list %s for %s", arg, name);
2520#undef iseq
2521}
2522
2523static enum ieee80211_opmode
2524get80211opmode(int s)
2525{
2526	struct ifmediareq ifmr;
2527
2528	(void) memset(&ifmr, 0, sizeof(ifmr));
2529	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
2530
2531	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
2532		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC)
2533			return IEEE80211_M_IBSS;	/* XXX ahdemo */
2534		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
2535			return IEEE80211_M_HOSTAP;
2536		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
2537			return IEEE80211_M_MONITOR;
2538	}
2539	return IEEE80211_M_STA;
2540}
2541
2542#if 0
2543static void
2544printcipher(int s, struct ieee80211req *ireq, int keylenop)
2545{
2546	switch (ireq->i_val) {
2547	case IEEE80211_CIPHER_WEP:
2548		ireq->i_type = keylenop;
2549		if (ioctl(s, SIOCG80211, ireq) != -1)
2550			printf("WEP-%s",
2551			    ireq->i_len <= 5 ? "40" :
2552			    ireq->i_len <= 13 ? "104" : "128");
2553		else
2554			printf("WEP");
2555		break;
2556	case IEEE80211_CIPHER_TKIP:
2557		printf("TKIP");
2558		break;
2559	case IEEE80211_CIPHER_AES_OCB:
2560		printf("AES-OCB");
2561		break;
2562	case IEEE80211_CIPHER_AES_CCM:
2563		printf("AES-CCM");
2564		break;
2565	case IEEE80211_CIPHER_CKIP:
2566		printf("CKIP");
2567		break;
2568	case IEEE80211_CIPHER_NONE:
2569		printf("NONE");
2570		break;
2571	default:
2572		printf("UNKNOWN (0x%x)", ireq->i_val);
2573		break;
2574	}
2575}
2576#endif
2577
2578static void
2579printkey(const struct ieee80211req_key *ik)
2580{
2581	static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
2582	int keylen = ik->ik_keylen;
2583	int printcontents;
2584
2585	printcontents = printkeys &&
2586		(memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
2587	if (printcontents)
2588		LINE_BREAK();
2589	switch (ik->ik_type) {
2590	case IEEE80211_CIPHER_WEP:
2591		/* compatibility */
2592		LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
2593		    keylen <= 5 ? "40-bit" :
2594		    keylen <= 13 ? "104-bit" : "128-bit");
2595		break;
2596	case IEEE80211_CIPHER_TKIP:
2597		if (keylen > 128/8)
2598			keylen -= 128/8;	/* ignore MIC for now */
2599		LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2600		break;
2601	case IEEE80211_CIPHER_AES_OCB:
2602		LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2603		break;
2604	case IEEE80211_CIPHER_AES_CCM:
2605		LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2606		break;
2607	case IEEE80211_CIPHER_CKIP:
2608		LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2609		break;
2610	case IEEE80211_CIPHER_NONE:
2611		LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2612		break;
2613	default:
2614		LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
2615			ik->ik_type, ik->ik_keyix+1, 8*keylen);
2616		break;
2617	}
2618	if (printcontents) {
2619		int i;
2620
2621		printf(" <");
2622		for (i = 0; i < keylen; i++)
2623			printf("%02x", ik->ik_keydata[i]);
2624		printf(">");
2625		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
2626		    (ik->ik_keyrsc != 0 || verbose))
2627			printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
2628		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
2629		    (ik->ik_keytsc != 0 || verbose))
2630			printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
2631		if (ik->ik_flags != 0 && verbose) {
2632			const char *sep = " ";
2633
2634			if (ik->ik_flags & IEEE80211_KEY_XMIT)
2635				printf("%stx", sep), sep = "+";
2636			if (ik->ik_flags & IEEE80211_KEY_RECV)
2637				printf("%srx", sep), sep = "+";
2638			if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
2639				printf("%sdef", sep), sep = "+";
2640		}
2641		LINE_BREAK();
2642	}
2643}
2644
2645static void
2646printrate(const char *tag, int v, int defrate, int defmcs)
2647{
2648	if (v == 11)
2649		LINE_CHECK("%s 5.5", tag);
2650	else if (v & 0x80) {
2651		if (v != defmcs)
2652			LINE_CHECK("%s %d", tag, v &~ 0x80);
2653	} else {
2654		if (v != defrate)
2655			LINE_CHECK("%s %d", tag, v/2);
2656	}
2657}
2658
2659static int
2660getssid(int s, int ix, void *data, size_t len, int *plen)
2661{
2662	struct ieee80211req ireq;
2663
2664	(void) memset(&ireq, 0, sizeof(ireq));
2665	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2666	ireq.i_type = IEEE80211_IOC_SSID;
2667	ireq.i_val = ix;
2668	ireq.i_data = data;
2669	ireq.i_len = len;
2670	if (ioctl(s, SIOCG80211, &ireq) < 0)
2671		return -1;
2672	*plen = ireq.i_len;
2673	return 0;
2674}
2675
2676static void
2677printrssi(const char *tag, int rssi)
2678{
2679	if (rssi & 1)
2680		LINE_CHECK("%s %u.5", tag, rssi/2);
2681	else
2682		LINE_CHECK("%s %u", tag, rssi/2);
2683}
2684
2685static void
2686ieee80211_status(int s)
2687{
2688	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
2689	enum ieee80211_opmode opmode = get80211opmode(s);
2690	int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode;
2691	uint8_t data[32];
2692	const struct ieee80211_channel *c;
2693
2694	if (getssid(s, -1, data, sizeof(data), &len) < 0) {
2695		/* If we can't get the SSID, this isn't an 802.11 device. */
2696		return;
2697	}
2698
2699	/*
2700	 * Invalidate cached state so printing status for multiple
2701	 * if's doesn't reuse the first interfaces' cached state.
2702	 */
2703	gotcurchan = 0;
2704	gothtconf = 0;
2705
2706	if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
2707		num = 0;
2708	printf("\tssid ");
2709	if (num > 1) {
2710		for (i = 0; i < num; i++) {
2711			if (getssid(s, i, data, sizeof(data), &len) >= 0 && len > 0) {
2712				printf(" %d:", i + 1);
2713				print_string(data, len);
2714			}
2715		}
2716	} else
2717		print_string(data, len);
2718
2719	c = getcurchan(s);
2720	if (c->ic_freq != IEEE80211_CHAN_ANY) {
2721		char buf[14];
2722		printf(" channel %d (%u Mhz%s)", c->ic_ieee, c->ic_freq,
2723			get_chaninfo(c, 1, buf, sizeof(buf)));
2724	} else if (verbose)
2725		printf(" channel UNDEF");
2726
2727	if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 &&
2728	    (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
2729		printf(" bssid %s", ether_ntoa((struct ether_addr *)data));
2730
2731	if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) {
2732		printf("\n\tstationname ");
2733		print_string(data, len);
2734	}
2735
2736	spacer = ' ';		/* force first break */
2737	LINE_BREAK();
2738
2739	wpa = 0;
2740	if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) {
2741		switch (val) {
2742		case IEEE80211_AUTH_NONE:
2743			LINE_CHECK("authmode NONE");
2744			break;
2745		case IEEE80211_AUTH_OPEN:
2746			LINE_CHECK("authmode OPEN");
2747			break;
2748		case IEEE80211_AUTH_SHARED:
2749			LINE_CHECK("authmode SHARED");
2750			break;
2751		case IEEE80211_AUTH_8021X:
2752			LINE_CHECK("authmode 802.1x");
2753			break;
2754		case IEEE80211_AUTH_WPA:
2755			if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0)
2756				wpa = 1;	/* default to WPA1 */
2757			switch (wpa) {
2758			case 2:
2759				LINE_CHECK("authmode WPA2/802.11i");
2760				break;
2761			case 3:
2762				LINE_CHECK("authmode WPA1+WPA2/802.11i");
2763				break;
2764			default:
2765				LINE_CHECK("authmode WPA");
2766				break;
2767			}
2768			break;
2769		case IEEE80211_AUTH_AUTO:
2770			LINE_CHECK("authmode AUTO");
2771			break;
2772		default:
2773			LINE_CHECK("authmode UNKNOWN (0x%x)", val);
2774			break;
2775		}
2776	}
2777
2778	if (wpa || verbose) {
2779		if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) {
2780			if (val)
2781				LINE_CHECK("countermeasures");
2782			else if (verbose)
2783				LINE_CHECK("-countermeasures");
2784		}
2785	}
2786
2787	if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 &&
2788	    wepmode != IEEE80211_WEP_NOSUP) {
2789		int firstkey;
2790
2791		switch (wepmode) {
2792		case IEEE80211_WEP_OFF:
2793			LINE_CHECK("privacy OFF");
2794			break;
2795		case IEEE80211_WEP_ON:
2796			LINE_CHECK("privacy ON");
2797			break;
2798		case IEEE80211_WEP_MIXED:
2799			LINE_CHECK("privacy MIXED");
2800			break;
2801		default:
2802			LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
2803			break;
2804		}
2805
2806		/*
2807		 * If we get here then we've got WEP support so we need
2808		 * to print WEP status.
2809		 */
2810
2811		if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) {
2812			warn("WEP support, but no tx key!");
2813			goto end;
2814		}
2815		if (val != -1)
2816			LINE_CHECK("deftxkey %d", val+1);
2817		else if (wepmode != IEEE80211_WEP_OFF || verbose)
2818			LINE_CHECK("deftxkey UNDEF");
2819
2820		if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) {
2821			warn("WEP support, but no NUMWEPKEYS support!");
2822			goto end;
2823		}
2824
2825		firstkey = 1;
2826		for (i = 0; i < num; i++) {
2827			struct ieee80211req_key ik;
2828
2829			memset(&ik, 0, sizeof(ik));
2830			ik.ik_keyix = i;
2831			if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) {
2832				warn("WEP support, but can get keys!");
2833				goto end;
2834			}
2835			if (ik.ik_keylen != 0) {
2836				if (verbose)
2837					LINE_BREAK();
2838				printkey(&ik);
2839				firstkey = 0;
2840			}
2841		}
2842end:
2843		;
2844	}
2845
2846	if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 &&
2847	    val != IEEE80211_POWERSAVE_NOSUP ) {
2848		if (val != IEEE80211_POWERSAVE_OFF || verbose) {
2849			switch (val) {
2850			case IEEE80211_POWERSAVE_OFF:
2851				LINE_CHECK("powersavemode OFF");
2852				break;
2853			case IEEE80211_POWERSAVE_CAM:
2854				LINE_CHECK("powersavemode CAM");
2855				break;
2856			case IEEE80211_POWERSAVE_PSP:
2857				LINE_CHECK("powersavemode PSP");
2858				break;
2859			case IEEE80211_POWERSAVE_PSP_CAM:
2860				LINE_CHECK("powersavemode PSP-CAM");
2861				break;
2862			}
2863			if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1)
2864				LINE_CHECK("powersavesleep %d", val);
2865		}
2866	}
2867
2868	if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) {
2869		if (val & 1)
2870			LINE_CHECK("txpower %d.5", val/2);
2871		else
2872			LINE_CHECK("txpower %d", val/2);
2873	}
2874	if (verbose) {
2875		if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1)
2876			LINE_CHECK("txpowmax %.1f", val/2.);
2877	}
2878
2879	if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) {
2880		if (val != IEEE80211_RTS_MAX || verbose)
2881			LINE_CHECK("rtsthreshold %d", val);
2882	}
2883
2884	if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) {
2885		if (val != IEEE80211_FRAG_MAX || verbose)
2886			LINE_CHECK("fragthreshold %d", val);
2887	}
2888	if (opmode == IEEE80211_M_STA || verbose) {
2889		if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) {
2890			if (val != IEEE80211_HWBMISS_MAX || verbose)
2891				LINE_CHECK("bmiss %d", val);
2892		}
2893	}
2894
2895	if (get80211val(s, IEEE80211_IOC_MCAST_RATE, &val) != -1)
2896		printrate("mcastrate", val, 2*1, 0/*XXX*/);
2897
2898	bgscaninterval = -1;
2899	(void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval);
2900
2901	if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) {
2902		if (val != bgscaninterval || verbose)
2903			LINE_CHECK("scanvalid %u", val);
2904	}
2905
2906	bgscan = 0;
2907	if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) {
2908		if (bgscan)
2909			LINE_CHECK("bgscan");
2910		else if (verbose)
2911			LINE_CHECK("-bgscan");
2912	}
2913	if (bgscan || verbose) {
2914		if (bgscaninterval != -1)
2915			LINE_CHECK("bgscanintvl %u", bgscaninterval);
2916		if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1)
2917			LINE_CHECK("bgscanidle %u", val);
2918		if (IEEE80211_IS_CHAN_A(c) || verbose) {
2919			if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11A, &val) != -1)
2920				printrssi("roam:rssi11a", val);
2921			if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11A, &val) != -1)
2922				printrate("roam:rate11a", val, -1, -1);
2923		}
2924		if (IEEE80211_IS_CHAN_B(c) || verbose) {
2925			if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11B, &val) != -1)
2926				printrssi("roam:rssi11b", val);
2927			if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11B, &val) != -1)
2928				printrate("roam:rate11b", val, -1, -1);
2929		}
2930		if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
2931			if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11G, &val) != -1)
2932				printrssi("roam:rssi11g", val);
2933			if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11G, &val) != -1)
2934				printrate("roam:rate11g", val, -1, -1);
2935		}
2936	}
2937
2938	if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
2939		if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) {
2940			if (val)
2941				LINE_CHECK("pureg");
2942			else if (verbose)
2943				LINE_CHECK("-pureg");
2944		}
2945		if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) {
2946			switch (val) {
2947			case IEEE80211_PROTMODE_OFF:
2948				LINE_CHECK("protmode OFF");
2949				break;
2950			case IEEE80211_PROTMODE_CTS:
2951				LINE_CHECK("protmode CTS");
2952				break;
2953			case IEEE80211_PROTMODE_RTSCTS:
2954				LINE_CHECK("protmode RTSCTS");
2955				break;
2956			default:
2957				LINE_CHECK("protmode UNKNOWN (0x%x)", val);
2958				break;
2959			}
2960		}
2961	}
2962
2963	if (IEEE80211_IS_CHAN_HT(c) || verbose) {
2964		gethtconf(s);
2965		switch (htconf & 3) {
2966		case 0:
2967		case 2:
2968			LINE_CHECK("-ht");
2969			break;
2970		case 1:
2971			LINE_CHECK("ht20");
2972			break;
2973		case 3:
2974			if (verbose)
2975				LINE_CHECK("ht");
2976			break;
2977		}
2978		if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) {
2979			if (!val)
2980				LINE_CHECK("-htcompat");
2981			else if (verbose)
2982				LINE_CHECK("htcompat");
2983		}
2984		if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) {
2985			switch (val) {
2986			case 0:
2987				LINE_CHECK("-ampdu");
2988				break;
2989			case 1:
2990				LINE_CHECK("ampdutx -ampdurx");
2991				break;
2992			case 2:
2993				LINE_CHECK("-ampdutx ampdurx");
2994				break;
2995			case 3:
2996				if (verbose)
2997					LINE_CHECK("ampdu");
2998				break;
2999			}
3000		}
3001		if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) {
3002			switch (val) {
3003			case IEEE80211_HTCAP_MAXRXAMPDU_8K:
3004				LINE_CHECK("ampdulimit 8k");
3005				break;
3006			case IEEE80211_HTCAP_MAXRXAMPDU_16K:
3007				LINE_CHECK("ampdulimit 16k");
3008				break;
3009			case IEEE80211_HTCAP_MAXRXAMPDU_32K:
3010				LINE_CHECK("ampdulimit 32k");
3011				break;
3012			case IEEE80211_HTCAP_MAXRXAMPDU_64K:
3013				LINE_CHECK("ampdulimit 64k");
3014				break;
3015			}
3016		}
3017		if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) {
3018			switch (val) {
3019			case IEEE80211_HTCAP_MPDUDENSITY_NA:
3020				if (verbose)
3021					LINE_CHECK("ampdudensity -");
3022				break;
3023			case IEEE80211_HTCAP_MPDUDENSITY_025:
3024				LINE_CHECK("ampdudensity .25");
3025				break;
3026			case IEEE80211_HTCAP_MPDUDENSITY_05:
3027				LINE_CHECK("ampdudensity .5");
3028				break;
3029			case IEEE80211_HTCAP_MPDUDENSITY_1:
3030				LINE_CHECK("ampdudensity 1");
3031				break;
3032			case IEEE80211_HTCAP_MPDUDENSITY_2:
3033				LINE_CHECK("ampdudensity 2");
3034				break;
3035			case IEEE80211_HTCAP_MPDUDENSITY_4:
3036				LINE_CHECK("ampdudensity 4");
3037				break;
3038			case IEEE80211_HTCAP_MPDUDENSITY_8:
3039				LINE_CHECK("ampdudensity 8");
3040				break;
3041			case IEEE80211_HTCAP_MPDUDENSITY_16:
3042				LINE_CHECK("ampdudensity 16");
3043				break;
3044			}
3045		}
3046		if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) {
3047			switch (val) {
3048			case 0:
3049				LINE_CHECK("-amsdu");
3050				break;
3051			case 1:
3052				LINE_CHECK("amsdutx -amsdurx");
3053				break;
3054			case 2:
3055				LINE_CHECK("-amsdutx amsdurx");
3056				break;
3057			case 3:
3058				if (verbose)
3059					LINE_CHECK("amsdu");
3060				break;
3061			}
3062		}
3063		/* XXX amsdu limit */
3064		/* XXX 20/40 */
3065		if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) {
3066			if (val)
3067				LINE_CHECK("shortgi");
3068			else if (verbose)
3069				LINE_CHECK("-shortgi");
3070		}
3071		if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) {
3072			if (val == IEEE80211_PROTMODE_OFF)
3073				LINE_CHECK("htprotmode OFF");
3074			else if (val != IEEE80211_PROTMODE_RTSCTS)
3075				LINE_CHECK("htprotmode UNKNOWN (0x%x)", val);
3076			else if (verbose)
3077				LINE_CHECK("htprotmode RTSCTS");
3078		}
3079		if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) {
3080			if (val)
3081				LINE_CHECK("puren");
3082			else if (verbose)
3083				LINE_CHECK("-puren");
3084		}
3085	}
3086
3087	if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) {
3088		if (wme)
3089			LINE_CHECK("wme");
3090		else if (verbose)
3091			LINE_CHECK("-wme");
3092	} else
3093		wme = 0;
3094
3095	if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) {
3096		if (val)
3097			LINE_CHECK("burst");
3098		else if (verbose)
3099			LINE_CHECK("-burst");
3100	}
3101
3102	if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) {
3103		if (val)
3104			LINE_CHECK("ff");
3105		else if (verbose)
3106			LINE_CHECK("-ff");
3107	}
3108	if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) {
3109		if (val)
3110			LINE_CHECK("dturbo");
3111		else if (verbose)
3112			LINE_CHECK("-dturbo");
3113	}
3114
3115	if (opmode == IEEE80211_M_HOSTAP) {
3116		if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) {
3117			if (val)
3118				LINE_CHECK("hidessid");
3119			else if (verbose)
3120				LINE_CHECK("-hidessid");
3121		}
3122		if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) {
3123			if (!val)
3124				LINE_CHECK("-apbridge");
3125			else if (verbose)
3126				LINE_CHECK("apbridge");
3127		}
3128		if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1)
3129			LINE_CHECK("dtimperiod %u", val);
3130
3131		if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) {
3132			if (!val)
3133				LINE_CHECK("-doth");
3134			else if (verbose)
3135				LINE_CHECK("doth");
3136		}
3137		if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) {
3138			if (!val)
3139				LINE_CHECK("-inact");
3140			else if (verbose)
3141				LINE_CHECK("inact");
3142		}
3143	} else {
3144		if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) {
3145			if (val != IEEE80211_ROAMING_AUTO || verbose) {
3146				switch (val) {
3147				case IEEE80211_ROAMING_DEVICE:
3148					LINE_CHECK("roaming DEVICE");
3149					break;
3150				case IEEE80211_ROAMING_AUTO:
3151					LINE_CHECK("roaming AUTO");
3152					break;
3153				case IEEE80211_ROAMING_MANUAL:
3154					LINE_CHECK("roaming MANUAL");
3155					break;
3156				default:
3157					LINE_CHECK("roaming UNKNOWN (0x%x)",
3158						val);
3159					break;
3160				}
3161			}
3162		}
3163	}
3164	if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) {
3165		/* XXX default define not visible */
3166		if (val != 100 || verbose)
3167			LINE_CHECK("bintval %u", val);
3168	}
3169
3170	if (wme && verbose) {
3171		LINE_BREAK();
3172		list_wme(s);
3173	}
3174	LINE_BREAK();
3175}
3176
3177static int
3178get80211(int s, int type, void *data, int len)
3179{
3180	struct ieee80211req ireq;
3181
3182	(void) memset(&ireq, 0, sizeof(ireq));
3183	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3184	ireq.i_type = type;
3185	ireq.i_data = data;
3186	ireq.i_len = len;
3187	return ioctl(s, SIOCG80211, &ireq);
3188}
3189
3190static int
3191get80211len(int s, int type, void *data, int len, int *plen)
3192{
3193	struct ieee80211req ireq;
3194
3195	(void) memset(&ireq, 0, sizeof(ireq));
3196	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3197	ireq.i_type = type;
3198	ireq.i_len = len;
3199	ireq.i_data = data;
3200	if (ioctl(s, SIOCG80211, &ireq) < 0)
3201		return -1;
3202	*plen = ireq.i_len;
3203	return 0;
3204}
3205
3206static int
3207get80211val(int s, int type, int *val)
3208{
3209	struct ieee80211req ireq;
3210
3211	(void) memset(&ireq, 0, sizeof(ireq));
3212	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3213	ireq.i_type = type;
3214	if (ioctl(s, SIOCG80211, &ireq) < 0)
3215		return -1;
3216	*val = ireq.i_val;
3217	return 0;
3218}
3219
3220static void
3221set80211(int s, int type, int val, int len, void *data)
3222{
3223	struct ieee80211req	ireq;
3224
3225	(void) memset(&ireq, 0, sizeof(ireq));
3226	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3227	ireq.i_type = type;
3228	ireq.i_val = val;
3229	ireq.i_len = len;
3230	ireq.i_data = data;
3231	if (ioctl(s, SIOCS80211, &ireq) < 0)
3232		err(1, "SIOCS80211");
3233}
3234
3235static const char *
3236get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
3237{
3238	int len;
3239	int hexstr;
3240	u_int8_t *p;
3241
3242	len = *lenp;
3243	p = buf;
3244	hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
3245	if (hexstr)
3246		val += 2;
3247	for (;;) {
3248		if (*val == '\0')
3249			break;
3250		if (sep != NULL && strchr(sep, *val) != NULL) {
3251			val++;
3252			break;
3253		}
3254		if (hexstr) {
3255			if (!isxdigit((u_char)val[0])) {
3256				warnx("bad hexadecimal digits");
3257				return NULL;
3258			}
3259			if (!isxdigit((u_char)val[1])) {
3260				warnx("odd count hexadecimal digits");
3261				return NULL;
3262			}
3263		}
3264		if (p >= buf + len) {
3265			if (hexstr)
3266				warnx("hexadecimal digits too long");
3267			else
3268				warnx("string too long");
3269			return NULL;
3270		}
3271		if (hexstr) {
3272#define	tohex(x)	(isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
3273			*p++ = (tohex((u_char)val[0]) << 4) |
3274			    tohex((u_char)val[1]);
3275#undef tohex
3276			val += 2;
3277		} else
3278			*p++ = *val++;
3279	}
3280	len = p - buf;
3281	/* The string "-" is treated as the empty string. */
3282	if (!hexstr && len == 1 && buf[0] == '-') {
3283		len = 0;
3284		memset(buf, 0, *lenp);
3285	} else if (len < *lenp)
3286		memset(p, 0, *lenp - len);
3287	*lenp = len;
3288	return val;
3289}
3290
3291static void
3292print_string(const u_int8_t *buf, int len)
3293{
3294	int i;
3295	int hasspc;
3296
3297	i = 0;
3298	hasspc = 0;
3299	for (; i < len; i++) {
3300		if (!isprint(buf[i]) && buf[i] != '\0')
3301			break;
3302		if (isspace(buf[i]))
3303			hasspc++;
3304	}
3305	if (i == len) {
3306		if (hasspc || len == 0 || buf[0] == '\0')
3307			printf("\"%.*s\"", len, buf);
3308		else
3309			printf("%.*s", len, buf);
3310	} else {
3311		printf("0x");
3312		for (i = 0; i < len; i++)
3313			printf("%02x", buf[i]);
3314	}
3315}
3316
3317static struct cmd ieee80211_cmds[] = {
3318	DEF_CMD_ARG("ssid",		set80211ssid),
3319	DEF_CMD_ARG("nwid",		set80211ssid),
3320	DEF_CMD_ARG("stationname",	set80211stationname),
3321	DEF_CMD_ARG("station",		set80211stationname),	/* BSD/OS */
3322	DEF_CMD_ARG("channel",		set80211channel),
3323	DEF_CMD_ARG("authmode",		set80211authmode),
3324	DEF_CMD_ARG("powersavemode",	set80211powersavemode),
3325	DEF_CMD("powersave",	1,	set80211powersave),
3326	DEF_CMD("-powersave",	0,	set80211powersave),
3327	DEF_CMD_ARG("powersavesleep", 	set80211powersavesleep),
3328	DEF_CMD_ARG("wepmode",		set80211wepmode),
3329	DEF_CMD("wep",		1,	set80211wep),
3330	DEF_CMD("-wep",		0,	set80211wep),
3331	DEF_CMD_ARG("deftxkey",		set80211weptxkey),
3332	DEF_CMD_ARG("weptxkey",		set80211weptxkey),
3333	DEF_CMD_ARG("wepkey",		set80211wepkey),
3334	DEF_CMD_ARG("nwkey",		set80211nwkey),		/* NetBSD */
3335	DEF_CMD("-nwkey",	0,	set80211wep),		/* NetBSD */
3336	DEF_CMD_ARG("rtsthreshold",	set80211rtsthreshold),
3337	DEF_CMD_ARG("protmode",		set80211protmode),
3338	DEF_CMD_ARG("txpower",		set80211txpower),
3339	DEF_CMD_ARG("roaming",		set80211roaming),
3340	DEF_CMD("wme",		1,	set80211wme),
3341	DEF_CMD("-wme",		0,	set80211wme),
3342	DEF_CMD("hidessid",	1,	set80211hidessid),
3343	DEF_CMD("-hidessid",	0,	set80211hidessid),
3344	DEF_CMD("apbridge",	1,	set80211apbridge),
3345	DEF_CMD("-apbridge",	0,	set80211apbridge),
3346	DEF_CMD_ARG("chanlist",		set80211chanlist),
3347	DEF_CMD_ARG("bssid",		set80211bssid),
3348	DEF_CMD_ARG("ap",		set80211bssid),
3349	DEF_CMD("scan",	0,		set80211scan),
3350	DEF_CMD_ARG("list",		set80211list),
3351	DEF_CMD_ARG2("cwmin",		set80211cwmin),
3352	DEF_CMD_ARG2("cwmax",		set80211cwmax),
3353	DEF_CMD_ARG2("aifs",		set80211aifs),
3354	DEF_CMD_ARG2("txoplimit",	set80211txoplimit),
3355	DEF_CMD_ARG("acm",		set80211acm),
3356	DEF_CMD_ARG("-acm",		set80211noacm),
3357	DEF_CMD_ARG("ack",		set80211ackpolicy),
3358	DEF_CMD_ARG("-ack",		set80211noackpolicy),
3359	DEF_CMD_ARG2("bss:cwmin",	set80211bsscwmin),
3360	DEF_CMD_ARG2("bss:cwmax",	set80211bsscwmax),
3361	DEF_CMD_ARG2("bss:aifs",	set80211bssaifs),
3362	DEF_CMD_ARG2("bss:txoplimit",	set80211bsstxoplimit),
3363	DEF_CMD_ARG("dtimperiod",	set80211dtimperiod),
3364	DEF_CMD_ARG("bintval",		set80211bintval),
3365	DEF_CMD("mac:open",	IEEE80211_MACCMD_POLICY_OPEN,	set80211maccmd),
3366	DEF_CMD("mac:allow",	IEEE80211_MACCMD_POLICY_ALLOW,	set80211maccmd),
3367	DEF_CMD("mac:deny",	IEEE80211_MACCMD_POLICY_DENY,	set80211maccmd),
3368	DEF_CMD("mac:flush",	IEEE80211_MACCMD_FLUSH,		set80211maccmd),
3369	DEF_CMD("mac:detach",	IEEE80211_MACCMD_DETACH,	set80211maccmd),
3370	DEF_CMD_ARG("mac:add",		set80211addmac),
3371	DEF_CMD_ARG("mac:del",		set80211delmac),
3372	DEF_CMD_ARG("mac:kick",		set80211kickmac),
3373	DEF_CMD("pureg",	1,	set80211pureg),
3374	DEF_CMD("-pureg",	0,	set80211pureg),
3375	DEF_CMD("ff",		1,	set80211fastframes),
3376	DEF_CMD("-ff",		0,	set80211fastframes),
3377	DEF_CMD("dturbo",	1,	set80211dturbo),
3378	DEF_CMD("-dturbo",	0,	set80211dturbo),
3379	DEF_CMD("bgscan",	1,	set80211bgscan),
3380	DEF_CMD("-bgscan",	0,	set80211bgscan),
3381	DEF_CMD_ARG("bgscanidle",	set80211bgscanidle),
3382	DEF_CMD_ARG("bgscanintvl",	set80211bgscanintvl),
3383	DEF_CMD_ARG("scanvalid",	set80211scanvalid),
3384	DEF_CMD_ARG("roam:rssi11a",	set80211roamrssi11a),
3385	DEF_CMD_ARG("roam:rssi11b",	set80211roamrssi11b),
3386	DEF_CMD_ARG("roam:rssi11g",	set80211roamrssi11g),
3387	DEF_CMD_ARG("roam:rate11a",	set80211roamrate11a),
3388	DEF_CMD_ARG("roam:rate11b",	set80211roamrate11b),
3389	DEF_CMD_ARG("roam:rate11g",	set80211roamrate11g),
3390	DEF_CMD_ARG("mcastrate",	set80211mcastrate),
3391	DEF_CMD_ARG("fragthreshold",	set80211fragthreshold),
3392	DEF_CMD("burst",	1,	set80211burst),
3393	DEF_CMD("-burst",	0,	set80211burst),
3394	DEF_CMD_ARG("bmiss",		set80211bmissthreshold),
3395	DEF_CMD_ARG("bmissthreshold",	set80211bmissthreshold),
3396	DEF_CMD("shortgi",	1,	set80211shortgi),
3397	DEF_CMD("-shortgi",	0,	set80211shortgi),
3398	DEF_CMD("ampdurx",	2,	set80211ampdu),
3399	DEF_CMD("-ampdurx",	-2,	set80211ampdu),
3400	DEF_CMD("ampdutx",	1,	set80211ampdu),
3401	DEF_CMD("-ampdutx",	-1,	set80211ampdu),
3402	DEF_CMD("ampdu",	3,	set80211ampdu),		/* NB: tx+rx */
3403	DEF_CMD("-ampdu",	-3,	set80211ampdu),
3404	DEF_CMD_ARG("ampdulimit",	set80211ampdulimit),
3405	DEF_CMD_ARG("ampdudensity",	set80211ampdudensity),
3406	DEF_CMD("amsdurx",	2,	set80211amsdu),
3407	DEF_CMD("-amsdurx",	-2,	set80211amsdu),
3408	DEF_CMD("amsdutx",	1,	set80211amsdu),
3409	DEF_CMD("-amsdutx",	-1,	set80211amsdu),
3410	DEF_CMD("amsdu",	3,	set80211amsdu),		/* NB: tx+rx */
3411	DEF_CMD("-amsdu",	-3,	set80211amsdu),
3412	DEF_CMD_ARG("amsdulimit",	set80211amsdulimit),
3413	DEF_CMD("puren",	1,	set80211puren),
3414	DEF_CMD("-puren",	0,	set80211puren),
3415	DEF_CMD("doth",		1,	set80211doth),
3416	DEF_CMD("-doth",	0,	set80211doth),
3417	DEF_CMD("htcompat",	1,	set80211htcompat),
3418	DEF_CMD("-htcompat",	0,	set80211htcompat),
3419	DEF_CMD("inact",	1,	set80211inact),
3420	DEF_CMD("-inact",	0,	set80211inact),
3421	DEF_CMD_ARG("htprotmode",	set80211htprotmode),
3422	DEF_CMD("ht20",		1,	set80211htconf),
3423	DEF_CMD("-ht20",	0,	set80211htconf),
3424	DEF_CMD("ht40",		3,	set80211htconf),	/* NB: 20+40 */
3425	DEF_CMD("-ht40",	0,	set80211htconf),
3426	DEF_CMD("ht",		3,	set80211htconf),	/* NB: 20+40 */
3427	DEF_CMD("-ht",		0,	set80211htconf),
3428};
3429static struct afswtch af_ieee80211 = {
3430	.af_name	= "af_ieee80211",
3431	.af_af		= AF_UNSPEC,
3432	.af_other_status = ieee80211_status,
3433};
3434
3435static __constructor void
3436ieee80211_ctor(void)
3437{
3438#define	N(a)	(sizeof(a) / sizeof(a[0]))
3439	int i;
3440
3441	for (i = 0; i < N(ieee80211_cmds);  i++)
3442		cmd_register(&ieee80211_cmds[i]);
3443	af_register(&af_ieee80211);
3444#undef N
3445}
3446