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