xref: /freebsd/sbin/ifconfig/ifieee80211.c (revision b28624fde638caadd4a89f50c9b7e7da0f98c4d2)
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$
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 
95 #include "ifconfig.h"
96 
97 static void set80211(int s, int type, int val, int len, void *data);
98 static const char *get_string(const char *val, const char *sep,
99     u_int8_t *buf, int *lenp);
100 static void print_string(const u_int8_t *buf, int len);
101 
102 static struct ieee80211req_chaninfo chaninfo;
103 static struct ifmediareq *ifmr;
104 
105 /*
106  * Collect channel info from the kernel.  We use this (mostly)
107  * to handle mapping between frequency and IEEE channel number.
108  */
109 static void
110 getchaninfo(int s)
111 {
112 	struct ieee80211req ireq;
113 
114 	if (chaninfo.ic_nchans != 0)
115 		return;
116 	(void) memset(&ireq, 0, sizeof(ireq));
117 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
118 	ireq.i_type = IEEE80211_IOC_CHANINFO;
119 	ireq.i_data = &chaninfo;
120 	ireq.i_len = sizeof(chaninfo);
121 	if (ioctl(s, SIOCG80211, &ireq) < 0)
122 		errx(1, "unable to get channel information");
123 
124 	ifmr = ifmedia_getstate(s);
125 }
126 
127 /*
128  * Given the channel at index i with attributes from,
129  * check if there is a channel with attributes to in
130  * the channel table.  With suitable attributes this
131  * allows the caller to look for promotion; e.g. from
132  * 11b > 11g.
133  */
134 static int
135 canpromote(int i, int from, int to)
136 {
137 	const struct ieee80211_channel *fc = &chaninfo.ic_chans[i];
138 	int j;
139 
140 	if ((fc->ic_flags & from) != from)
141 		return i;
142 	/* NB: quick check exploiting ordering of chans w/ same frequency */
143 	if (i+1 < chaninfo.ic_nchans &&
144 	    chaninfo.ic_chans[i+1].ic_freq == fc->ic_freq &&
145 	    (chaninfo.ic_chans[i+1].ic_flags & to) == to)
146 		return i+1;
147 	/* brute force search in case channel list is not ordered */
148 	for (j = 0; j < chaninfo.ic_nchans; j++) {
149 		const struct ieee80211_channel *tc = &chaninfo.ic_chans[j];
150 		if (j != i &&
151 		    tc->ic_freq == fc->ic_freq && (tc->ic_flags & to) == to)
152 		return j;
153 	}
154 	return i;
155 }
156 
157 /*
158  * Handle channel promotion.  When a channel is specified with
159  * only a frequency we want to promote it to the ``best'' channel
160  * available.  The channel list has separate entries for 11b, 11g,
161  * 11a, and 11n[ga] channels so specifying a frequency w/o any
162  * attributes requires we upgrade, e.g. from 11b -> 11g.  This
163  * gets complicated when the channel is specified on the same
164  * command line with a media request that constrains the available
165  * channe list (e.g. mode 11a); we want to honor that to avoid
166  * confusing behaviour.
167  */
168 static int
169 promote(int i)
170 {
171 	/*
172 	 * Query the current mode of the interface in case it's
173 	 * constrained (e.g. to 11a).  We must do this carefully
174 	 * as there may be a pending ifmedia request in which case
175 	 * asking the kernel will give us the wrong answer.  This
176 	 * is an unfortunate side-effect of the way ifconfig is
177 	 * structure for modularity (yech).
178 	 *
179 	 * NB: ifmr is actually setup in getchaninfo (above); we
180 	 *     assume it's called coincident with to this call so
181 	 *     we have a ``current setting''; otherwise we must pass
182 	 *     the socket descriptor down to here so we can make
183 	 *     the ifmedia_getstate call ourselves.
184 	 */
185 	int chanmode = ifmr != NULL ? IFM_MODE(ifmr->ifm_current) : IFM_AUTO;
186 
187 	/* when ambiguous promote to ``best'' */
188 	/* NB: we abitrarily pick HT40+ over HT40- */
189 	if (chanmode != IFM_IEEE80211_11B)
190 		i = canpromote(i, IEEE80211_CHAN_B, IEEE80211_CHAN_G);
191 	if (chanmode != IFM_IEEE80211_11G) {
192 		i = canpromote(i, IEEE80211_CHAN_G,
193 			IEEE80211_CHAN_G | IEEE80211_CHAN_HT20);
194 		i = canpromote(i, IEEE80211_CHAN_G,
195 			IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D);
196 		i = canpromote(i, IEEE80211_CHAN_G,
197 			IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U);
198 	}
199 	if (chanmode != IFM_IEEE80211_11A) {
200 		i = canpromote(i, IEEE80211_CHAN_A,
201 			IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
202 		i = canpromote(i, IEEE80211_CHAN_A,
203 			IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
204 		i = canpromote(i, IEEE80211_CHAN_A,
205 			IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
206 	}
207 	return i;
208 }
209 
210 static void
211 mapfreq(struct ieee80211_channel *chan, int freq, int flags)
212 {
213 	int i;
214 
215 	for (i = 0; i < chaninfo.ic_nchans; i++) {
216 		const struct ieee80211_channel *c = &chaninfo.ic_chans[i];
217 
218 		if (c->ic_freq == freq && (c->ic_flags & flags) == flags) {
219 			if (flags == 0) {
220 				/* when ambiguous promote to ``best'' */
221 				c = &chaninfo.ic_chans[promote(i)];
222 			}
223 			*chan = *c;
224 			return;
225 		}
226 	}
227 	errx(1, "unknown/undefined frequency %u/0x%x", freq, flags);
228 }
229 
230 static void
231 mapchan(struct ieee80211_channel *chan, int ieee, int flags)
232 {
233 	int i;
234 
235 	for (i = 0; i < chaninfo.ic_nchans; i++) {
236 		const struct ieee80211_channel *c = &chaninfo.ic_chans[i];
237 
238 		if (c->ic_ieee == ieee && (c->ic_flags & flags) == flags) {
239 			if (flags == 0) {
240 				/* when ambiguous promote to ``best'' */
241 				c = &chaninfo.ic_chans[promote(i)];
242 			}
243 			*chan = *c;
244 			return;
245 		}
246 	}
247 	errx(1, "unknown/undefined channel number %d", ieee);
248 }
249 
250 static int
251 ieee80211_mhz2ieee(int freq, int flags)
252 {
253 	struct ieee80211_channel chan;
254 	mapfreq(&chan, freq, flags);
255 	return chan.ic_ieee;
256 }
257 
258 static int
259 isanyarg(const char *arg)
260 {
261 	return (strcmp(arg, "-") == 0 ||
262 	    strcasecmp(arg, "any") == 0 || strcasecmp(arg, "off") == 0);
263 }
264 
265 static void
266 set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
267 {
268 	int		ssid;
269 	int		len;
270 	u_int8_t	data[IEEE80211_NWID_LEN];
271 
272 	ssid = 0;
273 	len = strlen(val);
274 	if (len > 2 && isdigit(val[0]) && val[1] == ':') {
275 		ssid = atoi(val)-1;
276 		val += 2;
277 	}
278 
279 	bzero(data, sizeof(data));
280 	len = sizeof(data);
281 	if (get_string(val, NULL, data, &len) == NULL)
282 		exit(1);
283 
284 	set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
285 }
286 
287 static void
288 set80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
289 {
290 	int			len;
291 	u_int8_t		data[33];
292 
293 	bzero(data, sizeof(data));
294 	len = sizeof(data);
295 	get_string(val, NULL, data, &len);
296 
297 	set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
298 }
299 
300 /*
301  * Parse a channel specification for attributes/flags.
302  * The syntax is:
303  *	freq/xx		channel width (5,10,20,40,40+,40-)
304  *	freq:mode	channel mode (a,b,g,h,n,t,s,d)
305  *
306  * These can be combined in either order; e.g. 2437:ng/40.
307  * Modes are case insensitive.
308  *
309  * The result is not validated here; it's assumed to be
310  * checked against the channel table fetched from the kernel.
311  */
312 static int
313 getchannelflags(const char *val)
314 {
315 #define	CHAN_HT_DEFAULT	IEEE80211_CHAN_HT40U
316 #define	_CHAN_HT	0x80000000
317 	const char *cp;
318 	int flags;
319 
320 	flags = 0;
321 
322 	cp = strchr(val, ':');
323 	if (cp != NULL) {
324 		for (cp++; isalpha((int) *cp); cp++) {
325 			/* accept mixed case */
326 			int c = *cp;
327 			if (isupper(c))
328 				c = tolower(c);
329 			switch (c) {
330 			case 'a':		/* 802.11a */
331 				flags |= IEEE80211_CHAN_A;
332 				break;
333 			case 'b':		/* 802.11b */
334 				flags |= IEEE80211_CHAN_B;
335 				break;
336 			case 'g':		/* 802.11g */
337 				flags |= IEEE80211_CHAN_G;
338 				break;
339 			case 'h':		/* ht = 802.11n */
340 			case 'n':		/* 802.11n */
341 				flags |= _CHAN_HT;	/* NB: private */
342 				break;
343 			case 'd':		/* dt = Atheros Dynamic Turbo */
344 				flags |= IEEE80211_CHAN_TURBO;
345 				break;
346 			case 't':		/* ht, dt, st, t */
347 				/* dt and unadorned t specify Dynamic Turbo */
348 				if ((flags & (IEEE80211_CHAN_STURBO|_CHAN_HT)) == 0)
349 					flags |= IEEE80211_CHAN_TURBO;
350 				break;
351 			case 's':		/* st = Atheros Static Turbo */
352 				flags |= IEEE80211_CHAN_STURBO;
353 				break;
354 			default:
355 				errx(-1, "%s: Invalid channel attribute %c",
356 				    val, *cp);
357 			}
358 		}
359 	}
360 	cp = strchr(val, '/');
361 	if (cp != NULL) {
362 		char *ep;
363 		u_long cw = strtoul(cp+1, &ep, 10);
364 
365 		switch (cw) {
366 		case 5:
367 			flags |= IEEE80211_CHAN_QUARTER;
368 			break;
369 		case 10:
370 			flags |= IEEE80211_CHAN_HALF;
371 			break;
372 		case 20:
373 			/* NB: this may be removed below */
374 			flags |= IEEE80211_CHAN_HT20;
375 			break;
376 		case 40:
377 			if (ep != NULL && *ep == '+')
378 				flags |= IEEE80211_CHAN_HT40U;
379 			else if (ep != NULL && *ep == '-')
380 				flags |= IEEE80211_CHAN_HT40D;
381 			else		/* NB: pick something */
382 				flags |= CHAN_HT_DEFAULT;
383 			break;
384 		default:
385 			errx(-1, "%s: Invalid channel width", val);
386 		}
387 	}
388 	/*
389 	 * Cleanup specifications.
390 	 */
391 	if ((flags & _CHAN_HT) == 0) {
392 		/*
393 		 * If user specified freq/20 or freq/40 quietly remove
394 		 * HT cw attributes depending on channel use.  To give
395 		 * an explicit 20/40 width for an HT channel you must
396 		 * indicate it is an HT channel since all HT channels
397 		 * are also usable for legacy operation; e.g. freq:n/40.
398 		 */
399 		flags &= ~IEEE80211_CHAN_HT;
400 	} else {
401 		/*
402 		 * Remove private indicator that this is an HT channel
403 		 * and if no explicit channel width has been given
404 		 * provide the default settings.
405 		 */
406 		flags &= ~_CHAN_HT;
407 		if ((flags & IEEE80211_CHAN_HT) == 0)
408 			flags |= CHAN_HT_DEFAULT;
409 	}
410 	return flags;
411 #undef CHAN_HT_DEFAULT
412 #undef _CHAN_HT
413 }
414 
415 static void
416 set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
417 {
418 	struct ieee80211_channel chan;
419 
420 	memset(&chan, 0, sizeof(chan));
421 	if (!isanyarg(val)) {
422 		int v = atoi(val);
423 		int flags = getchannelflags(val);
424 
425 		getchaninfo(s);
426 		if (v > 255) {		/* treat as frequency */
427 			mapfreq(&chan, v, flags);
428 		} else {
429 			mapchan(&chan, v, flags);
430 		}
431 	} else {
432 		chan.ic_freq = IEEE80211_CHAN_ANY;
433 	}
434 	set80211(s, IEEE80211_IOC_CURCHAN, 0, sizeof(chan), &chan);
435 }
436 
437 static void
438 set80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
439 {
440 	int	mode;
441 
442 	if (strcasecmp(val, "none") == 0) {
443 		mode = IEEE80211_AUTH_NONE;
444 	} else if (strcasecmp(val, "open") == 0) {
445 		mode = IEEE80211_AUTH_OPEN;
446 	} else if (strcasecmp(val, "shared") == 0) {
447 		mode = IEEE80211_AUTH_SHARED;
448 	} else if (strcasecmp(val, "8021x") == 0) {
449 		mode = IEEE80211_AUTH_8021X;
450 	} else if (strcasecmp(val, "wpa") == 0) {
451 		mode = IEEE80211_AUTH_WPA;
452 	} else {
453 		errx(1, "unknown authmode");
454 	}
455 
456 	set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
457 }
458 
459 static void
460 set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
461 {
462 	int	mode;
463 
464 	if (strcasecmp(val, "off") == 0) {
465 		mode = IEEE80211_POWERSAVE_OFF;
466 	} else if (strcasecmp(val, "on") == 0) {
467 		mode = IEEE80211_POWERSAVE_ON;
468 	} else if (strcasecmp(val, "cam") == 0) {
469 		mode = IEEE80211_POWERSAVE_CAM;
470 	} else if (strcasecmp(val, "psp") == 0) {
471 		mode = IEEE80211_POWERSAVE_PSP;
472 	} else if (strcasecmp(val, "psp-cam") == 0) {
473 		mode = IEEE80211_POWERSAVE_PSP_CAM;
474 	} else {
475 		errx(1, "unknown powersavemode");
476 	}
477 
478 	set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
479 }
480 
481 static void
482 set80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
483 {
484 	if (d == 0)
485 		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
486 		    0, NULL);
487 	else
488 		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
489 		    0, NULL);
490 }
491 
492 static void
493 set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
494 {
495 	set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
496 }
497 
498 static void
499 set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
500 {
501 	int	mode;
502 
503 	if (strcasecmp(val, "off") == 0) {
504 		mode = IEEE80211_WEP_OFF;
505 	} else if (strcasecmp(val, "on") == 0) {
506 		mode = IEEE80211_WEP_ON;
507 	} else if (strcasecmp(val, "mixed") == 0) {
508 		mode = IEEE80211_WEP_MIXED;
509 	} else {
510 		errx(1, "unknown wep mode");
511 	}
512 
513 	set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
514 }
515 
516 static void
517 set80211wep(const char *val, int d, int s, const struct afswtch *rafp)
518 {
519 	set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
520 }
521 
522 static int
523 isundefarg(const char *arg)
524 {
525 	return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0);
526 }
527 
528 static void
529 set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
530 {
531 	if (isundefarg(val))
532 		set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
533 	else
534 		set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
535 }
536 
537 static void
538 set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
539 {
540 	int		key = 0;
541 	int		len;
542 	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
543 
544 	if (isdigit(val[0]) && val[1] == ':') {
545 		key = atoi(val)-1;
546 		val += 2;
547 	}
548 
549 	bzero(data, sizeof(data));
550 	len = sizeof(data);
551 	get_string(val, NULL, data, &len);
552 
553 	set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
554 }
555 
556 /*
557  * This function is purely a NetBSD compatability interface.  The NetBSD
558  * interface is too inflexible, but it's there so we'll support it since
559  * it's not all that hard.
560  */
561 static void
562 set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
563 {
564 	int		txkey;
565 	int		i, len;
566 	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
567 
568 	set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
569 
570 	if (isdigit(val[0]) && val[1] == ':') {
571 		txkey = val[0]-'0'-1;
572 		val += 2;
573 
574 		for (i = 0; i < 4; i++) {
575 			bzero(data, sizeof(data));
576 			len = sizeof(data);
577 			val = get_string(val, ",", data, &len);
578 			if (val == NULL)
579 				exit(1);
580 
581 			set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
582 		}
583 	} else {
584 		bzero(data, sizeof(data));
585 		len = sizeof(data);
586 		get_string(val, NULL, data, &len);
587 		txkey = 0;
588 
589 		set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
590 
591 		bzero(data, sizeof(data));
592 		for (i = 1; i < 4; i++)
593 			set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
594 	}
595 
596 	set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
597 }
598 
599 static void
600 set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
601 {
602 	set80211(s, IEEE80211_IOC_RTSTHRESHOLD,
603 		isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL);
604 }
605 
606 static void
607 set80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
608 {
609 	int	mode;
610 
611 	if (strcasecmp(val, "off") == 0) {
612 		mode = IEEE80211_PROTMODE_OFF;
613 	} else if (strcasecmp(val, "cts") == 0) {
614 		mode = IEEE80211_PROTMODE_CTS;
615 	} else if (strcasecmp(val, "rtscts") == 0) {
616 		mode = IEEE80211_PROTMODE_RTSCTS;
617 	} else {
618 		errx(1, "unknown protection mode");
619 	}
620 
621 	set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
622 }
623 
624 static void
625 set80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
626 {
627 	set80211(s, IEEE80211_IOC_TXPOWER, atoi(val), 0, NULL);
628 }
629 
630 #define	IEEE80211_ROAMING_DEVICE	0
631 #define	IEEE80211_ROAMING_AUTO		1
632 #define	IEEE80211_ROAMING_MANUAL	2
633 
634 static void
635 set80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
636 {
637 	int mode;
638 
639 	if (strcasecmp(val, "device") == 0) {
640 		mode = IEEE80211_ROAMING_DEVICE;
641 	} else if (strcasecmp(val, "auto") == 0) {
642 		mode = IEEE80211_ROAMING_AUTO;
643 	} else if (strcasecmp(val, "manual") == 0) {
644 		mode = IEEE80211_ROAMING_MANUAL;
645 	} else {
646 		errx(1, "unknown roaming mode");
647 	}
648 	set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
649 }
650 
651 static void
652 set80211wme(const char *val, int d, int s, const struct afswtch *rafp)
653 {
654 	set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
655 }
656 
657 static void
658 set80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
659 {
660 	set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
661 }
662 
663 static void
664 set80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
665 {
666 	set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
667 }
668 
669 static void
670 set80211fastframes(const char *val, int d, int s, const struct afswtch *rafp)
671 {
672 	set80211(s, IEEE80211_IOC_FF, d, 0, NULL);
673 }
674 
675 static void
676 set80211dturbo(const char *val, int d, int s, const struct afswtch *rafp)
677 {
678 	set80211(s, IEEE80211_IOC_TURBOP, d, 0, NULL);
679 }
680 
681 static void
682 set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
683 {
684 	struct ieee80211req_chanlist chanlist;
685 #define	MAXCHAN	(sizeof(chanlist.ic_channels)*NBBY)
686 	char *temp, *cp, *tp;
687 
688 	temp = malloc(strlen(val) + 1);
689 	if (temp == NULL)
690 		errx(1, "malloc failed");
691 	strcpy(temp, val);
692 	memset(&chanlist, 0, sizeof(chanlist));
693 	cp = temp;
694 	for (;;) {
695 		int first, last, f;
696 
697 		tp = strchr(cp, ',');
698 		if (tp != NULL)
699 			*tp++ = '\0';
700 		switch (sscanf(cp, "%u-%u", &first, &last)) {
701 		case 1:
702 			if (first > MAXCHAN)
703 				errx(-1, "channel %u out of range, max %zu",
704 					first, MAXCHAN);
705 			setbit(chanlist.ic_channels, first);
706 			break;
707 		case 2:
708 			if (first > MAXCHAN)
709 				errx(-1, "channel %u out of range, max %zu",
710 					first, MAXCHAN);
711 			if (last > MAXCHAN)
712 				errx(-1, "channel %u out of range, max %zu",
713 					last, MAXCHAN);
714 			if (first > last)
715 				errx(-1, "void channel range, %u > %u",
716 					first, last);
717 			for (f = first; f <= last; f++)
718 				setbit(chanlist.ic_channels, f);
719 			break;
720 		}
721 		if (tp == NULL)
722 			break;
723 		while (isspace(*tp))
724 			tp++;
725 		if (!isdigit(*tp))
726 			break;
727 		cp = tp;
728 	}
729 	set80211(s, IEEE80211_IOC_CHANLIST, 0, sizeof(chanlist), &chanlist);
730 #undef MAXCHAN
731 }
732 
733 static void
734 set80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
735 {
736 
737 	if (!isanyarg(val)) {
738 		char *temp;
739 		struct sockaddr_dl sdl;
740 
741 		temp = malloc(strlen(val) + 2); /* ':' and '\0' */
742 		if (temp == NULL)
743 			errx(1, "malloc failed");
744 		temp[0] = ':';
745 		strcpy(temp + 1, val);
746 		sdl.sdl_len = sizeof(sdl);
747 		link_addr(temp, &sdl);
748 		free(temp);
749 		if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
750 			errx(1, "malformed link-level address");
751 		set80211(s, IEEE80211_IOC_BSSID, 0,
752 			IEEE80211_ADDR_LEN, LLADDR(&sdl));
753 	} else {
754 		uint8_t zerobssid[IEEE80211_ADDR_LEN];
755 		memset(zerobssid, 0, sizeof(zerobssid));
756 		set80211(s, IEEE80211_IOC_BSSID, 0,
757 			IEEE80211_ADDR_LEN, zerobssid);
758 	}
759 }
760 
761 static int
762 getac(const char *ac)
763 {
764 	if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0)
765 		return WME_AC_BE;
766 	if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0)
767 		return WME_AC_BK;
768 	if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0)
769 		return WME_AC_VI;
770 	if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0)
771 		return WME_AC_VO;
772 	errx(1, "unknown wme access class %s", ac);
773 }
774 
775 static
776 DECL_CMD_FUNC2(set80211cwmin, ac, val)
777 {
778 	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
779 }
780 
781 static
782 DECL_CMD_FUNC2(set80211cwmax, ac, val)
783 {
784 	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
785 }
786 
787 static
788 DECL_CMD_FUNC2(set80211aifs, ac, val)
789 {
790 	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
791 }
792 
793 static
794 DECL_CMD_FUNC2(set80211txoplimit, ac, val)
795 {
796 	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
797 }
798 
799 static
800 DECL_CMD_FUNC(set80211acm, ac, d)
801 {
802 	set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL);
803 }
804 static
805 DECL_CMD_FUNC(set80211noacm, ac, d)
806 {
807 	set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL);
808 }
809 
810 static
811 DECL_CMD_FUNC(set80211ackpolicy, ac, d)
812 {
813 	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL);
814 }
815 static
816 DECL_CMD_FUNC(set80211noackpolicy, ac, d)
817 {
818 	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL);
819 }
820 
821 static
822 DECL_CMD_FUNC2(set80211bsscwmin, ac, val)
823 {
824 	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
825 		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
826 }
827 
828 static
829 DECL_CMD_FUNC2(set80211bsscwmax, ac, val)
830 {
831 	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
832 		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
833 }
834 
835 static
836 DECL_CMD_FUNC2(set80211bssaifs, ac, val)
837 {
838 	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
839 		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
840 }
841 
842 static
843 DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
844 {
845 	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
846 		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
847 }
848 
849 static
850 DECL_CMD_FUNC(set80211dtimperiod, val, d)
851 {
852 	set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
853 }
854 
855 static
856 DECL_CMD_FUNC(set80211bintval, val, d)
857 {
858 	set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
859 }
860 
861 static void
862 set80211macmac(int s, int op, const char *val)
863 {
864 	char *temp;
865 	struct sockaddr_dl sdl;
866 
867 	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
868 	if (temp == NULL)
869 		errx(1, "malloc failed");
870 	temp[0] = ':';
871 	strcpy(temp + 1, val);
872 	sdl.sdl_len = sizeof(sdl);
873 	link_addr(temp, &sdl);
874 	free(temp);
875 	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
876 		errx(1, "malformed link-level address");
877 	set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
878 }
879 
880 static
881 DECL_CMD_FUNC(set80211addmac, val, d)
882 {
883 	set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
884 }
885 
886 static
887 DECL_CMD_FUNC(set80211delmac, val, d)
888 {
889 	set80211macmac(s, IEEE80211_IOC_DELMAC, val);
890 }
891 
892 static
893 DECL_CMD_FUNC(set80211kickmac, val, d)
894 {
895 	char *temp;
896 	struct sockaddr_dl sdl;
897 	struct ieee80211req_mlme mlme;
898 
899 	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
900 	if (temp == NULL)
901 		errx(1, "malloc failed");
902 	temp[0] = ':';
903 	strcpy(temp + 1, val);
904 	sdl.sdl_len = sizeof(sdl);
905 	link_addr(temp, &sdl);
906 	free(temp);
907 	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
908 		errx(1, "malformed link-level address");
909 	memset(&mlme, 0, sizeof(mlme));
910 	mlme.im_op = IEEE80211_MLME_DEAUTH;
911 	mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE;
912 	memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN);
913 	set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), &mlme);
914 }
915 
916 static
917 DECL_CMD_FUNC(set80211maccmd, val, d)
918 {
919 	set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
920 }
921 
922 static void
923 set80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
924 {
925 	set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
926 }
927 
928 static void
929 set80211bgscan(const char *val, int d, int s, const struct afswtch *rafp)
930 {
931 	set80211(s, IEEE80211_IOC_BGSCAN, d, 0, NULL);
932 }
933 
934 static
935 DECL_CMD_FUNC(set80211bgscanidle, val, d)
936 {
937 	set80211(s, IEEE80211_IOC_BGSCAN_IDLE, atoi(val), 0, NULL);
938 }
939 
940 static
941 DECL_CMD_FUNC(set80211bgscanintvl, val, d)
942 {
943 	set80211(s, IEEE80211_IOC_BGSCAN_INTERVAL, atoi(val), 0, NULL);
944 }
945 
946 static
947 DECL_CMD_FUNC(set80211scanvalid, val, d)
948 {
949 	set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL);
950 }
951 
952 static
953 DECL_CMD_FUNC(set80211roamrssi11a, val, d)
954 {
955 	set80211(s, IEEE80211_IOC_ROAM_RSSI_11A, atoi(val), 0, NULL);
956 }
957 
958 static
959 DECL_CMD_FUNC(set80211roamrssi11b, val, d)
960 {
961 	set80211(s, IEEE80211_IOC_ROAM_RSSI_11B, atoi(val), 0, NULL);
962 }
963 
964 static
965 DECL_CMD_FUNC(set80211roamrssi11g, val, d)
966 {
967 	set80211(s, IEEE80211_IOC_ROAM_RSSI_11G, atoi(val), 0, NULL);
968 }
969 
970 static
971 DECL_CMD_FUNC(set80211roamrate11a, val, d)
972 {
973 	set80211(s, IEEE80211_IOC_ROAM_RATE_11A, 2*atoi(val), 0, NULL);
974 }
975 
976 static
977 DECL_CMD_FUNC(set80211roamrate11b, val, d)
978 {
979 	set80211(s, IEEE80211_IOC_ROAM_RATE_11B, 2*atoi(val), 0, NULL);
980 }
981 
982 static
983 DECL_CMD_FUNC(set80211roamrate11g, val, d)
984 {
985 	set80211(s, IEEE80211_IOC_ROAM_RATE_11G, 2*atoi(val), 0, NULL);
986 }
987 
988 static
989 DECL_CMD_FUNC(set80211mcastrate, val, d)
990 {
991 	set80211(s, IEEE80211_IOC_MCAST_RATE, 2*atoi(val), 0, NULL);
992 }
993 
994 static
995 DECL_CMD_FUNC(set80211fragthreshold, val, d)
996 {
997 	set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
998 		isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
999 }
1000 
1001 static
1002 DECL_CMD_FUNC(set80211bmissthreshold, val, d)
1003 {
1004 	set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
1005 		isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
1006 }
1007 
1008 static void
1009 set80211burst(const char *val, int d, int s, const struct afswtch *rafp)
1010 {
1011 	set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
1012 }
1013 
1014 static void
1015 set80211doth(const char *val, int d, int s, const struct afswtch *rafp)
1016 {
1017 	set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL);
1018 }
1019 
1020 static int
1021 getmaxrate(const uint8_t rates[15], uint8_t nrates)
1022 {
1023 	int i, maxrate = -1;
1024 
1025 	for (i = 0; i < nrates; i++) {
1026 		int rate = rates[i] & IEEE80211_RATE_VAL;
1027 		if (rate > maxrate)
1028 			maxrate = rate;
1029 	}
1030 	return maxrate / 2;
1031 }
1032 
1033 static const char *
1034 getcaps(int capinfo)
1035 {
1036 	static char capstring[32];
1037 	char *cp = capstring;
1038 
1039 	if (capinfo & IEEE80211_CAPINFO_ESS)
1040 		*cp++ = 'E';
1041 	if (capinfo & IEEE80211_CAPINFO_IBSS)
1042 		*cp++ = 'I';
1043 	if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
1044 		*cp++ = 'c';
1045 	if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
1046 		*cp++ = 'C';
1047 	if (capinfo & IEEE80211_CAPINFO_PRIVACY)
1048 		*cp++ = 'P';
1049 	if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
1050 		*cp++ = 'S';
1051 	if (capinfo & IEEE80211_CAPINFO_PBCC)
1052 		*cp++ = 'B';
1053 	if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
1054 		*cp++ = 'A';
1055 	if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
1056 		*cp++ = 's';
1057 	if (capinfo & IEEE80211_CAPINFO_RSN)
1058 		*cp++ = 'R';
1059 	if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
1060 		*cp++ = 'D';
1061 	*cp = '\0';
1062 	return capstring;
1063 }
1064 
1065 static const char *
1066 getflags(int flags)
1067 {
1068 /* XXX need these publicly defined or similar */
1069 #define	IEEE80211_NODE_AUTH	0x0001		/* authorized for data */
1070 #define	IEEE80211_NODE_QOS	0x0002		/* QoS enabled */
1071 #define	IEEE80211_NODE_ERP	0x0004		/* ERP enabled */
1072 #define	IEEE80211_NODE_PWR_MGT	0x0010		/* power save mode enabled */
1073 #define	IEEE80211_NODE_HT	0x0040		/* HT enabled */
1074 	static char flagstring[32];
1075 	char *cp = flagstring;
1076 
1077 	if (flags & IEEE80211_NODE_AUTH)
1078 		*cp++ = 'A';
1079 	if (flags & IEEE80211_NODE_QOS)
1080 		*cp++ = 'Q';
1081 	if (flags & IEEE80211_NODE_ERP)
1082 		*cp++ = 'E';
1083 	if (flags & IEEE80211_NODE_PWR_MGT)
1084 		*cp++ = 'P';
1085 	if (flags & IEEE80211_NODE_HT)
1086 		*cp++ = 'H';
1087 	*cp = '\0';
1088 	return flagstring;
1089 #undef IEEE80211_NODE_HT
1090 #undef IEEE80211_NODE_AUTH
1091 #undef IEEE80211_NODE_QOS
1092 #undef IEEE80211_NODE_ERP
1093 #undef IEEE80211_NODE_PWR_MGT
1094 }
1095 
1096 static void
1097 printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
1098 {
1099 	printf("%s", tag);
1100 	if (verbose) {
1101 		maxlen -= strlen(tag)+2;
1102 		if (2*ielen > maxlen)
1103 			maxlen--;
1104 		printf("<");
1105 		for (; ielen > 0; ie++, ielen--) {
1106 			if (maxlen-- <= 0)
1107 				break;
1108 			printf("%02x", *ie);
1109 		}
1110 		if (ielen != 0)
1111 			printf("-");
1112 		printf(">");
1113 	}
1114 }
1115 
1116 #define LE_READ_2(p)					\
1117 	((u_int16_t)					\
1118 	 ((((const u_int8_t *)(p))[0]      ) |		\
1119 	  (((const u_int8_t *)(p))[1] <<  8)))
1120 #define LE_READ_4(p)					\
1121 	((u_int32_t)					\
1122 	 ((((const u_int8_t *)(p))[0]      ) |		\
1123 	  (((const u_int8_t *)(p))[1] <<  8) |		\
1124 	  (((const u_int8_t *)(p))[2] << 16) |		\
1125 	  (((const u_int8_t *)(p))[3] << 24)))
1126 
1127 /*
1128  * NB: The decoding routines assume a properly formatted ie
1129  *     which should be safe as the kernel only retains them
1130  *     if they parse ok.
1131  */
1132 
1133 static void
1134 printwmeie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1135 {
1136 #define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
1137 	static const char *acnames[] = { "BE", "BK", "VO", "VI" };
1138 	int i;
1139 
1140 	printf("%s", tag);
1141 	if (verbose) {
1142 		printf("<qosinfo 0x%x", ie[
1143 			__offsetof(struct ieee80211_wme_param, param_qosInfo)]);
1144 		ie += __offsetof(struct ieee80211_wme_param, params_acParams);
1145 		for (i = 0; i < WME_NUM_AC; i++) {
1146 			printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]"
1147 				, acnames[i]
1148 				, MS(ie[0], WME_PARAM_ACM) ? "acm " : ""
1149 				, MS(ie[0], WME_PARAM_AIFSN)
1150 				, MS(ie[1], WME_PARAM_LOGCWMIN)
1151 				, MS(ie[1], WME_PARAM_LOGCWMAX)
1152 				, LE_READ_2(ie+2)
1153 			);
1154 			ie += 4;
1155 		}
1156 		printf(">");
1157 	}
1158 #undef MS
1159 }
1160 
1161 static void
1162 printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1163 {
1164 
1165 	printf("%s", tag);
1166 	if (verbose) {
1167 		const struct ieee80211_ath_ie *ath =
1168 			(const struct ieee80211_ath_ie *)ie;
1169 
1170 		printf("<");
1171 		if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME)
1172 			printf("DTURBO,");
1173 		if (ath->ath_capability & ATHEROS_CAP_COMPRESSION)
1174 			printf("COMP,");
1175 		if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME)
1176 			printf("FF,");
1177 		if (ath->ath_capability & ATHEROS_CAP_XR)
1178 			printf("XR,");
1179 		if (ath->ath_capability & ATHEROS_CAP_AR)
1180 			printf("AR,");
1181 		if (ath->ath_capability & ATHEROS_CAP_BURST)
1182 			printf("BURST,");
1183 		if (ath->ath_capability & ATHEROS_CAP_WME)
1184 			printf("WME,");
1185 		if (ath->ath_capability & ATHEROS_CAP_BOOST)
1186 			printf("BOOST,");
1187 		printf("0x%x>", LE_READ_2(ath->ath_defkeyix));
1188 	}
1189 }
1190 
1191 static const char *
1192 wpa_cipher(const u_int8_t *sel)
1193 {
1194 #define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
1195 	u_int32_t w = LE_READ_4(sel);
1196 
1197 	switch (w) {
1198 	case WPA_SEL(WPA_CSE_NULL):
1199 		return "NONE";
1200 	case WPA_SEL(WPA_CSE_WEP40):
1201 		return "WEP40";
1202 	case WPA_SEL(WPA_CSE_WEP104):
1203 		return "WEP104";
1204 	case WPA_SEL(WPA_CSE_TKIP):
1205 		return "TKIP";
1206 	case WPA_SEL(WPA_CSE_CCMP):
1207 		return "AES-CCMP";
1208 	}
1209 	return "?";		/* NB: so 1<< is discarded */
1210 #undef WPA_SEL
1211 }
1212 
1213 static const char *
1214 wpa_keymgmt(const u_int8_t *sel)
1215 {
1216 #define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
1217 	u_int32_t w = LE_READ_4(sel);
1218 
1219 	switch (w) {
1220 	case WPA_SEL(WPA_ASE_8021X_UNSPEC):
1221 		return "8021X-UNSPEC";
1222 	case WPA_SEL(WPA_ASE_8021X_PSK):
1223 		return "8021X-PSK";
1224 	case WPA_SEL(WPA_ASE_NONE):
1225 		return "NONE";
1226 	}
1227 	return "?";
1228 #undef WPA_SEL
1229 }
1230 
1231 static void
1232 printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1233 {
1234 	u_int8_t len = ie[1];
1235 
1236 	printf("%s", tag);
1237 	if (verbose) {
1238 		const char *sep;
1239 		int n;
1240 
1241 		ie += 6, len -= 4;		/* NB: len is payload only */
1242 
1243 		printf("<v%u", LE_READ_2(ie));
1244 		ie += 2, len -= 2;
1245 
1246 		printf(" mc:%s", wpa_cipher(ie));
1247 		ie += 4, len -= 4;
1248 
1249 		/* unicast ciphers */
1250 		n = LE_READ_2(ie);
1251 		ie += 2, len -= 2;
1252 		sep = " uc:";
1253 		for (; n > 0; n--) {
1254 			printf("%s%s", sep, wpa_cipher(ie));
1255 			ie += 4, len -= 4;
1256 			sep = "+";
1257 		}
1258 
1259 		/* key management algorithms */
1260 		n = LE_READ_2(ie);
1261 		ie += 2, len -= 2;
1262 		sep = " km:";
1263 		for (; n > 0; n--) {
1264 			printf("%s%s", sep, wpa_keymgmt(ie));
1265 			ie += 4, len -= 4;
1266 			sep = "+";
1267 		}
1268 
1269 		if (len > 2)		/* optional capabilities */
1270 			printf(", caps 0x%x", LE_READ_2(ie));
1271 		printf(">");
1272 	}
1273 }
1274 
1275 static const char *
1276 rsn_cipher(const u_int8_t *sel)
1277 {
1278 #define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
1279 	u_int32_t w = LE_READ_4(sel);
1280 
1281 	switch (w) {
1282 	case RSN_SEL(RSN_CSE_NULL):
1283 		return "NONE";
1284 	case RSN_SEL(RSN_CSE_WEP40):
1285 		return "WEP40";
1286 	case RSN_SEL(RSN_CSE_WEP104):
1287 		return "WEP104";
1288 	case RSN_SEL(RSN_CSE_TKIP):
1289 		return "TKIP";
1290 	case RSN_SEL(RSN_CSE_CCMP):
1291 		return "AES-CCMP";
1292 	case RSN_SEL(RSN_CSE_WRAP):
1293 		return "AES-OCB";
1294 	}
1295 	return "?";
1296 #undef WPA_SEL
1297 }
1298 
1299 static const char *
1300 rsn_keymgmt(const u_int8_t *sel)
1301 {
1302 #define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
1303 	u_int32_t w = LE_READ_4(sel);
1304 
1305 	switch (w) {
1306 	case RSN_SEL(RSN_ASE_8021X_UNSPEC):
1307 		return "8021X-UNSPEC";
1308 	case RSN_SEL(RSN_ASE_8021X_PSK):
1309 		return "8021X-PSK";
1310 	case RSN_SEL(RSN_ASE_NONE):
1311 		return "NONE";
1312 	}
1313 	return "?";
1314 #undef RSN_SEL
1315 }
1316 
1317 static void
1318 printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1319 {
1320 	u_int8_t len = ie[1];
1321 
1322 	printf("%s", tag);
1323 	if (verbose) {
1324 		const char *sep;
1325 		int n;
1326 
1327 		ie += 6, len -= 4;		/* NB: len is payload only */
1328 
1329 		printf("<v%u", LE_READ_2(ie));
1330 		ie += 2, len -= 2;
1331 
1332 		printf(" mc:%s", rsn_cipher(ie));
1333 		ie += 4, len -= 4;
1334 
1335 		/* unicast ciphers */
1336 		n = LE_READ_2(ie);
1337 		ie += 2, len -= 2;
1338 		sep = " uc:";
1339 		for (; n > 0; n--) {
1340 			printf("%s%s", sep, rsn_cipher(ie));
1341 			ie += 4, len -= 4;
1342 			sep = "+";
1343 		}
1344 
1345 		/* key management algorithms */
1346 		n = LE_READ_2(ie);
1347 		ie += 2, len -= 2;
1348 		sep = " km:";
1349 		for (; n > 0; n--) {
1350 			printf("%s%s", sep, rsn_keymgmt(ie));
1351 			ie += 4, len -= 4;
1352 			sep = "+";
1353 		}
1354 
1355 		if (len > 2)		/* optional capabilities */
1356 			printf(", caps 0x%x", LE_READ_2(ie));
1357 		/* XXXPMKID */
1358 		printf(">");
1359 	}
1360 }
1361 
1362 /*
1363  * Copy the ssid string contents into buf, truncating to fit.  If the
1364  * ssid is entirely printable then just copy intact.  Otherwise convert
1365  * to hexadecimal.  If the result is truncated then replace the last
1366  * three characters with "...".
1367  */
1368 static int
1369 copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
1370 {
1371 	const u_int8_t *p;
1372 	size_t maxlen;
1373 	int i;
1374 
1375 	if (essid_len > bufsize)
1376 		maxlen = bufsize;
1377 	else
1378 		maxlen = essid_len;
1379 	/* determine printable or not */
1380 	for (i = 0, p = essid; i < maxlen; i++, p++) {
1381 		if (*p < ' ' || *p > 0x7e)
1382 			break;
1383 	}
1384 	if (i != maxlen) {		/* not printable, print as hex */
1385 		if (bufsize < 3)
1386 			return 0;
1387 		strlcpy(buf, "0x", bufsize);
1388 		bufsize -= 2;
1389 		p = essid;
1390 		for (i = 0; i < maxlen && bufsize >= 2; i++) {
1391 			sprintf(&buf[2+2*i], "%02x", p[i]);
1392 			bufsize -= 2;
1393 		}
1394 		if (i != essid_len)
1395 			memcpy(&buf[2+2*i-3], "...", 3);
1396 	} else {			/* printable, truncate as needed */
1397 		memcpy(buf, essid, maxlen);
1398 		if (maxlen != essid_len)
1399 			memcpy(&buf[maxlen-3], "...", 3);
1400 	}
1401 	return maxlen;
1402 }
1403 
1404 /* unaligned little endian access */
1405 #define LE_READ_4(p)					\
1406 	((u_int32_t)					\
1407 	 ((((const u_int8_t *)(p))[0]      ) |		\
1408 	  (((const u_int8_t *)(p))[1] <<  8) |		\
1409 	  (((const u_int8_t *)(p))[2] << 16) |		\
1410 	  (((const u_int8_t *)(p))[3] << 24)))
1411 
1412 static int __inline
1413 iswpaoui(const u_int8_t *frm)
1414 {
1415 	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
1416 }
1417 
1418 static int __inline
1419 iswmeoui(const u_int8_t *frm)
1420 {
1421 	return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
1422 }
1423 
1424 static int __inline
1425 isatherosoui(const u_int8_t *frm)
1426 {
1427 	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
1428 }
1429 
1430 static void
1431 printies(const u_int8_t *vp, int ielen, int maxcols)
1432 {
1433 	while (ielen > 0) {
1434 		switch (vp[0]) {
1435 		case IEEE80211_ELEMID_VENDOR:
1436 			if (iswpaoui(vp))
1437 				printwpaie(" WPA", vp, 2+vp[1], maxcols);
1438 			else if (iswmeoui(vp))
1439 				printwmeie(" WME", vp, 2+vp[1], maxcols);
1440 			else if (isatherosoui(vp))
1441 				printathie(" ATH", vp, 2+vp[1], maxcols);
1442 			else
1443 				printie(" VEN", vp, 2+vp[1], maxcols);
1444 			break;
1445 		case IEEE80211_ELEMID_RSN:
1446 			printrsnie(" RSN", vp, 2+vp[1], maxcols);
1447 			break;
1448 		default:
1449 			printie(" ???", vp, 2+vp[1], maxcols);
1450 			break;
1451 		}
1452 		ielen -= 2+vp[1];
1453 		vp += 2+vp[1];
1454 	}
1455 }
1456 
1457 static void
1458 list_scan(int s)
1459 {
1460 	uint8_t buf[24*1024];
1461 	struct ieee80211req ireq;
1462 	char ssid[IEEE80211_NWID_LEN+1];
1463 	uint8_t *cp;
1464 	int len, ssidmax;
1465 
1466 	(void) memset(&ireq, 0, sizeof(ireq));
1467 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1468 	ireq.i_type = IEEE80211_IOC_SCAN_RESULTS;
1469 	ireq.i_data = buf;
1470 	ireq.i_len = sizeof(buf);
1471 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1472 		errx(1, "unable to get scan results");
1473 	len = ireq.i_len;
1474 	if (len < sizeof(struct ieee80211req_scan_result))
1475 		return;
1476 
1477 	getchaninfo(s);
1478 
1479 	ssidmax = verbose ? IEEE80211_NWID_LEN : 14;
1480 	printf("%-*.*s  %-17.17s  %4s %4s  %-7s  %3s %4s\n"
1481 		, ssidmax, ssidmax, "SSID"
1482 		, "BSSID"
1483 		, "CHAN"
1484 		, "RATE"
1485 		, " S:N"
1486 		, "INT"
1487 		, "CAPS"
1488 	);
1489 	cp = buf;
1490 	do {
1491 		const struct ieee80211req_scan_result *sr;
1492 		const uint8_t *vp;
1493 
1494 		sr = (const struct ieee80211req_scan_result *) cp;
1495 		vp = ((const u_int8_t *)sr) + sr->isr_ie_off;
1496 		printf("%-*.*s  %s  %3d  %3dM %3d:%-3d  %3d %-4.4s"
1497 			, ssidmax
1498 			  , copy_essid(ssid, ssidmax, vp, sr->isr_ssid_len)
1499 			  , ssid
1500 			, ether_ntoa((const struct ether_addr *) sr->isr_bssid)
1501 			, ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
1502 			, getmaxrate(sr->isr_rates, sr->isr_nrates)
1503 			, (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise
1504 			, sr->isr_intval
1505 			, getcaps(sr->isr_capinfo)
1506 		);
1507 		printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);
1508 		printf("\n");
1509 		cp += sr->isr_len, len -= sr->isr_len;
1510 	} while (len >= sizeof(struct ieee80211req_scan_result));
1511 }
1512 
1513 #include <net80211/ieee80211_freebsd.h>
1514 
1515 static void
1516 scan_and_wait(int s)
1517 {
1518 	struct ieee80211req ireq;
1519 	int sroute;
1520 
1521 	sroute = socket(PF_ROUTE, SOCK_RAW, 0);
1522 	if (sroute < 0) {
1523 		perror("socket(PF_ROUTE,SOCK_RAW)");
1524 		return;
1525 	}
1526 	(void) memset(&ireq, 0, sizeof(ireq));
1527 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1528 	ireq.i_type = IEEE80211_IOC_SCAN_REQ;
1529 	/* NB: only root can trigger a scan so ignore errors */
1530 	if (ioctl(s, SIOCS80211, &ireq) >= 0) {
1531 		char buf[2048];
1532 		struct if_announcemsghdr *ifan;
1533 		struct rt_msghdr *rtm;
1534 
1535 		do {
1536 			if (read(sroute, buf, sizeof(buf)) < 0) {
1537 				perror("read(PF_ROUTE)");
1538 				break;
1539 			}
1540 			rtm = (struct rt_msghdr *) buf;
1541 			if (rtm->rtm_version != RTM_VERSION)
1542 				break;
1543 			ifan = (struct if_announcemsghdr *) rtm;
1544 		} while (rtm->rtm_type != RTM_IEEE80211 ||
1545 		    ifan->ifan_what != RTM_IEEE80211_SCAN);
1546 	}
1547 	close(sroute);
1548 }
1549 
1550 static
1551 DECL_CMD_FUNC(set80211scan, val, d)
1552 {
1553 	scan_and_wait(s);
1554 	list_scan(s);
1555 }
1556 
1557 static enum ieee80211_opmode get80211opmode(int s);
1558 
1559 static void
1560 list_stations(int s)
1561 {
1562 	union {
1563 		struct ieee80211req_sta_req req;
1564 		uint8_t buf[24*1024];
1565 	} u;
1566 	enum ieee80211_opmode opmode = get80211opmode(s);
1567 	struct ieee80211req ireq;
1568 	const uint8_t *cp;
1569 	int len;
1570 
1571 	(void) memset(&ireq, 0, sizeof(ireq));
1572 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1573 	/* broadcast address =>'s get all stations */
1574 	(void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
1575 	if (opmode == IEEE80211_M_STA) {
1576 		/*
1577 		 * Get information about the associated AP.
1578 		 */
1579 		ireq.i_type = IEEE80211_IOC_BSSID;
1580 		ireq.i_data = u.req.is_u.macaddr;
1581 		ireq.i_len = IEEE80211_ADDR_LEN;
1582 		(void) ioctl(s, SIOCG80211, &ireq);
1583 	}
1584 	ireq.i_type = IEEE80211_IOC_STA_INFO;
1585 	ireq.i_data = &u;
1586 	ireq.i_len = sizeof(u);
1587 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1588 		errx(1, "unable to get station information");
1589 	len = ireq.i_len;
1590 	if (len < sizeof(struct ieee80211req_sta_info))
1591 		return;
1592 
1593 	getchaninfo(s);
1594 
1595 	printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %4s\n"
1596 		, "ADDR"
1597 		, "AID"
1598 		, "CHAN"
1599 		, "RATE"
1600 		, "RSSI"
1601 		, "IDLE"
1602 		, "TXSEQ"
1603 		, "RXSEQ"
1604 		, "CAPS"
1605 		, "FLAG"
1606 	);
1607 	cp = (const uint8_t *) u.req.info;
1608 	do {
1609 		const struct ieee80211req_sta_info *si;
1610 
1611 		si = (const struct ieee80211req_sta_info *) cp;
1612 		if (si->isi_len < sizeof(*si))
1613 			break;
1614 		printf("%s %4u %4d %3dM %3.1f %4d %6d %6d %-4.4s %-4.4s"
1615 			, ether_ntoa((const struct ether_addr*) si->isi_macaddr)
1616 			, IEEE80211_AID(si->isi_associd)
1617 			, ieee80211_mhz2ieee(si->isi_freq, si->isi_flags)
1618 			, (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL)/2
1619 			, si->isi_rssi/2.
1620 			, si->isi_inact
1621 			, si->isi_txseqs[0]
1622 			, si->isi_rxseqs[0]
1623 			, getcaps(si->isi_capinfo)
1624 			, getflags(si->isi_state)
1625 		);
1626 		printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
1627 		printf("\n");
1628 		cp += si->isi_len, len -= si->isi_len;
1629 	} while (len >= sizeof(struct ieee80211req_sta_info));
1630 }
1631 
1632 static const char *
1633 get_chaninfo(const struct ieee80211_channel *c, int precise,
1634 	char buf[], size_t bsize)
1635 {
1636 	buf[0] = '\0';
1637 	if (IEEE80211_IS_CHAN_FHSS(c))
1638 		strlcat(buf, " FHSS", bsize);
1639 	if (IEEE80211_IS_CHAN_A(c)) {
1640 		if (IEEE80211_IS_CHAN_HALF(c))
1641 			strlcat(buf, " 11a/10Mhz", bsize);
1642 		else if (IEEE80211_IS_CHAN_QUARTER(c))
1643 			strlcat(buf, " 11a/5Mhz", bsize);
1644 		else
1645 			strlcat(buf, " 11a", bsize);
1646 	}
1647 	if (IEEE80211_IS_CHAN_ANYG(c)) {
1648 		if (IEEE80211_IS_CHAN_HALF(c))
1649 			strlcat(buf, " 11g/10Mhz", bsize);
1650 		else if (IEEE80211_IS_CHAN_QUARTER(c))
1651 			strlcat(buf, " 11g/5Mhz", bsize);
1652 		else
1653 			strlcat(buf, " 11g", bsize);
1654 	} else if (IEEE80211_IS_CHAN_B(c))
1655 		strlcat(buf, " 11b", bsize);
1656 	if (IEEE80211_IS_CHAN_TURBO(c))
1657 		strlcat(buf, " Turbo", bsize);
1658 	if (precise) {
1659 		if (IEEE80211_IS_CHAN_HT20(c))
1660 			strlcat(buf, " ht/20", bsize);
1661 		else if (IEEE80211_IS_CHAN_HT40D(c))
1662 			strlcat(buf, " ht/40-", bsize);
1663 		else if (IEEE80211_IS_CHAN_HT40U(c))
1664 			strlcat(buf, " ht/40+", bsize);
1665 	} else {
1666 		if (IEEE80211_IS_CHAN_HT(c))
1667 			strlcat(buf, " ht", bsize);
1668 	}
1669 	return buf;
1670 }
1671 
1672 static void
1673 print_chaninfo(const struct ieee80211_channel *c)
1674 {
1675 	char buf[14];
1676 
1677 	printf("Channel %3u : %u%c Mhz%-14.14s",
1678 		ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
1679 		IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
1680 		get_chaninfo(c, verbose, buf, sizeof(buf)));
1681 }
1682 
1683 static void
1684 list_channels(int s, int allchans)
1685 {
1686 	struct ieee80211req_chaninfo achans;
1687 	uint8_t reported[IEEE80211_CHAN_BYTES];
1688 	const struct ieee80211_channel *c;
1689 	int i, half;
1690 
1691 	getchaninfo(s);
1692 	memset(&achans, 0, sizeof(achans));
1693 	memset(reported, 0, sizeof(reported));
1694 	if (!allchans) {
1695 		struct ieee80211req_chanlist active;
1696 		struct ieee80211req ireq;
1697 
1698 		(void) memset(&ireq, 0, sizeof(ireq));
1699 		(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1700 		ireq.i_type = IEEE80211_IOC_CHANLIST;
1701 		ireq.i_data = &active;
1702 		ireq.i_len = sizeof(active);
1703 		if (ioctl(s, SIOCG80211, &ireq) < 0)
1704 			errx(1, "unable to get active channel list");
1705 		memset(&achans, 0, sizeof(achans));
1706 		for (i = 0; i < chaninfo.ic_nchans; i++) {
1707 			c = &chaninfo.ic_chans[i];
1708 			if (!isset(active.ic_channels, c->ic_ieee))
1709 				continue;
1710 			/*
1711 			 * Suppress compatible duplicates unless
1712 			 * verbose.  The kernel gives us it's
1713 			 * complete channel list which has separate
1714 			 * entries for 11g/11b and 11a/turbo.
1715 			 */
1716 			if (isset(reported, c->ic_ieee) && !verbose) {
1717 				/* XXX we assume duplicates are adjacent */
1718 				achans.ic_chans[achans.ic_nchans-1] = *c;
1719 			} else {
1720 				achans.ic_chans[achans.ic_nchans++] = *c;
1721 				setbit(reported, c->ic_ieee);
1722 			}
1723 		}
1724 	} else {
1725 		for (i = 0; i < chaninfo.ic_nchans; i++) {
1726 			c = &chaninfo.ic_chans[i];
1727 			/* suppress duplicates as above */
1728 			if (isset(reported, c->ic_ieee) && !verbose) {
1729 				/* XXX we assume duplicates are adjacent */
1730 				achans.ic_chans[achans.ic_nchans-1] = *c;
1731 			} else {
1732 				achans.ic_chans[achans.ic_nchans++] = *c;
1733 				setbit(reported, c->ic_ieee);
1734 			}
1735 		}
1736 	}
1737 	half = achans.ic_nchans / 2;
1738 	if (achans.ic_nchans % 2)
1739 		half++;
1740 
1741 	for (i = 0; i < achans.ic_nchans / 2; i++) {
1742 		print_chaninfo(&achans.ic_chans[i]);
1743 		print_chaninfo(&achans.ic_chans[half+i]);
1744 		printf("\n");
1745 	}
1746 	if (achans.ic_nchans % 2) {
1747 		print_chaninfo(&achans.ic_chans[i]);
1748 		printf("\n");
1749 	}
1750 }
1751 
1752 static void
1753 print_txpow(const struct ieee80211_channel *c)
1754 {
1755 	printf("Channel %3u : %u Mhz %3.1f reg %2d  ",
1756 	    c->ic_ieee, c->ic_freq,
1757 	    c->ic_maxpower/2., c->ic_maxregpower);
1758 }
1759 
1760 static void
1761 print_txpow_verbose(const struct ieee80211_channel *c)
1762 {
1763 	print_chaninfo(c);
1764 	printf("min %4.1f dBm  max %3.1f dBm  reg %2d dBm",
1765 	    c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
1766 	/* indicate where regulatory cap limits power use */
1767 	if (c->ic_maxpower > 2*c->ic_maxregpower)
1768 		printf(" <");
1769 }
1770 
1771 static void
1772 list_txpow(int s)
1773 {
1774 	struct ieee80211req_chaninfo achans;
1775 	uint8_t reported[IEEE80211_CHAN_BYTES];
1776 	struct ieee80211_channel *c, *prev;
1777 	int i, half;
1778 
1779 	getchaninfo(s);
1780 	memset(&achans, 0, sizeof(achans));
1781 	memset(reported, 0, sizeof(reported));
1782 	for (i = 0; i < chaninfo.ic_nchans; i++) {
1783 		c = &chaninfo.ic_chans[i];
1784 		/* suppress duplicates as above */
1785 		if (isset(reported, c->ic_ieee) && !verbose) {
1786 			/* XXX we assume duplicates are adjacent */
1787 			prev = &achans.ic_chans[achans.ic_nchans-1];
1788 			/* display highest power on channel */
1789 			if (c->ic_maxpower > prev->ic_maxpower)
1790 				*prev = *c;
1791 		} else {
1792 			achans.ic_chans[achans.ic_nchans++] = *c;
1793 			setbit(reported, c->ic_ieee);
1794 		}
1795 	}
1796 	if (!verbose) {
1797 		half = achans.ic_nchans / 2;
1798 		if (achans.ic_nchans % 2)
1799 			half++;
1800 
1801 		for (i = 0; i < achans.ic_nchans / 2; i++) {
1802 			print_txpow(&achans.ic_chans[i]);
1803 			print_txpow(&achans.ic_chans[half+i]);
1804 			printf("\n");
1805 		}
1806 		if (achans.ic_nchans % 2) {
1807 			print_txpow(&achans.ic_chans[i]);
1808 			printf("\n");
1809 		}
1810 	} else {
1811 		for (i = 0; i < achans.ic_nchans; i++) {
1812 			print_txpow_verbose(&achans.ic_chans[i]);
1813 			printf("\n");
1814 		}
1815 	}
1816 }
1817 
1818 static void
1819 list_keys(int s)
1820 {
1821 }
1822 
1823 #define	IEEE80211_C_BITS \
1824 "\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\7FF\10TURBOP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \
1825 "\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \
1826 "\31WPA2\32BURST\33WME\34WDS\36BGSCAN\37TXFRAG"
1827 
1828 static void
1829 list_capabilities(int s)
1830 {
1831 	struct ieee80211req ireq;
1832 	u_int32_t caps;
1833 
1834 	(void) memset(&ireq, 0, sizeof(ireq));
1835 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1836 	ireq.i_type = IEEE80211_IOC_DRIVER_CAPS;
1837 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1838 		errx(1, "unable to get driver capabilities");
1839 	caps = (((u_int16_t) ireq.i_val) << 16) | ((u_int16_t) ireq.i_len);
1840 	printb(name, caps, IEEE80211_C_BITS);
1841 	putchar('\n');
1842 }
1843 
1844 static void
1845 list_wme(int s)
1846 {
1847 	static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
1848 	struct ieee80211req ireq;
1849 	int ac;
1850 
1851 	(void) memset(&ireq, 0, sizeof(ireq));
1852 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1853 	ireq.i_len = 0;
1854 	for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
1855 again:
1856 		if (ireq.i_len & IEEE80211_WMEPARAM_BSS)
1857 			printf("\t%s", "     ");
1858 		else
1859 			printf("\t%s", acnames[ac]);
1860 
1861 		ireq.i_len = (ireq.i_len & IEEE80211_WMEPARAM_BSS) | ac;
1862 
1863 		/* show WME BSS parameters */
1864 		ireq.i_type = IEEE80211_IOC_WME_CWMIN;
1865 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1866 			printf(" cwmin %2u", ireq.i_val);
1867 		ireq.i_type = IEEE80211_IOC_WME_CWMAX;
1868 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1869 			printf(" cwmax %2u", ireq.i_val);
1870 		ireq.i_type = IEEE80211_IOC_WME_AIFS;
1871 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1872 			printf(" aifs %2u", ireq.i_val);
1873 		ireq.i_type = IEEE80211_IOC_WME_TXOPLIMIT;
1874 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1875 			printf(" txopLimit %3u", ireq.i_val);
1876 		ireq.i_type = IEEE80211_IOC_WME_ACM;
1877 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1878 			if (ireq.i_val)
1879 				printf(" acm");
1880 			else if (verbose)
1881 				printf(" -acm");
1882 		}
1883 		/* !BSS only */
1884 		if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) {
1885 			ireq.i_type = IEEE80211_IOC_WME_ACKPOLICY;
1886 			if (ioctl(s, SIOCG80211, &ireq) != -1) {
1887 				if (!ireq.i_val)
1888 					printf(" -ack");
1889 				else if (verbose)
1890 					printf(" ack");
1891 			}
1892 		}
1893 		printf("\n");
1894 		if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) {
1895 			ireq.i_len |= IEEE80211_WMEPARAM_BSS;
1896 			goto again;
1897 		} else
1898 			ireq.i_len &= ~IEEE80211_WMEPARAM_BSS;
1899 	}
1900 }
1901 
1902 static void
1903 list_mac(int s)
1904 {
1905 	struct ieee80211req ireq;
1906 	struct ieee80211req_maclist *acllist;
1907 	int i, nacls, policy;
1908 	char c;
1909 
1910 	(void) memset(&ireq, 0, sizeof(ireq));
1911 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
1912 	ireq.i_type = IEEE80211_IOC_MACCMD;
1913 	ireq.i_val = IEEE80211_MACCMD_POLICY;
1914 	if (ioctl(s, SIOCG80211, &ireq) < 0) {
1915 		if (errno == EINVAL) {
1916 			printf("No acl policy loaded\n");
1917 			return;
1918 		}
1919 		err(1, "unable to get mac policy");
1920 	}
1921 	policy = ireq.i_val;
1922 
1923 	ireq.i_val = IEEE80211_MACCMD_LIST;
1924 	ireq.i_len = 0;
1925 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1926 		err(1, "unable to get mac acl list size");
1927 	if (ireq.i_len == 0)		/* NB: no acls */
1928 		return;
1929 
1930 	ireq.i_data = malloc(ireq.i_len);
1931 	if (ireq.i_data == NULL)
1932 		err(1, "out of memory for acl list");
1933 
1934 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1935 		err(1, "unable to get mac acl list");
1936 	if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
1937 		if (verbose)
1938 			printf("policy: open\n");
1939 		c = '*';
1940 	} else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
1941 		if (verbose)
1942 			printf("policy: allow\n");
1943 		c = '+';
1944 	} else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
1945 		if (verbose)
1946 			printf("policy: deny\n");
1947 		c = '-';
1948 	} else {
1949 		printf("policy: unknown (%u)\n", policy);
1950 		c = '?';
1951 	}
1952 	nacls = ireq.i_len / sizeof(*acllist);
1953 	acllist = (struct ieee80211req_maclist *) ireq.i_data;
1954 	for (i = 0; i < nacls; i++)
1955 		printf("%c%s\n", c, ether_ntoa(
1956 			(const struct ether_addr *) acllist[i].ml_macaddr));
1957 }
1958 
1959 static
1960 DECL_CMD_FUNC(set80211list, arg, d)
1961 {
1962 #define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
1963 
1964 	if (iseq(arg, "sta"))
1965 		list_stations(s);
1966 	else if (iseq(arg, "scan") || iseq(arg, "ap"))
1967 		list_scan(s);
1968 	else if (iseq(arg, "chan") || iseq(arg, "freq"))
1969 		list_channels(s, 1);
1970 	else if (iseq(arg, "active"))
1971 		list_channels(s, 0);
1972 	else if (iseq(arg, "keys"))
1973 		list_keys(s);
1974 	else if (iseq(arg, "caps"))
1975 		list_capabilities(s);
1976 	else if (iseq(arg, "wme"))
1977 		list_wme(s);
1978 	else if (iseq(arg, "mac"))
1979 		list_mac(s);
1980 	else if (iseq(arg, "txpow"))
1981 		list_txpow(s);
1982 	else
1983 		errx(1, "Don't know how to list %s for %s", arg, name);
1984 #undef iseq
1985 }
1986 
1987 static enum ieee80211_opmode
1988 get80211opmode(int s)
1989 {
1990 	struct ifmediareq ifmr;
1991 
1992 	(void) memset(&ifmr, 0, sizeof(ifmr));
1993 	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
1994 
1995 	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
1996 		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC)
1997 			return IEEE80211_M_IBSS;	/* XXX ahdemo */
1998 		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
1999 			return IEEE80211_M_HOSTAP;
2000 		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
2001 			return IEEE80211_M_MONITOR;
2002 	}
2003 	return IEEE80211_M_STA;
2004 }
2005 
2006 #if 0
2007 static void
2008 printcipher(int s, struct ieee80211req *ireq, int keylenop)
2009 {
2010 	switch (ireq->i_val) {
2011 	case IEEE80211_CIPHER_WEP:
2012 		ireq->i_type = keylenop;
2013 		if (ioctl(s, SIOCG80211, ireq) != -1)
2014 			printf("WEP-%s",
2015 			    ireq->i_len <= 5 ? "40" :
2016 			    ireq->i_len <= 13 ? "104" : "128");
2017 		else
2018 			printf("WEP");
2019 		break;
2020 	case IEEE80211_CIPHER_TKIP:
2021 		printf("TKIP");
2022 		break;
2023 	case IEEE80211_CIPHER_AES_OCB:
2024 		printf("AES-OCB");
2025 		break;
2026 	case IEEE80211_CIPHER_AES_CCM:
2027 		printf("AES-CCM");
2028 		break;
2029 	case IEEE80211_CIPHER_CKIP:
2030 		printf("CKIP");
2031 		break;
2032 	case IEEE80211_CIPHER_NONE:
2033 		printf("NONE");
2034 		break;
2035 	default:
2036 		printf("UNKNOWN (0x%x)", ireq->i_val);
2037 		break;
2038 	}
2039 }
2040 #endif
2041 
2042 #define	MAXCOL	78
2043 static	int col;
2044 static	char spacer;
2045 
2046 static void
2047 LINE_BREAK(void)
2048 {
2049 	if (spacer != '\t') {
2050 		printf("\n");
2051 		spacer = '\t';
2052 	}
2053 	col = 8;	/* 8-col tab */
2054 }
2055 
2056 static void
2057 LINE_CHECK(const char *fmt, ...)
2058 {
2059 	char buf[80];
2060 	va_list ap;
2061 	int n;
2062 
2063 	va_start(ap, fmt);
2064 	n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
2065 	va_end(ap);
2066 	col += 1+n;
2067 	if (col > MAXCOL) {
2068 		LINE_BREAK();
2069 		col += n;
2070 	}
2071 	buf[0] = spacer;
2072 	printf("%s", buf);
2073 	spacer = ' ';
2074 }
2075 
2076 static void
2077 printkey(const struct ieee80211req_key *ik)
2078 {
2079 	static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
2080 	int keylen = ik->ik_keylen;
2081 	int printcontents;
2082 
2083 	printcontents = printkeys &&
2084 		(memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
2085 	if (printcontents)
2086 		LINE_BREAK();
2087 	switch (ik->ik_type) {
2088 	case IEEE80211_CIPHER_WEP:
2089 		/* compatibility */
2090 		LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
2091 		    keylen <= 5 ? "40-bit" :
2092 		    keylen <= 13 ? "104-bit" : "128-bit");
2093 		break;
2094 	case IEEE80211_CIPHER_TKIP:
2095 		if (keylen > 128/8)
2096 			keylen -= 128/8;	/* ignore MIC for now */
2097 		LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2098 		break;
2099 	case IEEE80211_CIPHER_AES_OCB:
2100 		LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2101 		break;
2102 	case IEEE80211_CIPHER_AES_CCM:
2103 		LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2104 		break;
2105 	case IEEE80211_CIPHER_CKIP:
2106 		LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2107 		break;
2108 	case IEEE80211_CIPHER_NONE:
2109 		LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2110 		break;
2111 	default:
2112 		LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
2113 			ik->ik_type, ik->ik_keyix+1, 8*keylen);
2114 		break;
2115 	}
2116 	if (printcontents) {
2117 		int i;
2118 
2119 		printf(" <");
2120 		for (i = 0; i < keylen; i++)
2121 			printf("%02x", ik->ik_keydata[i]);
2122 		printf(">");
2123 		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
2124 		    (ik->ik_keyrsc != 0 || verbose))
2125 			printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
2126 		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
2127 		    (ik->ik_keytsc != 0 || verbose))
2128 			printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
2129 		if (ik->ik_flags != 0 && verbose) {
2130 			const char *sep = " ";
2131 
2132 			if (ik->ik_flags & IEEE80211_KEY_XMIT)
2133 				printf("%stx", sep), sep = "+";
2134 			if (ik->ik_flags & IEEE80211_KEY_RECV)
2135 				printf("%srx", sep), sep = "+";
2136 			if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
2137 				printf("%sdef", sep), sep = "+";
2138 		}
2139 		LINE_BREAK();
2140 	}
2141 }
2142 
2143 static void
2144 ieee80211_status(int s)
2145 {
2146 	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
2147 	enum ieee80211_opmode opmode = get80211opmode(s);
2148 	int i, num, wpa, wme, bgscan, bgscaninterval;
2149 	struct ieee80211req ireq;
2150 	u_int8_t data[32];
2151 	struct ieee80211_channel chan;
2152 	const struct ieee80211_channel *c;
2153 
2154 	(void) memset(&ireq, 0, sizeof(ireq));
2155 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2156 	ireq.i_data = &data;
2157 
2158 	wpa = 0;		/* unknown/not set */
2159 	bgscan = 0;		/* unknown/not set */
2160 
2161 	ireq.i_type = IEEE80211_IOC_SSID;
2162 	ireq.i_val = -1;
2163 	if (ioctl(s, SIOCG80211, &ireq) < 0) {
2164 		/* If we can't get the SSID, this isn't an 802.11 device. */
2165 		return;
2166 	}
2167 	num = 0;
2168 	ireq.i_type = IEEE80211_IOC_NUMSSIDS;
2169 	if (ioctl(s, SIOCG80211, &ireq) >= 0)
2170 		num = ireq.i_val;
2171 	printf("\tssid ");
2172 	if (num > 1) {
2173 		ireq.i_type = IEEE80211_IOC_SSID;
2174 		for (ireq.i_val = 0; ireq.i_val < num; ireq.i_val++) {
2175 			if (ioctl(s, SIOCG80211, &ireq) >= 0 && ireq.i_len > 0) {
2176 				printf(" %d:", ireq.i_val + 1);
2177 				print_string(data, ireq.i_len);
2178 			}
2179 		}
2180 	} else
2181 		print_string(data, ireq.i_len);
2182 
2183 	ireq.i_data = &chan;
2184 	ireq.i_len = sizeof(chan);
2185 	ireq.i_type = IEEE80211_IOC_CURCHAN;
2186 	if (ioctl(s, SIOCG80211, &ireq) < 0) {
2187 		/* fall back to legacy ioctl */
2188 		ireq.i_data = NULL;
2189 		ireq.i_len = 0;
2190 		ireq.i_type = IEEE80211_IOC_CHANNEL;
2191 		if (ioctl(s, SIOCG80211, &ireq) < 0)
2192 			goto end;
2193 		getchaninfo(s);
2194 		mapchan(&chan, ireq.i_val, 0);
2195 	}
2196 	c = &chan;
2197 	if (c->ic_freq != IEEE80211_CHAN_ANY) {
2198 		char buf[14];
2199 		printf(" channel %d (%u Mhz%s)", c->ic_ieee, c->ic_freq,
2200 			get_chaninfo(c, 1, buf, sizeof(buf)));
2201 	} else if (verbose)
2202 		printf(" channel UNDEF");
2203 	ireq.i_data = &data;    /* reset data buffer */
2204 
2205 	ireq.i_type = IEEE80211_IOC_BSSID;
2206 	ireq.i_len = IEEE80211_ADDR_LEN;
2207 	if (ioctl(s, SIOCG80211, &ireq) >= 0 &&
2208 	    (memcmp(ireq.i_data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
2209 		printf(" bssid %s", ether_ntoa(ireq.i_data));
2210 
2211 	ireq.i_type = IEEE80211_IOC_STATIONNAME;
2212 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
2213 		printf("\n\tstationname ");
2214 		print_string(data, ireq.i_len);
2215 	}
2216 
2217 	spacer = ' ';		/* force first break */
2218 	LINE_BREAK();
2219 
2220 	ireq.i_type = IEEE80211_IOC_AUTHMODE;
2221 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
2222 		switch (ireq.i_val) {
2223 			case IEEE80211_AUTH_NONE:
2224 				LINE_CHECK("authmode NONE");
2225 				break;
2226 			case IEEE80211_AUTH_OPEN:
2227 				LINE_CHECK("authmode OPEN");
2228 				break;
2229 			case IEEE80211_AUTH_SHARED:
2230 				LINE_CHECK("authmode SHARED");
2231 				break;
2232 			case IEEE80211_AUTH_8021X:
2233 				LINE_CHECK("authmode 802.1x");
2234 				break;
2235 			case IEEE80211_AUTH_WPA:
2236 				ireq.i_type = IEEE80211_IOC_WPA;
2237 				if (ioctl(s, SIOCG80211, &ireq) != -1)
2238 					wpa = ireq.i_val;
2239 				if (!wpa)
2240 					wpa = 1;	/* default to WPA1 */
2241 				switch (wpa) {
2242 				case 2:
2243 					LINE_CHECK("authmode WPA2/802.11i");
2244 					break;
2245 				case 3:
2246 					LINE_CHECK("authmode WPA1+WPA2/802.11i");
2247 					break;
2248 				default:
2249 					LINE_CHECK("authmode WPA");
2250 					break;
2251 				}
2252 				break;
2253 			case IEEE80211_AUTH_AUTO:
2254 				LINE_CHECK("authmode AUTO");
2255 				break;
2256 			default:
2257 				LINE_CHECK("authmode UNKNOWN (0x%x)",
2258 					ireq.i_val);
2259 				break;
2260 		}
2261 	}
2262 
2263 	ireq.i_type = IEEE80211_IOC_WEP;
2264 	if (ioctl(s, SIOCG80211, &ireq) != -1 &&
2265 	    ireq.i_val != IEEE80211_WEP_NOSUP) {
2266 		int firstkey, wepmode;
2267 
2268 		wepmode = ireq.i_val;
2269 		switch (wepmode) {
2270 			case IEEE80211_WEP_OFF:
2271 				LINE_CHECK("privacy OFF");
2272 				break;
2273 			case IEEE80211_WEP_ON:
2274 				LINE_CHECK("privacy ON");
2275 				break;
2276 			case IEEE80211_WEP_MIXED:
2277 				LINE_CHECK("privacy MIXED");
2278 				break;
2279 			default:
2280 				LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
2281 				break;
2282 		}
2283 
2284 		/*
2285 		 * If we get here then we've got WEP support so we need
2286 		 * to print WEP status.
2287 		 */
2288 
2289 		ireq.i_type = IEEE80211_IOC_WEPTXKEY;
2290 		if (ioctl(s, SIOCG80211, &ireq) < 0) {
2291 			warn("WEP support, but no tx key!");
2292 			goto end;
2293 		}
2294 		if (ireq.i_val != -1)
2295 			LINE_CHECK("deftxkey %d", ireq.i_val+1);
2296 		else if (wepmode != IEEE80211_WEP_OFF || verbose)
2297 			LINE_CHECK("deftxkey UNDEF");
2298 
2299 		ireq.i_type = IEEE80211_IOC_NUMWEPKEYS;
2300 		if (ioctl(s, SIOCG80211, &ireq) < 0) {
2301 			warn("WEP support, but no NUMWEPKEYS support!");
2302 			goto end;
2303 		}
2304 		num = ireq.i_val;
2305 
2306 		firstkey = 1;
2307 		for (i = 0; i < num; i++) {
2308 			struct ieee80211req_key ik;
2309 
2310 			memset(&ik, 0, sizeof(ik));
2311 			ik.ik_keyix = i;
2312 			ireq.i_type = IEEE80211_IOC_WPAKEY;
2313 			ireq.i_data = &ik;
2314 			ireq.i_len = sizeof(ik);
2315 			if (ioctl(s, SIOCG80211, &ireq) < 0) {
2316 				warn("WEP support, but can get keys!");
2317 				goto end;
2318 			}
2319 			if (ik.ik_keylen != 0) {
2320 				if (verbose)
2321 					LINE_BREAK();
2322 				printkey(&ik);
2323 				firstkey = 0;
2324 			}
2325 		}
2326 		ireq.i_data = &data;    /* reset data buffer */
2327 	}
2328 
2329 	ireq.i_type = IEEE80211_IOC_POWERSAVE;
2330 	if (ioctl(s, SIOCG80211, &ireq) != -1 &&
2331 	    ireq.i_val != IEEE80211_POWERSAVE_NOSUP ) {
2332 		if (ireq.i_val != IEEE80211_POWERSAVE_OFF || verbose) {
2333 			switch (ireq.i_val) {
2334 				case IEEE80211_POWERSAVE_OFF:
2335 					LINE_CHECK("powersavemode OFF");
2336 					break;
2337 				case IEEE80211_POWERSAVE_CAM:
2338 					LINE_CHECK("powersavemode CAM");
2339 					break;
2340 				case IEEE80211_POWERSAVE_PSP:
2341 					LINE_CHECK("powersavemode PSP");
2342 					break;
2343 				case IEEE80211_POWERSAVE_PSP_CAM:
2344 					LINE_CHECK("powersavemode PSP-CAM");
2345 					break;
2346 			}
2347 			ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP;
2348 			if (ioctl(s, SIOCG80211, &ireq) != -1)
2349 				LINE_CHECK("powersavesleep %d", ireq.i_val);
2350 		}
2351 	}
2352 
2353 	ireq.i_type = IEEE80211_IOC_TXPOWMAX;
2354 	if (ioctl(s, SIOCG80211, &ireq) != -1)
2355 		LINE_CHECK("txpowmax %d", ireq.i_val);
2356 
2357 	if (verbose) {
2358 		ireq.i_type = IEEE80211_IOC_TXPOWER;
2359 		if (ioctl(s, SIOCG80211, &ireq) != -1)
2360 			LINE_CHECK("txpower %d", ireq.i_val);
2361 	}
2362 
2363 	ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD;
2364 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
2365 		if (ireq.i_val != IEEE80211_RTS_MAX || verbose)
2366 			LINE_CHECK("rtsthreshold %d", ireq.i_val);
2367 	}
2368 
2369 	ireq.i_type = IEEE80211_IOC_FRAGTHRESHOLD;
2370 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
2371 		if (ireq.i_val != IEEE80211_FRAG_MAX || verbose)
2372 			LINE_CHECK("fragthreshold %d", ireq.i_val);
2373 	}
2374 	ireq.i_type = IEEE80211_IOC_BMISSTHRESHOLD;
2375 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
2376 		if (ireq.i_val != IEEE80211_HWBMISS_MAX || verbose)
2377 			LINE_CHECK("bmiss %d", ireq.i_val);
2378 	}
2379 
2380 	ireq.i_type = IEEE80211_IOC_MCAST_RATE;
2381 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
2382 		if (ireq.i_val != 2*1 || verbose) {
2383 			if (ireq.i_val == 11)
2384 				LINE_CHECK("mcastrate 5.5");
2385 			else
2386 				LINE_CHECK("mcastrate %d", ireq.i_val/2);
2387 		}
2388 	}
2389 
2390 	ireq.i_type = IEEE80211_IOC_BGSCAN_INTERVAL;
2391 	if (ioctl(s, SIOCG80211, &ireq) != -1)
2392 		bgscaninterval = ireq.i_val;
2393 	else
2394 		bgscaninterval = -1;
2395 
2396 	ireq.i_type = IEEE80211_IOC_SCANVALID;
2397 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
2398 		if (ireq.i_val != bgscaninterval || verbose)
2399 			LINE_CHECK("scanvalid %u", ireq.i_val);
2400 	}
2401 
2402 	ireq.i_type = IEEE80211_IOC_BGSCAN;
2403 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
2404 		bgscan = ireq.i_val;
2405 		if (ireq.i_val)
2406 			LINE_CHECK("bgscan");
2407 		else if (verbose)
2408 			LINE_CHECK("-bgscan");
2409 	}
2410 	if (bgscan || verbose) {
2411 		if (bgscaninterval != -1)
2412 			LINE_CHECK("bgscanintvl %u", bgscaninterval);
2413 		ireq.i_type = IEEE80211_IOC_BGSCAN_IDLE;
2414 		if (ioctl(s, SIOCG80211, &ireq) != -1)
2415 			LINE_CHECK("bgscanidle %u", ireq.i_val);
2416 		if (IEEE80211_IS_CHAN_A(c) || verbose) {
2417 			ireq.i_type = IEEE80211_IOC_ROAM_RSSI_11A;
2418 			if (ioctl(s, SIOCG80211, &ireq) != -1)
2419 				LINE_CHECK("roam:rssi11a %d", ireq.i_val);
2420 			ireq.i_type = IEEE80211_IOC_ROAM_RATE_11A;
2421 			if (ioctl(s, SIOCG80211, &ireq) != -1)
2422 				LINE_CHECK("roam:rate11a %u", ireq.i_val/2);
2423 		}
2424 		if (IEEE80211_IS_CHAN_B(c) || verbose) {
2425 			ireq.i_type = IEEE80211_IOC_ROAM_RSSI_11B;
2426 			if (ioctl(s, SIOCG80211, &ireq) != -1)
2427 				LINE_CHECK("roam:rssi11b %d", ireq.i_val);
2428 			ireq.i_type = IEEE80211_IOC_ROAM_RATE_11B;
2429 			if (ioctl(s, SIOCG80211, &ireq) != -1)
2430 				LINE_CHECK("roam:rate11b %u", ireq.i_val/2);
2431 		}
2432 		if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
2433 			ireq.i_type = IEEE80211_IOC_ROAM_RSSI_11G;
2434 			if (ioctl(s, SIOCG80211, &ireq) != -1)
2435 				LINE_CHECK("roam:rssi11g %d", ireq.i_val);
2436 			ireq.i_type = IEEE80211_IOC_ROAM_RATE_11G;
2437 			if (ioctl(s, SIOCG80211, &ireq) != -1)
2438 				LINE_CHECK("roam:rate11g %u", ireq.i_val/2);
2439 		}
2440 	}
2441 
2442 	if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
2443 		ireq.i_type = IEEE80211_IOC_PUREG;
2444 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
2445 			if (ireq.i_val)
2446 				LINE_CHECK("pureg");
2447 			else if (verbose)
2448 				LINE_CHECK("-pureg");
2449 		}
2450 		ireq.i_type = IEEE80211_IOC_PROTMODE;
2451 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
2452 			switch (ireq.i_val) {
2453 				case IEEE80211_PROTMODE_OFF:
2454 					LINE_CHECK("protmode OFF");
2455 					break;
2456 				case IEEE80211_PROTMODE_CTS:
2457 					LINE_CHECK("protmode CTS");
2458 					break;
2459 				case IEEE80211_PROTMODE_RTSCTS:
2460 					LINE_CHECK("protmode RTSCTS");
2461 					break;
2462 				default:
2463 					LINE_CHECK("protmode UNKNOWN (0x%x)",
2464 						ireq.i_val);
2465 					break;
2466 			}
2467 		}
2468 	}
2469 
2470 	ireq.i_type = IEEE80211_IOC_WME;
2471 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
2472 		wme = ireq.i_val;
2473 		if (wme)
2474 			LINE_CHECK("wme");
2475 		else if (verbose)
2476 			LINE_CHECK("-wme");
2477 	} else
2478 		wme = 0;
2479 
2480 	ireq.i_type = IEEE80211_IOC_BURST;
2481 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
2482 		if (ireq.i_val)
2483 			LINE_CHECK("burst");
2484 		else if (verbose)
2485 			LINE_CHECK("-burst");
2486 	}
2487 
2488 	ireq.i_type = IEEE80211_IOC_FF;
2489 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
2490 		if (ireq.i_val)
2491 			LINE_CHECK("ff");
2492 		else if (verbose)
2493 			LINE_CHECK("-ff");
2494 	}
2495 	ireq.i_type = IEEE80211_IOC_TURBOP;
2496 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
2497 		if (ireq.i_val)
2498 			LINE_CHECK("dturbo");
2499 		else if (verbose)
2500 			LINE_CHECK("-dturbo");
2501 	}
2502 
2503 	if (opmode == IEEE80211_M_HOSTAP) {
2504 		ireq.i_type = IEEE80211_IOC_HIDESSID;
2505 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
2506 			if (ireq.i_val)
2507 				LINE_CHECK("hidessid");
2508 			else if (verbose)
2509 				LINE_CHECK("-hidessid");
2510 		}
2511 
2512 		ireq.i_type = IEEE80211_IOC_APBRIDGE;
2513 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
2514 			if (!ireq.i_val)
2515 				LINE_CHECK("-apbridge");
2516 			else if (verbose)
2517 				LINE_CHECK("apbridge");
2518 		}
2519 
2520 		ireq.i_type = IEEE80211_IOC_DTIM_PERIOD;
2521 		if (ioctl(s, SIOCG80211, &ireq) != -1)
2522 			LINE_CHECK("dtimperiod %u", ireq.i_val);
2523 
2524 		ireq.i_type = IEEE80211_IOC_DOTH;
2525 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
2526 			if (!ireq.i_val)
2527 				LINE_CHECK("-doth");
2528 			else if (verbose)
2529 				LINE_CHECK("doth");
2530 		}
2531 	} else {
2532 		ireq.i_type = IEEE80211_IOC_ROAMING;
2533 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
2534 			if (ireq.i_val != IEEE80211_ROAMING_AUTO || verbose) {
2535 				switch (ireq.i_val) {
2536 				case IEEE80211_ROAMING_DEVICE:
2537 					LINE_CHECK("roaming DEVICE");
2538 					break;
2539 				case IEEE80211_ROAMING_AUTO:
2540 					LINE_CHECK("roaming AUTO");
2541 					break;
2542 				case IEEE80211_ROAMING_MANUAL:
2543 					LINE_CHECK("roaming MANUAL");
2544 					break;
2545 				default:
2546 					LINE_CHECK("roaming UNKNOWN (0x%x)",
2547 						ireq.i_val);
2548 					break;
2549 				}
2550 			}
2551 		}
2552 	}
2553 	ireq.i_type = IEEE80211_IOC_BEACON_INTERVAL;
2554 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
2555 		if (ireq.i_val)
2556 			LINE_CHECK("bintval %u", ireq.i_val);
2557 		else if (verbose)
2558 			LINE_CHECK("bintval %u", ireq.i_val);
2559 	}
2560 
2561 	if (wme && verbose) {
2562 		LINE_BREAK();
2563 		list_wme(s);
2564 	}
2565 
2566 	if (wpa) {
2567 		ireq.i_type = IEEE80211_IOC_COUNTERMEASURES;
2568 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
2569 			if (ireq.i_val)
2570 				LINE_CHECK("countermeasures");
2571 			else if (verbose)
2572 				LINE_CHECK("-countermeasures");
2573 		}
2574 #if 0
2575 		/* XXX not interesting with WPA done in user space */
2576 		ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
2577 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
2578 		}
2579 
2580 		ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
2581 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
2582 			LINE_CHECK("mcastcipher ");
2583 			printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
2584 			spacer = ' ';
2585 		}
2586 
2587 		ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
2588 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
2589 			LINE_CHECK("ucastcipher ");
2590 			printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
2591 		}
2592 
2593 		if (wpa & 2) {
2594 			ireq.i_type = IEEE80211_IOC_RSNCAPS;
2595 			if (ioctl(s, SIOCG80211, &ireq) != -1) {
2596 				LINE_CHECK("RSN caps 0x%x", ireq.i_val);
2597 				spacer = ' ';
2598 			}
2599 		}
2600 
2601 		ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
2602 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
2603 		}
2604 #endif
2605 		LINE_BREAK();
2606 	}
2607 	LINE_BREAK();
2608 
2609 end:
2610 	return;
2611 }
2612 
2613 static void
2614 set80211(int s, int type, int val, int len, void *data)
2615 {
2616 	struct ieee80211req	ireq;
2617 
2618 	(void) memset(&ireq, 0, sizeof(ireq));
2619 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2620 	ireq.i_type = type;
2621 	ireq.i_val = val;
2622 	ireq.i_len = len;
2623 	ireq.i_data = data;
2624 	if (ioctl(s, SIOCS80211, &ireq) < 0)
2625 		err(1, "SIOCS80211");
2626 }
2627 
2628 static const char *
2629 get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
2630 {
2631 	int len;
2632 	int hexstr;
2633 	u_int8_t *p;
2634 
2635 	len = *lenp;
2636 	p = buf;
2637 	hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
2638 	if (hexstr)
2639 		val += 2;
2640 	for (;;) {
2641 		if (*val == '\0')
2642 			break;
2643 		if (sep != NULL && strchr(sep, *val) != NULL) {
2644 			val++;
2645 			break;
2646 		}
2647 		if (hexstr) {
2648 			if (!isxdigit((u_char)val[0])) {
2649 				warnx("bad hexadecimal digits");
2650 				return NULL;
2651 			}
2652 			if (!isxdigit((u_char)val[1])) {
2653 				warnx("odd count hexadecimal digits");
2654 				return NULL;
2655 			}
2656 		}
2657 		if (p >= buf + len) {
2658 			if (hexstr)
2659 				warnx("hexadecimal digits too long");
2660 			else
2661 				warnx("string too long");
2662 			return NULL;
2663 		}
2664 		if (hexstr) {
2665 #define	tohex(x)	(isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
2666 			*p++ = (tohex((u_char)val[0]) << 4) |
2667 			    tohex((u_char)val[1]);
2668 #undef tohex
2669 			val += 2;
2670 		} else
2671 			*p++ = *val++;
2672 	}
2673 	len = p - buf;
2674 	/* The string "-" is treated as the empty string. */
2675 	if (!hexstr && len == 1 && buf[0] == '-') {
2676 		len = 0;
2677 		memset(buf, 0, *lenp);
2678 	} else if (len < *lenp)
2679 		memset(p, 0, *lenp - len);
2680 	*lenp = len;
2681 	return val;
2682 }
2683 
2684 static void
2685 print_string(const u_int8_t *buf, int len)
2686 {
2687 	int i;
2688 	int hasspc;
2689 
2690 	i = 0;
2691 	hasspc = 0;
2692 	for (; i < len; i++) {
2693 		if (!isprint(buf[i]) && buf[i] != '\0')
2694 			break;
2695 		if (isspace(buf[i]))
2696 			hasspc++;
2697 	}
2698 	if (i == len) {
2699 		if (hasspc || len == 0 || buf[0] == '\0')
2700 			printf("\"%.*s\"", len, buf);
2701 		else
2702 			printf("%.*s", len, buf);
2703 	} else {
2704 		printf("0x");
2705 		for (i = 0; i < len; i++)
2706 			printf("%02x", buf[i]);
2707 	}
2708 }
2709 
2710 static struct cmd ieee80211_cmds[] = {
2711 	DEF_CMD_ARG("ssid",		set80211ssid),
2712 	DEF_CMD_ARG("nwid",		set80211ssid),
2713 	DEF_CMD_ARG("stationname",	set80211stationname),
2714 	DEF_CMD_ARG("station",		set80211stationname),	/* BSD/OS */
2715 	DEF_CMD_ARG("channel",		set80211channel),
2716 	DEF_CMD_ARG("authmode",		set80211authmode),
2717 	DEF_CMD_ARG("powersavemode",	set80211powersavemode),
2718 	DEF_CMD("powersave",	1,	set80211powersave),
2719 	DEF_CMD("-powersave",	0,	set80211powersave),
2720 	DEF_CMD_ARG("powersavesleep", 	set80211powersavesleep),
2721 	DEF_CMD_ARG("wepmode",		set80211wepmode),
2722 	DEF_CMD("wep",		1,	set80211wep),
2723 	DEF_CMD("-wep",		0,	set80211wep),
2724 	DEF_CMD_ARG("deftxkey",		set80211weptxkey),
2725 	DEF_CMD_ARG("weptxkey",		set80211weptxkey),
2726 	DEF_CMD_ARG("wepkey",		set80211wepkey),
2727 	DEF_CMD_ARG("nwkey",		set80211nwkey),		/* NetBSD */
2728 	DEF_CMD("-nwkey",	0,	set80211wep),		/* NetBSD */
2729 	DEF_CMD_ARG("rtsthreshold",	set80211rtsthreshold),
2730 	DEF_CMD_ARG("protmode",		set80211protmode),
2731 	DEF_CMD_ARG("txpower",		set80211txpower),
2732 	DEF_CMD_ARG("roaming",		set80211roaming),
2733 	DEF_CMD("wme",		1,	set80211wme),
2734 	DEF_CMD("-wme",		0,	set80211wme),
2735 	DEF_CMD("hidessid",	1,	set80211hidessid),
2736 	DEF_CMD("-hidessid",	0,	set80211hidessid),
2737 	DEF_CMD("apbridge",	1,	set80211apbridge),
2738 	DEF_CMD("-apbridge",	0,	set80211apbridge),
2739 	DEF_CMD_ARG("chanlist",		set80211chanlist),
2740 	DEF_CMD_ARG("bssid",		set80211bssid),
2741 	DEF_CMD_ARG("ap",		set80211bssid),
2742 	DEF_CMD("scan",	0,		set80211scan),
2743 	DEF_CMD_ARG("list",		set80211list),
2744 	DEF_CMD_ARG2("cwmin",		set80211cwmin),
2745 	DEF_CMD_ARG2("cwmax",		set80211cwmax),
2746 	DEF_CMD_ARG2("aifs",		set80211aifs),
2747 	DEF_CMD_ARG2("txoplimit",	set80211txoplimit),
2748 	DEF_CMD_ARG("acm",		set80211acm),
2749 	DEF_CMD_ARG("-acm",		set80211noacm),
2750 	DEF_CMD_ARG("ack",		set80211ackpolicy),
2751 	DEF_CMD_ARG("-ack",		set80211noackpolicy),
2752 	DEF_CMD_ARG2("bss:cwmin",	set80211bsscwmin),
2753 	DEF_CMD_ARG2("bss:cwmax",	set80211bsscwmax),
2754 	DEF_CMD_ARG2("bss:aifs",	set80211bssaifs),
2755 	DEF_CMD_ARG2("bss:txoplimit",	set80211bsstxoplimit),
2756 	DEF_CMD_ARG("dtimperiod",	set80211dtimperiod),
2757 	DEF_CMD_ARG("bintval",		set80211bintval),
2758 	DEF_CMD("mac:open",	IEEE80211_MACCMD_POLICY_OPEN,	set80211maccmd),
2759 	DEF_CMD("mac:allow",	IEEE80211_MACCMD_POLICY_ALLOW,	set80211maccmd),
2760 	DEF_CMD("mac:deny",	IEEE80211_MACCMD_POLICY_DENY,	set80211maccmd),
2761 	DEF_CMD("mac:flush",	IEEE80211_MACCMD_FLUSH,		set80211maccmd),
2762 	DEF_CMD("mac:detach",	IEEE80211_MACCMD_DETACH,	set80211maccmd),
2763 	DEF_CMD_ARG("mac:add",		set80211addmac),
2764 	DEF_CMD_ARG("mac:del",		set80211delmac),
2765 	DEF_CMD_ARG("mac:kick",		set80211kickmac),
2766 	DEF_CMD("pureg",	1,	set80211pureg),
2767 	DEF_CMD("-pureg",	0,	set80211pureg),
2768 	DEF_CMD("ff",		1,	set80211fastframes),
2769 	DEF_CMD("-ff",		0,	set80211fastframes),
2770 	DEF_CMD("dturbo",	1,	set80211dturbo),
2771 	DEF_CMD("-dturbo",	0,	set80211dturbo),
2772 	DEF_CMD("bgscan",	1,	set80211bgscan),
2773 	DEF_CMD("-bgscan",	0,	set80211bgscan),
2774 	DEF_CMD_ARG("bgscanidle",	set80211bgscanidle),
2775 	DEF_CMD_ARG("bgscanintvl",	set80211bgscanintvl),
2776 	DEF_CMD_ARG("scanvalid",	set80211scanvalid),
2777 	DEF_CMD_ARG("roam:rssi11a",	set80211roamrssi11a),
2778 	DEF_CMD_ARG("roam:rssi11b",	set80211roamrssi11b),
2779 	DEF_CMD_ARG("roam:rssi11g",	set80211roamrssi11g),
2780 	DEF_CMD_ARG("roam:rate11a",	set80211roamrate11a),
2781 	DEF_CMD_ARG("roam:rate11b",	set80211roamrate11b),
2782 	DEF_CMD_ARG("roam:rate11g",	set80211roamrate11g),
2783 	DEF_CMD_ARG("mcastrate",	set80211mcastrate),
2784 	DEF_CMD_ARG("fragthreshold",	set80211fragthreshold),
2785 	DEF_CMD("burst",	1,	set80211burst),
2786 	DEF_CMD("-burst",	0,	set80211burst),
2787 	DEF_CMD_ARG("bmiss",		set80211bmissthreshold),
2788 	DEF_CMD_ARG("bmissthreshold",	set80211bmissthreshold),
2789 	DEF_CMD("doth",		1,	set80211doth),
2790 	DEF_CMD("-doth",	0,	set80211doth),
2791 };
2792 static struct afswtch af_ieee80211 = {
2793 	.af_name	= "af_ieee80211",
2794 	.af_af		= AF_UNSPEC,
2795 	.af_other_status = ieee80211_status,
2796 };
2797 
2798 static __constructor void
2799 ieee80211_ctor(void)
2800 {
2801 #define	N(a)	(sizeof(a) / sizeof(a[0]))
2802 	int i;
2803 
2804 	for (i = 0; i < N(ieee80211_cmds);  i++)
2805 		cmd_register(&ieee80211_cmds[i]);
2806 	af_register(&af_ieee80211);
2807 #undef N
2808 }
2809