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