xref: /dragonfly/sbin/ifconfig/ifieee80211.c (revision 23265324)
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.17 2006/12/08 14:25:07 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
684 		errx(1, "unknown ratectl");
685 
686 	set80211(s, IEEE80211_IOC_RATECTL, ratectl, 0, NULL);
687 }
688 
689 static
690 DECL_CMD_FUNC(set80211mcastrate, val, d)
691 {
692 	set80211(s, IEEE80211_IOC_MCAST_RATE, (int) 2*atof(val), 0, NULL);
693 }
694 
695 static
696 DECL_CMD_FUNC(set80211fragthreshold, val, d)
697 {
698 	set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
699 		isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
700 }
701 
702 static
703 DECL_CMD_FUNC(set80211bmissthreshold, val, d)
704 {
705 	set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
706 		isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
707 }
708 
709 static int
710 getmaxrate(uint8_t rates[15], uint8_t nrates)
711 {
712 	int i, maxrate = -1;
713 
714 	for (i = 0; i < nrates; i++) {
715 		int rate = rates[i] & IEEE80211_RATE_VAL;
716 		if (rate > maxrate)
717 			maxrate = rate;
718 	}
719 	return maxrate / 2;
720 }
721 
722 static const char *
723 getcaps(int capinfo)
724 {
725 	static char capstring[32];
726 	char *cp = capstring;
727 
728 	if (capinfo & IEEE80211_CAPINFO_ESS)
729 		*cp++ = 'E';
730 	if (capinfo & IEEE80211_CAPINFO_IBSS)
731 		*cp++ = 'I';
732 	if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
733 		*cp++ = 'c';
734 	if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
735 		*cp++ = 'C';
736 	if (capinfo & IEEE80211_CAPINFO_PRIVACY)
737 		*cp++ = 'P';
738 	if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
739 		*cp++ = 'S';
740 	if (capinfo & IEEE80211_CAPINFO_PBCC)
741 		*cp++ = 'B';
742 	if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
743 		*cp++ = 'A';
744 	if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
745 		*cp++ = 's';
746 	if (capinfo & IEEE80211_CAPINFO_RSN)
747 		*cp++ = 'R';
748 	if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
749 		*cp++ = 'D';
750 	*cp = '\0';
751 	return capstring;
752 }
753 
754 static void
755 printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
756 {
757 	printf("%s", tag);
758 	if (verbose) {
759 		maxlen -= strlen(tag)+2;
760 		if (2*ielen > maxlen)
761 			maxlen--;
762 		printf("<");
763 		for (; ielen > 0; ie++, ielen--) {
764 			if (maxlen-- <= 0)
765 				break;
766 			printf("%02x", *ie);
767 		}
768 		if (ielen != 0)
769 			printf("-");
770 		printf(">");
771 	}
772 }
773 
774 /*
775  * Copy the ssid string contents into buf, truncating to fit.  If the
776  * ssid is entirely printable then just copy intact.  Otherwise convert
777  * to hexadecimal.  If the result is truncated then replace the last
778  * three characters with "...".
779  */
780 static int
781 copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
782 {
783 	const u_int8_t *p;
784 	size_t maxlen;
785 	int i;
786 
787 	if (essid_len > bufsize)
788 		maxlen = bufsize;
789 	else
790 		maxlen = essid_len;
791 	/* determine printable or not */
792 	for (i = 0, p = essid; i < maxlen; i++, p++) {
793 		if (*p < ' ' || *p > 0x7e)
794 			break;
795 	}
796 	if (i != maxlen) {		/* not printable, print as hex */
797 		if (bufsize < 3)
798 			return 0;
799 		strlcpy(buf, "0x", bufsize);
800 		bufsize -= 2;
801 		p = essid;
802 		for (i = 0; i < maxlen && bufsize >= 2; i++) {
803 			sprintf(&buf[2+2*i], "%02x", p[i]);
804 			bufsize -= 2;
805 		}
806 		if (i != essid_len)
807 			memcpy(&buf[2+2*i-3], "...", 3);
808 	} else {			/* printable, truncate as needed */
809 		memcpy(buf, essid, maxlen);
810 		if (maxlen != essid_len)
811 			memcpy(&buf[maxlen-3], "...", 3);
812 	}
813 	return maxlen;
814 }
815 
816 /* unaligned little endian access */
817 #define LE_READ_4(p)					\
818 	((u_int32_t)					\
819 	 ((((const u_int8_t *)(p))[0]      ) |		\
820 	  (((const u_int8_t *)(p))[1] <<  8) |		\
821 	  (((const u_int8_t *)(p))[2] << 16) |		\
822 	  (((const u_int8_t *)(p))[3] << 24)))
823 
824 static int __inline
825 iswpaoui(const u_int8_t *frm)
826 {
827 	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
828 }
829 
830 static int __inline
831 iswmeoui(const u_int8_t *frm)
832 {
833 	return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
834 }
835 
836 static int __inline
837 isatherosoui(const u_int8_t *frm)
838 {
839 	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
840 }
841 
842 static void
843 printies(const u_int8_t *vp, int ielen, int maxcols)
844 {
845 	while (ielen > 0) {
846 		switch (vp[0]) {
847 		case IEEE80211_ELEMID_VENDOR:
848 			if (iswpaoui(vp))
849 				printie(" WPA", vp, 2+vp[1], maxcols);
850 			else if (iswmeoui(vp))
851 				printie(" WME", vp, 2+vp[1], maxcols);
852 			else if (isatherosoui(vp))
853 				printie(" ATH", vp, 2+vp[1], maxcols);
854 			else
855 				printie(" VEN", vp, 2+vp[1], maxcols);
856 			break;
857 		case IEEE80211_ELEMID_RSN:
858 			printie(" RSN", vp, 2+vp[1], maxcols);
859 			break;
860 		default:
861 			printie(" ???", vp, 2+vp[1], maxcols);
862 			break;
863 		}
864 		ielen -= 2+vp[1];
865 		vp += 2+vp[1];
866 	}
867 }
868 
869 static void
870 list_scan(int s)
871 {
872 	uint8_t buf[24*1024];
873 	struct ieee80211req ireq;
874 	char ssid[IEEE80211_NWID_LEN+1];
875 	uint8_t *cp;
876 	int len, ssidmax;
877 
878 	(void) memset(&ireq, 0, sizeof(ireq));
879 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
880 	ireq.i_type = IEEE80211_IOC_SCAN_RESULTS;
881 	ireq.i_data = buf;
882 	ireq.i_len = sizeof(buf);
883 	if (ioctl(s, SIOCG80211, &ireq) < 0)
884 		errx(1, "unable to get scan results");
885 	len = ireq.i_len;
886 	if (len < sizeof(struct ieee80211req_scan_result))
887 		return;
888 
889 	ssidmax = verbose ? IEEE80211_NWID_LEN : 14;
890 	printf("%-*.*s  %-17.17s  %4s %4s  %-5s %3s %4s\n"
891 		, ssidmax, ssidmax, "SSID"
892 		, "BSSID"
893 		, "CHAN"
894 		, "RATE"
895 		, "S:N"
896 		, "INT"
897 		, "CAPS"
898 	);
899 	cp = buf;
900 	do {
901 		struct ieee80211req_scan_result *sr;
902 		uint8_t *vp;
903 
904 		sr = (struct ieee80211req_scan_result *) cp;
905 		vp = (u_int8_t *)(sr+1);
906 		printf("%-*.*s  %s  %3d  %3dM %2d:%-2d  %3d %-4.4s"
907 			, ssidmax
908 			  , copy_essid(ssid, ssidmax, vp, sr->isr_ssid_len)
909 			  , ssid
910 			, ether_ntoa((const struct ether_addr *) sr->isr_bssid)
911 			, ieee80211_mhz2ieee(sr->isr_freq)
912 			, getmaxrate(sr->isr_rates, sr->isr_nrates)
913 			, sr->isr_rssi, sr->isr_noise
914 			, sr->isr_intval
915 			, getcaps(sr->isr_capinfo)
916 		);
917 		printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);
918 		printf("\n");
919 		cp += sr->isr_len, len -= sr->isr_len;
920 	} while (len >= sizeof(struct ieee80211req_scan_result));
921 }
922 
923 #include <netproto/802_11/ieee80211_dragonfly.h>
924 
925 static void
926 scan_and_wait(int s)
927 {
928 	struct ieee80211req ireq;
929 	int sroute;
930 
931 	sroute = socket(PF_ROUTE, SOCK_RAW, 0);
932 	if (sroute < 0) {
933 		perror("socket(PF_ROUTE,SOCK_RAW)");
934 		return;
935 	}
936 	(void) memset(&ireq, 0, sizeof(ireq));
937 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
938 	ireq.i_type = IEEE80211_IOC_SCAN_REQ;
939 	/* NB: only root can trigger a scan so ignore errors */
940 	if (ioctl(s, SIOCS80211, &ireq) >= 0) {
941 		char buf[2048];
942 		struct if_announcemsghdr *ifan;
943 		struct rt_msghdr *rtm;
944 
945 		do {
946 			if (read(sroute, buf, sizeof(buf)) < 0) {
947 				perror("read(PF_ROUTE)");
948 				break;
949 			}
950 			rtm = (struct rt_msghdr *) buf;
951 			if (rtm->rtm_version != RTM_VERSION)
952 				break;
953 			ifan = (struct if_announcemsghdr *) rtm;
954 		} while (rtm->rtm_type != RTM_IEEE80211 ||
955 		    ifan->ifan_what != RTM_IEEE80211_SCAN);
956 	}
957 	close(sroute);
958 }
959 
960 static
961 DECL_CMD_FUNC(set80211scan, val, d)
962 {
963 	scan_and_wait(s);
964 	list_scan(s);
965 }
966 
967 static enum ieee80211_opmode get80211opmode(int s);
968 
969 static void
970 list_stations(int s)
971 {
972 	union {
973 		struct ieee80211req_sta_req req;
974 		uint8_t buf[24*1024];
975 	} u;
976 	enum ieee80211_opmode opmode = get80211opmode(s);
977 	struct ieee80211req ireq;
978 	uint8_t *cp;
979 	int len;
980 
981 	(void) memset(&ireq, 0, sizeof(ireq));
982 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
983 	/* broadcast address =>'s get all stations */
984 	(void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
985 	if (opmode == IEEE80211_M_STA) {
986 		/*
987 		 * Get information about the associated AP.
988 		 */
989 		ireq.i_type = IEEE80211_IOC_BSSID;
990 		ireq.i_data = u.req.is_u.macaddr;
991 		ireq.i_len = IEEE80211_ADDR_LEN;
992 		(void) ioctl(s, SIOCG80211, &ireq);
993 	}
994 	ireq.i_type = IEEE80211_IOC_STA_INFO;
995 	ireq.i_data = &u;
996 	ireq.i_len = sizeof(u);
997 	if (ioctl(s, SIOCG80211, &ireq) < 0)
998 		errx(1, "unable to get station information");
999 	len = ireq.i_len;
1000 	if (len < sizeof(struct ieee80211req_sta_info))
1001 		return;
1002 
1003 	printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %3s\n"
1004 		, "ADDR"
1005 		, "AID"
1006 		, "CHAN"
1007 		, "RATE"
1008 		, "RSSI"
1009 		, "IDLE"
1010 		, "TXSEQ"
1011 		, "RXSEQ"
1012 		, "CAPS"
1013 		, "ERP"
1014 	);
1015 	cp = (uint8_t *) u.req.info;
1016 	do {
1017 		struct ieee80211req_sta_info *si;
1018 		uint8_t *vp;
1019 
1020 		si = (struct ieee80211req_sta_info *) cp;
1021 		if (si->isi_len < sizeof(*si))
1022 			break;
1023 		vp = (u_int8_t *)(si+1);
1024 		printf("%s %4u %4d %3dM %4d %4d %6d %6d %-4.4s %3x"
1025 			, ether_ntoa((const struct ether_addr*) si->isi_macaddr)
1026 			, IEEE80211_AID(si->isi_associd)
1027 			, ieee80211_mhz2ieee(si->isi_freq)
1028 			, (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL)/2
1029 			, si->isi_rssi
1030 			, si->isi_inact
1031 			, si->isi_txseqs[0]
1032 			, si->isi_rxseqs[0]
1033 			, getcaps(si->isi_capinfo)
1034 			, si->isi_erp
1035 		);
1036 		printies(vp, si->isi_ie_len, 24);
1037 		printf("\n");
1038 		cp += si->isi_len, len -= si->isi_len;
1039 	} while (len >= sizeof(struct ieee80211req_sta_info));
1040 }
1041 
1042 static void
1043 print_chaninfo(const struct ieee80211_channel *c)
1044 {
1045 #define	IEEE80211_IS_CHAN_PASSIVE(_c) \
1046 	(((_c)->ic_flags & IEEE80211_CHAN_PASSIVE))
1047 	char buf[14];
1048 
1049 	buf[0] = '\0';
1050 	if (IEEE80211_IS_CHAN_FHSS(c))
1051 		strlcat(buf, " FHSS", sizeof(buf));
1052 	if (IEEE80211_IS_CHAN_A(c))
1053 		strlcat(buf, " 11a", sizeof(buf));
1054 	/* XXX 11g schizophrenia */
1055 	if (IEEE80211_IS_CHAN_G(c) ||
1056 	    IEEE80211_IS_CHAN_PUREG(c))
1057 		strlcat(buf, " 11g", sizeof(buf));
1058 	else if (IEEE80211_IS_CHAN_B(c))
1059 		strlcat(buf, " 11b", sizeof(buf));
1060 	if (IEEE80211_IS_CHAN_T(c))
1061 		strlcat(buf, " Turbo", sizeof(buf));
1062 	printf("Channel %3u : %u%c Mhz%-14.14s",
1063 		ieee80211_mhz2ieee(c->ic_freq), c->ic_freq,
1064 		IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ', buf);
1065 #undef IEEE80211_IS_CHAN_PASSIVE
1066 }
1067 
1068 static void
1069 list_channels(int s, int allchans)
1070 {
1071 	struct ieee80211req ireq;
1072 	struct ieee80211req_chaninfo chans;
1073 	struct ieee80211req_chaninfo achans;
1074 	const struct ieee80211_channel *c;
1075 	int i, half;
1076 
1077 	(void) memset(&ireq, 0, sizeof(ireq));
1078 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1079 	ireq.i_type = IEEE80211_IOC_CHANINFO;
1080 	ireq.i_data = &chans;
1081 	ireq.i_len = sizeof(chans);
1082 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1083 		errx(1, "unable to get channel information");
1084 	if (!allchans) {
1085 		struct ieee80211req_chanlist active;
1086 
1087 		ireq.i_type = IEEE80211_IOC_CHANLIST;
1088 		ireq.i_data = &active;
1089 		ireq.i_len = sizeof(active);
1090 		if (ioctl(s, SIOCG80211, &ireq) < 0)
1091 			errx(1, "unable to get active channel list");
1092 		memset(&achans, 0, sizeof(achans));
1093 		for (i = 0; i < chans.ic_nchans; i++) {
1094 			c = &chans.ic_chans[i];
1095 			if (isset(active.ic_channels, ieee80211_mhz2ieee(c->ic_freq)) || allchans)
1096 				achans.ic_chans[achans.ic_nchans++] = *c;
1097 		}
1098 	} else
1099 		achans = chans;
1100 	half = achans.ic_nchans / 2;
1101 	if (achans.ic_nchans % 2)
1102 		half++;
1103 	for (i = 0; i < achans.ic_nchans / 2; i++) {
1104 		print_chaninfo(&achans.ic_chans[i]);
1105 		print_chaninfo(&achans.ic_chans[half+i]);
1106 		printf("\n");
1107 	}
1108 	if (achans.ic_nchans % 2) {
1109 		print_chaninfo(&achans.ic_chans[i]);
1110 		printf("\n");
1111 	}
1112 }
1113 
1114 static void
1115 list_keys(int s)
1116 {
1117 }
1118 
1119 #define	IEEE80211_C_BITS \
1120 "\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \
1121 "\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \
1122 "\31WPA2\32BURST\33WME"
1123 
1124 #define IEEE80211_CEXT_BITS	"\020\1PBCC"
1125 
1126 static void
1127 list_capabilities(int s)
1128 {
1129 	struct ieee80211req ireq;
1130 	uint32_t caps, caps_ext;
1131 
1132 	memset(&ireq, 0, sizeof(ireq));
1133 	caps_ext = 0;
1134 
1135 	strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1136 	ireq.i_data = &caps_ext;
1137 	ireq.i_len = sizeof(caps_ext);
1138 	ireq.i_type = IEEE80211_IOC_DRIVER_CAPS;
1139 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1140 		errx(1, "unable to get driver capabilities");
1141 	caps = (((uint16_t)ireq.i_val) << 16) | ((uint16_t)ireq.i_len);
1142 	printb(name, caps, IEEE80211_C_BITS);
1143 	if (caps_ext != 0)
1144 		printb(", ext", caps_ext, IEEE80211_CEXT_BITS);
1145 	putchar('\n');
1146 }
1147 
1148 static void
1149 list_wme(int s)
1150 {
1151 	static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
1152 	struct ieee80211req ireq;
1153 	int ac;
1154 
1155 	(void) memset(&ireq, 0, sizeof(ireq));
1156 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1157 	ireq.i_len = 0;
1158 	for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
1159 again:
1160 		if (ireq.i_len & IEEE80211_WMEPARAM_BSS)
1161 			printf("\t%s", "     ");
1162 		else
1163 			printf("\t%s", acnames[ac]);
1164 
1165 		ireq.i_len = (ireq.i_len & IEEE80211_WMEPARAM_BSS) | ac;
1166 
1167 		/* show WME BSS parameters */
1168 		ireq.i_type = IEEE80211_IOC_WME_CWMIN;
1169 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1170 			printf(" cwmin %2u", ireq.i_val);
1171 		ireq.i_type = IEEE80211_IOC_WME_CWMAX;
1172 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1173 			printf(" cwmax %2u", ireq.i_val);
1174 		ireq.i_type = IEEE80211_IOC_WME_AIFS;
1175 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1176 			printf(" aifs %2u", ireq.i_val);
1177 		ireq.i_type = IEEE80211_IOC_WME_TXOPLIMIT;
1178 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1179 			printf(" txopLimit %3u", ireq.i_val);
1180 		ireq.i_type = IEEE80211_IOC_WME_ACM;
1181 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1182 			if (ireq.i_val)
1183 				printf(" acm");
1184 			else if (verbose)
1185 				printf(" -acm");
1186 		}
1187 		/* !BSS only */
1188 		if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) {
1189 			ireq.i_type = IEEE80211_IOC_WME_ACKPOLICY;
1190 			if (ioctl(s, SIOCG80211, &ireq) != -1) {
1191 				if (!ireq.i_val)
1192 					printf(" -ack");
1193 				else if (verbose)
1194 					printf(" ack");
1195 			}
1196 		}
1197 		printf("\n");
1198 		if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) {
1199 			ireq.i_len |= IEEE80211_WMEPARAM_BSS;
1200 			goto again;
1201 		} else
1202 			ireq.i_len &= ~IEEE80211_WMEPARAM_BSS;
1203 	}
1204 }
1205 
1206 static void
1207 list_mac(int s)
1208 {
1209 	struct ieee80211req ireq;
1210 	struct ieee80211req_maclist *acllist;
1211 	int i, nacls, policy;
1212 	char c;
1213 
1214 	(void) memset(&ireq, 0, sizeof(ireq));
1215 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
1216 	ireq.i_type = IEEE80211_IOC_MACCMD;
1217 	ireq.i_val = IEEE80211_MACCMD_POLICY;
1218 	if (ioctl(s, SIOCG80211, &ireq) < 0) {
1219 		if (errno == EINVAL) {
1220 			printf("No acl policy loaded\n");
1221 			return;
1222 		}
1223 		err(1, "unable to get mac policy");
1224 	}
1225 	policy = ireq.i_val;
1226 
1227 	ireq.i_val = IEEE80211_MACCMD_LIST;
1228 	ireq.i_len = 0;
1229 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1230 		err(1, "unable to get mac acl list size");
1231 	if (ireq.i_len == 0)		/* NB: no acls */
1232 		return;
1233 
1234 	ireq.i_data = malloc(ireq.i_len);
1235 	if (ireq.i_data == NULL)
1236 		err(1, "out of memory for acl list");
1237 
1238 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1239 		err(1, "unable to get mac acl list");
1240 	if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
1241 		if (verbose)
1242 			printf("policy: open\n");
1243 		c = '*';
1244 	} else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
1245 		if (verbose)
1246 			printf("policy: allow\n");
1247 		c = '+';
1248 	} else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
1249 		if (verbose)
1250 			printf("policy: deny\n");
1251 		c = '-';
1252 	} else {
1253 		printf("policy: unknown (%u)\n", policy);
1254 		c = '?';
1255 	}
1256 	nacls = ireq.i_len / sizeof(*acllist);
1257 	acllist = (struct ieee80211req_maclist *) ireq.i_data;
1258 	for (i = 0; i < nacls; i++)
1259 		printf("%c%s\n", c, ether_ntoa(
1260 			(const struct ether_addr *) acllist[i].ml_macaddr));
1261 }
1262 
1263 static
1264 DECL_CMD_FUNC(set80211list, arg, d)
1265 {
1266 #define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
1267 
1268 	if (iseq(arg, "sta"))
1269 		list_stations(s);
1270 	else if (iseq(arg, "scan") || iseq(arg, "ap"))
1271 		list_scan(s);
1272 	else if (iseq(arg, "chan") || iseq(arg, "freq"))
1273 		list_channels(s, 1);
1274 	else if (iseq(arg, "active"))
1275 		list_channels(s, 0);
1276 	else if (iseq(arg, "keys"))
1277 		list_keys(s);
1278 	else if (iseq(arg, "caps"))
1279 		list_capabilities(s);
1280 	else if (iseq(arg, "wme"))
1281 		list_wme(s);
1282 	else if (iseq(arg, "mac"))
1283 		list_mac(s);
1284 	else
1285 		errx(1, "Don't know how to list %s for %s", arg, name);
1286 #undef iseq
1287 }
1288 
1289 static enum ieee80211_opmode
1290 get80211opmode(int s)
1291 {
1292 	struct ifmediareq ifmr;
1293 
1294 	(void) memset(&ifmr, 0, sizeof(ifmr));
1295 	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
1296 
1297 	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
1298 		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC)
1299 			return IEEE80211_M_IBSS;	/* XXX ahdemo */
1300 		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
1301 			return IEEE80211_M_HOSTAP;
1302 		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
1303 			return IEEE80211_M_MONITOR;
1304 	}
1305 	return IEEE80211_M_STA;
1306 }
1307 
1308 static const struct ieee80211_channel *
1309 getchaninfo(int s, int chan)
1310 {
1311 	struct ieee80211req ireq;
1312 	static struct ieee80211req_chaninfo chans;
1313 	static struct ieee80211_channel undef;
1314 	const struct ieee80211_channel *c;
1315 	int i, freq;
1316 
1317 	(void) memset(&ireq, 0, sizeof(ireq));
1318 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1319 	ireq.i_type = IEEE80211_IOC_CHANINFO;
1320 	ireq.i_data = &chans;
1321 	ireq.i_len = sizeof(chans);
1322 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1323 		errx(1, "unable to get channel information");
1324 	freq = ieee80211_ieee2mhz(chan);
1325 	for (i = 0; i < chans.ic_nchans; i++) {
1326 		c = &chans.ic_chans[i];
1327 		if (c->ic_freq == freq)
1328 			return c;
1329 	}
1330 	return &undef;
1331 }
1332 
1333 #if 0
1334 static void
1335 printcipher(int s, struct ieee80211req *ireq, int keylenop)
1336 {
1337 	switch (ireq->i_val) {
1338 	case IEEE80211_CIPHER_WEP:
1339 		ireq->i_type = keylenop;
1340 		if (ioctl(s, SIOCG80211, ireq) != -1)
1341 			printf("WEP-%s",
1342 			    ireq->i_len <= 5 ? "40" :
1343 			    ireq->i_len <= 13 ? "104" : "128");
1344 		else
1345 			printf("WEP");
1346 		break;
1347 	case IEEE80211_CIPHER_TKIP:
1348 		printf("TKIP");
1349 		break;
1350 	case IEEE80211_CIPHER_AES_OCB:
1351 		printf("AES-OCB");
1352 		break;
1353 	case IEEE80211_CIPHER_AES_CCM:
1354 		printf("AES-CCM");
1355 		break;
1356 	case IEEE80211_CIPHER_CKIP:
1357 		printf("CKIP");
1358 		break;
1359 	case IEEE80211_CIPHER_NONE:
1360 		printf("NONE");
1361 		break;
1362 	default:
1363 		printf("UNKNOWN (0x%x)", ireq->i_val);
1364 		break;
1365 	}
1366 }
1367 #endif
1368 
1369 #define	MAXCOL	78
1370 static	int col;
1371 static	char spacer;
1372 
1373 static void
1374 LINE_BREAK(void)
1375 {
1376 	if (spacer != '\t') {
1377 		printf("\n");
1378 		spacer = '\t';
1379 	}
1380 	col = 8;	/* 8-col tab */
1381 }
1382 
1383 static void
1384 LINE_CHECK(const char *fmt, ...)
1385 {
1386 	char buf[80];
1387 	va_list ap;
1388 	int n;
1389 
1390 	va_start(ap, fmt);
1391 	n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
1392 	va_end(ap);
1393 	col += 1+n;
1394 	if (col > MAXCOL) {
1395 		LINE_BREAK();
1396 		col += n;
1397 	}
1398 	buf[0] = spacer;
1399 	printf("%s", buf);
1400 	spacer = ' ';
1401 }
1402 
1403 static void
1404 printkey(const struct ieee80211req_key *ik)
1405 {
1406 	static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
1407 	int keylen = ik->ik_keylen;
1408 	int printcontents;
1409 
1410 	printcontents = printkeys &&
1411 		(memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
1412 	if (printcontents)
1413 		LINE_BREAK();
1414 	switch (ik->ik_type) {
1415 	case IEEE80211_CIPHER_WEP:
1416 		/* compatibility */
1417 		LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
1418 		    keylen <= 5 ? "40-bit" :
1419 		    keylen <= 13 ? "104-bit" : "128-bit");
1420 		break;
1421 	case IEEE80211_CIPHER_TKIP:
1422 		if (keylen > 128/8)
1423 			keylen -= 128/8;	/* ignore MIC for now */
1424 		LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
1425 		break;
1426 	case IEEE80211_CIPHER_AES_OCB:
1427 		LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
1428 		break;
1429 	case IEEE80211_CIPHER_AES_CCM:
1430 		LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
1431 		break;
1432 	case IEEE80211_CIPHER_CKIP:
1433 		LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
1434 		break;
1435 	case IEEE80211_CIPHER_NONE:
1436 		LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
1437 		break;
1438 	default:
1439 		LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
1440 			ik->ik_type, ik->ik_keyix+1, 8*keylen);
1441 		break;
1442 	}
1443 	if (printcontents) {
1444 		int i;
1445 
1446 		printf(" <");
1447 		for (i = 0; i < keylen; i++)
1448 			printf("%02x", ik->ik_keydata[i]);
1449 		printf(">");
1450 		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
1451 		    (ik->ik_keyrsc != 0 || verbose))
1452 			printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
1453 		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
1454 		    (ik->ik_keytsc != 0 || verbose))
1455 			printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
1456 		if (ik->ik_flags != 0 && verbose) {
1457 			const char *sep = " ";
1458 
1459 			if (ik->ik_flags & IEEE80211_KEY_XMIT)
1460 				printf("%stx", sep), sep = "+";
1461 			if (ik->ik_flags & IEEE80211_KEY_RECV)
1462 				printf("%srx", sep), sep = "+";
1463 			if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
1464 				printf("%sdef", sep), sep = "+";
1465 		}
1466 		LINE_BREAK();
1467 	}
1468 }
1469 
1470 static void
1471 ieee80211_status(int s)
1472 {
1473 	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
1474 	enum ieee80211_opmode opmode = get80211opmode(s);
1475 	int i, num, wpa, wme;
1476 	struct ieee80211req ireq;
1477 	u_int8_t data[32];
1478 	const struct ieee80211_channel *c;
1479 
1480 	(void) memset(&ireq, 0, sizeof(ireq));
1481 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1482 	ireq.i_data = &data;
1483 
1484 	wpa = 0;		/* unknown/not set */
1485 
1486 	ireq.i_type = IEEE80211_IOC_SSID;
1487 	ireq.i_val = -1;
1488 	if (ioctl(s, SIOCG80211, &ireq) < 0) {
1489 		/* If we can't get the SSID, this isn't an 802.11 device. */
1490 		return;
1491 	}
1492 
1493 	ireq.i_type = IEEE80211_IOC_RATECTL;
1494 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1495 		int lineb = 1;
1496 
1497 		switch (ireq.i_val) {
1498 		case IEEE80211_RATECTL_ONOE:
1499 			printf("\tratectl: onoe");
1500 			break;
1501 		case IEEE80211_RATECTL_AMRR:
1502 			printf("\tratectl: amrr");
1503 			break;
1504 		default:
1505 			if (verbose)
1506 				printf("\tratectl: none");
1507 			else
1508 				lineb = 0;
1509 			break;
1510 		}
1511 		if (lineb)
1512 			LINE_BREAK();
1513 	}
1514 
1515 	num = 0;
1516 	ireq.i_type = IEEE80211_IOC_NUMSSIDS;
1517 	if (ioctl(s, SIOCG80211, &ireq) >= 0)
1518 		num = ireq.i_val;
1519 	printf("\tssid ");
1520 	if (num > 1) {
1521 		ireq.i_type = IEEE80211_IOC_SSID;
1522 		for (ireq.i_val = 0; ireq.i_val < num; ireq.i_val++) {
1523 			if (ioctl(s, SIOCG80211, &ireq) >= 0 && ireq.i_len > 0) {
1524 				printf(" %d:", ireq.i_val + 1);
1525 				print_string(data, ireq.i_len);
1526 			}
1527 		}
1528 	} else
1529 		print_string(data, ireq.i_len);
1530 
1531 	ireq.i_type = IEEE80211_IOC_CHANNEL;
1532 	if (ioctl(s, SIOCG80211, &ireq) < 0)
1533 		goto end;
1534 	c = getchaninfo(s, ireq.i_val);
1535 	if (ireq.i_val != -1) {
1536 		printf(" channel %d", ireq.i_val);
1537 		if (verbose)
1538 			printf(" (%u)", c->ic_freq);
1539 	} else if (verbose)
1540 		printf(" channel UNDEF");
1541 
1542 	ireq.i_type = IEEE80211_IOC_BSSID;
1543 	ireq.i_len = IEEE80211_ADDR_LEN;
1544 	if (ioctl(s, SIOCG80211, &ireq) >= 0 &&
1545 	    (memcmp(ireq.i_data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
1546 		printf(" bssid %s", ether_ntoa(ireq.i_data));
1547 
1548 	ireq.i_type = IEEE80211_IOC_STATIONNAME;
1549 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1550 		printf("\n\tstationname ");
1551 		print_string(data, ireq.i_len);
1552 	}
1553 
1554 	spacer = ' ';		/* force first break */
1555 	LINE_BREAK();
1556 
1557 	ireq.i_type = IEEE80211_IOC_AUTHMODE;
1558 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1559 		switch (ireq.i_val) {
1560 			case IEEE80211_AUTH_NONE:
1561 				LINE_CHECK("authmode NONE");
1562 				break;
1563 			case IEEE80211_AUTH_OPEN:
1564 				LINE_CHECK("authmode OPEN");
1565 				break;
1566 			case IEEE80211_AUTH_SHARED:
1567 				LINE_CHECK("authmode SHARED");
1568 				break;
1569 			case IEEE80211_AUTH_8021X:
1570 				LINE_CHECK("authmode 802.1x");
1571 				break;
1572 			case IEEE80211_AUTH_WPA:
1573 				ireq.i_type = IEEE80211_IOC_WPA;
1574 				if (ioctl(s, SIOCG80211, &ireq) != -1)
1575 					wpa = ireq.i_val;
1576 				if (!wpa)
1577 					wpa = 1;	/* default to WPA1 */
1578 				switch (wpa) {
1579 				case 2:
1580 					LINE_CHECK("authmode WPA2/802.11i");
1581 					break;
1582 				case 3:
1583 					LINE_CHECK("authmode WPA1+WPA2/802.11i");
1584 					break;
1585 				default:
1586 					LINE_CHECK("authmode WPA");
1587 					break;
1588 				}
1589 				break;
1590 			case IEEE80211_AUTH_AUTO:
1591 				LINE_CHECK("authmode AUTO");
1592 				break;
1593 			default:
1594 				LINE_CHECK("authmode UNKNOWN (0x%x)",
1595 					ireq.i_val);
1596 				break;
1597 		}
1598 	}
1599 
1600 	ireq.i_type = IEEE80211_IOC_WEP;
1601 	if (ioctl(s, SIOCG80211, &ireq) != -1 &&
1602 	    ireq.i_val != IEEE80211_WEP_NOSUP) {
1603 		int firstkey, wepmode;
1604 
1605 		wepmode = ireq.i_val;
1606 		switch (wepmode) {
1607 			case IEEE80211_WEP_OFF:
1608 				LINE_CHECK("privacy OFF");
1609 				break;
1610 			case IEEE80211_WEP_ON:
1611 				LINE_CHECK("privacy ON");
1612 				break;
1613 			case IEEE80211_WEP_MIXED:
1614 				LINE_CHECK("privacy MIXED");
1615 				break;
1616 			default:
1617 				LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
1618 				break;
1619 		}
1620 
1621 		/*
1622 		 * If we get here then we've got WEP support so we need
1623 		 * to print WEP status.
1624 		 */
1625 
1626 		ireq.i_type = IEEE80211_IOC_WEPTXKEY;
1627 		if (ioctl(s, SIOCG80211, &ireq) < 0) {
1628 			warn("WEP support, but no tx key!");
1629 			goto end;
1630 		}
1631 		if (ireq.i_val != -1)
1632 			LINE_CHECK("deftxkey %d", ireq.i_val+1);
1633 		else if (wepmode != IEEE80211_WEP_OFF || verbose)
1634 			LINE_CHECK("deftxkey UNDEF");
1635 
1636 		ireq.i_type = IEEE80211_IOC_NUMWEPKEYS;
1637 		if (ioctl(s, SIOCG80211, &ireq) < 0) {
1638 			warn("WEP support, but no NUMWEPKEYS support!");
1639 			goto end;
1640 		}
1641 		num = ireq.i_val;
1642 
1643 		firstkey = 1;
1644 		for (i = 0; i < num; i++) {
1645 			struct ieee80211req_key ik;
1646 
1647 			memset(&ik, 0, sizeof(ik));
1648 			ik.ik_keyix = i;
1649 			ireq.i_type = IEEE80211_IOC_WPAKEY;
1650 			ireq.i_data = &ik;
1651 			ireq.i_len = sizeof(ik);
1652 			if (ioctl(s, SIOCG80211, &ireq) < 0) {
1653 				warn("WEP support, but can get keys!");
1654 				goto end;
1655 			}
1656 			if (ik.ik_keylen != 0) {
1657 				if (verbose)
1658 					LINE_BREAK();
1659 				printkey(&ik);
1660 				firstkey = 0;
1661 			}
1662 		}
1663 	}
1664 
1665 	ireq.i_type = IEEE80211_IOC_POWERSAVE;
1666 	if (ioctl(s, SIOCG80211, &ireq) != -1 &&
1667 	    ireq.i_val != IEEE80211_POWERSAVE_NOSUP ) {
1668 		if (ireq.i_val != IEEE80211_POWERSAVE_OFF || verbose) {
1669 			switch (ireq.i_val) {
1670 				case IEEE80211_POWERSAVE_OFF:
1671 					LINE_CHECK("powersavemode OFF");
1672 					break;
1673 				case IEEE80211_POWERSAVE_CAM:
1674 					LINE_CHECK("powersavemode CAM");
1675 					break;
1676 				case IEEE80211_POWERSAVE_PSP:
1677 					LINE_CHECK("powersavemode PSP");
1678 					break;
1679 				case IEEE80211_POWERSAVE_PSP_CAM:
1680 					LINE_CHECK("powersavemode PSP-CAM");
1681 					break;
1682 			}
1683 			ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP;
1684 			if (ioctl(s, SIOCG80211, &ireq) != -1)
1685 				LINE_CHECK("powersavesleep %d", ireq.i_val);
1686 		}
1687 	}
1688 
1689 	ireq.i_type = IEEE80211_IOC_TXPOWMAX;
1690 	if (ioctl(s, SIOCG80211, &ireq) != -1)
1691 		LINE_CHECK("txpowmax %d", ireq.i_val);
1692 
1693 	if (verbose) {
1694 		ireq.i_type = IEEE80211_IOC_TXPOWER;
1695 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1696 			LINE_CHECK("txpower %d", ireq.i_val);
1697 	}
1698 
1699 	ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD;
1700 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1701 		if (ireq.i_val != IEEE80211_RTS_MAX || verbose)
1702 			LINE_CHECK("rtsthreshold %d", ireq.i_val);
1703 	}
1704 
1705 	ireq.i_type = IEEE80211_IOC_MCAST_RATE;
1706 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1707 		if (ireq.i_val != 2*1 || verbose) {
1708 			if (ireq.i_val == 11)
1709 				LINE_CHECK("mcastrate 5.5");
1710 			else
1711 				LINE_CHECK("mcastrate %d", ireq.i_val/2);
1712 		}
1713 	}
1714 
1715 	ireq.i_type = IEEE80211_IOC_FRAGTHRESHOLD;
1716 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1717 		if (ireq.i_val != IEEE80211_FRAG_MAX || verbose)
1718 			LINE_CHECK("fragthreshold %d", ireq.i_val);
1719 	}
1720 
1721 	ireq.i_type = IEEE80211_IOC_BMISSTHRESHOLD;
1722 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1723 		if (ireq.i_val != IEEE80211_HWBMISS_MAX || verbose)
1724 			LINE_CHECK("bmiss %d", ireq.i_val);
1725 	}
1726 
1727 	if (IEEE80211_IS_CHAN_G(c) || IEEE80211_IS_CHAN_PUREG(c) || verbose) {
1728 		ireq.i_type = IEEE80211_IOC_PUREG;
1729 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1730 			if (ireq.i_val)
1731 				LINE_CHECK("pureg");
1732 			else if (verbose)
1733 				LINE_CHECK("-pureg");
1734 		}
1735 		ireq.i_type = IEEE80211_IOC_PROTMODE;
1736 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1737 			switch (ireq.i_val) {
1738 				case IEEE80211_PROTMODE_OFF:
1739 					LINE_CHECK("protmode OFF");
1740 					break;
1741 				case IEEE80211_PROTMODE_CTS:
1742 					LINE_CHECK("protmode CTS");
1743 					break;
1744 				case IEEE80211_PROTMODE_RTSCTS:
1745 					LINE_CHECK("protmode RTSCTS");
1746 					break;
1747 				default:
1748 					LINE_CHECK("protmode UNKNOWN (0x%x)",
1749 						ireq.i_val);
1750 					break;
1751 			}
1752 		}
1753 	}
1754 
1755 	ireq.i_type = IEEE80211_IOC_WME;
1756 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1757 		wme = ireq.i_val;
1758 		if (wme)
1759 			LINE_CHECK("wme");
1760 		else if (verbose)
1761 			LINE_CHECK("-wme");
1762 	} else
1763 		wme = 0;
1764 
1765 	ireq.i_type = IEEE80211_IOC_BURST;
1766 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1767 		if (ireq.i_val)
1768 			LINE_CHECK("burst");
1769 		else if (verbose)
1770 			LINE_CHECK("-burst");
1771 	}
1772 
1773 	if (opmode == IEEE80211_M_HOSTAP) {
1774 		ireq.i_type = IEEE80211_IOC_HIDESSID;
1775 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1776 			if (ireq.i_val)
1777 				LINE_CHECK("ssid HIDE");
1778 			else if (verbose)
1779 				LINE_CHECK("ssid SHOW");
1780 		}
1781 
1782 		ireq.i_type = IEEE80211_IOC_APBRIDGE;
1783 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1784 			if (!ireq.i_val)
1785 				LINE_CHECK("-apbridge");
1786 			else if (verbose)
1787 				LINE_CHECK("apbridge");
1788 		}
1789 
1790 		ireq.i_type = IEEE80211_IOC_DTIM_PERIOD;
1791 		if (ioctl(s, SIOCG80211, &ireq) != -1)
1792 			LINE_CHECK("dtimperiod %u", ireq.i_val);
1793 	} else {
1794 		ireq.i_type = IEEE80211_IOC_ROAMING;
1795 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1796 			if (ireq.i_val != IEEE80211_ROAMING_AUTO || verbose) {
1797 				switch (ireq.i_val) {
1798 				case IEEE80211_ROAMING_DEVICE:
1799 					LINE_CHECK("roaming DEVICE");
1800 					break;
1801 				case IEEE80211_ROAMING_AUTO:
1802 					LINE_CHECK("roaming AUTO");
1803 					break;
1804 				case IEEE80211_ROAMING_MANUAL:
1805 					LINE_CHECK("roaming MANUAL");
1806 					break;
1807 				default:
1808 					LINE_CHECK("roaming UNKNOWN (0x%x)",
1809 						ireq.i_val);
1810 					break;
1811 				}
1812 			}
1813 		}
1814 	}
1815 	ireq.i_type = IEEE80211_IOC_BEACON_INTERVAL;
1816 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
1817 		if (ireq.i_val)
1818 			LINE_CHECK("bintval %u", ireq.i_val);
1819 		else if (verbose)
1820 			LINE_CHECK("bintval %u", ireq.i_val);
1821 	}
1822 
1823 	if (wme && verbose) {
1824 		LINE_BREAK();
1825 		list_wme(s);
1826 	}
1827 
1828 	if (wpa) {
1829 		ireq.i_type = IEEE80211_IOC_COUNTERMEASURES;
1830 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1831 			if (ireq.i_val)
1832 				LINE_CHECK("countermeasures");
1833 			else if (verbose)
1834 				LINE_CHECK("-countermeasures");
1835 		}
1836 #if 0
1837 		/* XXX not interesting with WPA done in user space */
1838 		ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
1839 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1840 		}
1841 
1842 		ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
1843 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1844 			LINE_CHECK("mcastcipher ");
1845 			printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
1846 			spacer = ' ';
1847 		}
1848 
1849 		ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
1850 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1851 			LINE_CHECK("ucastcipher ");
1852 			printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
1853 		}
1854 
1855 		if (wpa & 2) {
1856 			ireq.i_type = IEEE80211_IOC_RSNCAPS;
1857 			if (ioctl(s, SIOCG80211, &ireq) != -1) {
1858 				LINE_CHECK("RSN caps 0x%x", ireq.i_val);
1859 				spacer = ' ';
1860 			}
1861 		}
1862 
1863 		ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
1864 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
1865 		}
1866 #endif
1867 		LINE_BREAK();
1868 	}
1869 	LINE_BREAK();
1870 
1871 end:
1872 	return;
1873 }
1874 
1875 static void
1876 set80211(int s, int type, int val, int len, u_int8_t *data)
1877 {
1878 	struct ieee80211req	ireq;
1879 
1880 	(void) memset(&ireq, 0, sizeof(ireq));
1881 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1882 	ireq.i_type = type;
1883 	ireq.i_val = val;
1884 	ireq.i_len = len;
1885 	ireq.i_data = data;
1886 	if (ioctl(s, SIOCS80211, &ireq) < 0)
1887 		err(1, "SIOCS80211");
1888 }
1889 
1890 static const char *
1891 get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
1892 {
1893 	int len;
1894 	int hexstr;
1895 	u_int8_t *p;
1896 
1897 	len = *lenp;
1898 	p = buf;
1899 	hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
1900 	if (hexstr)
1901 		val += 2;
1902 	for (;;) {
1903 		if (*val == '\0')
1904 			break;
1905 		if (sep != NULL && strchr(sep, *val) != NULL) {
1906 			val++;
1907 			break;
1908 		}
1909 		if (hexstr) {
1910 			if (!isxdigit((u_char)val[0])) {
1911 				warnx("bad hexadecimal digits");
1912 				return NULL;
1913 			}
1914 			if (!isxdigit((u_char)val[1])) {
1915 				warnx("odd count hexadecimal digits");
1916 				return NULL;
1917 			}
1918 		}
1919 		if (p >= buf + len) {
1920 			if (hexstr)
1921 				warnx("hexadecimal digits too long");
1922 			else
1923 				warnx("string too long");
1924 			return NULL;
1925 		}
1926 		if (hexstr) {
1927 #define	tohex(x)	(isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
1928 			*p++ = (tohex((u_char)val[0]) << 4) |
1929 			    tohex((u_char)val[1]);
1930 #undef tohex
1931 			val += 2;
1932 		} else
1933 			*p++ = *val++;
1934 	}
1935 	len = p - buf;
1936 	if (len < *lenp)
1937 		memset(p, 0, *lenp - len);
1938 	/* The string "-" is treated as the empty string. */
1939 	if (!hexstr && len == 1 && buf[0] == '-')
1940 		len = 0;
1941 	*lenp = len;
1942 	return val;
1943 }
1944 
1945 static void
1946 print_string(const u_int8_t *buf, int len)
1947 {
1948 	int i;
1949 	int hasspc;
1950 
1951 	i = 0;
1952 	hasspc = 0;
1953 	for (; i < len; i++) {
1954 		if (!isprint(buf[i]) && buf[i] != '\0')
1955 			break;
1956 		if (isspace(buf[i]))
1957 			hasspc++;
1958 	}
1959 	if (i == len) {
1960 		if (hasspc || len == 0 || buf[0] == '\0')
1961 			printf("\"%.*s\"", len, buf);
1962 		else
1963 			printf("%.*s", len, buf);
1964 	} else {
1965 		printf("0x");
1966 		for (i = 0; i < len; i++)
1967 			printf("%02x", buf[i]);
1968 	}
1969 }
1970 
1971 static struct cmd ieee80211_cmds[] = {
1972 	DEF_CMD_ARG("ssid",		set80211ssid),
1973 	DEF_CMD_ARG("nwid",		set80211ssid),
1974 	DEF_CMD_ARG("stationname",	set80211stationname),
1975 	DEF_CMD_ARG("station",		set80211stationname),	/* BSD/OS */
1976 	DEF_CMD_ARG("channel",		set80211channel),
1977 	DEF_CMD_ARG("authmode",		set80211authmode),
1978 	DEF_CMD_ARG("powersavemode",	set80211powersavemode),
1979 	DEF_CMD("powersave",	1,	set80211powersave),
1980 	DEF_CMD("-powersave",	0,	set80211powersave),
1981 	DEF_CMD_ARG("powersavesleep", 	set80211powersavesleep),
1982 	DEF_CMD_ARG("wepmode",		set80211wepmode),
1983 	DEF_CMD("wep",		1,	set80211wep),
1984 	DEF_CMD("-wep",		0,	set80211wep),
1985 	DEF_CMD_ARG("deftxkey",		set80211weptxkey),
1986 	DEF_CMD_ARG("weptxkey",		set80211weptxkey),
1987 	DEF_CMD_ARG("wepkey",		set80211wepkey),
1988 	DEF_CMD_ARG("nwkey",		set80211nwkey),		/* NetBSD */
1989 	DEF_CMD("-nwkey",	0,	set80211wep),		/* NetBSD */
1990 	DEF_CMD_ARG("rtsthreshold",	set80211rtsthreshold),
1991 	DEF_CMD_ARG("protmode",		set80211protmode),
1992 	DEF_CMD_ARG("txpower",		set80211txpower),
1993 	DEF_CMD_ARG("roaming",		set80211roaming),
1994 	DEF_CMD("wme",		1,	set80211wme),
1995 	DEF_CMD("-wme",		0,	set80211wme),
1996 	DEF_CMD("hidessid",	1,	set80211hidessid),
1997 	DEF_CMD("-hidessid",	0,	set80211hidessid),
1998 	DEF_CMD("apbridge",	1,	set80211apbridge),
1999 	DEF_CMD("-apbridge",	0,	set80211apbridge),
2000 	DEF_CMD_ARG("chanlist",		set80211chanlist),
2001 	DEF_CMD_ARG("bssid",		set80211bssid),
2002 	DEF_CMD_ARG("ap",		set80211bssid),
2003 	DEF_CMD("scan",	0,		set80211scan),
2004 	DEF_CMD_ARG("list",		set80211list),
2005 	DEF_CMD_ARG2("cwmin",		set80211cwmin),
2006 	DEF_CMD_ARG2("cwmax",		set80211cwmax),
2007 	DEF_CMD_ARG2("aifs",		set80211aifs),
2008 	DEF_CMD_ARG2("txoplimit",	set80211txoplimit),
2009 	DEF_CMD_ARG("acm",		set80211acm),
2010 	DEF_CMD_ARG("-acm",		set80211noacm),
2011 	DEF_CMD_ARG("ack",		set80211ackpolicy),
2012 	DEF_CMD_ARG("-ack",		set80211noackpolicy),
2013 	DEF_CMD_ARG2("bss:cwmin",	set80211bsscwmin),
2014 	DEF_CMD_ARG2("bss:cwmax",	set80211bsscwmax),
2015 	DEF_CMD_ARG2("bss:aifs",	set80211bssaifs),
2016 	DEF_CMD_ARG2("bss:txoplimit",	set80211bsstxoplimit),
2017 	DEF_CMD_ARG("dtimperiod",	set80211dtimperiod),
2018 	DEF_CMD_ARG("bintval",		set80211bintval),
2019 	DEF_CMD("mac:open",	IEEE80211_MACCMD_POLICY_OPEN,	set80211maccmd),
2020 	DEF_CMD("mac:allow",	IEEE80211_MACCMD_POLICY_ALLOW,	set80211maccmd),
2021 	DEF_CMD("mac:deny",	IEEE80211_MACCMD_POLICY_DENY,	set80211maccmd),
2022 	DEF_CMD("mac:flush",	IEEE80211_MACCMD_FLUSH,		set80211maccmd),
2023 	DEF_CMD("mac:detach",	IEEE80211_MACCMD_DETACH,	set80211maccmd),
2024 	DEF_CMD_ARG("mac:add",		set80211addmac),
2025 	DEF_CMD_ARG("mac:del",		set80211delmac),
2026 	DEF_CMD_ARG("mac:kick",		set80211kickmac),
2027 	DEF_CMD("pureg",	1,	set80211pureg),
2028 	DEF_CMD("-pureg",	0,	set80211pureg),
2029 	DEF_CMD_ARG("mcastrate",	set80211mcastrate),
2030 	DEF_CMD_ARG("fragthreshold",	set80211fragthreshold),
2031 	DEF_CMD("burst",	1,	set80211burst),
2032 	DEF_CMD("-burst",	0,	set80211burst),
2033 	DEF_CMD_ARG("ratectl",		set80211ratectl),
2034 	DEF_CMD_ARG("bmiss",            set80211bmissthreshold),
2035 	DEF_CMD_ARG("bmissthreshold",   set80211bmissthreshold)
2036 };
2037 static struct afswtch af_ieee80211 = {
2038 	.af_name	= "af_ieee80211",
2039 	.af_af		= AF_UNSPEC,
2040 	.af_other_status = ieee80211_status,
2041 };
2042 
2043 static __constructor void
2044 ieee80211_ctor(void)
2045 {
2046 #define	N(a)	(sizeof(a) / sizeof(a[0]))
2047 	int i;
2048 
2049 	for (i = 0; i < N(ieee80211_cmds);  i++)
2050 		cmd_register(&ieee80211_cmds[i]);
2051 	af_register(&af_ieee80211);
2052 #undef N
2053 }
2054