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