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