xref: /dragonfly/sbin/ifconfig/ifieee80211.c (revision 8a7bdfea)
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: src/sbin/ifconfig/ifieee80211.c,v 1.18.2.10 2006/08/10 06:09:23 sam Exp $
28  * $DragonFly: src/sbin/ifconfig/ifieee80211.c,v 1.20 2008/01/19 07:34:13 sephe Exp $
29  */
30 
31 /*-
32  * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
33  * All rights reserved.
34  *
35  * This code is derived from software contributed to The NetBSD Foundation
36  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
37  * NASA Ames Research Center.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in the
46  *    documentation and/or other materials provided with the distribution.
47  * 3. All advertising materials mentioning features or use of this software
48  *    must display the following acknowledgement:
49  *	This product includes software developed by the NetBSD
50  *	Foundation, Inc. and its contributors.
51  * 4. Neither the name of The NetBSD Foundation nor the names of its
52  *    contributors may be used to endorse or promote products derived
53  *    from this software without specific prior written permission.
54  *
55  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
56  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
57  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
58  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
59  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
60  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
61  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
62  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
63  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
64  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
65  * POSSIBILITY OF SUCH DAMAGE.
66  */
67 
68 #include <sys/param.h>
69 #include <sys/ioctl.h>
70 #include <sys/socket.h>
71 #include <sys/sysctl.h>
72 #include <sys/time.h>
73 
74 #include <net/ethernet.h>
75 #include <net/if.h>
76 #include <net/if_dl.h>
77 #include <net/if_types.h>
78 #include <net/if_media.h>
79 #include <net/route.h>
80 
81 #include <netproto/802_11/ieee80211.h>
82 #include <netproto/802_11/ieee80211_crypto.h>
83 #include <netproto/802_11/ieee80211_ioctl.h>
84 #include <netproto/802_11/ieee80211_ratectl.h>
85 
86 #include <ctype.h>
87 #include <err.h>
88 #include <errno.h>
89 #include <fcntl.h>
90 #include <inttypes.h>
91 #include <stdio.h>
92 #include <stdlib.h>
93 #include <string.h>
94 #include <unistd.h>
95 #include <stdarg.h>
96 
97 #include "ifconfig.h"
98 
99 static void set80211(int s, int type, int val, int len, u_int8_t *data);
100 static const char *get_string(const char *val, const char *sep,
101     u_int8_t *buf, int *lenp);
102 static void print_string(const u_int8_t *buf, int len);
103 
104 static int
105 isanyarg(const char *arg)
106 {
107 	return (strcmp(arg, "-") == 0 ||
108 	    strcasecmp(arg, "any") == 0 || strcasecmp(arg, "off") == 0);
109 }
110 
111 static void
112 set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
113 {
114 	int		ssid;
115 	int		len;
116 	u_int8_t	data[IEEE80211_NWID_LEN];
117 
118 	ssid = 0;
119 	len = strlen(val);
120 	if (len > 2 && isdigit(val[0]) && val[1] == ':') {
121 		ssid = atoi(val)-1;
122 		val += 2;
123 	}
124 
125 	bzero(data, sizeof(data));
126 	len = sizeof(data);
127 	if (get_string(val, NULL, data, &len) == NULL)
128 		exit(1);
129 
130 	set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
131 }
132 
133 static void
134 set80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
135 {
136 	int			len;
137 	u_int8_t		data[33];
138 
139 	bzero(data, sizeof(data));
140 	len = sizeof(data);
141 	get_string(val, NULL, data, &len);
142 
143 	set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
144 }
145 
146 /*
147  * Convert IEEE channel number to MHz frequency.
148  */
149 static u_int
150 ieee80211_ieee2mhz(u_int chan)
151 {
152 	if (chan == 14)
153 		return 2484;
154 	if (chan < 14)			/* 0-13 */
155 		return 2407 + chan*5;
156 	if (chan < 27)			/* 15-26 */
157 		return 2512 + ((chan-15)*20);
158 	return 5000 + (chan*5);
159 }
160 
161 /*
162  * Convert MHz frequency to IEEE channel number.
163  */
164 static u_int
165 ieee80211_mhz2ieee(u_int freq)
166 {
167 	if (freq == 2484)
168 		return 14;
169 	if (freq < 2484)
170 		return (freq - 2407) / 5;
171 	if (freq < 5000)
172 		return 15 + ((freq - 2512) / 20);
173 	return (freq - 5000) / 5;
174 }
175 
176 static void
177 set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
178 {
179 	if (!isanyarg(val)) {
180 		int v = atoi(val);
181 		if (v > 255)		/* treat as frequency */
182 			v = ieee80211_mhz2ieee(v);
183 		set80211(s, IEEE80211_IOC_CHANNEL, v, 0, NULL);
184 	} else
185 		set80211(s, IEEE80211_IOC_CHANNEL, IEEE80211_CHAN_ANY, 0, NULL);
186 }
187 
188 static void
189 set80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
190 {
191 	int	mode;
192 
193 	if (strcasecmp(val, "none") == 0) {
194 		mode = IEEE80211_AUTH_NONE;
195 	} else if (strcasecmp(val, "open") == 0) {
196 		mode = IEEE80211_AUTH_OPEN;
197 	} else if (strcasecmp(val, "shared") == 0) {
198 		mode = IEEE80211_AUTH_SHARED;
199 	} else if (strcasecmp(val, "8021x") == 0) {
200 		mode = IEEE80211_AUTH_8021X;
201 	} else if (strcasecmp(val, "wpa") == 0) {
202 		mode = IEEE80211_AUTH_WPA;
203 	} else {
204 		errx(1, "unknown authmode");
205 	}
206 
207 	set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
208 }
209 
210 static void
211 set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
212 {
213 	int	mode;
214 
215 	if (strcasecmp(val, "off") == 0) {
216 		mode = IEEE80211_POWERSAVE_OFF;
217 	} else if (strcasecmp(val, "on") == 0) {
218 		mode = IEEE80211_POWERSAVE_ON;
219 	} else if (strcasecmp(val, "cam") == 0) {
220 		mode = IEEE80211_POWERSAVE_CAM;
221 	} else if (strcasecmp(val, "psp") == 0) {
222 		mode = IEEE80211_POWERSAVE_PSP;
223 	} else if (strcasecmp(val, "psp-cam") == 0) {
224 		mode = IEEE80211_POWERSAVE_PSP_CAM;
225 	} else {
226 		errx(1, "unknown powersavemode");
227 	}
228 
229 	set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
230 }
231 
232 static void
233 set80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
234 {
235 	if (d == 0)
236 		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
237 		    0, NULL);
238 	else
239 		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
240 		    0, NULL);
241 }
242 
243 static void
244 set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
245 {
246 	set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
247 }
248 
249 static void
250 set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
251 {
252 	int	mode;
253 
254 	if (strcasecmp(val, "off") == 0) {
255 		mode = IEEE80211_WEP_OFF;
256 	} else if (strcasecmp(val, "on") == 0) {
257 		mode = IEEE80211_WEP_ON;
258 	} else if (strcasecmp(val, "mixed") == 0) {
259 		mode = IEEE80211_WEP_MIXED;
260 	} else {
261 		errx(1, "unknown wep mode");
262 	}
263 
264 	set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
265 }
266 
267 static void
268 set80211wep(const char *val, int d, int s, const struct afswtch *rafp)
269 {
270 	set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
271 }
272 
273 static int
274 isundefarg(const char *arg)
275 {
276 	return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0);
277 }
278 
279 static void
280 set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
281 {
282 	if (isundefarg(val))
283 		set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
284 	else
285 		set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
286 }
287 
288 static void
289 set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
290 {
291 	int		key = 0;
292 	int		len;
293 	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
294 
295 	if (isdigit(val[0]) && val[1] == ':') {
296 		key = atoi(val)-1;
297 		val += 2;
298 	}
299 
300 	bzero(data, sizeof(data));
301 	len = sizeof(data);
302 	get_string(val, NULL, data, &len);
303 
304 	set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
305 }
306 
307 /*
308  * This function is purely a NetBSD compatability interface.  The NetBSD
309  * interface is too inflexible, but it's there so we'll support it since
310  * it's not all that hard.
311  */
312 static void
313 set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
314 {
315 	int		txkey;
316 	int		i, len;
317 	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
318 
319 	set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
320 
321 	if (isdigit(val[0]) && val[1] == ':') {
322 		txkey = val[0]-'0'-1;
323 		val += 2;
324 
325 		for (i = 0; i < 4; i++) {
326 			bzero(data, sizeof(data));
327 			len = sizeof(data);
328 			val = get_string(val, ",", data, &len);
329 			if (val == NULL)
330 				exit(1);
331 
332 			set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
333 		}
334 	} else {
335 		bzero(data, sizeof(data));
336 		len = sizeof(data);
337 		get_string(val, NULL, data, &len);
338 		txkey = 0;
339 
340 		set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
341 
342 		bzero(data, sizeof(data));
343 		for (i = 1; i < 4; i++)
344 			set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
345 	}
346 
347 	set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
348 }
349 
350 static void
351 set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
352 {
353 	set80211(s, IEEE80211_IOC_RTSTHRESHOLD,
354 		isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL);
355 }
356 
357 static void
358 set80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
359 {
360 	int	mode;
361 
362 	if (strcasecmp(val, "off") == 0) {
363 		mode = IEEE80211_PROTMODE_OFF;
364 	} else if (strcasecmp(val, "cts") == 0) {
365 		mode = IEEE80211_PROTMODE_CTS;
366 	} else if (strcasecmp(val, "rtscts") == 0) {
367 		mode = IEEE80211_PROTMODE_RTSCTS;
368 	} else {
369 		errx(1, "unknown protection mode");
370 	}
371 
372 	set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
373 }
374 
375 static void
376 set80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
377 {
378 	set80211(s, IEEE80211_IOC_TXPOWER, atoi(val), 0, NULL);
379 }
380 
381 #define	IEEE80211_ROAMING_DEVICE	0
382 #define	IEEE80211_ROAMING_AUTO		1
383 #define	IEEE80211_ROAMING_MANUAL	2
384 
385 static void
386 set80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
387 {
388 	int mode;
389 
390 	if (strcasecmp(val, "device") == 0) {
391 		mode = IEEE80211_ROAMING_DEVICE;
392 	} else if (strcasecmp(val, "auto") == 0) {
393 		mode = IEEE80211_ROAMING_AUTO;
394 	} else if (strcasecmp(val, "manual") == 0) {
395 		mode = IEEE80211_ROAMING_MANUAL;
396 	} else {
397 		errx(1, "unknown roaming mode");
398 	}
399 	set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
400 }
401 
402 static void
403 set80211wme(const char *val, int d, int s, const struct afswtch *rafp)
404 {
405 	set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
406 }
407 
408 static void
409 set80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
410 {
411 	set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
412 }
413 
414 static void
415 set80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
416 {
417 	set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
418 }
419 
420 static void
421 set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
422 {
423 	struct ieee80211req_chanlist chanlist;
424 #define	MAXCHAN	(sizeof(chanlist.ic_channels)*NBBY)
425 	char *temp, *cp, *tp;
426 
427 	temp = malloc(strlen(val) + 1);
428 	if (temp == NULL)
429 		errx(1, "malloc failed");
430 	strcpy(temp, val);
431 	memset(&chanlist, 0, sizeof(chanlist));
432 	cp = temp;
433 	for (;;) {
434 		int first, last, f;
435 
436 		tp = strchr(cp, ',');
437 		if (tp != NULL)
438 			*tp++ = '\0';
439 		switch (sscanf(cp, "%u-%u", &first, &last)) {
440 		case 1:
441 			if (first > MAXCHAN)
442 				errx(-1, "channel %u out of range, max %zu",
443 					first, MAXCHAN);
444 			setbit(chanlist.ic_channels, first);
445 			break;
446 		case 2:
447 			if (first > MAXCHAN)
448 				errx(-1, "channel %u out of range, max %zu",
449 					first, MAXCHAN);
450 			if (last > MAXCHAN)
451 				errx(-1, "channel %u out of range, max %zu",
452 					last, MAXCHAN);
453 			if (first > last)
454 				errx(-1, "void channel range, %u > %u",
455 					first, last);
456 			for (f = first; f <= last; f++)
457 				setbit(chanlist.ic_channels, f);
458 			break;
459 		}
460 		if (tp == NULL)
461 			break;
462 		while (isspace(*tp))
463 			tp++;
464 		if (!isdigit(*tp))
465 			break;
466 		cp = tp;
467 	}
468 	set80211(s, IEEE80211_IOC_CHANLIST, 0,
469 		sizeof(chanlist), (uint8_t *) &chanlist);
470 #undef MAXCHAN
471 }
472 
473 static void
474 set80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
475 {
476 
477 	if (!isanyarg(val)) {
478 		char *temp;
479 		struct sockaddr_dl sdl;
480 
481 		temp = malloc(strlen(val) + 2); /* ':' and '\0' */
482 		if (temp == NULL)
483 			errx(1, "malloc failed");
484 		temp[0] = ':';
485 		strcpy(temp + 1, val);
486 		sdl.sdl_len = sizeof(sdl);
487 		link_addr(temp, &sdl);
488 		free(temp);
489 		if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
490 			errx(1, "malformed link-level address");
491 		set80211(s, IEEE80211_IOC_BSSID, 0,
492 			IEEE80211_ADDR_LEN, LLADDR(&sdl));
493 	} else {
494 		uint8_t zerobssid[IEEE80211_ADDR_LEN];
495 		memset(zerobssid, 0, sizeof(zerobssid));
496 		set80211(s, IEEE80211_IOC_BSSID, 0,
497 			IEEE80211_ADDR_LEN, zerobssid);
498 	}
499 }
500 
501 static int
502 getac(const char *ac)
503 {
504 	if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0)
505 		return WME_AC_BE;
506 	if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0)
507 		return WME_AC_BK;
508 	if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0)
509 		return WME_AC_VI;
510 	if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0)
511 		return WME_AC_VO;
512 	errx(1, "unknown wme access class %s", ac);
513 }
514 
515 static
516 DECL_CMD_FUNC2(set80211cwmin, ac, val)
517 {
518 	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
519 }
520 
521 static
522 DECL_CMD_FUNC2(set80211cwmax, ac, val)
523 {
524 	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
525 }
526 
527 static
528 DECL_CMD_FUNC2(set80211aifs, ac, val)
529 {
530 	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
531 }
532 
533 static
534 DECL_CMD_FUNC2(set80211txoplimit, ac, val)
535 {
536 	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
537 }
538 
539 static
540 DECL_CMD_FUNC(set80211acm, ac, d)
541 {
542 	set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL);
543 }
544 static
545 DECL_CMD_FUNC(set80211noacm, ac, d)
546 {
547 	set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL);
548 }
549 
550 static
551 DECL_CMD_FUNC(set80211ackpolicy, ac, d)
552 {
553 	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL);
554 }
555 static
556 DECL_CMD_FUNC(set80211noackpolicy, ac, d)
557 {
558 	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL);
559 }
560 
561 static
562 DECL_CMD_FUNC2(set80211bsscwmin, ac, val)
563 {
564 	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
565 		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
566 }
567 
568 static
569 DECL_CMD_FUNC2(set80211bsscwmax, ac, val)
570 {
571 	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
572 		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
573 }
574 
575 static
576 DECL_CMD_FUNC2(set80211bssaifs, ac, val)
577 {
578 	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
579 		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
580 }
581 
582 static
583 DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
584 {
585 	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
586 		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
587 }
588 
589 static
590 DECL_CMD_FUNC(set80211dtimperiod, val, d)
591 {
592 	set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
593 }
594 
595 static
596 DECL_CMD_FUNC(set80211bintval, val, d)
597 {
598 	set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
599 }
600 
601 static void
602 set80211macmac(int s, int op, const char *val)
603 {
604 	char *temp;
605 	struct sockaddr_dl sdl;
606 
607 	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
608 	if (temp == NULL)
609 		errx(1, "malloc failed");
610 	temp[0] = ':';
611 	strcpy(temp + 1, val);
612 	sdl.sdl_len = sizeof(sdl);
613 	link_addr(temp, &sdl);
614 	free(temp);
615 	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
616 		errx(1, "malformed link-level address");
617 	set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
618 }
619 
620 static
621 DECL_CMD_FUNC(set80211addmac, val, d)
622 {
623 	set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
624 }
625 
626 static
627 DECL_CMD_FUNC(set80211delmac, val, d)
628 {
629 	set80211macmac(s, IEEE80211_IOC_DELMAC, val);
630 }
631 
632 static
633 DECL_CMD_FUNC(set80211kickmac, val, d)
634 {
635 	char *temp;
636 	struct sockaddr_dl sdl;
637 	struct ieee80211req_mlme mlme;
638 
639 	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
640 	if (temp == NULL)
641 		errx(1, "malloc failed");
642 	temp[0] = ':';
643 	strcpy(temp + 1, val);
644 	sdl.sdl_len = sizeof(sdl);
645 	link_addr(temp, &sdl);
646 	free(temp);
647 	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
648 		errx(1, "malformed link-level address");
649 	memset(&mlme, 0, sizeof(mlme));
650 	mlme.im_op = IEEE80211_MLME_DEAUTH;
651 	mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE;
652 	memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN);
653 	set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), (u_int8_t *) &mlme);
654 }
655 
656 static
657 DECL_CMD_FUNC(set80211maccmd, val, d)
658 {
659 	set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
660 }
661 
662 static void
663 set80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
664 {
665 	set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
666 }
667 
668 static void
669 set80211burst(const char *val, int d, int s, const struct afswtch *rafp)
670 {
671 	set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
672 }
673 
674 static void
675 set80211ratectl(const char *val, int d, int s, const struct afswtch *rafp)
676 {
677 	int ratectl = 0;
678 
679 	if (strcmp("onoe", val) == 0)
680 		ratectl = IEEE80211_RATECTL_ONOE;
681 	else if (strcmp("amrr", val) == 0)
682 		ratectl = IEEE80211_RATECTL_AMRR;
683 	else if (strcmp("sample", val) == 0)
684 		ratectl = IEEE80211_RATECTL_SAMPLE;
685 	else
686 		errx(1, "unknown ratectl");
687 
688 	set80211(s, IEEE80211_IOC_RATECTL, ratectl, 0, NULL);
689 }
690 
691 static
692 DECL_CMD_FUNC(set80211mcastrate, val, d)
693 {
694 	set80211(s, IEEE80211_IOC_MCAST_RATE, (int) 2*atof(val), 0, NULL);
695 }
696 
697 static
698 DECL_CMD_FUNC(set80211fragthreshold, val, d)
699 {
700 	set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
701 		isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
702 }
703 
704 static
705 DECL_CMD_FUNC(set80211bmissthreshold, val, d)
706 {
707 	set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
708 		isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
709 }
710 
711 static int
712 getmaxrate(uint8_t rates[15], uint8_t nrates)
713 {
714 	int i, maxrate = -1;
715 
716 	for (i = 0; i < nrates; i++) {
717 		int rate = rates[i] & IEEE80211_RATE_VAL;
718 		if (rate > maxrate)
719 			maxrate = rate;
720 	}
721 	return maxrate / 2;
722 }
723 
724 static const char *
725 getcaps(int capinfo)
726 {
727 	static char capstring[32];
728 	char *cp = capstring;
729 
730 	if (capinfo & IEEE80211_CAPINFO_ESS)
731 		*cp++ = 'E';
732 	if (capinfo & IEEE80211_CAPINFO_IBSS)
733 		*cp++ = 'I';
734 	if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
735 		*cp++ = 'c';
736 	if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
737 		*cp++ = 'C';
738 	if (capinfo & IEEE80211_CAPINFO_PRIVACY)
739 		*cp++ = 'P';
740 	if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
741 		*cp++ = 'S';
742 	if (capinfo & IEEE80211_CAPINFO_PBCC)
743 		*cp++ = 'B';
744 	if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
745 		*cp++ = 'A';
746 	if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
747 		*cp++ = 's';
748 	if (capinfo & IEEE80211_CAPINFO_RSN)
749 		*cp++ = 'R';
750 	if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
751 		*cp++ = 'D';
752 	*cp = '\0';
753 	return capstring;
754 }
755 
756 static void
757 printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
758 {
759 	printf("%s", tag);
760 	if (verbose) {
761 		maxlen -= strlen(tag)+2;
762 		if (2*ielen > maxlen)
763 			maxlen--;
764 		printf("<");
765 		for (; ielen > 0; ie++, ielen--) {
766 			if (maxlen-- <= 0)
767 				break;
768 			printf("%02x", *ie);
769 		}
770 		if (ielen != 0)
771 			printf("-");
772 		printf(">");
773 	}
774 }
775 
776 /*
777  * Copy the ssid string contents into buf, truncating to fit.  If the
778  * ssid is entirely printable then just copy intact.  Otherwise convert
779  * to hexadecimal.  If the result is truncated then replace the last
780  * three characters with "...".
781  */
782 static int
783 copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
784 {
785 	const u_int8_t *p;
786 	size_t maxlen;
787 	int i;
788 
789 	if (essid_len > bufsize)
790 		maxlen = bufsize;
791 	else
792 		maxlen = essid_len;
793 	/* determine printable or not */
794 	for (i = 0, p = essid; i < maxlen; i++, p++) {
795 		if (*p < ' ' || *p > 0x7e)
796 			break;
797 	}
798 	if (i != maxlen) {		/* not printable, print as hex */
799 		if (bufsize < 3)
800 			return 0;
801 		strlcpy(buf, "0x", bufsize);
802 		bufsize -= 2;
803 		p = essid;
804 		for (i = 0; i < maxlen && bufsize >= 2; i++) {
805 			sprintf(&buf[2+2*i], "%02x", p[i]);
806 			bufsize -= 2;
807 		}
808 		if (i != essid_len)
809 			memcpy(&buf[2+2*i-3], "...", 3);
810 	} else {			/* printable, truncate as needed */
811 		memcpy(buf, essid, maxlen);
812 		if (maxlen != essid_len)
813 			memcpy(&buf[maxlen-3], "...", 3);
814 	}
815 	return maxlen;
816 }
817 
818 /* unaligned little endian access */
819 #define LE_READ_4(p)					\
820 	((u_int32_t)					\
821 	 ((((const u_int8_t *)(p))[0]      ) |		\
822 	  (((const u_int8_t *)(p))[1] <<  8) |		\
823 	  (((const u_int8_t *)(p))[2] << 16) |		\
824 	  (((const u_int8_t *)(p))[3] << 24)))
825 
826 static int __inline
827 iswpaoui(const u_int8_t *frm)
828 {
829 	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
830 }
831 
832 static int __inline
833 iswmeoui(const u_int8_t *frm)
834 {
835 	return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
836 }
837 
838 static int __inline
839 isatherosoui(const u_int8_t *frm)
840 {
841 	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
842 }
843 
844 static void
845 printies(const u_int8_t *vp, int ielen, int maxcols)
846 {
847 	while (ielen > 0) {
848 		switch (vp[0]) {
849 		case IEEE80211_ELEMID_VENDOR:
850 			if (iswpaoui(vp))
851 				printie(" WPA", vp, 2+vp[1], maxcols);
852 			else if (iswmeoui(vp))
853 				printie(" WME", vp, 2+vp[1], maxcols);
854 			else if (isatherosoui(vp))
855 				printie(" ATH", vp, 2+vp[1], maxcols);
856 			else
857 				printie(" VEN", vp, 2+vp[1], maxcols);
858 			break;
859 		case IEEE80211_ELEMID_RSN:
860 			printie(" RSN", vp, 2+vp[1], maxcols);
861 			break;
862 		default:
863 			printie(" ???", vp, 2+vp[1], maxcols);
864 			break;
865 		}
866 		ielen -= 2+vp[1];
867 		vp += 2+vp[1];
868 	}
869 }
870 
871 static void
872 list_scan(int s)
873 {
874 	uint8_t buf[24*1024];
875 	struct ieee80211req ireq;
876 	char ssid[IEEE80211_NWID_LEN+1];
877 	uint8_t *cp;
878 	int len, ssidmax;
879 
880 	(void) memset(&ireq, 0, sizeof(ireq));
881 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
882 	ireq.i_type = IEEE80211_IOC_SCAN_RESULTS;
883 	ireq.i_data = buf;
884 	ireq.i_len = sizeof(buf);
885 	if (ioctl(s, SIOCG80211, &ireq) < 0)
886 		errx(1, "unable to get scan results");
887 	len = ireq.i_len;
888 	if (len < sizeof(struct ieee80211req_scan_result))
889 		return;
890 
891 	ssidmax = verbose ? IEEE80211_NWID_LEN : 14;
892 	printf("%-*.*s  %-17.17s  %4s %4s  %-5s %3s %4s\n"
893 		, ssidmax, ssidmax, "SSID"
894 		, "BSSID"
895 		, "CHAN"
896 		, "RATE"
897 		, "S:N"
898 		, "INT"
899 		, "CAPS"
900 	);
901 	cp = buf;
902 	do {
903 		struct ieee80211req_scan_result *sr;
904 		uint8_t *vp;
905 
906 		sr = (struct ieee80211req_scan_result *) cp;
907 		vp = (u_int8_t *)(sr+1);
908 		printf("%-*.*s  %s  %3d  %3dM %2d:%-2d  %3d %-4.4s"
909 			, ssidmax
910 			  , copy_essid(ssid, ssidmax, vp, sr->isr_ssid_len)
911 			  , ssid
912 			, ether_ntoa((const struct ether_addr *) sr->isr_bssid)
913 			, ieee80211_mhz2ieee(sr->isr_freq)
914 			, getmaxrate(sr->isr_rates, sr->isr_nrates)
915 			, sr->isr_rssi, sr->isr_noise
916 			, sr->isr_intval
917 			, getcaps(sr->isr_capinfo2)
918 		);
919 		printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);
920 		printf("\n");
921 		cp += sr->isr_len, len -= sr->isr_len;
922 	} while (len >= sizeof(struct ieee80211req_scan_result));
923 }
924 
925 #include <netproto/802_11/ieee80211_dragonfly.h>
926 
927 static void
928 scan_and_wait(int s)
929 {
930 	struct ieee80211req ireq;
931 	int sroute;
932 
933 	sroute = socket(PF_ROUTE, SOCK_RAW, 0);
934 	if (sroute < 0) {
935 		perror("socket(PF_ROUTE,SOCK_RAW)");
936 		return;
937 	}
938 	(void) memset(&ireq, 0, sizeof(ireq));
939 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
940 	ireq.i_type = IEEE80211_IOC_SCAN_REQ;
941 	/* NB: only root can trigger a scan so ignore errors */
942 	if (ioctl(s, SIOCS80211, &ireq) >= 0) {
943 		char buf[2048];
944 		struct if_announcemsghdr *ifan;
945 		struct rt_msghdr *rtm;
946 
947 		do {
948 			if (read(sroute, buf, sizeof(buf)) < 0) {
949 				perror("read(PF_ROUTE)");
950 				break;
951 			}
952 			rtm = (struct rt_msghdr *) buf;
953 			if (rtm->rtm_version != RTM_VERSION)
954 				break;
955 			ifan = (struct if_announcemsghdr *) rtm;
956 		} while (rtm->rtm_type != RTM_IEEE80211 ||
957 		    ifan->ifan_what != RTM_IEEE80211_SCAN);
958 	}
959 	close(sroute);
960 }
961 
962 static
963 DECL_CMD_FUNC(set80211scan, val, d)
964 {
965 	scan_and_wait(s);
966 	list_scan(s);
967 }
968 
969 static enum ieee80211_opmode get80211opmode(int s);
970 
971 static void
972 list_stations(int s)
973 {
974 	union {
975 		struct ieee80211req_sta_req req;
976 		uint8_t buf[24*1024];
977 	} u;
978 	enum ieee80211_opmode opmode = get80211opmode(s);
979 	struct ieee80211req ireq;
980 	uint8_t *cp;
981 	int len;
982 
983 	(void) memset(&ireq, 0, sizeof(ireq));
984 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
985 	/* broadcast address =>'s get all stations */
986 	(void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
987 	if (opmode == IEEE80211_M_STA) {
988 		/*
989 		 * Get information about the associated AP.
990 		 */
991 		ireq.i_type = IEEE80211_IOC_BSSID;
992 		ireq.i_data = u.req.is_u.macaddr;
993 		ireq.i_len = IEEE80211_ADDR_LEN;
994 		(void) ioctl(s, SIOCG80211, &ireq);
995 	}
996 	ireq.i_type = IEEE80211_IOC_STA_INFO;
997 	ireq.i_data = &u;
998 	ireq.i_len = sizeof(u);
999 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1000 		errx(1, "unable to get station information");
1001 	len = ireq.i_len;
1002 	if (len < sizeof(struct ieee80211req_sta_info))
1003 		return;
1004 
1005 	printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %3s\n"
1006 		, "ADDR"
1007 		, "AID"
1008 		, "CHAN"
1009 		, "RATE"
1010 		, "RSSI"
1011 		, "IDLE"
1012 		, "TXSEQ"
1013 		, "RXSEQ"
1014 		, "CAPS"
1015 		, "ERP"
1016 	);
1017 	cp = (uint8_t *) u.req.info;
1018 	do {
1019 		struct ieee80211req_sta_info *si;
1020 		uint8_t *vp;
1021 
1022 		si = (struct ieee80211req_sta_info *) cp;
1023 		if (si->isi_len < sizeof(*si))
1024 			break;
1025 		vp = (u_int8_t *)(si+1);
1026 		printf("%s %4u %4d %3dM %4d %4d %6d %6d %-4.4s %3x"
1027 			, ether_ntoa((const struct ether_addr*) si->isi_macaddr)
1028 			, IEEE80211_AID(si->isi_associd)
1029 			, ieee80211_mhz2ieee(si->isi_freq)
1030 			, (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL)/2
1031 			, si->isi_rssi
1032 			, si->isi_inact
1033 			, si->isi_txseqs[0]
1034 			, si->isi_rxseqs[0]
1035 			, getcaps(si->isi_capinfo2)
1036 			, si->isi_erp
1037 		);
1038 		printies(vp, si->isi_ie_len, 24);
1039 		printf("\n");
1040 		cp += si->isi_len, len -= si->isi_len;
1041 	} while (len >= sizeof(struct ieee80211req_sta_info));
1042 }
1043 
1044 static void
1045 print_chaninfo(const struct ieee80211_channel *c)
1046 {
1047 #define	IEEE80211_IS_CHAN_PASSIVE(_c) \
1048 	(((_c)->ic_flags & IEEE80211_CHAN_PASSIVE))
1049 	char buf[14];
1050 
1051 	buf[0] = '\0';
1052 	if (IEEE80211_IS_CHAN_FHSS(c))
1053 		strlcat(buf, " FHSS", sizeof(buf));
1054 	if (IEEE80211_IS_CHAN_A(c))
1055 		strlcat(buf, " 11a", sizeof(buf));
1056 	/* XXX 11g schizophrenia */
1057 	if (IEEE80211_IS_CHAN_G(c) ||
1058 	    IEEE80211_IS_CHAN_PUREG(c))
1059 		strlcat(buf, " 11g", sizeof(buf));
1060 	else if (IEEE80211_IS_CHAN_B(c))
1061 		strlcat(buf, " 11b", sizeof(buf));
1062 	if (IEEE80211_IS_CHAN_T(c))
1063 		strlcat(buf, " Turbo", sizeof(buf));
1064 	printf("Channel %3u : %u%c Mhz%-14.14s",
1065 		ieee80211_mhz2ieee(c->ic_freq), c->ic_freq,
1066 		IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ', buf);
1067 #undef IEEE80211_IS_CHAN_PASSIVE
1068 }
1069 
1070 static void
1071 list_channels(int s, int allchans)
1072 {
1073 	struct ieee80211req ireq;
1074 	struct ieee80211req_chaninfo chans;
1075 	struct ieee80211req_chaninfo achans;
1076 	const struct ieee80211_channel *c;
1077 	int i, half;
1078 
1079 	(void) memset(&ireq, 0, sizeof(ireq));
1080 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1081 	ireq.i_type = IEEE80211_IOC_CHANINFO;
1082 	ireq.i_data = &chans;
1083 	ireq.i_len = sizeof(chans);
1084 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1085 		errx(1, "unable to get channel information");
1086 	if (!allchans) {
1087 		struct ieee80211req_chanlist active;
1088 
1089 		ireq.i_type = IEEE80211_IOC_CHANLIST;
1090 		ireq.i_data = &active;
1091 		ireq.i_len = sizeof(active);
1092 		if (ioctl(s, SIOCG80211, &ireq) < 0)
1093 			errx(1, "unable to get active channel list");
1094 		memset(&achans, 0, sizeof(achans));
1095 		for (i = 0; i < chans.ic_nchans; i++) {
1096 			c = &chans.ic_chans[i];
1097 			if (isset(active.ic_channels, ieee80211_mhz2ieee(c->ic_freq)) || allchans)
1098 				achans.ic_chans[achans.ic_nchans++] = *c;
1099 		}
1100 	} else
1101 		achans = chans;
1102 	half = achans.ic_nchans / 2;
1103 	if (achans.ic_nchans % 2)
1104 		half++;
1105 	for (i = 0; i < achans.ic_nchans / 2; i++) {
1106 		print_chaninfo(&achans.ic_chans[i]);
1107 		print_chaninfo(&achans.ic_chans[half+i]);
1108 		printf("\n");
1109 	}
1110 	if (achans.ic_nchans % 2) {
1111 		print_chaninfo(&achans.ic_chans[i]);
1112 		printf("\n");
1113 	}
1114 }
1115 
1116 static void
1117 list_keys(int s)
1118 {
1119 }
1120 
1121 #define	IEEE80211_C_BITS \
1122 "\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \
1123 "\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \
1124 "\31WPA2\32BURST\33WME"
1125 
1126 #define IEEE80211_CEXT_BITS	"\020\1PBCC"
1127 
1128 static void
1129 list_capabilities(int s)
1130 {
1131 	struct ieee80211req ireq;
1132 	uint32_t caps, caps_ext;
1133 
1134 	memset(&ireq, 0, sizeof(ireq));
1135 	caps_ext = 0;
1136 
1137 	strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1138 	ireq.i_data = &caps_ext;
1139 	ireq.i_len = sizeof(caps_ext);
1140 	ireq.i_type = IEEE80211_IOC_DRIVER_CAPS;
1141 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1142 		errx(1, "unable to get driver capabilities");
1143 	caps = (((uint16_t)ireq.i_val) << 16) | ((uint16_t)ireq.i_len);
1144 	printb(name, caps, IEEE80211_C_BITS);
1145 	if (caps_ext != 0)
1146 		printb(", ext", caps_ext, IEEE80211_CEXT_BITS);
1147 	putchar('\n');
1148 }
1149 
1150 static void
1151 list_wme(int s)
1152 {
1153 	static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
1154 	struct ieee80211req ireq;
1155 	int ac;
1156 
1157 	(void) memset(&ireq, 0, sizeof(ireq));
1158 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1159 	ireq.i_len = 0;
1160 	for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
1161 again:
1162 		if (ireq.i_len & IEEE80211_WMEPARAM_BSS)
1163 			printf("\t%s", "     ");
1164 		else
1165 			printf("\t%s", acnames[ac]);
1166 
1167 		ireq.i_len = (ireq.i_len & IEEE80211_WMEPARAM_BSS) | ac;
1168 
1169 		/* show WME BSS parameters */
1170 		ireq.i_type = IEEE80211_IOC_WME_CWMIN;
1171 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1172 			printf(" cwmin %2u", ireq.i_val);
1173 		ireq.i_type = IEEE80211_IOC_WME_CWMAX;
1174 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1175 			printf(" cwmax %2u", ireq.i_val);
1176 		ireq.i_type = IEEE80211_IOC_WME_AIFS;
1177 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1178 			printf(" aifs %2u", ireq.i_val);
1179 		ireq.i_type = IEEE80211_IOC_WME_TXOPLIMIT;
1180 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1181 			printf(" txopLimit %3u", ireq.i_val);
1182 		ireq.i_type = IEEE80211_IOC_WME_ACM;
1183 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1184 			if (ireq.i_val)
1185 				printf(" acm");
1186 			else if (verbose)
1187 				printf(" -acm");
1188 		}
1189 		/* !BSS only */
1190 		if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) {
1191 			ireq.i_type = IEEE80211_IOC_WME_ACKPOLICY;
1192 			if (ioctl(s, SIOCG80211, &ireq) != -1) {
1193 				if (!ireq.i_val)
1194 					printf(" -ack");
1195 				else if (verbose)
1196 					printf(" ack");
1197 			}
1198 		}
1199 		printf("\n");
1200 		if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) {
1201 			ireq.i_len |= IEEE80211_WMEPARAM_BSS;
1202 			goto again;
1203 		} else
1204 			ireq.i_len &= ~IEEE80211_WMEPARAM_BSS;
1205 	}
1206 }
1207 
1208 static void
1209 list_mac(int s)
1210 {
1211 	struct ieee80211req ireq;
1212 	struct ieee80211req_maclist *acllist;
1213 	int i, nacls, policy;
1214 	char c;
1215 
1216 	(void) memset(&ireq, 0, sizeof(ireq));
1217 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
1218 	ireq.i_type = IEEE80211_IOC_MACCMD;
1219 	ireq.i_val = IEEE80211_MACCMD_POLICY;
1220 	if (ioctl(s, SIOCG80211, &ireq) < 0) {
1221 		if (errno == EINVAL) {
1222 			printf("No acl policy loaded\n");
1223 			return;
1224 		}
1225 		err(1, "unable to get mac policy");
1226 	}
1227 	policy = ireq.i_val;
1228 
1229 	ireq.i_val = IEEE80211_MACCMD_LIST;
1230 	ireq.i_len = 0;
1231 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1232 		err(1, "unable to get mac acl list size");
1233 	if (ireq.i_len == 0)		/* NB: no acls */
1234 		return;
1235 
1236 	ireq.i_data = malloc(ireq.i_len);
1237 	if (ireq.i_data == NULL)
1238 		err(1, "out of memory for acl list");
1239 
1240 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1241 		err(1, "unable to get mac acl list");
1242 	if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
1243 		if (verbose)
1244 			printf("policy: open\n");
1245 		c = '*';
1246 	} else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
1247 		if (verbose)
1248 			printf("policy: allow\n");
1249 		c = '+';
1250 	} else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
1251 		if (verbose)
1252 			printf("policy: deny\n");
1253 		c = '-';
1254 	} else {
1255 		printf("policy: unknown (%u)\n", policy);
1256 		c = '?';
1257 	}
1258 	nacls = ireq.i_len / sizeof(*acllist);
1259 	acllist = (struct ieee80211req_maclist *) ireq.i_data;
1260 	for (i = 0; i < nacls; i++)
1261 		printf("%c%s\n", c, ether_ntoa(
1262 			(const struct ether_addr *) acllist[i].ml_macaddr));
1263 }
1264 
1265 static
1266 DECL_CMD_FUNC(set80211list, arg, d)
1267 {
1268 #define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
1269 
1270 	if (iseq(arg, "sta"))
1271 		list_stations(s);
1272 	else if (iseq(arg, "scan") || iseq(arg, "ap"))
1273 		list_scan(s);
1274 	else if (iseq(arg, "chan") || iseq(arg, "freq"))
1275 		list_channels(s, 1);
1276 	else if (iseq(arg, "active"))
1277 		list_channels(s, 0);
1278 	else if (iseq(arg, "keys"))
1279 		list_keys(s);
1280 	else if (iseq(arg, "caps"))
1281 		list_capabilities(s);
1282 	else if (iseq(arg, "wme"))
1283 		list_wme(s);
1284 	else if (iseq(arg, "mac"))
1285 		list_mac(s);
1286 	else
1287 		errx(1, "Don't know how to list %s for %s", arg, name);
1288 #undef iseq
1289 }
1290 
1291 static enum ieee80211_opmode
1292 get80211opmode(int s)
1293 {
1294 	struct ifmediareq ifmr;
1295 
1296 	(void) memset(&ifmr, 0, sizeof(ifmr));
1297 	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
1298 
1299 	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
1300 		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC)
1301 			return IEEE80211_M_IBSS;	/* XXX ahdemo */
1302 		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
1303 			return IEEE80211_M_HOSTAP;
1304 		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
1305 			return IEEE80211_M_MONITOR;
1306 	}
1307 	return IEEE80211_M_STA;
1308 }
1309 
1310 static const struct ieee80211_channel *
1311 getchaninfo(int s, int chan)
1312 {
1313 	struct ieee80211req ireq;
1314 	static struct ieee80211req_chaninfo chans;
1315 	static struct ieee80211_channel undef;
1316 	const struct ieee80211_channel *c;
1317 	int i, freq;
1318 
1319 	(void) memset(&ireq, 0, sizeof(ireq));
1320 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1321 	ireq.i_type = IEEE80211_IOC_CHANINFO;
1322 	ireq.i_data = &chans;
1323 	ireq.i_len = sizeof(chans);
1324 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1325 		errx(1, "unable to get channel information");
1326 	freq = ieee80211_ieee2mhz(chan);
1327 	for (i = 0; i < chans.ic_nchans; i++) {
1328 		c = &chans.ic_chans[i];
1329 		if (c->ic_freq == freq)
1330 			return c;
1331 	}
1332 	return &undef;
1333 }
1334 
1335 #if 0
1336 static void
1337 printcipher(int s, struct ieee80211req *ireq, int keylenop)
1338 {
1339 	switch (ireq->i_val) {
1340 	case IEEE80211_CIPHER_WEP:
1341 		ireq->i_type = keylenop;
1342 		if (ioctl(s, SIOCG80211, ireq) != -1)
1343 			printf("WEP-%s",
1344 			    ireq->i_len <= 5 ? "40" :
1345 			    ireq->i_len <= 13 ? "104" : "128");
1346 		else
1347 			printf("WEP");
1348 		break;
1349 	case IEEE80211_CIPHER_TKIP:
1350 		printf("TKIP");
1351 		break;
1352 	case IEEE80211_CIPHER_AES_OCB:
1353 		printf("AES-OCB");
1354 		break;
1355 	case IEEE80211_CIPHER_AES_CCM:
1356 		printf("AES-CCM");
1357 		break;
1358 	case IEEE80211_CIPHER_CKIP:
1359 		printf("CKIP");
1360 		break;
1361 	case IEEE80211_CIPHER_NONE:
1362 		printf("NONE");
1363 		break;
1364 	default:
1365 		printf("UNKNOWN (0x%x)", ireq->i_val);
1366 		break;
1367 	}
1368 }
1369 #endif
1370 
1371 #define	MAXCOL	78
1372 static	int col;
1373 static	char spacer;
1374 
1375 static void
1376 LINE_BREAK(void)
1377 {
1378 	if (spacer != '\t') {
1379 		printf("\n");
1380 		spacer = '\t';
1381 	}
1382 	col = 8;	/* 8-col tab */
1383 }
1384 
1385 static void
1386 LINE_CHECK(const char *fmt, ...)
1387 {
1388 	char buf[80];
1389 	va_list ap;
1390 	int n;
1391 
1392 	va_start(ap, fmt);
1393 	n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
1394 	va_end(ap);
1395 	col += 1+n;
1396 	if (col > MAXCOL) {
1397 		LINE_BREAK();
1398 		col += n;
1399 	}
1400 	buf[0] = spacer;
1401 	printf("%s", buf);
1402 	spacer = ' ';
1403 }
1404 
1405 static void
1406 printkey(const struct ieee80211req_key *ik)
1407 {
1408 	static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
1409 	int keylen = ik->ik_keylen;
1410 	int printcontents;
1411 
1412 	printcontents = printkeys &&
1413 		(memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
1414 	if (printcontents)
1415 		LINE_BREAK();
1416 	switch (ik->ik_type) {
1417 	case IEEE80211_CIPHER_WEP:
1418 		/* compatibility */
1419 		LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
1420 		    keylen <= 5 ? "40-bit" :
1421 		    keylen <= 13 ? "104-bit" : "128-bit");
1422 		break;
1423 	case IEEE80211_CIPHER_TKIP:
1424 		if (keylen > 128/8)
1425 			keylen -= 128/8;	/* ignore MIC for now */
1426 		LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
1427 		break;
1428 	case IEEE80211_CIPHER_AES_OCB:
1429 		LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
1430 		break;
1431 	case IEEE80211_CIPHER_AES_CCM:
1432 		LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
1433 		break;
1434 	case IEEE80211_CIPHER_CKIP:
1435 		LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
1436 		break;
1437 	case IEEE80211_CIPHER_NONE:
1438 		LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
1439 		break;
1440 	default:
1441 		LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
1442 			ik->ik_type, ik->ik_keyix+1, 8*keylen);
1443 		break;
1444 	}
1445 	if (printcontents) {
1446 		int i;
1447 
1448 		printf(" <");
1449 		for (i = 0; i < keylen; i++)
1450 			printf("%02x", ik->ik_keydata[i]);
1451 		printf(">");
1452 		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
1453 		    (ik->ik_keyrsc != 0 || verbose))
1454 			printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
1455 		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
1456 		    (ik->ik_keytsc != 0 || verbose))
1457 			printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
1458 		if (ik->ik_flags != 0 && verbose) {
1459 			const char *sep = " ";
1460 
1461 			if (ik->ik_flags & IEEE80211_KEY_XMIT)
1462 				printf("%stx", sep), sep = "+";
1463 			if (ik->ik_flags & IEEE80211_KEY_RECV)
1464 				printf("%srx", sep), sep = "+";
1465 			if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
1466 				printf("%sdef", sep), sep = "+";
1467 		}
1468 		LINE_BREAK();
1469 	}
1470 }
1471 
1472 static void
1473 ieee80211_status(int s)
1474 {
1475 	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
1476 	enum ieee80211_opmode opmode = get80211opmode(s);
1477 	int i, num, wpa, wme;
1478 	struct ieee80211req ireq;
1479 	u_int8_t data[32];
1480 	const struct ieee80211_channel *c;
1481 
1482 	(void) memset(&ireq, 0, sizeof(ireq));
1483 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1484 	ireq.i_data = &data;
1485 
1486 	wpa = 0;		/* unknown/not set */
1487 
1488 	ireq.i_type = IEEE80211_IOC_SSID;
1489 	ireq.i_val = -1;
1490 	if (ioctl(s, SIOCG80211, &ireq) < 0) {
1491 		/* If we can't get the SSID, this isn't an 802.11 device. */
1492 		return;
1493 	}
1494 
1495 	ireq.i_type = IEEE80211_IOC_RATECTL;
1496 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1497 		int lineb = 1;
1498 
1499 		switch (ireq.i_val) {
1500 		case IEEE80211_RATECTL_ONOE:
1501 			printf("\tratectl: onoe");
1502 			break;
1503 		case IEEE80211_RATECTL_AMRR:
1504 			printf("\tratectl: amrr");
1505 			break;
1506 		case IEEE80211_RATECTL_SAMPLE:
1507 			printf("\tratectl: sample");
1508 			break;
1509 		default:
1510 			if (verbose)
1511 				printf("\tratectl: none");
1512 			else
1513 				lineb = 0;
1514 			break;
1515 		}
1516 		if (lineb)
1517 			LINE_BREAK();
1518 	}
1519 
1520 	num = 0;
1521 	ireq.i_type = IEEE80211_IOC_NUMSSIDS;
1522 	if (ioctl(s, SIOCG80211, &ireq) >= 0)
1523 		num = ireq.i_val;
1524 	printf("\tssid ");
1525 	if (num > 1) {
1526 		ireq.i_type = IEEE80211_IOC_SSID;
1527 		for (ireq.i_val = 0; ireq.i_val < num; ireq.i_val++) {
1528 			if (ioctl(s, SIOCG80211, &ireq) >= 0 && ireq.i_len > 0) {
1529 				printf(" %d:", ireq.i_val + 1);
1530 				print_string(data, ireq.i_len);
1531 			}
1532 		}
1533 	} else
1534 		print_string(data, ireq.i_len);
1535 
1536 	ireq.i_type = IEEE80211_IOC_CHANNEL;
1537 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1538 		goto end;
1539 	c = getchaninfo(s, ireq.i_val);
1540 	if (ireq.i_val != -1) {
1541 		printf(" channel %d", ireq.i_val);
1542 		if (verbose)
1543 			printf(" (%u)", c->ic_freq);
1544 	} else if (verbose)
1545 		printf(" channel UNDEF");
1546 
1547 	ireq.i_type = IEEE80211_IOC_BSSID;
1548 	ireq.i_len = IEEE80211_ADDR_LEN;
1549 	if (ioctl(s, SIOCG80211, &ireq) >= 0 &&
1550 	    (memcmp(ireq.i_data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
1551 		printf(" bssid %s", ether_ntoa(ireq.i_data));
1552 
1553 	ireq.i_type = IEEE80211_IOC_STATIONNAME;
1554 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1555 		printf("\n\tstationname ");
1556 		print_string(data, ireq.i_len);
1557 	}
1558 
1559 	spacer = ' ';		/* force first break */
1560 	LINE_BREAK();
1561 
1562 	ireq.i_type = IEEE80211_IOC_AUTHMODE;
1563 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1564 		switch (ireq.i_val) {
1565 			case IEEE80211_AUTH_NONE:
1566 				LINE_CHECK("authmode NONE");
1567 				break;
1568 			case IEEE80211_AUTH_OPEN:
1569 				LINE_CHECK("authmode OPEN");
1570 				break;
1571 			case IEEE80211_AUTH_SHARED:
1572 				LINE_CHECK("authmode SHARED");
1573 				break;
1574 			case IEEE80211_AUTH_8021X:
1575 				LINE_CHECK("authmode 802.1x");
1576 				break;
1577 			case IEEE80211_AUTH_WPA:
1578 				ireq.i_type = IEEE80211_IOC_WPA;
1579 				if (ioctl(s, SIOCG80211, &ireq) != -1)
1580 					wpa = ireq.i_val;
1581 				if (!wpa)
1582 					wpa = 1;	/* default to WPA1 */
1583 				switch (wpa) {
1584 				case 2:
1585 					LINE_CHECK("authmode WPA2/802.11i");
1586 					break;
1587 				case 3:
1588 					LINE_CHECK("authmode WPA1+WPA2/802.11i");
1589 					break;
1590 				default:
1591 					LINE_CHECK("authmode WPA");
1592 					break;
1593 				}
1594 				break;
1595 			case IEEE80211_AUTH_AUTO:
1596 				LINE_CHECK("authmode AUTO");
1597 				break;
1598 			default:
1599 				LINE_CHECK("authmode UNKNOWN (0x%x)",
1600 					ireq.i_val);
1601 				break;
1602 		}
1603 	}
1604 
1605 	ireq.i_type = IEEE80211_IOC_WEP;
1606 	if (ioctl(s, SIOCG80211, &ireq) != -1 &&
1607 	    ireq.i_val != IEEE80211_WEP_NOSUP) {
1608 		int firstkey, wepmode;
1609 
1610 		wepmode = ireq.i_val;
1611 		switch (wepmode) {
1612 			case IEEE80211_WEP_OFF:
1613 				LINE_CHECK("privacy OFF");
1614 				break;
1615 			case IEEE80211_WEP_ON:
1616 				LINE_CHECK("privacy ON");
1617 				break;
1618 			case IEEE80211_WEP_MIXED:
1619 				LINE_CHECK("privacy MIXED");
1620 				break;
1621 			default:
1622 				LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
1623 				break;
1624 		}
1625 
1626 		/*
1627 		 * If we get here then we've got WEP support so we need
1628 		 * to print WEP status.
1629 		 */
1630 
1631 		ireq.i_type = IEEE80211_IOC_WEPTXKEY;
1632 		if (ioctl(s, SIOCG80211, &ireq) < 0) {
1633 			warn("WEP support, but no tx key!");
1634 			goto end;
1635 		}
1636 		if (ireq.i_val != -1)
1637 			LINE_CHECK("deftxkey %d", ireq.i_val+1);
1638 		else if (wepmode != IEEE80211_WEP_OFF || verbose)
1639 			LINE_CHECK("deftxkey UNDEF");
1640 
1641 		ireq.i_type = IEEE80211_IOC_NUMWEPKEYS;
1642 		if (ioctl(s, SIOCG80211, &ireq) < 0) {
1643 			warn("WEP support, but no NUMWEPKEYS support!");
1644 			goto end;
1645 		}
1646 		num = ireq.i_val;
1647 
1648 		firstkey = 1;
1649 		for (i = 0; i < num; i++) {
1650 			struct ieee80211req_key ik;
1651 
1652 			memset(&ik, 0, sizeof(ik));
1653 			ik.ik_keyix = i;
1654 			ireq.i_type = IEEE80211_IOC_WPAKEY;
1655 			ireq.i_data = &ik;
1656 			ireq.i_len = sizeof(ik);
1657 			if (ioctl(s, SIOCG80211, &ireq) < 0) {
1658 				warn("WEP support, but can get keys!");
1659 				goto end;
1660 			}
1661 			if (ik.ik_keylen != 0) {
1662 				if (verbose)
1663 					LINE_BREAK();
1664 				printkey(&ik);
1665 				firstkey = 0;
1666 			}
1667 		}
1668 	}
1669 
1670 	ireq.i_type = IEEE80211_IOC_POWERSAVE;
1671 	if (ioctl(s, SIOCG80211, &ireq) != -1 &&
1672 	    ireq.i_val != IEEE80211_POWERSAVE_NOSUP ) {
1673 		if (ireq.i_val != IEEE80211_POWERSAVE_OFF || verbose) {
1674 			switch (ireq.i_val) {
1675 				case IEEE80211_POWERSAVE_OFF:
1676 					LINE_CHECK("powersavemode OFF");
1677 					break;
1678 				case IEEE80211_POWERSAVE_CAM:
1679 					LINE_CHECK("powersavemode CAM");
1680 					break;
1681 				case IEEE80211_POWERSAVE_PSP:
1682 					LINE_CHECK("powersavemode PSP");
1683 					break;
1684 				case IEEE80211_POWERSAVE_PSP_CAM:
1685 					LINE_CHECK("powersavemode PSP-CAM");
1686 					break;
1687 			}
1688 			ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP;
1689 			if (ioctl(s, SIOCG80211, &ireq) != -1)
1690 				LINE_CHECK("powersavesleep %d", ireq.i_val);
1691 		}
1692 	}
1693 
1694 	ireq.i_type = IEEE80211_IOC_TXPOWMAX;
1695 	if (ioctl(s, SIOCG80211, &ireq) != -1)
1696 		LINE_CHECK("txpowmax %d", ireq.i_val);
1697 
1698 	if (verbose) {
1699 		ireq.i_type = IEEE80211_IOC_TXPOWER;
1700 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1701 			LINE_CHECK("txpower %d", ireq.i_val);
1702 	}
1703 
1704 	ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD;
1705 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1706 		if (ireq.i_val != IEEE80211_RTS_MAX || verbose)
1707 			LINE_CHECK("rtsthreshold %d", ireq.i_val);
1708 	}
1709 
1710 	ireq.i_type = IEEE80211_IOC_MCAST_RATE;
1711 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1712 		if (ireq.i_val != 2*1 || verbose) {
1713 			if (ireq.i_val == 11)
1714 				LINE_CHECK("mcastrate 5.5");
1715 			else
1716 				LINE_CHECK("mcastrate %d", ireq.i_val/2);
1717 		}
1718 	}
1719 
1720 	ireq.i_type = IEEE80211_IOC_FRAGTHRESHOLD;
1721 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1722 		if (ireq.i_val != IEEE80211_FRAG_MAX || verbose)
1723 			LINE_CHECK("fragthreshold %d", ireq.i_val);
1724 	}
1725 
1726 	ireq.i_type = IEEE80211_IOC_BMISSTHRESHOLD;
1727 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1728 		if (ireq.i_val != IEEE80211_HWBMISS_MAX || verbose)
1729 			LINE_CHECK("bmiss %d", ireq.i_val);
1730 	}
1731 
1732 	if (IEEE80211_IS_CHAN_G(c) || IEEE80211_IS_CHAN_PUREG(c) || verbose) {
1733 		ireq.i_type = IEEE80211_IOC_PUREG;
1734 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1735 			if (ireq.i_val)
1736 				LINE_CHECK("pureg");
1737 			else if (verbose)
1738 				LINE_CHECK("-pureg");
1739 		}
1740 		ireq.i_type = IEEE80211_IOC_PROTMODE;
1741 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1742 			switch (ireq.i_val) {
1743 				case IEEE80211_PROTMODE_OFF:
1744 					LINE_CHECK("protmode OFF");
1745 					break;
1746 				case IEEE80211_PROTMODE_CTS:
1747 					LINE_CHECK("protmode CTS");
1748 					break;
1749 				case IEEE80211_PROTMODE_RTSCTS:
1750 					LINE_CHECK("protmode RTSCTS");
1751 					break;
1752 				default:
1753 					LINE_CHECK("protmode UNKNOWN (0x%x)",
1754 						ireq.i_val);
1755 					break;
1756 			}
1757 		}
1758 	}
1759 
1760 	ireq.i_type = IEEE80211_IOC_WME;
1761 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1762 		wme = ireq.i_val;
1763 		if (wme)
1764 			LINE_CHECK("wme");
1765 		else if (verbose)
1766 			LINE_CHECK("-wme");
1767 	} else
1768 		wme = 0;
1769 
1770 	ireq.i_type = IEEE80211_IOC_BURST;
1771 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1772 		if (ireq.i_val)
1773 			LINE_CHECK("burst");
1774 		else if (verbose)
1775 			LINE_CHECK("-burst");
1776 	}
1777 
1778 	if (opmode == IEEE80211_M_HOSTAP) {
1779 		ireq.i_type = IEEE80211_IOC_HIDESSID;
1780 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1781 			if (ireq.i_val)
1782 				LINE_CHECK("ssid HIDE");
1783 			else if (verbose)
1784 				LINE_CHECK("ssid SHOW");
1785 		}
1786 
1787 		ireq.i_type = IEEE80211_IOC_APBRIDGE;
1788 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1789 			if (!ireq.i_val)
1790 				LINE_CHECK("-apbridge");
1791 			else if (verbose)
1792 				LINE_CHECK("apbridge");
1793 		}
1794 
1795 		ireq.i_type = IEEE80211_IOC_DTIM_PERIOD;
1796 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1797 			LINE_CHECK("dtimperiod %u", ireq.i_val);
1798 	} else {
1799 		ireq.i_type = IEEE80211_IOC_ROAMING;
1800 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1801 			if (ireq.i_val != IEEE80211_ROAMING_AUTO || verbose) {
1802 				switch (ireq.i_val) {
1803 				case IEEE80211_ROAMING_DEVICE:
1804 					LINE_CHECK("roaming DEVICE");
1805 					break;
1806 				case IEEE80211_ROAMING_AUTO:
1807 					LINE_CHECK("roaming AUTO");
1808 					break;
1809 				case IEEE80211_ROAMING_MANUAL:
1810 					LINE_CHECK("roaming MANUAL");
1811 					break;
1812 				default:
1813 					LINE_CHECK("roaming UNKNOWN (0x%x)",
1814 						ireq.i_val);
1815 					break;
1816 				}
1817 			}
1818 		}
1819 	}
1820 	ireq.i_type = IEEE80211_IOC_BEACON_INTERVAL;
1821 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1822 		if (ireq.i_val)
1823 			LINE_CHECK("bintval %u", ireq.i_val);
1824 		else if (verbose)
1825 			LINE_CHECK("bintval %u", ireq.i_val);
1826 	}
1827 
1828 	if (wme && verbose) {
1829 		LINE_BREAK();
1830 		list_wme(s);
1831 	}
1832 
1833 	if (wpa) {
1834 		ireq.i_type = IEEE80211_IOC_COUNTERMEASURES;
1835 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1836 			if (ireq.i_val)
1837 				LINE_CHECK("countermeasures");
1838 			else if (verbose)
1839 				LINE_CHECK("-countermeasures");
1840 		}
1841 #if 0
1842 		/* XXX not interesting with WPA done in user space */
1843 		ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
1844 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1845 		}
1846 
1847 		ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
1848 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1849 			LINE_CHECK("mcastcipher ");
1850 			printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
1851 			spacer = ' ';
1852 		}
1853 
1854 		ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
1855 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1856 			LINE_CHECK("ucastcipher ");
1857 			printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
1858 		}
1859 
1860 		if (wpa & 2) {
1861 			ireq.i_type = IEEE80211_IOC_RSNCAPS;
1862 			if (ioctl(s, SIOCG80211, &ireq) != -1) {
1863 				LINE_CHECK("RSN caps 0x%x", ireq.i_val);
1864 				spacer = ' ';
1865 			}
1866 		}
1867 
1868 		ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
1869 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1870 		}
1871 #endif
1872 		LINE_BREAK();
1873 	}
1874 	LINE_BREAK();
1875 
1876 end:
1877 	return;
1878 }
1879 
1880 static void
1881 set80211(int s, int type, int val, int len, u_int8_t *data)
1882 {
1883 	struct ieee80211req	ireq;
1884 
1885 	(void) memset(&ireq, 0, sizeof(ireq));
1886 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1887 	ireq.i_type = type;
1888 	ireq.i_val = val;
1889 	ireq.i_len = len;
1890 	ireq.i_data = data;
1891 	if (ioctl(s, SIOCS80211, &ireq) < 0)
1892 		err(1, "SIOCS80211");
1893 }
1894 
1895 static const char *
1896 get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
1897 {
1898 	int len;
1899 	int hexstr;
1900 	u_int8_t *p;
1901 
1902 	len = *lenp;
1903 	p = buf;
1904 	hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
1905 	if (hexstr)
1906 		val += 2;
1907 	for (;;) {
1908 		if (*val == '\0')
1909 			break;
1910 		if (sep != NULL && strchr(sep, *val) != NULL) {
1911 			val++;
1912 			break;
1913 		}
1914 		if (hexstr) {
1915 			if (!isxdigit((u_char)val[0])) {
1916 				warnx("bad hexadecimal digits");
1917 				return NULL;
1918 			}
1919 			if (!isxdigit((u_char)val[1])) {
1920 				warnx("odd count hexadecimal digits");
1921 				return NULL;
1922 			}
1923 		}
1924 		if (p >= buf + len) {
1925 			if (hexstr)
1926 				warnx("hexadecimal digits too long");
1927 			else
1928 				warnx("string too long");
1929 			return NULL;
1930 		}
1931 		if (hexstr) {
1932 #define	tohex(x)	(isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
1933 			*p++ = (tohex((u_char)val[0]) << 4) |
1934 			    tohex((u_char)val[1]);
1935 #undef tohex
1936 			val += 2;
1937 		} else
1938 			*p++ = *val++;
1939 	}
1940 	len = p - buf;
1941 	if (len < *lenp)
1942 		memset(p, 0, *lenp - len);
1943 	/* The string "-" is treated as the empty string. */
1944 	if (!hexstr && len == 1 && buf[0] == '-')
1945 		len = 0;
1946 	*lenp = len;
1947 	return val;
1948 }
1949 
1950 static void
1951 print_string(const u_int8_t *buf, int len)
1952 {
1953 	int i;
1954 	int hasspc;
1955 
1956 	i = 0;
1957 	hasspc = 0;
1958 	for (; i < len; i++) {
1959 		if (!isprint(buf[i]) && buf[i] != '\0')
1960 			break;
1961 		if (isspace(buf[i]))
1962 			hasspc++;
1963 	}
1964 	if (i == len) {
1965 		if (hasspc || len == 0 || buf[0] == '\0')
1966 			printf("\"%.*s\"", len, buf);
1967 		else
1968 			printf("%.*s", len, buf);
1969 	} else {
1970 		printf("0x");
1971 		for (i = 0; i < len; i++)
1972 			printf("%02x", buf[i]);
1973 	}
1974 }
1975 
1976 static struct cmd ieee80211_cmds[] = {
1977 	DEF_CMD_ARG("ssid",		set80211ssid),
1978 	DEF_CMD_ARG("nwid",		set80211ssid),
1979 	DEF_CMD_ARG("stationname",	set80211stationname),
1980 	DEF_CMD_ARG("station",		set80211stationname),	/* BSD/OS */
1981 	DEF_CMD_ARG("channel",		set80211channel),
1982 	DEF_CMD_ARG("authmode",		set80211authmode),
1983 	DEF_CMD_ARG("powersavemode",	set80211powersavemode),
1984 	DEF_CMD("powersave",	1,	set80211powersave),
1985 	DEF_CMD("-powersave",	0,	set80211powersave),
1986 	DEF_CMD_ARG("powersavesleep", 	set80211powersavesleep),
1987 	DEF_CMD_ARG("wepmode",		set80211wepmode),
1988 	DEF_CMD("wep",		1,	set80211wep),
1989 	DEF_CMD("-wep",		0,	set80211wep),
1990 	DEF_CMD_ARG("deftxkey",		set80211weptxkey),
1991 	DEF_CMD_ARG("weptxkey",		set80211weptxkey),
1992 	DEF_CMD_ARG("wepkey",		set80211wepkey),
1993 	DEF_CMD_ARG("nwkey",		set80211nwkey),		/* NetBSD */
1994 	DEF_CMD("-nwkey",	0,	set80211wep),		/* NetBSD */
1995 	DEF_CMD_ARG("rtsthreshold",	set80211rtsthreshold),
1996 	DEF_CMD_ARG("protmode",		set80211protmode),
1997 	DEF_CMD_ARG("txpower",		set80211txpower),
1998 	DEF_CMD_ARG("roaming",		set80211roaming),
1999 	DEF_CMD("wme",		1,	set80211wme),
2000 	DEF_CMD("-wme",		0,	set80211wme),
2001 	DEF_CMD("hidessid",	1,	set80211hidessid),
2002 	DEF_CMD("-hidessid",	0,	set80211hidessid),
2003 	DEF_CMD("apbridge",	1,	set80211apbridge),
2004 	DEF_CMD("-apbridge",	0,	set80211apbridge),
2005 	DEF_CMD_ARG("chanlist",		set80211chanlist),
2006 	DEF_CMD_ARG("bssid",		set80211bssid),
2007 	DEF_CMD_ARG("ap",		set80211bssid),
2008 	DEF_CMD("scan",	0,		set80211scan),
2009 	DEF_CMD_ARG("list",		set80211list),
2010 	DEF_CMD_ARG2("cwmin",		set80211cwmin),
2011 	DEF_CMD_ARG2("cwmax",		set80211cwmax),
2012 	DEF_CMD_ARG2("aifs",		set80211aifs),
2013 	DEF_CMD_ARG2("txoplimit",	set80211txoplimit),
2014 	DEF_CMD_ARG("acm",		set80211acm),
2015 	DEF_CMD_ARG("-acm",		set80211noacm),
2016 	DEF_CMD_ARG("ack",		set80211ackpolicy),
2017 	DEF_CMD_ARG("-ack",		set80211noackpolicy),
2018 	DEF_CMD_ARG2("bss:cwmin",	set80211bsscwmin),
2019 	DEF_CMD_ARG2("bss:cwmax",	set80211bsscwmax),
2020 	DEF_CMD_ARG2("bss:aifs",	set80211bssaifs),
2021 	DEF_CMD_ARG2("bss:txoplimit",	set80211bsstxoplimit),
2022 	DEF_CMD_ARG("dtimperiod",	set80211dtimperiod),
2023 	DEF_CMD_ARG("bintval",		set80211bintval),
2024 	DEF_CMD("mac:open",	IEEE80211_MACCMD_POLICY_OPEN,	set80211maccmd),
2025 	DEF_CMD("mac:allow",	IEEE80211_MACCMD_POLICY_ALLOW,	set80211maccmd),
2026 	DEF_CMD("mac:deny",	IEEE80211_MACCMD_POLICY_DENY,	set80211maccmd),
2027 	DEF_CMD("mac:flush",	IEEE80211_MACCMD_FLUSH,		set80211maccmd),
2028 	DEF_CMD("mac:detach",	IEEE80211_MACCMD_DETACH,	set80211maccmd),
2029 	DEF_CMD_ARG("mac:add",		set80211addmac),
2030 	DEF_CMD_ARG("mac:del",		set80211delmac),
2031 	DEF_CMD_ARG("mac:kick",		set80211kickmac),
2032 	DEF_CMD("pureg",	1,	set80211pureg),
2033 	DEF_CMD("-pureg",	0,	set80211pureg),
2034 	DEF_CMD_ARG("mcastrate",	set80211mcastrate),
2035 	DEF_CMD_ARG("fragthreshold",	set80211fragthreshold),
2036 	DEF_CMD("burst",	1,	set80211burst),
2037 	DEF_CMD("-burst",	0,	set80211burst),
2038 	DEF_CMD_ARG("ratectl",		set80211ratectl),
2039 	DEF_CMD_ARG("bmiss",            set80211bmissthreshold),
2040 	DEF_CMD_ARG("bmissthreshold",   set80211bmissthreshold)
2041 };
2042 static struct afswtch af_ieee80211 = {
2043 	.af_name	= "af_ieee80211",
2044 	.af_af		= AF_UNSPEC,
2045 	.af_other_status = ieee80211_status,
2046 };
2047 
2048 static __constructor void
2049 ieee80211_ctor(void)
2050 {
2051 #define	N(a)	(sizeof(a) / sizeof(a[0]))
2052 	int i;
2053 
2054 	for (i = 0; i < N(ieee80211_cmds);  i++)
2055 		cmd_register(&ieee80211_cmds[i]);
2056 	af_register(&af_ieee80211);
2057 #undef N
2058 }
2059