ifieee80211.c revision 175952
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 175952 2008-02-03 18:21:13Z 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(int txrate, int chanflags)
2060{
2061	if (txrate & 0x80) {
2062		txrate = htrates[txrate & 0xf];
2063		/* NB: could bump this more based on short gi */
2064		return chanflags & IEEE80211_CHAN_HT40 ? txrate : txrate / 2;
2065	} else
2066		return (txrate & IEEE80211_RATE_VAL) / 2;
2067}
2068
2069static void
2070list_stations(int s)
2071{
2072	union {
2073		struct ieee80211req_sta_req req;
2074		uint8_t buf[24*1024];
2075	} u;
2076	enum ieee80211_opmode opmode = get80211opmode(s);
2077	const uint8_t *cp;
2078	int len;
2079
2080	/* broadcast address =>'s get all stations */
2081	(void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
2082	if (opmode == IEEE80211_M_STA) {
2083		/*
2084		 * Get information about the associated AP.
2085		 */
2086		(void) get80211(s, IEEE80211_IOC_BSSID,
2087		    u.req.is_u.macaddr, IEEE80211_ADDR_LEN);
2088	}
2089	if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0)
2090		errx(1, "unable to get station information");
2091	if (len < sizeof(struct ieee80211req_sta_info))
2092		return;
2093
2094	getchaninfo(s);
2095
2096	printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %4s\n"
2097		, "ADDR"
2098		, "AID"
2099		, "CHAN"
2100		, "RATE"
2101		, "RSSI"
2102		, "IDLE"
2103		, "TXSEQ"
2104		, "RXSEQ"
2105		, "CAPS"
2106		, "FLAG"
2107	);
2108	cp = (const uint8_t *) u.req.info;
2109	do {
2110		const struct ieee80211req_sta_info *si;
2111
2112		si = (const struct ieee80211req_sta_info *) cp;
2113		if (si->isi_len < sizeof(*si))
2114			break;
2115		printf("%s %4u %4d %3dM %3.1f %4d %6d %6d %-4.4s %-4.4s"
2116			, ether_ntoa((const struct ether_addr*) si->isi_macaddr)
2117			, IEEE80211_AID(si->isi_associd)
2118			, ieee80211_mhz2ieee(si->isi_freq, si->isi_flags)
2119			, gettxrate(si->isi_txrate, si->isi_flags)
2120			, si->isi_rssi/2.
2121			, si->isi_inact
2122			, gettxseq(si)
2123			, getrxseq(si)
2124			, getcaps(si->isi_capinfo)
2125			, getflags(si->isi_state)
2126		);
2127		printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
2128		printf("\n");
2129		cp += si->isi_len, len -= si->isi_len;
2130	} while (len >= sizeof(struct ieee80211req_sta_info));
2131}
2132
2133static const char *
2134get_chaninfo(const struct ieee80211_channel *c, int precise,
2135	char buf[], size_t bsize)
2136{
2137	buf[0] = '\0';
2138	if (IEEE80211_IS_CHAN_FHSS(c))
2139		strlcat(buf, " FHSS", bsize);
2140	if (IEEE80211_IS_CHAN_A(c)) {
2141		if (IEEE80211_IS_CHAN_HALF(c))
2142			strlcat(buf, " 11a/10Mhz", bsize);
2143		else if (IEEE80211_IS_CHAN_QUARTER(c))
2144			strlcat(buf, " 11a/5Mhz", bsize);
2145		else
2146			strlcat(buf, " 11a", bsize);
2147	}
2148	if (IEEE80211_IS_CHAN_ANYG(c)) {
2149		if (IEEE80211_IS_CHAN_HALF(c))
2150			strlcat(buf, " 11g/10Mhz", bsize);
2151		else if (IEEE80211_IS_CHAN_QUARTER(c))
2152			strlcat(buf, " 11g/5Mhz", bsize);
2153		else
2154			strlcat(buf, " 11g", bsize);
2155	} else if (IEEE80211_IS_CHAN_B(c))
2156		strlcat(buf, " 11b", bsize);
2157	if (IEEE80211_IS_CHAN_TURBO(c))
2158		strlcat(buf, " Turbo", bsize);
2159	if (precise) {
2160		if (IEEE80211_IS_CHAN_HT20(c))
2161			strlcat(buf, " ht/20", bsize);
2162		else if (IEEE80211_IS_CHAN_HT40D(c))
2163			strlcat(buf, " ht/40-", bsize);
2164		else if (IEEE80211_IS_CHAN_HT40U(c))
2165			strlcat(buf, " ht/40+", bsize);
2166	} else {
2167		if (IEEE80211_IS_CHAN_HT(c))
2168			strlcat(buf, " ht", bsize);
2169	}
2170	return buf;
2171}
2172
2173static void
2174print_chaninfo(const struct ieee80211_channel *c, int verb)
2175{
2176	char buf[14];
2177
2178	printf("Channel %3u : %u%c Mhz%-14.14s",
2179		ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
2180		IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
2181		get_chaninfo(c, verb, buf, sizeof(buf)));
2182}
2183
2184static void
2185print_channels(int s, const struct ieee80211req_chaninfo *chans,
2186	int allchans, int verb)
2187{
2188	struct ieee80211req_chaninfo achans;
2189	uint8_t reported[IEEE80211_CHAN_BYTES];
2190	const struct ieee80211_channel *c;
2191	int i, half;
2192
2193	memset(&achans, 0, sizeof(achans));
2194	memset(reported, 0, sizeof(reported));
2195	if (!allchans) {
2196		struct ieee80211req_chanlist active;
2197
2198		if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0)
2199			errx(1, "unable to get active channel list");
2200		memset(&achans, 0, sizeof(achans));
2201		for (i = 0; i < chans->ic_nchans; i++) {
2202			c = &chans->ic_chans[i];
2203			if (!isset(active.ic_channels, c->ic_ieee))
2204				continue;
2205			/*
2206			 * Suppress compatible duplicates unless
2207			 * verbose.  The kernel gives us it's
2208			 * complete channel list which has separate
2209			 * entries for 11g/11b and 11a/turbo.
2210			 */
2211			if (isset(reported, c->ic_ieee) && !verb) {
2212				/* XXX we assume duplicates are adjacent */
2213				achans.ic_chans[achans.ic_nchans-1] = *c;
2214			} else {
2215				achans.ic_chans[achans.ic_nchans++] = *c;
2216				setbit(reported, c->ic_ieee);
2217			}
2218		}
2219	} else {
2220		for (i = 0; i < chans->ic_nchans; i++) {
2221			c = &chans->ic_chans[i];
2222			/* suppress duplicates as above */
2223			if (isset(reported, c->ic_ieee) && !verb) {
2224				/* XXX we assume duplicates are adjacent */
2225				achans.ic_chans[achans.ic_nchans-1] = *c;
2226			} else {
2227				achans.ic_chans[achans.ic_nchans++] = *c;
2228				setbit(reported, c->ic_ieee);
2229			}
2230		}
2231	}
2232	half = achans.ic_nchans / 2;
2233	if (achans.ic_nchans % 2)
2234		half++;
2235
2236	for (i = 0; i < achans.ic_nchans / 2; i++) {
2237		print_chaninfo(&achans.ic_chans[i], verb);
2238		print_chaninfo(&achans.ic_chans[half+i], verb);
2239		printf("\n");
2240	}
2241	if (achans.ic_nchans % 2) {
2242		print_chaninfo(&achans.ic_chans[i], verb);
2243		printf("\n");
2244	}
2245}
2246
2247static void
2248list_channels(int s, int allchans)
2249{
2250	getchaninfo(s);
2251	print_channels(s, &chaninfo, allchans, verbose);
2252}
2253
2254static void
2255print_txpow(const struct ieee80211_channel *c)
2256{
2257	printf("Channel %3u : %u Mhz %3.1f reg %2d  ",
2258	    c->ic_ieee, c->ic_freq,
2259	    c->ic_maxpower/2., c->ic_maxregpower);
2260}
2261
2262static void
2263print_txpow_verbose(const struct ieee80211_channel *c)
2264{
2265	print_chaninfo(c, 1);
2266	printf("min %4.1f dBm  max %3.1f dBm  reg %2d dBm",
2267	    c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
2268	/* indicate where regulatory cap limits power use */
2269	if (c->ic_maxpower > 2*c->ic_maxregpower)
2270		printf(" <");
2271}
2272
2273static void
2274list_txpow(int s)
2275{
2276	struct ieee80211req_chaninfo achans;
2277	uint8_t reported[IEEE80211_CHAN_BYTES];
2278	struct ieee80211_channel *c, *prev;
2279	int i, half;
2280
2281	getchaninfo(s);
2282	memset(&achans, 0, sizeof(achans));
2283	memset(reported, 0, sizeof(reported));
2284	for (i = 0; i < chaninfo.ic_nchans; i++) {
2285		c = &chaninfo.ic_chans[i];
2286		/* suppress duplicates as above */
2287		if (isset(reported, c->ic_ieee) && !verbose) {
2288			/* XXX we assume duplicates are adjacent */
2289			prev = &achans.ic_chans[achans.ic_nchans-1];
2290			/* display highest power on channel */
2291			if (c->ic_maxpower > prev->ic_maxpower)
2292				*prev = *c;
2293		} else {
2294			achans.ic_chans[achans.ic_nchans++] = *c;
2295			setbit(reported, c->ic_ieee);
2296		}
2297	}
2298	if (!verbose) {
2299		half = achans.ic_nchans / 2;
2300		if (achans.ic_nchans % 2)
2301			half++;
2302
2303		for (i = 0; i < achans.ic_nchans / 2; i++) {
2304			print_txpow(&achans.ic_chans[i]);
2305			print_txpow(&achans.ic_chans[half+i]);
2306			printf("\n");
2307		}
2308		if (achans.ic_nchans % 2) {
2309			print_txpow(&achans.ic_chans[i]);
2310			printf("\n");
2311		}
2312	} else {
2313		for (i = 0; i < achans.ic_nchans; i++) {
2314			print_txpow_verbose(&achans.ic_chans[i]);
2315			printf("\n");
2316		}
2317	}
2318}
2319
2320static void
2321list_keys(int s)
2322{
2323}
2324
2325#define	IEEE80211_C_BITS \
2326"\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\7FF\10TURBOP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \
2327"\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \
2328"\31WPA2\32BURST\33WME\34WDS\36BGSCAN\37TXFRAG"
2329
2330static void
2331list_capabilities(int s)
2332{
2333	struct ieee80211req ireq;
2334	u_int32_t caps;
2335
2336	(void) memset(&ireq, 0, sizeof(ireq));
2337	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2338	ireq.i_type = IEEE80211_IOC_DRIVER_CAPS;
2339	if (ioctl(s, SIOCG80211, &ireq) < 0)
2340		errx(1, "unable to get driver capabilities");
2341	caps = (((u_int16_t) ireq.i_val) << 16) | ((u_int16_t) ireq.i_len);
2342	printb(name, caps, IEEE80211_C_BITS);
2343	putchar('\n');
2344}
2345
2346static int
2347get80211wme(int s, int param, int ac, int *val)
2348{
2349	struct ieee80211req ireq;
2350
2351	(void) memset(&ireq, 0, sizeof(ireq));
2352	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2353	ireq.i_type = param;
2354	ireq.i_len = ac;
2355	if (ioctl(s, SIOCG80211, &ireq) < 0) {
2356		warn("cannot get WME parameter %d, ac %d%s",
2357		    param, ac & IEEE80211_WMEPARAM_VAL,
2358		    ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : "");
2359		return -1;
2360	}
2361	*val = ireq.i_val;
2362	return 0;
2363}
2364
2365static void
2366list_wme(int s)
2367{
2368	static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
2369	int ac, val;
2370
2371	for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
2372again:
2373		if (ac & IEEE80211_WMEPARAM_BSS)
2374			printf("\t%s", "     ");
2375		else
2376			printf("\t%s", acnames[ac]);
2377
2378		/* show WME BSS parameters */
2379		if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1)
2380			printf(" cwmin %2u", val);
2381		if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1)
2382			printf(" cwmax %2u", val);
2383		if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1)
2384			printf(" aifs %2u", val);
2385		if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1)
2386			printf(" txopLimit %3u", val);
2387		if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) {
2388			if (val)
2389				printf(" acm");
2390			else if (verbose)
2391				printf(" -acm");
2392		}
2393		/* !BSS only */
2394		if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
2395			if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) {
2396				if (!val)
2397					printf(" -ack");
2398				else if (verbose)
2399					printf(" ack");
2400			}
2401		}
2402		printf("\n");
2403		if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
2404			ac |= IEEE80211_WMEPARAM_BSS;
2405			goto again;
2406		} else
2407			ac &= ~IEEE80211_WMEPARAM_BSS;
2408	}
2409}
2410
2411static void
2412printpolicy(int policy)
2413{
2414	switch (policy) {
2415	case IEEE80211_MACCMD_POLICY_OPEN:
2416		printf("policy: open\n");
2417		break;
2418	case IEEE80211_MACCMD_POLICY_ALLOW:
2419		printf("policy: allow\n");
2420		break;
2421	case IEEE80211_MACCMD_POLICY_DENY:
2422		printf("policy: deny\n");
2423		break;
2424	default:
2425		printf("policy: unknown (%u)\n", policy);
2426		break;
2427	}
2428}
2429
2430static void
2431list_mac(int s)
2432{
2433	struct ieee80211req ireq;
2434	struct ieee80211req_maclist *acllist;
2435	int i, nacls, policy, len;
2436	uint8_t *data;
2437	char c;
2438
2439	(void) memset(&ireq, 0, sizeof(ireq));
2440	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
2441	ireq.i_type = IEEE80211_IOC_MACCMD;
2442	ireq.i_val = IEEE80211_MACCMD_POLICY;
2443	if (ioctl(s, SIOCG80211, &ireq) < 0) {
2444		if (errno == EINVAL) {
2445			printf("No acl policy loaded\n");
2446			return;
2447		}
2448		err(1, "unable to get mac policy");
2449	}
2450	policy = ireq.i_val;
2451	if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
2452		c = '*';
2453	} else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
2454		c = '+';
2455	} else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
2456		c = '-';
2457	} else {
2458		printf("policy: unknown (%u)\n", policy);
2459		c = '?';
2460	}
2461	if (verbose || c == '?')
2462		printpolicy(policy);
2463
2464	ireq.i_val = IEEE80211_MACCMD_LIST;
2465	ireq.i_len = 0;
2466	if (ioctl(s, SIOCG80211, &ireq) < 0)
2467		err(1, "unable to get mac acl list size");
2468	if (ireq.i_len == 0) {		/* NB: no acls */
2469		if (!(verbose || c == '?'))
2470			printpolicy(policy);
2471		return;
2472	}
2473	len = ireq.i_len;
2474
2475	data = malloc(len);
2476	if (data == NULL)
2477		err(1, "out of memory for acl list");
2478
2479	ireq.i_data = data;
2480	if (ioctl(s, SIOCG80211, &ireq) < 0)
2481		err(1, "unable to get mac acl list");
2482	nacls = len / sizeof(*acllist);
2483	acllist = (struct ieee80211req_maclist *) data;
2484	for (i = 0; i < nacls; i++)
2485		printf("%c%s\n", c, ether_ntoa(
2486			(const struct ether_addr *) acllist[i].ml_macaddr));
2487	free(data);
2488}
2489
2490static
2491DECL_CMD_FUNC(set80211list, arg, d)
2492{
2493#define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
2494
2495	LINE_INIT('\t');
2496
2497	if (iseq(arg, "sta"))
2498		list_stations(s);
2499	else if (iseq(arg, "scan") || iseq(arg, "ap"))
2500		list_scan(s);
2501	else if (iseq(arg, "chan") || iseq(arg, "freq"))
2502		list_channels(s, 1);
2503	else if (iseq(arg, "active"))
2504		list_channels(s, 0);
2505	else if (iseq(arg, "keys"))
2506		list_keys(s);
2507	else if (iseq(arg, "caps"))
2508		list_capabilities(s);
2509	else if (iseq(arg, "wme"))
2510		list_wme(s);
2511	else if (iseq(arg, "mac"))
2512		list_mac(s);
2513	else if (iseq(arg, "txpow"))
2514		list_txpow(s);
2515	else
2516		errx(1, "Don't know how to list %s for %s", arg, name);
2517#undef iseq
2518}
2519
2520static enum ieee80211_opmode
2521get80211opmode(int s)
2522{
2523	struct ifmediareq ifmr;
2524
2525	(void) memset(&ifmr, 0, sizeof(ifmr));
2526	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
2527
2528	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
2529		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC)
2530			return IEEE80211_M_IBSS;	/* XXX ahdemo */
2531		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
2532			return IEEE80211_M_HOSTAP;
2533		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
2534			return IEEE80211_M_MONITOR;
2535	}
2536	return IEEE80211_M_STA;
2537}
2538
2539#if 0
2540static void
2541printcipher(int s, struct ieee80211req *ireq, int keylenop)
2542{
2543	switch (ireq->i_val) {
2544	case IEEE80211_CIPHER_WEP:
2545		ireq->i_type = keylenop;
2546		if (ioctl(s, SIOCG80211, ireq) != -1)
2547			printf("WEP-%s",
2548			    ireq->i_len <= 5 ? "40" :
2549			    ireq->i_len <= 13 ? "104" : "128");
2550		else
2551			printf("WEP");
2552		break;
2553	case IEEE80211_CIPHER_TKIP:
2554		printf("TKIP");
2555		break;
2556	case IEEE80211_CIPHER_AES_OCB:
2557		printf("AES-OCB");
2558		break;
2559	case IEEE80211_CIPHER_AES_CCM:
2560		printf("AES-CCM");
2561		break;
2562	case IEEE80211_CIPHER_CKIP:
2563		printf("CKIP");
2564		break;
2565	case IEEE80211_CIPHER_NONE:
2566		printf("NONE");
2567		break;
2568	default:
2569		printf("UNKNOWN (0x%x)", ireq->i_val);
2570		break;
2571	}
2572}
2573#endif
2574
2575static void
2576printkey(const struct ieee80211req_key *ik)
2577{
2578	static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
2579	int keylen = ik->ik_keylen;
2580	int printcontents;
2581
2582	printcontents = printkeys &&
2583		(memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
2584	if (printcontents)
2585		LINE_BREAK();
2586	switch (ik->ik_type) {
2587	case IEEE80211_CIPHER_WEP:
2588		/* compatibility */
2589		LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
2590		    keylen <= 5 ? "40-bit" :
2591		    keylen <= 13 ? "104-bit" : "128-bit");
2592		break;
2593	case IEEE80211_CIPHER_TKIP:
2594		if (keylen > 128/8)
2595			keylen -= 128/8;	/* ignore MIC for now */
2596		LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2597		break;
2598	case IEEE80211_CIPHER_AES_OCB:
2599		LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2600		break;
2601	case IEEE80211_CIPHER_AES_CCM:
2602		LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2603		break;
2604	case IEEE80211_CIPHER_CKIP:
2605		LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2606		break;
2607	case IEEE80211_CIPHER_NONE:
2608		LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2609		break;
2610	default:
2611		LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
2612			ik->ik_type, ik->ik_keyix+1, 8*keylen);
2613		break;
2614	}
2615	if (printcontents) {
2616		int i;
2617
2618		printf(" <");
2619		for (i = 0; i < keylen; i++)
2620			printf("%02x", ik->ik_keydata[i]);
2621		printf(">");
2622		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
2623		    (ik->ik_keyrsc != 0 || verbose))
2624			printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
2625		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
2626		    (ik->ik_keytsc != 0 || verbose))
2627			printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
2628		if (ik->ik_flags != 0 && verbose) {
2629			const char *sep = " ";
2630
2631			if (ik->ik_flags & IEEE80211_KEY_XMIT)
2632				printf("%stx", sep), sep = "+";
2633			if (ik->ik_flags & IEEE80211_KEY_RECV)
2634				printf("%srx", sep), sep = "+";
2635			if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
2636				printf("%sdef", sep), sep = "+";
2637		}
2638		LINE_BREAK();
2639	}
2640}
2641
2642static void
2643printrate(const char *tag, int v, int defrate, int defmcs)
2644{
2645	if (v == 11)
2646		LINE_CHECK("%s 5.5", tag);
2647	else if (v & 0x80) {
2648		if (v != defmcs)
2649			LINE_CHECK("%s %d", tag, v &~ 0x80);
2650	} else {
2651		if (v != defrate)
2652			LINE_CHECK("%s %d", tag, v/2);
2653	}
2654}
2655
2656static int
2657getssid(int s, int ix, void *data, size_t len, int *plen)
2658{
2659	struct ieee80211req ireq;
2660
2661	(void) memset(&ireq, 0, sizeof(ireq));
2662	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2663	ireq.i_type = IEEE80211_IOC_SSID;
2664	ireq.i_val = ix;
2665	ireq.i_data = data;
2666	ireq.i_len = len;
2667	if (ioctl(s, SIOCG80211, &ireq) < 0)
2668		return -1;
2669	*plen = ireq.i_len;
2670	return 0;
2671}
2672
2673static void
2674printrssi(const char *tag, int rssi)
2675{
2676	if (rssi & 1)
2677		LINE_CHECK("%s %u.5", tag, rssi/2);
2678	else
2679		LINE_CHECK("%s %u", tag, rssi/2);
2680}
2681
2682static void
2683ieee80211_status(int s)
2684{
2685	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
2686	enum ieee80211_opmode opmode = get80211opmode(s);
2687	int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode;
2688	uint8_t data[32];
2689	const struct ieee80211_channel *c;
2690
2691	if (getssid(s, -1, data, sizeof(data), &len) < 0) {
2692		/* If we can't get the SSID, this isn't an 802.11 device. */
2693		return;
2694	}
2695
2696	/*
2697	 * Invalidate cached state so printing status for multiple
2698	 * if's doesn't reuse the first interfaces' cached state.
2699	 */
2700	gotcurchan = 0;
2701	gothtconf = 0;
2702
2703	if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
2704		num = 0;
2705	printf("\tssid ");
2706	if (num > 1) {
2707		for (i = 0; i < num; i++) {
2708			if (getssid(s, i, data, sizeof(data), &len) >= 0 && len > 0) {
2709				printf(" %d:", i + 1);
2710				print_string(data, len);
2711			}
2712		}
2713	} else
2714		print_string(data, len);
2715
2716	c = getcurchan(s);
2717	if (c->ic_freq != IEEE80211_CHAN_ANY) {
2718		char buf[14];
2719		printf(" channel %d (%u Mhz%s)", c->ic_ieee, c->ic_freq,
2720			get_chaninfo(c, 1, buf, sizeof(buf)));
2721	} else if (verbose)
2722		printf(" channel UNDEF");
2723
2724	if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 &&
2725	    (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
2726		printf(" bssid %s", ether_ntoa((struct ether_addr *)data));
2727
2728	if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) {
2729		printf("\n\tstationname ");
2730		print_string(data, len);
2731	}
2732
2733	spacer = ' ';		/* force first break */
2734	LINE_BREAK();
2735
2736	wpa = 0;
2737	if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) {
2738		switch (val) {
2739		case IEEE80211_AUTH_NONE:
2740			LINE_CHECK("authmode NONE");
2741			break;
2742		case IEEE80211_AUTH_OPEN:
2743			LINE_CHECK("authmode OPEN");
2744			break;
2745		case IEEE80211_AUTH_SHARED:
2746			LINE_CHECK("authmode SHARED");
2747			break;
2748		case IEEE80211_AUTH_8021X:
2749			LINE_CHECK("authmode 802.1x");
2750			break;
2751		case IEEE80211_AUTH_WPA:
2752			if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0)
2753				wpa = 1;	/* default to WPA1 */
2754			switch (wpa) {
2755			case 2:
2756				LINE_CHECK("authmode WPA2/802.11i");
2757				break;
2758			case 3:
2759				LINE_CHECK("authmode WPA1+WPA2/802.11i");
2760				break;
2761			default:
2762				LINE_CHECK("authmode WPA");
2763				break;
2764			}
2765			break;
2766		case IEEE80211_AUTH_AUTO:
2767			LINE_CHECK("authmode AUTO");
2768			break;
2769		default:
2770			LINE_CHECK("authmode UNKNOWN (0x%x)", val);
2771			break;
2772		}
2773	}
2774
2775	if (wpa || verbose) {
2776		if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) {
2777			if (val)
2778				LINE_CHECK("countermeasures");
2779			else if (verbose)
2780				LINE_CHECK("-countermeasures");
2781		}
2782	}
2783
2784	if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 &&
2785	    wepmode != IEEE80211_WEP_NOSUP) {
2786		int firstkey;
2787
2788		switch (wepmode) {
2789		case IEEE80211_WEP_OFF:
2790			LINE_CHECK("privacy OFF");
2791			break;
2792		case IEEE80211_WEP_ON:
2793			LINE_CHECK("privacy ON");
2794			break;
2795		case IEEE80211_WEP_MIXED:
2796			LINE_CHECK("privacy MIXED");
2797			break;
2798		default:
2799			LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
2800			break;
2801		}
2802
2803		/*
2804		 * If we get here then we've got WEP support so we need
2805		 * to print WEP status.
2806		 */
2807
2808		if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) {
2809			warn("WEP support, but no tx key!");
2810			goto end;
2811		}
2812		if (val != -1)
2813			LINE_CHECK("deftxkey %d", val+1);
2814		else if (wepmode != IEEE80211_WEP_OFF || verbose)
2815			LINE_CHECK("deftxkey UNDEF");
2816
2817		if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) {
2818			warn("WEP support, but no NUMWEPKEYS support!");
2819			goto end;
2820		}
2821
2822		firstkey = 1;
2823		for (i = 0; i < num; i++) {
2824			struct ieee80211req_key ik;
2825
2826			memset(&ik, 0, sizeof(ik));
2827			ik.ik_keyix = i;
2828			if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) {
2829				warn("WEP support, but can get keys!");
2830				goto end;
2831			}
2832			if (ik.ik_keylen != 0) {
2833				if (verbose)
2834					LINE_BREAK();
2835				printkey(&ik);
2836				firstkey = 0;
2837			}
2838		}
2839end:
2840		;
2841	}
2842
2843	if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 &&
2844	    val != IEEE80211_POWERSAVE_NOSUP ) {
2845		if (val != IEEE80211_POWERSAVE_OFF || verbose) {
2846			switch (val) {
2847			case IEEE80211_POWERSAVE_OFF:
2848				LINE_CHECK("powersavemode OFF");
2849				break;
2850			case IEEE80211_POWERSAVE_CAM:
2851				LINE_CHECK("powersavemode CAM");
2852				break;
2853			case IEEE80211_POWERSAVE_PSP:
2854				LINE_CHECK("powersavemode PSP");
2855				break;
2856			case IEEE80211_POWERSAVE_PSP_CAM:
2857				LINE_CHECK("powersavemode PSP-CAM");
2858				break;
2859			}
2860			if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1)
2861				LINE_CHECK("powersavesleep %d", val);
2862		}
2863	}
2864
2865	if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) {
2866		if (val & 1)
2867			LINE_CHECK("txpower %d.5", val/2);
2868		else
2869			LINE_CHECK("txpower %d", val/2);
2870	}
2871	if (verbose) {
2872		if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1)
2873			LINE_CHECK("txpowmax %.1f", val/2.);
2874	}
2875
2876	if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) {
2877		if (val != IEEE80211_RTS_MAX || verbose)
2878			LINE_CHECK("rtsthreshold %d", val);
2879	}
2880
2881	if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) {
2882		if (val != IEEE80211_FRAG_MAX || verbose)
2883			LINE_CHECK("fragthreshold %d", val);
2884	}
2885	if (opmode == IEEE80211_M_STA || verbose) {
2886		if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) {
2887			if (val != IEEE80211_HWBMISS_MAX || verbose)
2888				LINE_CHECK("bmiss %d", val);
2889		}
2890	}
2891
2892	if (get80211val(s, IEEE80211_IOC_MCAST_RATE, &val) != -1)
2893		printrate("mcastrate", val, 2*1, 0/*XXX*/);
2894
2895	bgscaninterval = -1;
2896	(void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval);
2897
2898	if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) {
2899		if (val != bgscaninterval || verbose)
2900			LINE_CHECK("scanvalid %u", val);
2901	}
2902
2903	bgscan = 0;
2904	if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) {
2905		if (bgscan)
2906			LINE_CHECK("bgscan");
2907		else if (verbose)
2908			LINE_CHECK("-bgscan");
2909	}
2910	if (bgscan || verbose) {
2911		if (bgscaninterval != -1)
2912			LINE_CHECK("bgscanintvl %u", bgscaninterval);
2913		if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1)
2914			LINE_CHECK("bgscanidle %u", val);
2915		if (IEEE80211_IS_CHAN_A(c) || verbose) {
2916			if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11A, &val) != -1)
2917				printrssi("roam:rssi11a", val);
2918			if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11A, &val) != -1)
2919				printrate("roam:rate11a", val, -1, -1);
2920		}
2921		if (IEEE80211_IS_CHAN_B(c) || verbose) {
2922			if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11B, &val) != -1)
2923				printrssi("roam:rssi11b", val);
2924			if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11B, &val) != -1)
2925				printrate("roam:rate11b", val, -1, -1);
2926		}
2927		if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
2928			if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11G, &val) != -1)
2929				printrssi("roam:rssi11g", val);
2930			if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11G, &val) != -1)
2931				printrate("roam:rate11g", val, -1, -1);
2932		}
2933	}
2934
2935	if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
2936		if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) {
2937			if (val)
2938				LINE_CHECK("pureg");
2939			else if (verbose)
2940				LINE_CHECK("-pureg");
2941		}
2942		if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) {
2943			switch (val) {
2944			case IEEE80211_PROTMODE_OFF:
2945				LINE_CHECK("protmode OFF");
2946				break;
2947			case IEEE80211_PROTMODE_CTS:
2948				LINE_CHECK("protmode CTS");
2949				break;
2950			case IEEE80211_PROTMODE_RTSCTS:
2951				LINE_CHECK("protmode RTSCTS");
2952				break;
2953			default:
2954				LINE_CHECK("protmode UNKNOWN (0x%x)", val);
2955				break;
2956			}
2957		}
2958	}
2959
2960	if (IEEE80211_IS_CHAN_HT(c) || verbose) {
2961		gethtconf(s);
2962		switch (htconf & 3) {
2963		case 0:
2964		case 2:
2965			LINE_CHECK("-ht");
2966			break;
2967		case 1:
2968			LINE_CHECK("ht20");
2969			break;
2970		case 3:
2971			if (verbose)
2972				LINE_CHECK("ht");
2973			break;
2974		}
2975		if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) {
2976			if (!val)
2977				LINE_CHECK("-htcompat");
2978			else if (verbose)
2979				LINE_CHECK("htcompat");
2980		}
2981		if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) {
2982			switch (val) {
2983			case 0:
2984				LINE_CHECK("-ampdu");
2985				break;
2986			case 1:
2987				LINE_CHECK("ampdutx -ampdurx");
2988				break;
2989			case 2:
2990				LINE_CHECK("-ampdutx ampdurx");
2991				break;
2992			case 3:
2993				if (verbose)
2994					LINE_CHECK("ampdu");
2995				break;
2996			}
2997		}
2998		if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) {
2999			switch (val) {
3000			case IEEE80211_HTCAP_MAXRXAMPDU_8K:
3001				LINE_CHECK("ampdulimit 8k");
3002				break;
3003			case IEEE80211_HTCAP_MAXRXAMPDU_16K:
3004				LINE_CHECK("ampdulimit 16k");
3005				break;
3006			case IEEE80211_HTCAP_MAXRXAMPDU_32K:
3007				LINE_CHECK("ampdulimit 32k");
3008				break;
3009			case IEEE80211_HTCAP_MAXRXAMPDU_64K:
3010				LINE_CHECK("ampdulimit 64k");
3011				break;
3012			}
3013		}
3014		if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) {
3015			switch (val) {
3016			case IEEE80211_HTCAP_MPDUDENSITY_NA:
3017				if (verbose)
3018					LINE_CHECK("ampdudensity -");
3019				break;
3020			case IEEE80211_HTCAP_MPDUDENSITY_025:
3021				LINE_CHECK("ampdudensity .25");
3022				break;
3023			case IEEE80211_HTCAP_MPDUDENSITY_05:
3024				LINE_CHECK("ampdudensity .5");
3025				break;
3026			case IEEE80211_HTCAP_MPDUDENSITY_1:
3027				LINE_CHECK("ampdudensity 1");
3028				break;
3029			case IEEE80211_HTCAP_MPDUDENSITY_2:
3030				LINE_CHECK("ampdudensity 2");
3031				break;
3032			case IEEE80211_HTCAP_MPDUDENSITY_4:
3033				LINE_CHECK("ampdudensity 4");
3034				break;
3035			case IEEE80211_HTCAP_MPDUDENSITY_8:
3036				LINE_CHECK("ampdudensity 8");
3037				break;
3038			case IEEE80211_HTCAP_MPDUDENSITY_16:
3039				LINE_CHECK("ampdudensity 16");
3040				break;
3041			}
3042		}
3043		if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) {
3044			switch (val) {
3045			case 0:
3046				LINE_CHECK("-amsdu");
3047				break;
3048			case 1:
3049				LINE_CHECK("amsdutx -amsdurx");
3050				break;
3051			case 2:
3052				LINE_CHECK("-amsdutx amsdurx");
3053				break;
3054			case 3:
3055				if (verbose)
3056					LINE_CHECK("amsdu");
3057				break;
3058			}
3059		}
3060		/* XXX amsdu limit */
3061		/* XXX 20/40 */
3062		if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) {
3063			if (val)
3064				LINE_CHECK("shortgi");
3065			else if (verbose)
3066				LINE_CHECK("-shortgi");
3067		}
3068		if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) {
3069			if (val == IEEE80211_PROTMODE_OFF)
3070				LINE_CHECK("htprotmode OFF");
3071			else if (val != IEEE80211_PROTMODE_RTSCTS)
3072				LINE_CHECK("htprotmode UNKNOWN (0x%x)", val);
3073			else if (verbose)
3074				LINE_CHECK("htprotmode RTSCTS");
3075		}
3076		if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) {
3077			if (val)
3078				LINE_CHECK("puren");
3079			else if (verbose)
3080				LINE_CHECK("-puren");
3081		}
3082	}
3083
3084	if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) {
3085		if (wme)
3086			LINE_CHECK("wme");
3087		else if (verbose)
3088			LINE_CHECK("-wme");
3089	} else
3090		wme = 0;
3091
3092	if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) {
3093		if (val)
3094			LINE_CHECK("burst");
3095		else if (verbose)
3096			LINE_CHECK("-burst");
3097	}
3098
3099	if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) {
3100		if (val)
3101			LINE_CHECK("ff");
3102		else if (verbose)
3103			LINE_CHECK("-ff");
3104	}
3105	if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) {
3106		if (val)
3107			LINE_CHECK("dturbo");
3108		else if (verbose)
3109			LINE_CHECK("-dturbo");
3110	}
3111
3112	if (opmode == IEEE80211_M_HOSTAP) {
3113		if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) {
3114			if (val)
3115				LINE_CHECK("hidessid");
3116			else if (verbose)
3117				LINE_CHECK("-hidessid");
3118		}
3119		if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) {
3120			if (!val)
3121				LINE_CHECK("-apbridge");
3122			else if (verbose)
3123				LINE_CHECK("apbridge");
3124		}
3125		if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1)
3126			LINE_CHECK("dtimperiod %u", val);
3127
3128		if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) {
3129			if (!val)
3130				LINE_CHECK("-doth");
3131			else if (verbose)
3132				LINE_CHECK("doth");
3133		}
3134		if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) {
3135			if (!val)
3136				LINE_CHECK("-inact");
3137			else if (verbose)
3138				LINE_CHECK("inact");
3139		}
3140	} else {
3141		if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) {
3142			if (val != IEEE80211_ROAMING_AUTO || verbose) {
3143				switch (val) {
3144				case IEEE80211_ROAMING_DEVICE:
3145					LINE_CHECK("roaming DEVICE");
3146					break;
3147				case IEEE80211_ROAMING_AUTO:
3148					LINE_CHECK("roaming AUTO");
3149					break;
3150				case IEEE80211_ROAMING_MANUAL:
3151					LINE_CHECK("roaming MANUAL");
3152					break;
3153				default:
3154					LINE_CHECK("roaming UNKNOWN (0x%x)",
3155						val);
3156					break;
3157				}
3158			}
3159		}
3160	}
3161	if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) {
3162		/* XXX default define not visible */
3163		if (val != 100 || verbose)
3164			LINE_CHECK("bintval %u", val);
3165	}
3166
3167	if (wme && verbose) {
3168		LINE_BREAK();
3169		list_wme(s);
3170	}
3171	LINE_BREAK();
3172}
3173
3174static int
3175get80211(int s, int type, void *data, int len)
3176{
3177	struct ieee80211req ireq;
3178
3179	(void) memset(&ireq, 0, sizeof(ireq));
3180	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3181	ireq.i_type = type;
3182	ireq.i_data = data;
3183	ireq.i_len = len;
3184	return ioctl(s, SIOCG80211, &ireq);
3185}
3186
3187static int
3188get80211len(int s, int type, void *data, int len, int *plen)
3189{
3190	struct ieee80211req ireq;
3191
3192	(void) memset(&ireq, 0, sizeof(ireq));
3193	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3194	ireq.i_type = type;
3195	ireq.i_len = len;
3196	ireq.i_data = data;
3197	if (ioctl(s, SIOCG80211, &ireq) < 0)
3198		return -1;
3199	*plen = ireq.i_len;
3200	return 0;
3201}
3202
3203static int
3204get80211val(int s, int type, int *val)
3205{
3206	struct ieee80211req ireq;
3207
3208	(void) memset(&ireq, 0, sizeof(ireq));
3209	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3210	ireq.i_type = type;
3211	if (ioctl(s, SIOCG80211, &ireq) < 0)
3212		return -1;
3213	*val = ireq.i_val;
3214	return 0;
3215}
3216
3217static void
3218set80211(int s, int type, int val, int len, void *data)
3219{
3220	struct ieee80211req	ireq;
3221
3222	(void) memset(&ireq, 0, sizeof(ireq));
3223	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3224	ireq.i_type = type;
3225	ireq.i_val = val;
3226	ireq.i_len = len;
3227	ireq.i_data = data;
3228	if (ioctl(s, SIOCS80211, &ireq) < 0)
3229		err(1, "SIOCS80211");
3230}
3231
3232static const char *
3233get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
3234{
3235	int len;
3236	int hexstr;
3237	u_int8_t *p;
3238
3239	len = *lenp;
3240	p = buf;
3241	hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
3242	if (hexstr)
3243		val += 2;
3244	for (;;) {
3245		if (*val == '\0')
3246			break;
3247		if (sep != NULL && strchr(sep, *val) != NULL) {
3248			val++;
3249			break;
3250		}
3251		if (hexstr) {
3252			if (!isxdigit((u_char)val[0])) {
3253				warnx("bad hexadecimal digits");
3254				return NULL;
3255			}
3256			if (!isxdigit((u_char)val[1])) {
3257				warnx("odd count hexadecimal digits");
3258				return NULL;
3259			}
3260		}
3261		if (p >= buf + len) {
3262			if (hexstr)
3263				warnx("hexadecimal digits too long");
3264			else
3265				warnx("string too long");
3266			return NULL;
3267		}
3268		if (hexstr) {
3269#define	tohex(x)	(isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
3270			*p++ = (tohex((u_char)val[0]) << 4) |
3271			    tohex((u_char)val[1]);
3272#undef tohex
3273			val += 2;
3274		} else
3275			*p++ = *val++;
3276	}
3277	len = p - buf;
3278	/* The string "-" is treated as the empty string. */
3279	if (!hexstr && len == 1 && buf[0] == '-') {
3280		len = 0;
3281		memset(buf, 0, *lenp);
3282	} else if (len < *lenp)
3283		memset(p, 0, *lenp - len);
3284	*lenp = len;
3285	return val;
3286}
3287
3288static void
3289print_string(const u_int8_t *buf, int len)
3290{
3291	int i;
3292	int hasspc;
3293
3294	i = 0;
3295	hasspc = 0;
3296	for (; i < len; i++) {
3297		if (!isprint(buf[i]) && buf[i] != '\0')
3298			break;
3299		if (isspace(buf[i]))
3300			hasspc++;
3301	}
3302	if (i == len) {
3303		if (hasspc || len == 0 || buf[0] == '\0')
3304			printf("\"%.*s\"", len, buf);
3305		else
3306			printf("%.*s", len, buf);
3307	} else {
3308		printf("0x");
3309		for (i = 0; i < len; i++)
3310			printf("%02x", buf[i]);
3311	}
3312}
3313
3314static struct cmd ieee80211_cmds[] = {
3315	DEF_CMD_ARG("ssid",		set80211ssid),
3316	DEF_CMD_ARG("nwid",		set80211ssid),
3317	DEF_CMD_ARG("stationname",	set80211stationname),
3318	DEF_CMD_ARG("station",		set80211stationname),	/* BSD/OS */
3319	DEF_CMD_ARG("channel",		set80211channel),
3320	DEF_CMD_ARG("authmode",		set80211authmode),
3321	DEF_CMD_ARG("powersavemode",	set80211powersavemode),
3322	DEF_CMD("powersave",	1,	set80211powersave),
3323	DEF_CMD("-powersave",	0,	set80211powersave),
3324	DEF_CMD_ARG("powersavesleep", 	set80211powersavesleep),
3325	DEF_CMD_ARG("wepmode",		set80211wepmode),
3326	DEF_CMD("wep",		1,	set80211wep),
3327	DEF_CMD("-wep",		0,	set80211wep),
3328	DEF_CMD_ARG("deftxkey",		set80211weptxkey),
3329	DEF_CMD_ARG("weptxkey",		set80211weptxkey),
3330	DEF_CMD_ARG("wepkey",		set80211wepkey),
3331	DEF_CMD_ARG("nwkey",		set80211nwkey),		/* NetBSD */
3332	DEF_CMD("-nwkey",	0,	set80211wep),		/* NetBSD */
3333	DEF_CMD_ARG("rtsthreshold",	set80211rtsthreshold),
3334	DEF_CMD_ARG("protmode",		set80211protmode),
3335	DEF_CMD_ARG("txpower",		set80211txpower),
3336	DEF_CMD_ARG("roaming",		set80211roaming),
3337	DEF_CMD("wme",		1,	set80211wme),
3338	DEF_CMD("-wme",		0,	set80211wme),
3339	DEF_CMD("hidessid",	1,	set80211hidessid),
3340	DEF_CMD("-hidessid",	0,	set80211hidessid),
3341	DEF_CMD("apbridge",	1,	set80211apbridge),
3342	DEF_CMD("-apbridge",	0,	set80211apbridge),
3343	DEF_CMD_ARG("chanlist",		set80211chanlist),
3344	DEF_CMD_ARG("bssid",		set80211bssid),
3345	DEF_CMD_ARG("ap",		set80211bssid),
3346	DEF_CMD("scan",	0,		set80211scan),
3347	DEF_CMD_ARG("list",		set80211list),
3348	DEF_CMD_ARG2("cwmin",		set80211cwmin),
3349	DEF_CMD_ARG2("cwmax",		set80211cwmax),
3350	DEF_CMD_ARG2("aifs",		set80211aifs),
3351	DEF_CMD_ARG2("txoplimit",	set80211txoplimit),
3352	DEF_CMD_ARG("acm",		set80211acm),
3353	DEF_CMD_ARG("-acm",		set80211noacm),
3354	DEF_CMD_ARG("ack",		set80211ackpolicy),
3355	DEF_CMD_ARG("-ack",		set80211noackpolicy),
3356	DEF_CMD_ARG2("bss:cwmin",	set80211bsscwmin),
3357	DEF_CMD_ARG2("bss:cwmax",	set80211bsscwmax),
3358	DEF_CMD_ARG2("bss:aifs",	set80211bssaifs),
3359	DEF_CMD_ARG2("bss:txoplimit",	set80211bsstxoplimit),
3360	DEF_CMD_ARG("dtimperiod",	set80211dtimperiod),
3361	DEF_CMD_ARG("bintval",		set80211bintval),
3362	DEF_CMD("mac:open",	IEEE80211_MACCMD_POLICY_OPEN,	set80211maccmd),
3363	DEF_CMD("mac:allow",	IEEE80211_MACCMD_POLICY_ALLOW,	set80211maccmd),
3364	DEF_CMD("mac:deny",	IEEE80211_MACCMD_POLICY_DENY,	set80211maccmd),
3365	DEF_CMD("mac:flush",	IEEE80211_MACCMD_FLUSH,		set80211maccmd),
3366	DEF_CMD("mac:detach",	IEEE80211_MACCMD_DETACH,	set80211maccmd),
3367	DEF_CMD_ARG("mac:add",		set80211addmac),
3368	DEF_CMD_ARG("mac:del",		set80211delmac),
3369	DEF_CMD_ARG("mac:kick",		set80211kickmac),
3370	DEF_CMD("pureg",	1,	set80211pureg),
3371	DEF_CMD("-pureg",	0,	set80211pureg),
3372	DEF_CMD("ff",		1,	set80211fastframes),
3373	DEF_CMD("-ff",		0,	set80211fastframes),
3374	DEF_CMD("dturbo",	1,	set80211dturbo),
3375	DEF_CMD("-dturbo",	0,	set80211dturbo),
3376	DEF_CMD("bgscan",	1,	set80211bgscan),
3377	DEF_CMD("-bgscan",	0,	set80211bgscan),
3378	DEF_CMD_ARG("bgscanidle",	set80211bgscanidle),
3379	DEF_CMD_ARG("bgscanintvl",	set80211bgscanintvl),
3380	DEF_CMD_ARG("scanvalid",	set80211scanvalid),
3381	DEF_CMD_ARG("roam:rssi11a",	set80211roamrssi11a),
3382	DEF_CMD_ARG("roam:rssi11b",	set80211roamrssi11b),
3383	DEF_CMD_ARG("roam:rssi11g",	set80211roamrssi11g),
3384	DEF_CMD_ARG("roam:rate11a",	set80211roamrate11a),
3385	DEF_CMD_ARG("roam:rate11b",	set80211roamrate11b),
3386	DEF_CMD_ARG("roam:rate11g",	set80211roamrate11g),
3387	DEF_CMD_ARG("mcastrate",	set80211mcastrate),
3388	DEF_CMD_ARG("fragthreshold",	set80211fragthreshold),
3389	DEF_CMD("burst",	1,	set80211burst),
3390	DEF_CMD("-burst",	0,	set80211burst),
3391	DEF_CMD_ARG("bmiss",		set80211bmissthreshold),
3392	DEF_CMD_ARG("bmissthreshold",	set80211bmissthreshold),
3393	DEF_CMD("shortgi",	1,	set80211shortgi),
3394	DEF_CMD("-shortgi",	0,	set80211shortgi),
3395	DEF_CMD("ampdurx",	2,	set80211ampdu),
3396	DEF_CMD("-ampdurx",	-2,	set80211ampdu),
3397	DEF_CMD("ampdutx",	1,	set80211ampdu),
3398	DEF_CMD("-ampdutx",	-1,	set80211ampdu),
3399	DEF_CMD("ampdu",	3,	set80211ampdu),		/* NB: tx+rx */
3400	DEF_CMD("-ampdu",	-3,	set80211ampdu),
3401	DEF_CMD_ARG("ampdulimit",	set80211ampdulimit),
3402	DEF_CMD_ARG("ampdudensity",	set80211ampdudensity),
3403	DEF_CMD("amsdurx",	2,	set80211amsdu),
3404	DEF_CMD("-amsdurx",	-2,	set80211amsdu),
3405	DEF_CMD("amsdutx",	1,	set80211amsdu),
3406	DEF_CMD("-amsdutx",	-1,	set80211amsdu),
3407	DEF_CMD("amsdu",	3,	set80211amsdu),		/* NB: tx+rx */
3408	DEF_CMD("-amsdu",	-3,	set80211amsdu),
3409	DEF_CMD_ARG("amsdulimit",	set80211amsdulimit),
3410	DEF_CMD("puren",	1,	set80211puren),
3411	DEF_CMD("-puren",	0,	set80211puren),
3412	DEF_CMD("doth",		1,	set80211doth),
3413	DEF_CMD("-doth",	0,	set80211doth),
3414	DEF_CMD("htcompat",	1,	set80211htcompat),
3415	DEF_CMD("-htcompat",	0,	set80211htcompat),
3416	DEF_CMD("inact",	1,	set80211inact),
3417	DEF_CMD("-inact",	0,	set80211inact),
3418	DEF_CMD_ARG("htprotmode",	set80211htprotmode),
3419	DEF_CMD("ht20",		1,	set80211htconf),
3420	DEF_CMD("-ht20",	0,	set80211htconf),
3421	DEF_CMD("ht40",		3,	set80211htconf),	/* NB: 20+40 */
3422	DEF_CMD("-ht40",	0,	set80211htconf),
3423	DEF_CMD("ht",		3,	set80211htconf),	/* NB: 20+40 */
3424	DEF_CMD("-ht",		0,	set80211htconf),
3425};
3426static struct afswtch af_ieee80211 = {
3427	.af_name	= "af_ieee80211",
3428	.af_af		= AF_UNSPEC,
3429	.af_other_status = ieee80211_status,
3430};
3431
3432static __constructor void
3433ieee80211_ctor(void)
3434{
3435#define	N(a)	(sizeof(a) / sizeof(a[0]))
3436	int i;
3437
3438	for (i = 0; i < N(ieee80211_cmds);  i++)
3439		cmd_register(&ieee80211_cmds[i]);
3440	af_register(&af_ieee80211);
3441#undef N
3442}
3443