ifieee80211.c revision 187845
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 187845 2009-01-28 19:24:29Z 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_ioctl.h>
81
82#include <assert.h>
83#include <ctype.h>
84#include <err.h>
85#include <errno.h>
86#include <fcntl.h>
87#include <inttypes.h>
88#include <stdio.h>
89#include <stdlib.h>
90#include <string.h>
91#include <unistd.h>
92#include <stdarg.h>
93#include <stddef.h>		/* NB: for offsetof */
94
95#include "ifconfig.h"
96#include "regdomain.h"
97
98#ifndef IEEE80211_FIXED_RATE_NONE
99#define	IEEE80211_FIXED_RATE_NONE	0xff
100#endif
101
102#define	REQ_ECM		0x01000000	/* enable if ECM set */
103#define	REQ_OUTDOOR	0x02000000	/* enable for outdoor operation */
104#define	REQ_FLAGS	0xff000000	/* private flags, don't pass to os */
105
106/* XXX need these publicly defined or similar */
107#ifndef IEEE80211_NODE_AUTH
108#define	IEEE80211_NODE_AUTH	0x0001		/* authorized for data */
109#define	IEEE80211_NODE_QOS	0x0002		/* QoS enabled */
110#define	IEEE80211_NODE_ERP	0x0004		/* ERP enabled */
111#define	IEEE80211_NODE_PWR_MGT	0x0010		/* power save mode enabled */
112#define	IEEE80211_NODE_HT	0x0040		/* HT enabled */
113#define	IEEE80211_NODE_HTCOMPAT	0x0080		/* HT setup w/ vendor OUI's */
114#define	IEEE80211_NODE_WPS	0x0100		/* WPS association */
115#define	IEEE80211_NODE_TSN	0x0200		/* TSN association */
116#define	IEEE80211_NODE_AMPDU_RX	0x0400		/* AMPDU rx enabled */
117#define	IEEE80211_NODE_AMPDU_TX	0x0800		/* AMPDU tx enabled */
118#define	IEEE80211_NODE_MIMO_PS	0x1000		/* MIMO power save enabled */
119#define	IEEE80211_NODE_MIMO_RTS	0x2000		/* send RTS in MIMO PS */
120#define	IEEE80211_NODE_RIFS	0x4000		/* RIFS enabled */
121#endif
122
123#define	MAXCHAN	1536		/* max 1.5K channels */
124
125#define	MAXCOL	78
126static	int col;
127static	char spacer;
128
129static void LINE_INIT(char c);
130static void LINE_BREAK(void);
131static void LINE_CHECK(const char *fmt, ...);
132
133static const char *modename[] = {
134	"auto", "11a", "11b", "11g", "fh", "turboA", "turboG",
135	"sturbo", "11na", "11ng"
136};
137
138static void set80211(int s, int type, int val, int len, void *data);
139static int get80211(int s, int type, void *data, int len);
140static int get80211len(int s, int type, void *data, int len, int *plen);
141static int get80211val(int s, int type, int *val);
142static const char *get_string(const char *val, const char *sep,
143    u_int8_t *buf, int *lenp);
144static void print_string(const u_int8_t *buf, int len);
145static void print_regdomain(const struct ieee80211_regdomain *, int);
146static void print_channels(int, const struct ieee80211req_chaninfo *,
147    int allchans, int verbose);
148static void regdomain_makechannels(struct ieee80211_regdomain_req *,
149    const struct ieee80211_devcaps_req *);
150
151static struct ieee80211req_chaninfo *chaninfo;
152static struct ieee80211_regdomain regdomain;
153static int gotregdomain = 0;
154static struct ieee80211_roamparams_req roamparams;
155static int gotroam = 0;
156static struct ieee80211_txparams_req txparams;
157static int gottxparams = 0;
158static struct ieee80211_channel curchan;
159static int gotcurchan = 0;
160static struct ifmediareq *ifmr;
161static int htconf = 0;
162static	int gothtconf = 0;
163
164static void
165gethtconf(int s)
166{
167	if (gothtconf)
168		return;
169	if (get80211val(s, IEEE80211_IOC_HTCONF, &htconf) < 0)
170		warn("unable to get HT configuration information");
171	gothtconf = 1;
172}
173
174/*
175 * Collect channel info from the kernel.  We use this (mostly)
176 * to handle mapping between frequency and IEEE channel number.
177 */
178static void
179getchaninfo(int s)
180{
181	if (chaninfo != NULL)
182		return;
183	chaninfo = malloc(IEEE80211_CHANINFO_SIZE(MAXCHAN));
184	if (chaninfo == NULL)
185		errx(1, "no space for channel list");
186	if (get80211(s, IEEE80211_IOC_CHANINFO, chaninfo,
187	    IEEE80211_CHANINFO_SIZE(MAXCHAN)) < 0)
188		err(1, "unable to get channel information");
189	ifmr = ifmedia_getstate(s);
190	gethtconf(s);
191}
192
193static struct regdata *
194getregdata(void)
195{
196	static struct regdata *rdp = NULL;
197	if (rdp == NULL) {
198		rdp = lib80211_alloc_regdata();
199		if (rdp == NULL)
200			errx(-1, "missing or corrupted regdomain database");
201	}
202	return rdp;
203}
204
205/*
206 * Given the channel at index i with attributes from,
207 * check if there is a channel with attributes to in
208 * the channel table.  With suitable attributes this
209 * allows the caller to look for promotion; e.g. from
210 * 11b > 11g.
211 */
212static int
213canpromote(int i, int from, int to)
214{
215	const struct ieee80211_channel *fc = &chaninfo->ic_chans[i];
216	int j;
217
218	if ((fc->ic_flags & from) != from)
219		return i;
220	/* NB: quick check exploiting ordering of chans w/ same frequency */
221	if (i+1 < chaninfo->ic_nchans &&
222	    chaninfo->ic_chans[i+1].ic_freq == fc->ic_freq &&
223	    (chaninfo->ic_chans[i+1].ic_flags & to) == to)
224		return i+1;
225	/* brute force search in case channel list is not ordered */
226	for (j = 0; j < chaninfo->ic_nchans; j++) {
227		const struct ieee80211_channel *tc = &chaninfo->ic_chans[j];
228		if (j != i &&
229		    tc->ic_freq == fc->ic_freq && (tc->ic_flags & to) == to)
230		return j;
231	}
232	return i;
233}
234
235/*
236 * Handle channel promotion.  When a channel is specified with
237 * only a frequency we want to promote it to the ``best'' channel
238 * available.  The channel list has separate entries for 11b, 11g,
239 * 11a, and 11n[ga] channels so specifying a frequency w/o any
240 * attributes requires we upgrade, e.g. from 11b -> 11g.  This
241 * gets complicated when the channel is specified on the same
242 * command line with a media request that constrains the available
243 * channe list (e.g. mode 11a); we want to honor that to avoid
244 * confusing behaviour.
245 */
246static int
247promote(int i)
248{
249	/*
250	 * Query the current mode of the interface in case it's
251	 * constrained (e.g. to 11a).  We must do this carefully
252	 * as there may be a pending ifmedia request in which case
253	 * asking the kernel will give us the wrong answer.  This
254	 * is an unfortunate side-effect of the way ifconfig is
255	 * structure for modularity (yech).
256	 *
257	 * NB: ifmr is actually setup in getchaninfo (above); we
258	 *     assume it's called coincident with to this call so
259	 *     we have a ``current setting''; otherwise we must pass
260	 *     the socket descriptor down to here so we can make
261	 *     the ifmedia_getstate call ourselves.
262	 */
263	int chanmode = ifmr != NULL ? IFM_MODE(ifmr->ifm_current) : IFM_AUTO;
264
265	/* when ambiguous promote to ``best'' */
266	/* NB: we abitrarily pick HT40+ over HT40- */
267	if (chanmode != IFM_IEEE80211_11B)
268		i = canpromote(i, IEEE80211_CHAN_B, IEEE80211_CHAN_G);
269	if (chanmode != IFM_IEEE80211_11G && (htconf & 1)) {
270		i = canpromote(i, IEEE80211_CHAN_G,
271			IEEE80211_CHAN_G | IEEE80211_CHAN_HT20);
272		if (htconf & 2) {
273			i = canpromote(i, IEEE80211_CHAN_G,
274				IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D);
275			i = canpromote(i, IEEE80211_CHAN_G,
276				IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U);
277		}
278	}
279	if (chanmode != IFM_IEEE80211_11A && (htconf & 1)) {
280		i = canpromote(i, IEEE80211_CHAN_A,
281			IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
282		if (htconf & 2) {
283			i = canpromote(i, IEEE80211_CHAN_A,
284				IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
285			i = canpromote(i, IEEE80211_CHAN_A,
286				IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
287		}
288	}
289	return i;
290}
291
292static void
293mapfreq(struct ieee80211_channel *chan, int freq, int flags)
294{
295	int i;
296
297	for (i = 0; i < chaninfo->ic_nchans; i++) {
298		const struct ieee80211_channel *c = &chaninfo->ic_chans[i];
299
300		if (c->ic_freq == freq && (c->ic_flags & flags) == flags) {
301			if (flags == 0) {
302				/* when ambiguous promote to ``best'' */
303				c = &chaninfo->ic_chans[promote(i)];
304			}
305			*chan = *c;
306			return;
307		}
308	}
309	errx(1, "unknown/undefined frequency %u/0x%x", freq, flags);
310}
311
312static void
313mapchan(struct ieee80211_channel *chan, int ieee, int flags)
314{
315	int i;
316
317	for (i = 0; i < chaninfo->ic_nchans; i++) {
318		const struct ieee80211_channel *c = &chaninfo->ic_chans[i];
319
320		if (c->ic_ieee == ieee && (c->ic_flags & flags) == flags) {
321			if (flags == 0) {
322				/* when ambiguous promote to ``best'' */
323				c = &chaninfo->ic_chans[promote(i)];
324			}
325			*chan = *c;
326			return;
327		}
328	}
329	errx(1, "unknown/undefined channel number %d flags 0x%x", ieee, flags);
330}
331
332static const struct ieee80211_channel *
333getcurchan(int s)
334{
335	if (gotcurchan)
336		return &curchan;
337	if (get80211(s, IEEE80211_IOC_CURCHAN, &curchan, sizeof(curchan)) < 0) {
338		int val;
339		/* fall back to legacy ioctl */
340		if (get80211val(s, IEEE80211_IOC_CHANNEL, &val) < 0)
341			err(-1, "cannot figure out current channel");
342		getchaninfo(s);
343		mapchan(&curchan, val, 0);
344	}
345	gotcurchan = 1;
346	return &curchan;
347}
348
349static enum ieee80211_phymode
350chan2mode(const struct ieee80211_channel *c)
351{
352	if (IEEE80211_IS_CHAN_HTA(c))
353		return IEEE80211_MODE_11NA;
354	if (IEEE80211_IS_CHAN_HTG(c))
355		return IEEE80211_MODE_11NG;
356	if (IEEE80211_IS_CHAN_108A(c))
357		return IEEE80211_MODE_TURBO_A;
358	if (IEEE80211_IS_CHAN_108G(c))
359		return IEEE80211_MODE_TURBO_G;
360	if (IEEE80211_IS_CHAN_ST(c))
361		return IEEE80211_MODE_STURBO_A;
362	if (IEEE80211_IS_CHAN_FHSS(c))
363		return IEEE80211_MODE_FH;
364	if (IEEE80211_IS_CHAN_A(c))
365		return IEEE80211_MODE_11A;
366	if (IEEE80211_IS_CHAN_ANYG(c))
367		return IEEE80211_MODE_11G;
368	if (IEEE80211_IS_CHAN_B(c))
369		return IEEE80211_MODE_11B;
370	return IEEE80211_MODE_AUTO;
371}
372
373static void
374getroam(int s)
375{
376	if (gotroam)
377		return;
378	if (get80211(s, IEEE80211_IOC_ROAM,
379	    &roamparams, sizeof(roamparams)) < 0)
380		err(1, "unable to get roaming parameters");
381	gotroam = 1;
382}
383
384static void
385setroam_cb(int s, void *arg)
386{
387	struct ieee80211_roamparams_req *roam = arg;
388	set80211(s, IEEE80211_IOC_ROAM, 0, sizeof(*roam), roam);
389}
390
391static void
392gettxparams(int s)
393{
394	if (gottxparams)
395		return;
396	if (get80211(s, IEEE80211_IOC_TXPARAMS,
397	    &txparams, sizeof(txparams)) < 0)
398		err(1, "unable to get transmit parameters");
399	gottxparams = 1;
400}
401
402static void
403settxparams_cb(int s, void *arg)
404{
405	struct ieee80211_txparams_req *txp = arg;
406	set80211(s, IEEE80211_IOC_TXPARAMS, 0, sizeof(*txp), txp);
407}
408
409static void
410getregdomain(int s)
411{
412	if (gotregdomain)
413		return;
414	if (get80211(s, IEEE80211_IOC_REGDOMAIN,
415	    &regdomain, sizeof(regdomain)) < 0)
416		err(1, "unable to get regulatory domain info");
417	gotregdomain = 1;
418}
419
420static void
421getdevcaps(int s, struct ieee80211_devcaps_req *dc)
422{
423	if (get80211(s, IEEE80211_IOC_DEVCAPS, dc,
424	    IEEE80211_DEVCAPS_SPACE(dc)) < 0)
425		err(1, "unable to get device capabilities");
426}
427
428static void
429setregdomain_cb(int s, void *arg)
430{
431	struct ieee80211_regdomain_req *req;
432	struct ieee80211_regdomain *rd = arg;
433	struct ieee80211_devcaps_req *dc;
434	struct regdata *rdp = getregdata();
435
436	if (rd->country != NO_COUNTRY) {
437		const struct country *cc;
438		/*
439		 * Check current country seting to make sure it's
440		 * compatible with the new regdomain.  If not, then
441		 * override it with any default country for this
442		 * SKU.  If we cannot arrange a match, then abort.
443		 */
444		cc = lib80211_country_findbycc(rdp, rd->country);
445		if (cc == NULL)
446			errx(1, "unknown ISO country code %d", rd->country);
447		if (cc->rd->sku != rd->regdomain) {
448			const struct regdomain *rp;
449			/*
450			 * Check if country is incompatible with regdomain.
451			 * To enable multiple regdomains for a country code
452			 * we permit a mismatch between the regdomain and
453			 * the country's associated regdomain when the
454			 * regdomain is setup w/o a default country.  For
455			 * example, US is bound to the FCC regdomain but
456			 * we allow US to be combined with FCC3 because FCC3
457			 * has not default country.  This allows bogus
458			 * combinations like FCC3+DK which are resolved when
459			 * constructing the channel list by deferring to the
460			 * regdomain to construct the channel list.
461			 */
462			rp = lib80211_regdomain_findbysku(rdp, rd->regdomain);
463			if (rp == NULL)
464				errx(1, "country %s (%s) is not usable with "
465				    "regdomain %d", cc->isoname, cc->name,
466				    rd->regdomain);
467			else if (rp->cc != NULL && rp->cc != cc)
468				errx(1, "country %s (%s) is not usable with "
469				   "regdomain %s", cc->isoname, cc->name,
470				   rp->name);
471		}
472	}
473	/*
474	 * Fetch the device capabilities and calculate the
475	 * full set of netbands for which we request a new
476	 * channel list be constructed.  Once that's done we
477	 * push the regdomain info + channel list to the kernel.
478	 */
479	dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
480	if (dc == NULL)
481		errx(1, "no space for device capabilities");
482	dc->dc_chaninfo.ic_nchans = MAXCHAN;
483	getdevcaps(s, dc);
484#if 0
485	if (verbose) {
486		printf("drivercaps: 0x%x\n", dc->dc_drivercaps);
487		printf("cryptocaps: 0x%x\n", dc->dc_cryptocaps);
488		printf("htcaps    : 0x%x\n", dc->dc_htcaps);
489		memcpy(chaninfo, &dc->dc_chaninfo,
490		    IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
491		print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, 1/*verbose*/);
492	}
493#endif
494	req = malloc(IEEE80211_REGDOMAIN_SIZE(dc->dc_chaninfo.ic_nchans));
495	if (req == NULL)
496		errx(1, "no space for regdomain request");
497	req->rd = *rd;
498	regdomain_makechannels(req, dc);
499	if (verbose) {
500		LINE_INIT(':');
501		print_regdomain(rd, 1/*verbose*/);
502		LINE_BREAK();
503		/* blech, reallocate channel list for new data */
504		if (chaninfo != NULL)
505			free(chaninfo);
506		chaninfo = malloc(IEEE80211_CHANINFO_SPACE(&req->chaninfo));
507		if (chaninfo == NULL)
508			errx(1, "no space for channel list");
509		memcpy(chaninfo, &req->chaninfo,
510		    IEEE80211_CHANINFO_SPACE(&req->chaninfo));
511		print_channels(s, &req->chaninfo, 1/*allchans*/, 1/*verbose*/);
512	}
513	if (req->chaninfo.ic_nchans == 0)
514		errx(1, "no channels calculated");
515	set80211(s, IEEE80211_IOC_REGDOMAIN, 0,
516	    IEEE80211_REGDOMAIN_SPACE(req), req);
517	free(req);
518	free(dc);
519}
520
521static int
522ieee80211_mhz2ieee(int freq, int flags)
523{
524	struct ieee80211_channel chan;
525	mapfreq(&chan, freq, flags);
526	return chan.ic_ieee;
527}
528
529static int
530isanyarg(const char *arg)
531{
532	return (strncmp(arg, "-", 1) == 0 ||
533	    strncasecmp(arg, "any", 3) == 0 || strncasecmp(arg, "off", 3) == 0);
534}
535
536static void
537set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
538{
539	int		ssid;
540	int		len;
541	u_int8_t	data[IEEE80211_NWID_LEN];
542
543	ssid = 0;
544	len = strlen(val);
545	if (len > 2 && isdigit((int)val[0]) && val[1] == ':') {
546		ssid = atoi(val)-1;
547		val += 2;
548	}
549
550	bzero(data, sizeof(data));
551	len = sizeof(data);
552	if (get_string(val, NULL, data, &len) == NULL)
553		exit(1);
554
555	set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
556}
557
558static void
559set80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
560{
561	int			len;
562	u_int8_t		data[33];
563
564	bzero(data, sizeof(data));
565	len = sizeof(data);
566	get_string(val, NULL, data, &len);
567
568	set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
569}
570
571/*
572 * Parse a channel specification for attributes/flags.
573 * The syntax is:
574 *	freq/xx		channel width (5,10,20,40,40+,40-)
575 *	freq:mode	channel mode (a,b,g,h,n,t,s,d)
576 *
577 * These can be combined in either order; e.g. 2437:ng/40.
578 * Modes are case insensitive.
579 *
580 * The result is not validated here; it's assumed to be
581 * checked against the channel table fetched from the kernel.
582 */
583static int
584getchannelflags(const char *val, int freq)
585{
586#define	_CHAN_HT	0x80000000
587	const char *cp;
588	int flags;
589
590	flags = 0;
591
592	cp = strchr(val, ':');
593	if (cp != NULL) {
594		for (cp++; isalpha((int) *cp); cp++) {
595			/* accept mixed case */
596			int c = *cp;
597			if (isupper(c))
598				c = tolower(c);
599			switch (c) {
600			case 'a':		/* 802.11a */
601				flags |= IEEE80211_CHAN_A;
602				break;
603			case 'b':		/* 802.11b */
604				flags |= IEEE80211_CHAN_B;
605				break;
606			case 'g':		/* 802.11g */
607				flags |= IEEE80211_CHAN_G;
608				break;
609			case 'h':		/* ht = 802.11n */
610			case 'n':		/* 802.11n */
611				flags |= _CHAN_HT;	/* NB: private */
612				break;
613			case 'd':		/* dt = Atheros Dynamic Turbo */
614				flags |= IEEE80211_CHAN_TURBO;
615				break;
616			case 't':		/* ht, dt, st, t */
617				/* dt and unadorned t specify Dynamic Turbo */
618				if ((flags & (IEEE80211_CHAN_STURBO|_CHAN_HT)) == 0)
619					flags |= IEEE80211_CHAN_TURBO;
620				break;
621			case 's':		/* st = Atheros Static Turbo */
622				flags |= IEEE80211_CHAN_STURBO;
623				break;
624			default:
625				errx(-1, "%s: Invalid channel attribute %c\n",
626				    val, *cp);
627			}
628		}
629	}
630	cp = strchr(val, '/');
631	if (cp != NULL) {
632		char *ep;
633		u_long cw = strtoul(cp+1, &ep, 10);
634
635		switch (cw) {
636		case 5:
637			flags |= IEEE80211_CHAN_QUARTER;
638			break;
639		case 10:
640			flags |= IEEE80211_CHAN_HALF;
641			break;
642		case 20:
643			/* NB: this may be removed below */
644			flags |= IEEE80211_CHAN_HT20;
645			break;
646		case 40:
647			if (ep != NULL && *ep == '+')
648				flags |= IEEE80211_CHAN_HT40U;
649			else if (ep != NULL && *ep == '-')
650				flags |= IEEE80211_CHAN_HT40D;
651			break;
652		default:
653			errx(-1, "%s: Invalid channel width\n", val);
654		}
655	}
656	/*
657	 * Cleanup specifications.
658	 */
659	if ((flags & _CHAN_HT) == 0) {
660		/*
661		 * If user specified freq/20 or freq/40 quietly remove
662		 * HT cw attributes depending on channel use.  To give
663		 * an explicit 20/40 width for an HT channel you must
664		 * indicate it is an HT channel since all HT channels
665		 * are also usable for legacy operation; e.g. freq:n/40.
666		 */
667		flags &= ~IEEE80211_CHAN_HT;
668	} else {
669		/*
670		 * Remove private indicator that this is an HT channel
671		 * and if no explicit channel width has been given
672		 * provide the default settings.
673		 */
674		flags &= ~_CHAN_HT;
675		if ((flags & IEEE80211_CHAN_HT) == 0) {
676			struct ieee80211_channel chan;
677			/*
678			 * Consult the channel list to see if we can use
679			 * HT40+ or HT40- (if both the map routines choose).
680			 */
681			if (freq > 255)
682				mapfreq(&chan, freq, 0);
683			else
684				mapchan(&chan, freq, 0);
685			flags |= (chan.ic_flags & IEEE80211_CHAN_HT);
686		}
687	}
688	return flags;
689#undef _CHAN_HT
690}
691
692static void
693getchannel(int s, struct ieee80211_channel *chan, const char *val)
694{
695	int v, flags;
696	char *eptr;
697
698	memset(chan, 0, sizeof(*chan));
699	if (isanyarg(val)) {
700		chan->ic_freq = IEEE80211_CHAN_ANY;
701		return;
702	}
703	getchaninfo(s);
704	errno = 0;
705	v = strtol(val, &eptr, 10);
706	if (val[0] == '\0' || val == eptr || errno == ERANGE ||
707	    /* channel may be suffixed with nothing, :flag, or /width */
708	    (eptr[0] != '\0' && eptr[0] != ':' && eptr[0] != '/'))
709		errx(1, "invalid channel specification%s",
710		    errno == ERANGE ? " (out of range)" : "");
711	flags = getchannelflags(val, v);
712	if (v > 255) {		/* treat as frequency */
713		mapfreq(chan, v, flags);
714	} else {
715		mapchan(chan, v, flags);
716	}
717}
718
719static void
720set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
721{
722	struct ieee80211_channel chan;
723
724	getchannel(s, &chan, val);
725	set80211(s, IEEE80211_IOC_CURCHAN, 0, sizeof(chan), &chan);
726}
727
728static void
729set80211chanswitch(const char *val, int d, int s, const struct afswtch *rafp)
730{
731	struct ieee80211_chanswitch_req csr;
732
733	getchannel(s, &csr.csa_chan, val);
734	csr.csa_mode = 1;
735	csr.csa_count = 5;
736	set80211(s, IEEE80211_IOC_CHANSWITCH, 0, sizeof(csr), &csr);
737}
738
739static void
740set80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
741{
742	int	mode;
743
744	if (strcasecmp(val, "none") == 0) {
745		mode = IEEE80211_AUTH_NONE;
746	} else if (strcasecmp(val, "open") == 0) {
747		mode = IEEE80211_AUTH_OPEN;
748	} else if (strcasecmp(val, "shared") == 0) {
749		mode = IEEE80211_AUTH_SHARED;
750	} else if (strcasecmp(val, "8021x") == 0) {
751		mode = IEEE80211_AUTH_8021X;
752	} else if (strcasecmp(val, "wpa") == 0) {
753		mode = IEEE80211_AUTH_WPA;
754	} else {
755		errx(1, "unknown authmode");
756	}
757
758	set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
759}
760
761static void
762set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
763{
764	int	mode;
765
766	if (strcasecmp(val, "off") == 0) {
767		mode = IEEE80211_POWERSAVE_OFF;
768	} else if (strcasecmp(val, "on") == 0) {
769		mode = IEEE80211_POWERSAVE_ON;
770	} else if (strcasecmp(val, "cam") == 0) {
771		mode = IEEE80211_POWERSAVE_CAM;
772	} else if (strcasecmp(val, "psp") == 0) {
773		mode = IEEE80211_POWERSAVE_PSP;
774	} else if (strcasecmp(val, "psp-cam") == 0) {
775		mode = IEEE80211_POWERSAVE_PSP_CAM;
776	} else {
777		errx(1, "unknown powersavemode");
778	}
779
780	set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
781}
782
783static void
784set80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
785{
786	if (d == 0)
787		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
788		    0, NULL);
789	else
790		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
791		    0, NULL);
792}
793
794static void
795set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
796{
797	set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
798}
799
800static void
801set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
802{
803	int	mode;
804
805	if (strcasecmp(val, "off") == 0) {
806		mode = IEEE80211_WEP_OFF;
807	} else if (strcasecmp(val, "on") == 0) {
808		mode = IEEE80211_WEP_ON;
809	} else if (strcasecmp(val, "mixed") == 0) {
810		mode = IEEE80211_WEP_MIXED;
811	} else {
812		errx(1, "unknown wep mode");
813	}
814
815	set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
816}
817
818static void
819set80211wep(const char *val, int d, int s, const struct afswtch *rafp)
820{
821	set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
822}
823
824static int
825isundefarg(const char *arg)
826{
827	return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0);
828}
829
830static void
831set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
832{
833	if (isundefarg(val))
834		set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
835	else
836		set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
837}
838
839static void
840set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
841{
842	int		key = 0;
843	int		len;
844	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
845
846	if (isdigit((int)val[0]) && val[1] == ':') {
847		key = atoi(val)-1;
848		val += 2;
849	}
850
851	bzero(data, sizeof(data));
852	len = sizeof(data);
853	get_string(val, NULL, data, &len);
854
855	set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
856}
857
858/*
859 * This function is purely a NetBSD compatability interface.  The NetBSD
860 * interface is too inflexible, but it's there so we'll support it since
861 * it's not all that hard.
862 */
863static void
864set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
865{
866	int		txkey;
867	int		i, len;
868	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
869
870	set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
871
872	if (isdigit((int)val[0]) && val[1] == ':') {
873		txkey = val[0]-'0'-1;
874		val += 2;
875
876		for (i = 0; i < 4; i++) {
877			bzero(data, sizeof(data));
878			len = sizeof(data);
879			val = get_string(val, ",", data, &len);
880			if (val == NULL)
881				exit(1);
882
883			set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
884		}
885	} else {
886		bzero(data, sizeof(data));
887		len = sizeof(data);
888		get_string(val, NULL, data, &len);
889		txkey = 0;
890
891		set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
892
893		bzero(data, sizeof(data));
894		for (i = 1; i < 4; i++)
895			set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
896	}
897
898	set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
899}
900
901static void
902set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
903{
904	set80211(s, IEEE80211_IOC_RTSTHRESHOLD,
905		isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL);
906}
907
908static void
909set80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
910{
911	int	mode;
912
913	if (strcasecmp(val, "off") == 0) {
914		mode = IEEE80211_PROTMODE_OFF;
915	} else if (strcasecmp(val, "cts") == 0) {
916		mode = IEEE80211_PROTMODE_CTS;
917	} else if (strncasecmp(val, "rtscts", 3) == 0) {
918		mode = IEEE80211_PROTMODE_RTSCTS;
919	} else {
920		errx(1, "unknown protection mode");
921	}
922
923	set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
924}
925
926static void
927set80211htprotmode(const char *val, int d, int s, const struct afswtch *rafp)
928{
929	int	mode;
930
931	if (strcasecmp(val, "off") == 0) {
932		mode = IEEE80211_PROTMODE_OFF;
933	} else if (strncasecmp(val, "rts", 3) == 0) {
934		mode = IEEE80211_PROTMODE_RTSCTS;
935	} else {
936		errx(1, "unknown protection mode");
937	}
938
939	set80211(s, IEEE80211_IOC_HTPROTMODE, mode, 0, NULL);
940}
941
942static void
943set80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
944{
945	double v = atof(val);
946	int txpow;
947
948	txpow = (int) (2*v);
949	if (txpow != 2*v)
950		errx(-1, "invalid tx power (must be .5 dBm units)");
951	set80211(s, IEEE80211_IOC_TXPOWER, txpow, 0, NULL);
952}
953
954#define	IEEE80211_ROAMING_DEVICE	0
955#define	IEEE80211_ROAMING_AUTO		1
956#define	IEEE80211_ROAMING_MANUAL	2
957
958static void
959set80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
960{
961	int mode;
962
963	if (strcasecmp(val, "device") == 0) {
964		mode = IEEE80211_ROAMING_DEVICE;
965	} else if (strcasecmp(val, "auto") == 0) {
966		mode = IEEE80211_ROAMING_AUTO;
967	} else if (strcasecmp(val, "manual") == 0) {
968		mode = IEEE80211_ROAMING_MANUAL;
969	} else {
970		errx(1, "unknown roaming mode");
971	}
972	set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
973}
974
975static void
976set80211wme(const char *val, int d, int s, const struct afswtch *rafp)
977{
978	set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
979}
980
981static void
982set80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
983{
984	set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
985}
986
987static void
988set80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
989{
990	set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
991}
992
993static void
994set80211fastframes(const char *val, int d, int s, const struct afswtch *rafp)
995{
996	set80211(s, IEEE80211_IOC_FF, d, 0, NULL);
997}
998
999static void
1000set80211dturbo(const char *val, int d, int s, const struct afswtch *rafp)
1001{
1002	set80211(s, IEEE80211_IOC_TURBOP, d, 0, NULL);
1003}
1004
1005static void
1006set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
1007{
1008	struct ieee80211req_chanlist chanlist;
1009	char *temp, *cp, *tp;
1010
1011	temp = malloc(strlen(val) + 1);
1012	if (temp == NULL)
1013		errx(1, "malloc failed");
1014	strcpy(temp, val);
1015	memset(&chanlist, 0, sizeof(chanlist));
1016	cp = temp;
1017	for (;;) {
1018		int first, last, f, c;
1019
1020		tp = strchr(cp, ',');
1021		if (tp != NULL)
1022			*tp++ = '\0';
1023		switch (sscanf(cp, "%u-%u", &first, &last)) {
1024		case 1:
1025			if (first > IEEE80211_CHAN_MAX)
1026				errx(-1, "channel %u out of range, max %zu",
1027					first, IEEE80211_CHAN_MAX);
1028			setbit(chanlist.ic_channels, first);
1029			break;
1030		case 2:
1031			if (first > IEEE80211_CHAN_MAX)
1032				errx(-1, "channel %u out of range, max %zu",
1033					first, IEEE80211_CHAN_MAX);
1034			if (last > IEEE80211_CHAN_MAX)
1035				errx(-1, "channel %u out of range, max %zu",
1036					last, IEEE80211_CHAN_MAX);
1037			if (first > last)
1038				errx(-1, "void channel range, %u > %u",
1039					first, last);
1040			for (f = first; f <= last; f++)
1041				setbit(chanlist.ic_channels, f);
1042			break;
1043		}
1044		if (tp == NULL)
1045			break;
1046		c = *tp;
1047		while (isspace(c))
1048			tp++;
1049		if (!isdigit(c))
1050			break;
1051		cp = tp;
1052	}
1053	set80211(s, IEEE80211_IOC_CHANLIST, 0, sizeof(chanlist), &chanlist);
1054}
1055
1056static void
1057set80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
1058{
1059
1060	if (!isanyarg(val)) {
1061		char *temp;
1062		struct sockaddr_dl sdl;
1063
1064		temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1065		if (temp == NULL)
1066			errx(1, "malloc failed");
1067		temp[0] = ':';
1068		strcpy(temp + 1, val);
1069		sdl.sdl_len = sizeof(sdl);
1070		link_addr(temp, &sdl);
1071		free(temp);
1072		if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1073			errx(1, "malformed link-level address");
1074		set80211(s, IEEE80211_IOC_BSSID, 0,
1075			IEEE80211_ADDR_LEN, LLADDR(&sdl));
1076	} else {
1077		uint8_t zerobssid[IEEE80211_ADDR_LEN];
1078		memset(zerobssid, 0, sizeof(zerobssid));
1079		set80211(s, IEEE80211_IOC_BSSID, 0,
1080			IEEE80211_ADDR_LEN, zerobssid);
1081	}
1082}
1083
1084static int
1085getac(const char *ac)
1086{
1087	if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0)
1088		return WME_AC_BE;
1089	if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0)
1090		return WME_AC_BK;
1091	if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0)
1092		return WME_AC_VI;
1093	if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0)
1094		return WME_AC_VO;
1095	errx(1, "unknown wme access class %s", ac);
1096}
1097
1098static
1099DECL_CMD_FUNC2(set80211cwmin, ac, val)
1100{
1101	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
1102}
1103
1104static
1105DECL_CMD_FUNC2(set80211cwmax, ac, val)
1106{
1107	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
1108}
1109
1110static
1111DECL_CMD_FUNC2(set80211aifs, ac, val)
1112{
1113	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
1114}
1115
1116static
1117DECL_CMD_FUNC2(set80211txoplimit, ac, val)
1118{
1119	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
1120}
1121
1122static
1123DECL_CMD_FUNC(set80211acm, ac, d)
1124{
1125	set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL);
1126}
1127static
1128DECL_CMD_FUNC(set80211noacm, ac, d)
1129{
1130	set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL);
1131}
1132
1133static
1134DECL_CMD_FUNC(set80211ackpolicy, ac, d)
1135{
1136	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL);
1137}
1138static
1139DECL_CMD_FUNC(set80211noackpolicy, ac, d)
1140{
1141	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL);
1142}
1143
1144static
1145DECL_CMD_FUNC2(set80211bsscwmin, ac, val)
1146{
1147	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
1148		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1149}
1150
1151static
1152DECL_CMD_FUNC2(set80211bsscwmax, ac, val)
1153{
1154	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
1155		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1156}
1157
1158static
1159DECL_CMD_FUNC2(set80211bssaifs, ac, val)
1160{
1161	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
1162		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1163}
1164
1165static
1166DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
1167{
1168	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
1169		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1170}
1171
1172static
1173DECL_CMD_FUNC(set80211dtimperiod, val, d)
1174{
1175	set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
1176}
1177
1178static
1179DECL_CMD_FUNC(set80211bintval, val, d)
1180{
1181	set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
1182}
1183
1184static void
1185set80211macmac(int s, int op, const char *val)
1186{
1187	char *temp;
1188	struct sockaddr_dl sdl;
1189
1190	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1191	if (temp == NULL)
1192		errx(1, "malloc failed");
1193	temp[0] = ':';
1194	strcpy(temp + 1, val);
1195	sdl.sdl_len = sizeof(sdl);
1196	link_addr(temp, &sdl);
1197	free(temp);
1198	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1199		errx(1, "malformed link-level address");
1200	set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
1201}
1202
1203static
1204DECL_CMD_FUNC(set80211addmac, val, d)
1205{
1206	set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
1207}
1208
1209static
1210DECL_CMD_FUNC(set80211delmac, val, d)
1211{
1212	set80211macmac(s, IEEE80211_IOC_DELMAC, val);
1213}
1214
1215static
1216DECL_CMD_FUNC(set80211kickmac, val, d)
1217{
1218	char *temp;
1219	struct sockaddr_dl sdl;
1220	struct ieee80211req_mlme mlme;
1221
1222	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1223	if (temp == NULL)
1224		errx(1, "malloc failed");
1225	temp[0] = ':';
1226	strcpy(temp + 1, val);
1227	sdl.sdl_len = sizeof(sdl);
1228	link_addr(temp, &sdl);
1229	free(temp);
1230	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1231		errx(1, "malformed link-level address");
1232	memset(&mlme, 0, sizeof(mlme));
1233	mlme.im_op = IEEE80211_MLME_DEAUTH;
1234	mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE;
1235	memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN);
1236	set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), &mlme);
1237}
1238
1239static
1240DECL_CMD_FUNC(set80211maccmd, val, d)
1241{
1242	set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
1243}
1244
1245static void
1246set80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
1247{
1248	set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
1249}
1250
1251static void
1252set80211bgscan(const char *val, int d, int s, const struct afswtch *rafp)
1253{
1254	set80211(s, IEEE80211_IOC_BGSCAN, d, 0, NULL);
1255}
1256
1257static
1258DECL_CMD_FUNC(set80211bgscanidle, val, d)
1259{
1260	set80211(s, IEEE80211_IOC_BGSCAN_IDLE, atoi(val), 0, NULL);
1261}
1262
1263static
1264DECL_CMD_FUNC(set80211bgscanintvl, val, d)
1265{
1266	set80211(s, IEEE80211_IOC_BGSCAN_INTERVAL, atoi(val), 0, NULL);
1267}
1268
1269static
1270DECL_CMD_FUNC(set80211scanvalid, val, d)
1271{
1272	set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL);
1273}
1274
1275/*
1276 * Parse an optional trailing specification of which netbands
1277 * to apply a parameter to.  This is basically the same syntax
1278 * as used for channels but you can concatenate to specify
1279 * multiple.  For example:
1280 *	14:abg		apply to 11a, 11b, and 11g
1281 *	6:ht		apply to 11na and 11ng
1282 * We don't make a big effort to catch silly things; this is
1283 * really a convenience mechanism.
1284 */
1285static int
1286getmodeflags(const char *val)
1287{
1288	const char *cp;
1289	int flags;
1290
1291	flags = 0;
1292
1293	cp = strchr(val, ':');
1294	if (cp != NULL) {
1295		for (cp++; isalpha((int) *cp); cp++) {
1296			/* accept mixed case */
1297			int c = *cp;
1298			if (isupper(c))
1299				c = tolower(c);
1300			switch (c) {
1301			case 'a':		/* 802.11a */
1302				flags |= IEEE80211_CHAN_A;
1303				break;
1304			case 'b':		/* 802.11b */
1305				flags |= IEEE80211_CHAN_B;
1306				break;
1307			case 'g':		/* 802.11g */
1308				flags |= IEEE80211_CHAN_G;
1309				break;
1310			case 'h':		/* ht = 802.11n */
1311			case 'n':		/* 802.11n */
1312				flags |= IEEE80211_CHAN_HT;
1313				break;
1314			case 'd':		/* dt = Atheros Dynamic Turbo */
1315				flags |= IEEE80211_CHAN_TURBO;
1316				break;
1317			case 't':		/* ht, dt, st, t */
1318				/* dt and unadorned t specify Dynamic Turbo */
1319				if ((flags & (IEEE80211_CHAN_STURBO|IEEE80211_CHAN_HT)) == 0)
1320					flags |= IEEE80211_CHAN_TURBO;
1321				break;
1322			case 's':		/* st = Atheros Static Turbo */
1323				flags |= IEEE80211_CHAN_STURBO;
1324				break;
1325			default:
1326				errx(-1, "%s: Invalid mode attribute %c\n",
1327				    val, *cp);
1328			}
1329		}
1330	}
1331	return flags;
1332}
1333
1334#define	IEEE80211_CHAN_HTA	(IEEE80211_CHAN_HT|IEEE80211_CHAN_5GHZ)
1335#define	IEEE80211_CHAN_HTG	(IEEE80211_CHAN_HT|IEEE80211_CHAN_2GHZ)
1336
1337#define	_APPLY(_flags, _base, _param, _v) do {				\
1338    if (_flags & IEEE80211_CHAN_HT) {					\
1339	    if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1340		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1341		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1342	    } else if (_flags & IEEE80211_CHAN_5GHZ)			\
1343		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1344	    else							\
1345		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1346    }									\
1347    if (_flags & IEEE80211_CHAN_TURBO) {				\
1348	    if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1349		    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;	\
1350		    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;	\
1351	    } else if (_flags & IEEE80211_CHAN_5GHZ)			\
1352		    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;	\
1353	    else							\
1354		    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;	\
1355    }									\
1356    if (_flags & IEEE80211_CHAN_STURBO)					\
1357	    _base.params[IEEE80211_MODE_STURBO_A]._param = _v;		\
1358    if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)		\
1359	    _base.params[IEEE80211_MODE_11A]._param = _v;		\
1360    if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)		\
1361	    _base.params[IEEE80211_MODE_11G]._param = _v;		\
1362    if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)		\
1363	    _base.params[IEEE80211_MODE_11B]._param = _v;		\
1364} while (0)
1365#define	_APPLY1(_flags, _base, _param, _v) do {				\
1366    if (_flags & IEEE80211_CHAN_HT) {					\
1367	    if (_flags & IEEE80211_CHAN_5GHZ)				\
1368		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1369	    else							\
1370		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1371    } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A)	\
1372	    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;		\
1373    else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G)	\
1374	    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;		\
1375    else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST)		\
1376	    _base.params[IEEE80211_MODE_STURBO_A]._param = _v;		\
1377    else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)		\
1378	    _base.params[IEEE80211_MODE_11A]._param = _v;		\
1379    else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)		\
1380	    _base.params[IEEE80211_MODE_11G]._param = _v;		\
1381    else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)		\
1382	    _base.params[IEEE80211_MODE_11B]._param = _v;		\
1383} while (0)
1384#define	_APPLY_RATE(_flags, _base, _param, _v) do {			\
1385    if (_flags & IEEE80211_CHAN_HT) {					\
1386	    if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1387		    _base.params[IEEE80211_MODE_11NA]._param = _v|0x80;	\
1388		    _base.params[IEEE80211_MODE_11NG]._param = _v|0x80;	\
1389	    } else if (_flags & IEEE80211_CHAN_5GHZ)			\
1390		    _base.params[IEEE80211_MODE_11NA]._param = _v|0x80;	\
1391	    else							\
1392		    _base.params[IEEE80211_MODE_11NG]._param = _v|0x80;	\
1393    }									\
1394    if (_flags & IEEE80211_CHAN_TURBO) {				\
1395	    if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1396		    _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v;	\
1397		    _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v;	\
1398	    } else if (_flags & IEEE80211_CHAN_5GHZ)			\
1399		    _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v;	\
1400	    else							\
1401		    _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v;	\
1402    }									\
1403    if (_flags & IEEE80211_CHAN_STURBO)					\
1404	    _base.params[IEEE80211_MODE_STURBO_A]._param = 2*_v;	\
1405    if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)		\
1406	    _base.params[IEEE80211_MODE_11A]._param = 2*_v;		\
1407    if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)		\
1408	    _base.params[IEEE80211_MODE_11G]._param = (_v == 5 ? 11 : 2*_v);\
1409    if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)		\
1410	    _base.params[IEEE80211_MODE_11B]._param = (_v == 5 ? 11 : 2*_v);\
1411} while (0)
1412#define	_APPLY_RATE1(_flags, _base, _param, _v) do {			\
1413    if (_flags & IEEE80211_CHAN_HT) {					\
1414	    if (_flags & IEEE80211_CHAN_5GHZ)				\
1415		    _base.params[IEEE80211_MODE_11NA]._param = _v|0x80;	\
1416	    else							\
1417		    _base.params[IEEE80211_MODE_11NG]._param = _v|0x80;	\
1418    } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A)	\
1419	    _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v;		\
1420    else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G)	\
1421	    _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v;		\
1422    else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST)		\
1423	    _base.params[IEEE80211_MODE_STURBO_A]._param = 2*_v;	\
1424    else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)		\
1425	    _base.params[IEEE80211_MODE_11A]._param = 2*_v;		\
1426    else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)		\
1427	    _base.params[IEEE80211_MODE_11G]._param = (_v == 5 ? 11 : 2*_v);\
1428    else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)		\
1429	    _base.params[IEEE80211_MODE_11B]._param = (_v == 5 ? 11 : 2*_v);\
1430} while (0)
1431
1432static
1433DECL_CMD_FUNC(set80211roamrssi, val, d)
1434{
1435	double v = atof(val);
1436	int rssi, flags;
1437
1438	rssi = (int) (2*v);
1439	if (rssi != 2*v)
1440		errx(-1, "invalid rssi (must be .5 dBm units)");
1441	flags = getmodeflags(val);
1442	getroam(s);
1443	if (flags == 0) {		/* NB: no flags => current channel */
1444		flags = getcurchan(s)->ic_flags;
1445		_APPLY1(flags, roamparams, rssi, rssi);
1446	} else
1447		_APPLY(flags, roamparams, rssi, rssi);
1448	callback_register(setroam_cb, &roamparams);
1449}
1450
1451static
1452DECL_CMD_FUNC(set80211roamrate, val, d)
1453{
1454	int v = atoi(val), flags;
1455
1456	flags = getmodeflags(val);
1457	getroam(s);
1458	if (flags == 0) {		/* NB: no flags => current channel */
1459		flags = getcurchan(s)->ic_flags;
1460		_APPLY_RATE1(flags, roamparams, rate, v);
1461	} else
1462		_APPLY_RATE(flags, roamparams, rate, v);
1463	callback_register(setroam_cb, &roamparams);
1464}
1465
1466static
1467DECL_CMD_FUNC(set80211mcastrate, val, d)
1468{
1469	int v = atoi(val), flags;
1470
1471	flags = getmodeflags(val);
1472	gettxparams(s);
1473	if (flags == 0) {		/* NB: no flags => current channel */
1474		flags = getcurchan(s)->ic_flags;
1475		_APPLY_RATE1(flags, txparams, mcastrate, v);
1476	} else
1477		_APPLY_RATE(flags, txparams, mcastrate, v);
1478	callback_register(settxparams_cb, &txparams);
1479}
1480
1481static
1482DECL_CMD_FUNC(set80211mgtrate, val, d)
1483{
1484	int v = atoi(val), flags;
1485
1486	flags = getmodeflags(val);
1487	gettxparams(s);
1488	if (flags == 0) {		/* NB: no flags => current channel */
1489		flags = getcurchan(s)->ic_flags;
1490		_APPLY_RATE1(flags, txparams, mgmtrate, v);
1491	} else
1492		_APPLY_RATE(flags, txparams, mgmtrate, v);
1493	callback_register(settxparams_cb, &txparams);
1494}
1495
1496static
1497DECL_CMD_FUNC(set80211ucastrate, val, d)
1498{
1499	int v, flags;
1500
1501	gettxparams(s);
1502	flags = getmodeflags(val);
1503	if (isanyarg(val)) {
1504		if (flags == 0) {	/* NB: no flags => current channel */
1505			flags = getcurchan(s)->ic_flags;
1506			_APPLY1(flags, txparams, ucastrate,
1507			    IEEE80211_FIXED_RATE_NONE);
1508		} else
1509			_APPLY(flags, txparams, ucastrate,
1510			    IEEE80211_FIXED_RATE_NONE);
1511	} else {
1512		v = atoi(val);
1513		if (flags == 0) {	/* NB: no flags => current channel */
1514			flags = getcurchan(s)->ic_flags;
1515			_APPLY_RATE1(flags, txparams, ucastrate, v);
1516		} else
1517			_APPLY_RATE(flags, txparams, ucastrate, v);
1518	}
1519	callback_register(settxparams_cb, &txparams);
1520}
1521
1522static
1523DECL_CMD_FUNC(set80211maxretry, val, d)
1524{
1525	int v = atoi(val), flags;
1526
1527	flags = getmodeflags(val);
1528	gettxparams(s);
1529	if (flags == 0) {		/* NB: no flags => current channel */
1530		flags = getcurchan(s)->ic_flags;
1531		_APPLY1(flags, txparams, maxretry, v);
1532	} else
1533		_APPLY(flags, txparams, maxretry, v);
1534	callback_register(settxparams_cb, &txparams);
1535}
1536#undef _APPLY_RATE
1537#undef _APPLY
1538#undef IEEE80211_CHAN_HTA
1539#undef IEEE80211_CHAN_HTG
1540
1541static
1542DECL_CMD_FUNC(set80211fragthreshold, val, d)
1543{
1544	set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
1545		isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
1546}
1547
1548static
1549DECL_CMD_FUNC(set80211bmissthreshold, val, d)
1550{
1551	set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
1552		isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
1553}
1554
1555static void
1556set80211burst(const char *val, int d, int s, const struct afswtch *rafp)
1557{
1558	set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
1559}
1560
1561static void
1562set80211doth(const char *val, int d, int s, const struct afswtch *rafp)
1563{
1564	set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL);
1565}
1566
1567static void
1568set80211dfs(const char *val, int d, int s, const struct afswtch *rafp)
1569{
1570	set80211(s, IEEE80211_IOC_DFS, d, 0, NULL);
1571}
1572
1573static void
1574set80211shortgi(const char *val, int d, int s, const struct afswtch *rafp)
1575{
1576	set80211(s, IEEE80211_IOC_SHORTGI,
1577		d ? (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) : 0,
1578		0, NULL);
1579}
1580
1581static void
1582set80211ampdu(const char *val, int d, int s, const struct afswtch *rafp)
1583{
1584	int ampdu;
1585
1586	if (get80211val(s, IEEE80211_IOC_AMPDU, &ampdu) < 0)
1587		errx(-1, "cannot get AMPDU setting");
1588	if (d < 0) {
1589		d = -d;
1590		ampdu &= ~d;
1591	} else
1592		ampdu |= d;
1593	set80211(s, IEEE80211_IOC_AMPDU, ampdu, 0, NULL);
1594}
1595
1596static
1597DECL_CMD_FUNC(set80211ampdulimit, val, d)
1598{
1599	int v;
1600
1601	switch (atoi(val)) {
1602	case 8:
1603	case 8*1024:
1604		v = IEEE80211_HTCAP_MAXRXAMPDU_8K;
1605		break;
1606	case 16:
1607	case 16*1024:
1608		v = IEEE80211_HTCAP_MAXRXAMPDU_16K;
1609		break;
1610	case 32:
1611	case 32*1024:
1612		v = IEEE80211_HTCAP_MAXRXAMPDU_32K;
1613		break;
1614	case 64:
1615	case 64*1024:
1616		v = IEEE80211_HTCAP_MAXRXAMPDU_64K;
1617		break;
1618	default:
1619		errx(-1, "invalid A-MPDU limit %s", val);
1620	}
1621	set80211(s, IEEE80211_IOC_AMPDU_LIMIT, v, 0, NULL);
1622}
1623
1624static
1625DECL_CMD_FUNC(set80211ampdudensity, val, d)
1626{
1627	int v;
1628
1629	if (isanyarg(val) || strcasecmp(val, "na") == 0)
1630		v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1631	else switch ((int)(atof(val)*4)) {
1632	case 0:
1633		v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1634		break;
1635	case 1:
1636		v = IEEE80211_HTCAP_MPDUDENSITY_025;
1637		break;
1638	case 2:
1639		v = IEEE80211_HTCAP_MPDUDENSITY_05;
1640		break;
1641	case 4:
1642		v = IEEE80211_HTCAP_MPDUDENSITY_1;
1643		break;
1644	case 8:
1645		v = IEEE80211_HTCAP_MPDUDENSITY_2;
1646		break;
1647	case 16:
1648		v = IEEE80211_HTCAP_MPDUDENSITY_4;
1649		break;
1650	case 32:
1651		v = IEEE80211_HTCAP_MPDUDENSITY_8;
1652		break;
1653	case 64:
1654		v = IEEE80211_HTCAP_MPDUDENSITY_16;
1655		break;
1656	default:
1657		errx(-1, "invalid A-MPDU density %s", val);
1658	}
1659	set80211(s, IEEE80211_IOC_AMPDU_DENSITY, v, 0, NULL);
1660}
1661
1662static void
1663set80211amsdu(const char *val, int d, int s, const struct afswtch *rafp)
1664{
1665	int amsdu;
1666
1667	if (get80211val(s, IEEE80211_IOC_AMSDU, &amsdu) < 0)
1668		err(-1, "cannot get AMSDU setting");
1669	if (d < 0) {
1670		d = -d;
1671		amsdu &= ~d;
1672	} else
1673		amsdu |= d;
1674	set80211(s, IEEE80211_IOC_AMSDU, amsdu, 0, NULL);
1675}
1676
1677static
1678DECL_CMD_FUNC(set80211amsdulimit, val, d)
1679{
1680	set80211(s, IEEE80211_IOC_AMSDU_LIMIT, atoi(val), 0, NULL);
1681}
1682
1683static void
1684set80211puren(const char *val, int d, int s, const struct afswtch *rafp)
1685{
1686	set80211(s, IEEE80211_IOC_PUREN, d, 0, NULL);
1687}
1688
1689static void
1690set80211htcompat(const char *val, int d, int s, const struct afswtch *rafp)
1691{
1692	set80211(s, IEEE80211_IOC_HTCOMPAT, d, 0, NULL);
1693}
1694
1695static void
1696set80211htconf(const char *val, int d, int s, const struct afswtch *rafp)
1697{
1698	set80211(s, IEEE80211_IOC_HTCONF, d, 0, NULL);
1699	htconf = d;
1700}
1701
1702static void
1703set80211dwds(const char *val, int d, int s, const struct afswtch *rafp)
1704{
1705	set80211(s, IEEE80211_IOC_DWDS, d, 0, NULL);
1706}
1707
1708static void
1709set80211inact(const char *val, int d, int s, const struct afswtch *rafp)
1710{
1711	set80211(s, IEEE80211_IOC_INACTIVITY, d, 0, NULL);
1712}
1713
1714static void
1715set80211tsn(const char *val, int d, int s, const struct afswtch *rafp)
1716{
1717	set80211(s, IEEE80211_IOC_TSN, d, 0, NULL);
1718}
1719
1720static void
1721set80211dotd(const char *val, int d, int s, const struct afswtch *rafp)
1722{
1723	set80211(s, IEEE80211_IOC_DOTD, d, 0, NULL);
1724}
1725
1726static void
1727set80211smps(const char *val, int d, int s, const struct afswtch *rafp)
1728{
1729	set80211(s, IEEE80211_IOC_SMPS, d, 0, NULL);
1730}
1731
1732static void
1733set80211rifs(const char *val, int d, int s, const struct afswtch *rafp)
1734{
1735	set80211(s, IEEE80211_IOC_RIFS, d, 0, NULL);
1736}
1737
1738static
1739DECL_CMD_FUNC(set80211tdmaslot, val, d)
1740{
1741	set80211(s, IEEE80211_IOC_TDMA_SLOT, atoi(val), 0, NULL);
1742}
1743
1744static
1745DECL_CMD_FUNC(set80211tdmaslotcnt, val, d)
1746{
1747	set80211(s, IEEE80211_IOC_TDMA_SLOTCNT, atoi(val), 0, NULL);
1748}
1749
1750static
1751DECL_CMD_FUNC(set80211tdmaslotlen, val, d)
1752{
1753	set80211(s, IEEE80211_IOC_TDMA_SLOTLEN, atoi(val), 0, NULL);
1754}
1755
1756static
1757DECL_CMD_FUNC(set80211tdmabintval, val, d)
1758{
1759	set80211(s, IEEE80211_IOC_TDMA_BINTERVAL, atoi(val), 0, NULL);
1760}
1761
1762static int
1763regdomain_sort(const void *a, const void *b)
1764{
1765#define	CHAN_ALL \
1766	(IEEE80211_CHAN_ALLTURBO|IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)
1767	const struct ieee80211_channel *ca = a;
1768	const struct ieee80211_channel *cb = b;
1769
1770	return ca->ic_freq == cb->ic_freq ?
1771	    (ca->ic_flags & CHAN_ALL) - (cb->ic_flags & CHAN_ALL) :
1772	    ca->ic_freq - cb->ic_freq;
1773#undef CHAN_ALL
1774}
1775
1776static const struct ieee80211_channel *
1777chanlookup(const struct ieee80211_channel chans[], int nchans,
1778	int freq, int flags)
1779{
1780	int i;
1781
1782	flags &= IEEE80211_CHAN_ALLTURBO;
1783	for (i = 0; i < nchans; i++) {
1784		const struct ieee80211_channel *c = &chans[i];
1785		if (c->ic_freq == freq &&
1786		    (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
1787			return c;
1788	}
1789	return NULL;
1790}
1791
1792static int
1793chanfind(const struct ieee80211_channel chans[], int nchans, int flags)
1794{
1795	int i;
1796
1797	for (i = 0; i < nchans; i++) {
1798		const struct ieee80211_channel *c = &chans[i];
1799		if ((c->ic_flags & flags) == flags)
1800			return 1;
1801	}
1802	return 0;
1803}
1804
1805static void
1806regdomain_addchans(struct ieee80211req_chaninfo *ci,
1807	const netband_head *bands,
1808	const struct ieee80211_regdomain *reg,
1809	uint32_t chanFlags,
1810	const struct ieee80211req_chaninfo *avail)
1811{
1812	const struct netband *nb;
1813	const struct freqband *b;
1814	struct ieee80211_channel *c, *prev;
1815	int freq, channelSep, hasHalfChans, hasQuarterChans;
1816
1817	channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40;
1818	hasHalfChans = chanfind(avail->ic_chans, avail->ic_nchans,
1819	    IEEE80211_CHAN_HALF |
1820	       (chanFlags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
1821	hasQuarterChans = chanfind(avail->ic_chans, avail->ic_nchans,
1822	    IEEE80211_CHAN_QUARTER |
1823	        (chanFlags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
1824	LIST_FOREACH(nb, bands, next) {
1825		b = nb->band;
1826		if (verbose) {
1827			printf("%s:", __func__);
1828			printb(" chanFlags", chanFlags, IEEE80211_CHAN_BITS);
1829			printb(" bandFlags", nb->flags | b->flags,
1830			    IEEE80211_CHAN_BITS);
1831			putchar('\n');
1832		}
1833		prev = NULL;
1834		for (freq = b->freqStart; freq <= b->freqEnd; freq += b->chanSep) {
1835			uint32_t flags = nb->flags | b->flags;
1836
1837			/* check if device can operate on this frequency */
1838			/*
1839			 * XXX GSM frequency mapping is handled in the kernel
1840			 * so we cannot find them in the calibration table;
1841			 * just construct the list and the kernel will reject
1842			 * if it's wrong.
1843			 */
1844			if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, chanFlags) == NULL &&
1845			    (flags & IEEE80211_CHAN_GSM) == 0) {
1846				if (verbose) {
1847					printf("%u: skip, ", freq);
1848					printb("flags", chanFlags,
1849					    IEEE80211_CHAN_BITS);
1850					printf(" not available\n");
1851				}
1852				continue;
1853			}
1854			if ((flags & IEEE80211_CHAN_HALF) && !hasHalfChans) {
1855				if (verbose)
1856					printf("%u: skip, device does not "
1857					    "support half-rate channel\n",
1858					    freq);
1859				continue;
1860			}
1861			if ((flags & IEEE80211_CHAN_QUARTER) &&
1862			    !hasQuarterChans) {
1863				if (verbose)
1864					printf("%u: skip, device does not "
1865					    "support quarter-rate channel\n",
1866					    freq);
1867				continue;
1868			}
1869			if ((flags & IEEE80211_CHAN_HT20) &&
1870			    (chanFlags & IEEE80211_CHAN_HT20) == 0) {
1871				if (verbose)
1872					printf("%u: skip, device does not "
1873					    "support HT20 operation\n", freq);
1874				continue;
1875			}
1876			if ((flags & IEEE80211_CHAN_HT40) &&
1877			    (chanFlags & IEEE80211_CHAN_HT40) == 0) {
1878				if (verbose)
1879					printf("%u: skip, device does not "
1880					    "support HT40 operation\n", freq);
1881				continue;
1882			}
1883			if ((flags & REQ_ECM) && !reg->ecm) {
1884				if (verbose)
1885					printf("%u: skip, ECM channel\n", freq);
1886				continue;
1887			}
1888			if ((flags & REQ_OUTDOOR) && reg->location == 'I') {
1889				if (verbose)
1890					printf("%u: skip, outdoor channel\n", freq);
1891				continue;
1892			}
1893			if ((flags & IEEE80211_CHAN_HT40) &&
1894			    prev != NULL && (freq - prev->ic_freq) < channelSep) {
1895				if (verbose)
1896					printf("%u: skip, only %u channel "
1897					    "separation, need %d\n", freq,
1898					    freq - prev->ic_freq, channelSep);
1899				continue;
1900			}
1901			if (ci->ic_nchans == IEEE80211_CHAN_MAX) {
1902				if (verbose)
1903					printf("%u: skip, channel table full\n",
1904					    freq);
1905				break;
1906			}
1907			c = &ci->ic_chans[ci->ic_nchans++];
1908			memset(c, 0, sizeof(*c));
1909			c->ic_freq = freq;
1910			c->ic_flags = chanFlags |
1911			    (flags &~ (REQ_FLAGS | IEEE80211_CHAN_HT40));
1912			if (c->ic_flags & IEEE80211_CHAN_DFS)
1913				c->ic_maxregpower = nb->maxPowerDFS;
1914			else
1915				c->ic_maxregpower = nb->maxPower;
1916			if (verbose) {
1917				printf("[%3d] add freq %u ",
1918				    ci->ic_nchans-1, c->ic_freq);
1919				printb("flags", c->ic_flags, IEEE80211_CHAN_BITS);
1920				printf(" power %u\n", c->ic_maxregpower);
1921			}
1922			/* NB: kernel fills in other fields */
1923			prev = c;
1924		}
1925	}
1926}
1927
1928static void
1929regdomain_makechannels(
1930	struct ieee80211_regdomain_req *req,
1931	const struct ieee80211_devcaps_req *dc)
1932{
1933	struct regdata *rdp = getregdata();
1934	const struct country *cc;
1935	const struct ieee80211_regdomain *reg = &req->rd;
1936	struct ieee80211req_chaninfo *ci = &req->chaninfo;
1937	const struct regdomain *rd;
1938
1939	/*
1940	 * Locate construction table for new channel list.  We treat
1941	 * the regdomain/SKU as definitive so a country can be in
1942	 * multiple with different properties (e.g. US in FCC+FCC3).
1943	 * If no regdomain is specified then we fallback on the country
1944	 * code to find the associated regdomain since countries always
1945	 * belong to at least one regdomain.
1946	 */
1947	if (reg->regdomain == 0) {
1948		cc = lib80211_country_findbycc(rdp, reg->country);
1949		if (cc == NULL)
1950			errx(1, "internal error, country %d not found",
1951			    reg->country);
1952		rd = cc->rd;
1953	} else
1954		rd = lib80211_regdomain_findbysku(rdp, reg->regdomain);
1955	if (rd == NULL)
1956		errx(1, "internal error, regdomain %d not found",
1957			    reg->regdomain);
1958	if (rd->sku != SKU_DEBUG) {
1959		/*
1960		 * regdomain_addchans incrememnts the channel count for
1961		 * each channel it adds so initialize ic_nchans to zero.
1962		 * Note that we know we have enough space to hold all possible
1963		 * channels because the devcaps list size was used to
1964		 * allocate our request.
1965		 */
1966		ci->ic_nchans = 0;
1967		if (!LIST_EMPTY(&rd->bands_11b))
1968			regdomain_addchans(ci, &rd->bands_11b, reg,
1969			    IEEE80211_CHAN_B, &dc->dc_chaninfo);
1970		if (!LIST_EMPTY(&rd->bands_11g))
1971			regdomain_addchans(ci, &rd->bands_11g, reg,
1972			    IEEE80211_CHAN_G, &dc->dc_chaninfo);
1973		if (!LIST_EMPTY(&rd->bands_11a))
1974			regdomain_addchans(ci, &rd->bands_11a, reg,
1975			    IEEE80211_CHAN_A, &dc->dc_chaninfo);
1976		if (!LIST_EMPTY(&rd->bands_11na)) {
1977			regdomain_addchans(ci, &rd->bands_11na, reg,
1978			    IEEE80211_CHAN_A | IEEE80211_CHAN_HT20,
1979			    &dc->dc_chaninfo);
1980			regdomain_addchans(ci, &rd->bands_11na, reg,
1981			    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U,
1982			    &dc->dc_chaninfo);
1983			regdomain_addchans(ci, &rd->bands_11na, reg,
1984			    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D,
1985			    &dc->dc_chaninfo);
1986		}
1987		if (!LIST_EMPTY(&rd->bands_11ng)) {
1988			regdomain_addchans(ci, &rd->bands_11ng, reg,
1989			    IEEE80211_CHAN_G | IEEE80211_CHAN_HT20,
1990			    &dc->dc_chaninfo);
1991			regdomain_addchans(ci, &rd->bands_11ng, reg,
1992			    IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U,
1993			    &dc->dc_chaninfo);
1994			regdomain_addchans(ci, &rd->bands_11ng, reg,
1995			    IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D,
1996			    &dc->dc_chaninfo);
1997		}
1998		qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]),
1999		    regdomain_sort);
2000	} else
2001		memcpy(ci, &dc->dc_chaninfo,
2002		    IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
2003}
2004
2005static void
2006list_countries(void)
2007{
2008	struct regdata *rdp = getregdata();
2009	const struct country *cp;
2010	const struct regdomain *dp;
2011	int i;
2012
2013	i = 0;
2014	printf("\nCountry codes:\n");
2015	LIST_FOREACH(cp, &rdp->countries, next) {
2016		printf("%2s %-15.15s%s", cp->isoname,
2017		    cp->name, ((i+1)%4) == 0 ? "\n" : " ");
2018		i++;
2019	}
2020	i = 0;
2021	printf("\nRegulatory domains:\n");
2022	LIST_FOREACH(dp, &rdp->domains, next) {
2023		printf("%-15.15s%s", dp->name, ((i+1)%4) == 0 ? "\n" : " ");
2024		i++;
2025	}
2026	printf("\n");
2027}
2028
2029static void
2030defaultcountry(const struct regdomain *rd)
2031{
2032	struct regdata *rdp = getregdata();
2033	const struct country *cc;
2034
2035	cc = lib80211_country_findbycc(rdp, rd->cc->code);
2036	if (cc == NULL)
2037		errx(1, "internal error, ISO country code %d not "
2038		    "defined for regdomain %s", rd->cc->code, rd->name);
2039	regdomain.country = cc->code;
2040	regdomain.isocc[0] = cc->isoname[0];
2041	regdomain.isocc[1] = cc->isoname[1];
2042}
2043
2044static
2045DECL_CMD_FUNC(set80211regdomain, val, d)
2046{
2047	struct regdata *rdp = getregdata();
2048	const struct regdomain *rd;
2049
2050	rd = lib80211_regdomain_findbyname(rdp, val);
2051	if (rd == NULL) {
2052		char *eptr;
2053		long sku = strtol(val, &eptr, 0);
2054
2055		if (eptr != val)
2056			rd = lib80211_regdomain_findbysku(rdp, sku);
2057		if (eptr == val || rd == NULL)
2058			errx(1, "unknown regdomain %s", val);
2059	}
2060	getregdomain(s);
2061	regdomain.regdomain = rd->sku;
2062	if (regdomain.country == 0 && rd->cc != NULL) {
2063		/*
2064		 * No country code setup and there's a default
2065		 * one for this regdomain fill it in.
2066		 */
2067		defaultcountry(rd);
2068	}
2069	callback_register(setregdomain_cb, &regdomain);
2070}
2071
2072static
2073DECL_CMD_FUNC(set80211country, val, d)
2074{
2075	struct regdata *rdp = getregdata();
2076	const struct country *cc;
2077
2078	cc = lib80211_country_findbyname(rdp, val);
2079	if (cc == NULL) {
2080		char *eptr;
2081		long code = strtol(val, &eptr, 0);
2082
2083		if (eptr != val)
2084			cc = lib80211_country_findbycc(rdp, code);
2085		if (eptr == val || cc == NULL)
2086			errx(1, "unknown ISO country code %s", val);
2087	}
2088	getregdomain(s);
2089	regdomain.regdomain = cc->rd->sku;
2090	regdomain.country = cc->code;
2091	regdomain.isocc[0] = cc->isoname[0];
2092	regdomain.isocc[1] = cc->isoname[1];
2093	callback_register(setregdomain_cb, &regdomain);
2094}
2095
2096static void
2097set80211location(const char *val, int d, int s, const struct afswtch *rafp)
2098{
2099	getregdomain(s);
2100	regdomain.location = d;
2101	callback_register(setregdomain_cb, &regdomain);
2102}
2103
2104static void
2105set80211ecm(const char *val, int d, int s, const struct afswtch *rafp)
2106{
2107	getregdomain(s);
2108	regdomain.ecm = d;
2109	callback_register(setregdomain_cb, &regdomain);
2110}
2111
2112static void
2113LINE_INIT(char c)
2114{
2115	spacer = c;
2116	if (c == '\t')
2117		col = 8;
2118	else
2119		col = 1;
2120}
2121
2122static void
2123LINE_BREAK(void)
2124{
2125	if (spacer != '\t') {
2126		printf("\n");
2127		spacer = '\t';
2128	}
2129	col = 8;		/* 8-col tab */
2130}
2131
2132static void
2133LINE_CHECK(const char *fmt, ...)
2134{
2135	char buf[80];
2136	va_list ap;
2137	int n;
2138
2139	va_start(ap, fmt);
2140	n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
2141	va_end(ap);
2142	col += 1+n;
2143	if (col > MAXCOL) {
2144		LINE_BREAK();
2145		col += n;
2146	}
2147	buf[0] = spacer;
2148	printf("%s", buf);
2149	spacer = ' ';
2150}
2151
2152static int
2153getmaxrate(const uint8_t rates[15], uint8_t nrates)
2154{
2155	int i, maxrate = -1;
2156
2157	for (i = 0; i < nrates; i++) {
2158		int rate = rates[i] & IEEE80211_RATE_VAL;
2159		if (rate > maxrate)
2160			maxrate = rate;
2161	}
2162	return maxrate / 2;
2163}
2164
2165static const char *
2166getcaps(int capinfo)
2167{
2168	static char capstring[32];
2169	char *cp = capstring;
2170
2171	if (capinfo & IEEE80211_CAPINFO_ESS)
2172		*cp++ = 'E';
2173	if (capinfo & IEEE80211_CAPINFO_IBSS)
2174		*cp++ = 'I';
2175	if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
2176		*cp++ = 'c';
2177	if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
2178		*cp++ = 'C';
2179	if (capinfo & IEEE80211_CAPINFO_PRIVACY)
2180		*cp++ = 'P';
2181	if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
2182		*cp++ = 'S';
2183	if (capinfo & IEEE80211_CAPINFO_PBCC)
2184		*cp++ = 'B';
2185	if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
2186		*cp++ = 'A';
2187	if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
2188		*cp++ = 's';
2189	if (capinfo & IEEE80211_CAPINFO_RSN)
2190		*cp++ = 'R';
2191	if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
2192		*cp++ = 'D';
2193	*cp = '\0';
2194	return capstring;
2195}
2196
2197static const char *
2198getflags(int flags)
2199{
2200	static char flagstring[32];
2201	char *cp = flagstring;
2202
2203	if (flags & IEEE80211_NODE_AUTH)
2204		*cp++ = 'A';
2205	if (flags & IEEE80211_NODE_QOS)
2206		*cp++ = 'Q';
2207	if (flags & IEEE80211_NODE_ERP)
2208		*cp++ = 'E';
2209	if (flags & IEEE80211_NODE_PWR_MGT)
2210		*cp++ = 'P';
2211	if (flags & IEEE80211_NODE_HT) {
2212		*cp++ = 'H';
2213		if (flags & IEEE80211_NODE_HTCOMPAT)
2214			*cp++ = '+';
2215	}
2216	if (flags & IEEE80211_NODE_WPS)
2217		*cp++ = 'W';
2218	if (flags & IEEE80211_NODE_TSN)
2219		*cp++ = 'N';
2220	if (flags & IEEE80211_NODE_AMPDU_TX)
2221		*cp++ = 'T';
2222	if (flags & IEEE80211_NODE_AMPDU_RX)
2223		*cp++ = 'R';
2224	if (flags & IEEE80211_NODE_MIMO_PS) {
2225		*cp++ = 'M';
2226		if (flags & IEEE80211_NODE_MIMO_RTS)
2227			*cp++ = '+';
2228	}
2229	if (flags & IEEE80211_NODE_RIFS)
2230		*cp++ = 'I';
2231	*cp = '\0';
2232	return flagstring;
2233}
2234
2235static void
2236printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
2237{
2238	printf("%s", tag);
2239	if (verbose) {
2240		maxlen -= strlen(tag)+2;
2241		if (2*ielen > maxlen)
2242			maxlen--;
2243		printf("<");
2244		for (; ielen > 0; ie++, ielen--) {
2245			if (maxlen-- <= 0)
2246				break;
2247			printf("%02x", *ie);
2248		}
2249		if (ielen != 0)
2250			printf("-");
2251		printf(">");
2252	}
2253}
2254
2255#define LE_READ_2(p)					\
2256	((u_int16_t)					\
2257	 ((((const u_int8_t *)(p))[0]      ) |		\
2258	  (((const u_int8_t *)(p))[1] <<  8)))
2259#define LE_READ_4(p)					\
2260	((u_int32_t)					\
2261	 ((((const u_int8_t *)(p))[0]      ) |		\
2262	  (((const u_int8_t *)(p))[1] <<  8) |		\
2263	  (((const u_int8_t *)(p))[2] << 16) |		\
2264	  (((const u_int8_t *)(p))[3] << 24)))
2265
2266/*
2267 * NB: The decoding routines assume a properly formatted ie
2268 *     which should be safe as the kernel only retains them
2269 *     if they parse ok.
2270 */
2271
2272static void
2273printwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2274{
2275#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
2276	static const char *acnames[] = { "BE", "BK", "VO", "VI" };
2277	const struct ieee80211_wme_param *wme =
2278	    (const struct ieee80211_wme_param *) ie;
2279	int i;
2280
2281	printf("%s", tag);
2282	if (!verbose)
2283		return;
2284	printf("<qosinfo 0x%x", wme->param_qosInfo);
2285	ie += offsetof(struct ieee80211_wme_param, params_acParams);
2286	for (i = 0; i < WME_NUM_AC; i++) {
2287		const struct ieee80211_wme_acparams *ac =
2288		    &wme->params_acParams[i];
2289
2290		printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]"
2291			, acnames[i]
2292			, MS(ac->acp_aci_aifsn, WME_PARAM_ACM) ? "acm " : ""
2293			, MS(ac->acp_aci_aifsn, WME_PARAM_AIFSN)
2294			, MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMIN)
2295			, MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMAX)
2296			, LE_READ_2(&ac->acp_txop)
2297		);
2298	}
2299	printf(">");
2300#undef MS
2301}
2302
2303static void
2304printwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2305{
2306	printf("%s", tag);
2307	if (verbose) {
2308		const struct ieee80211_wme_info *wme =
2309		    (const struct ieee80211_wme_info *) ie;
2310		printf("<version 0x%x info 0x%x>",
2311		    wme->wme_version, wme->wme_info);
2312	}
2313}
2314
2315static void
2316printhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2317{
2318	printf("%s", tag);
2319	if (verbose) {
2320		const struct ieee80211_ie_htcap *htcap =
2321		    (const struct ieee80211_ie_htcap *) ie;
2322		const char *sep;
2323		int i, j;
2324
2325		printf("<cap 0x%x param 0x%x",
2326		    LE_READ_2(&htcap->hc_cap), htcap->hc_param);
2327		printf(" mcsset[");
2328		sep = "";
2329		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2330			if (isset(htcap->hc_mcsset, i)) {
2331				for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2332					if (isclr(htcap->hc_mcsset, j))
2333						break;
2334				j--;
2335				if (i == j)
2336					printf("%s%u", sep, i);
2337				else
2338					printf("%s%u-%u", sep, i, j);
2339				i += j-i;
2340				sep = ",";
2341			}
2342		printf("] extcap 0x%x txbf 0x%x antenna 0x%x>",
2343		    LE_READ_2(&htcap->hc_extcap),
2344		    LE_READ_4(&htcap->hc_txbf),
2345		    htcap->hc_antenna);
2346	}
2347}
2348
2349static void
2350printhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2351{
2352	printf("%s", tag);
2353	if (verbose) {
2354		const struct ieee80211_ie_htinfo *htinfo =
2355		    (const struct ieee80211_ie_htinfo *) ie;
2356		const char *sep;
2357		int i, j;
2358
2359		printf("<ctl %u, %x,%x,%x,%x", htinfo->hi_ctrlchannel,
2360		    htinfo->hi_byte1, htinfo->hi_byte2, htinfo->hi_byte3,
2361		    LE_READ_2(&htinfo->hi_byte45));
2362		printf(" basicmcs[");
2363		sep = "";
2364		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2365			if (isset(htinfo->hi_basicmcsset, i)) {
2366				for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2367					if (isclr(htinfo->hi_basicmcsset, j))
2368						break;
2369				j--;
2370				if (i == j)
2371					printf("%s%u", sep, i);
2372				else
2373					printf("%s%u-%u", sep, i, j);
2374				i += j-i;
2375				sep = ",";
2376			}
2377		printf("]>");
2378	}
2379}
2380
2381static void
2382printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2383{
2384
2385	printf("%s", tag);
2386	if (verbose) {
2387		const struct ieee80211_ath_ie *ath =
2388			(const struct ieee80211_ath_ie *)ie;
2389
2390		printf("<");
2391		if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME)
2392			printf("DTURBO,");
2393		if (ath->ath_capability & ATHEROS_CAP_COMPRESSION)
2394			printf("COMP,");
2395		if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME)
2396			printf("FF,");
2397		if (ath->ath_capability & ATHEROS_CAP_XR)
2398			printf("XR,");
2399		if (ath->ath_capability & ATHEROS_CAP_AR)
2400			printf("AR,");
2401		if (ath->ath_capability & ATHEROS_CAP_BURST)
2402			printf("BURST,");
2403		if (ath->ath_capability & ATHEROS_CAP_WME)
2404			printf("WME,");
2405		if (ath->ath_capability & ATHEROS_CAP_BOOST)
2406			printf("BOOST,");
2407		printf("0x%x>", LE_READ_2(ath->ath_defkeyix));
2408	}
2409}
2410
2411static const char *
2412wpa_cipher(const u_int8_t *sel)
2413{
2414#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
2415	u_int32_t w = LE_READ_4(sel);
2416
2417	switch (w) {
2418	case WPA_SEL(WPA_CSE_NULL):
2419		return "NONE";
2420	case WPA_SEL(WPA_CSE_WEP40):
2421		return "WEP40";
2422	case WPA_SEL(WPA_CSE_WEP104):
2423		return "WEP104";
2424	case WPA_SEL(WPA_CSE_TKIP):
2425		return "TKIP";
2426	case WPA_SEL(WPA_CSE_CCMP):
2427		return "AES-CCMP";
2428	}
2429	return "?";		/* NB: so 1<< is discarded */
2430#undef WPA_SEL
2431}
2432
2433static const char *
2434wpa_keymgmt(const u_int8_t *sel)
2435{
2436#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
2437	u_int32_t w = LE_READ_4(sel);
2438
2439	switch (w) {
2440	case WPA_SEL(WPA_ASE_8021X_UNSPEC):
2441		return "8021X-UNSPEC";
2442	case WPA_SEL(WPA_ASE_8021X_PSK):
2443		return "8021X-PSK";
2444	case WPA_SEL(WPA_ASE_NONE):
2445		return "NONE";
2446	}
2447	return "?";
2448#undef WPA_SEL
2449}
2450
2451static void
2452printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2453{
2454	u_int8_t len = ie[1];
2455
2456	printf("%s", tag);
2457	if (verbose) {
2458		const char *sep;
2459		int n;
2460
2461		ie += 6, len -= 4;		/* NB: len is payload only */
2462
2463		printf("<v%u", LE_READ_2(ie));
2464		ie += 2, len -= 2;
2465
2466		printf(" mc:%s", wpa_cipher(ie));
2467		ie += 4, len -= 4;
2468
2469		/* unicast ciphers */
2470		n = LE_READ_2(ie);
2471		ie += 2, len -= 2;
2472		sep = " uc:";
2473		for (; n > 0; n--) {
2474			printf("%s%s", sep, wpa_cipher(ie));
2475			ie += 4, len -= 4;
2476			sep = "+";
2477		}
2478
2479		/* key management algorithms */
2480		n = LE_READ_2(ie);
2481		ie += 2, len -= 2;
2482		sep = " km:";
2483		for (; n > 0; n--) {
2484			printf("%s%s", sep, wpa_keymgmt(ie));
2485			ie += 4, len -= 4;
2486			sep = "+";
2487		}
2488
2489		if (len > 2)		/* optional capabilities */
2490			printf(", caps 0x%x", LE_READ_2(ie));
2491		printf(">");
2492	}
2493}
2494
2495static const char *
2496rsn_cipher(const u_int8_t *sel)
2497{
2498#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
2499	u_int32_t w = LE_READ_4(sel);
2500
2501	switch (w) {
2502	case RSN_SEL(RSN_CSE_NULL):
2503		return "NONE";
2504	case RSN_SEL(RSN_CSE_WEP40):
2505		return "WEP40";
2506	case RSN_SEL(RSN_CSE_WEP104):
2507		return "WEP104";
2508	case RSN_SEL(RSN_CSE_TKIP):
2509		return "TKIP";
2510	case RSN_SEL(RSN_CSE_CCMP):
2511		return "AES-CCMP";
2512	case RSN_SEL(RSN_CSE_WRAP):
2513		return "AES-OCB";
2514	}
2515	return "?";
2516#undef WPA_SEL
2517}
2518
2519static const char *
2520rsn_keymgmt(const u_int8_t *sel)
2521{
2522#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
2523	u_int32_t w = LE_READ_4(sel);
2524
2525	switch (w) {
2526	case RSN_SEL(RSN_ASE_8021X_UNSPEC):
2527		return "8021X-UNSPEC";
2528	case RSN_SEL(RSN_ASE_8021X_PSK):
2529		return "8021X-PSK";
2530	case RSN_SEL(RSN_ASE_NONE):
2531		return "NONE";
2532	}
2533	return "?";
2534#undef RSN_SEL
2535}
2536
2537static void
2538printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2539{
2540	printf("%s", tag);
2541	if (verbose) {
2542		const char *sep;
2543		int n;
2544
2545		ie += 2, ielen -= 2;
2546
2547		printf("<v%u", LE_READ_2(ie));
2548		ie += 2, ielen -= 2;
2549
2550		printf(" mc:%s", rsn_cipher(ie));
2551		ie += 4, ielen -= 4;
2552
2553		/* unicast ciphers */
2554		n = LE_READ_2(ie);
2555		ie += 2, ielen -= 2;
2556		sep = " uc:";
2557		for (; n > 0; n--) {
2558			printf("%s%s", sep, rsn_cipher(ie));
2559			ie += 4, ielen -= 4;
2560			sep = "+";
2561		}
2562
2563		/* key management algorithms */
2564		n = LE_READ_2(ie);
2565		ie += 2, ielen -= 2;
2566		sep = " km:";
2567		for (; n > 0; n--) {
2568			printf("%s%s", sep, rsn_keymgmt(ie));
2569			ie += 4, ielen -= 4;
2570			sep = "+";
2571		}
2572
2573		if (ielen > 2)		/* optional capabilities */
2574			printf(", caps 0x%x", LE_READ_2(ie));
2575		/* XXXPMKID */
2576		printf(">");
2577	}
2578}
2579
2580/* XXX move to a public include file */
2581#define IEEE80211_WPS_DEV_PASS_ID	0x1012
2582#define IEEE80211_WPS_SELECTED_REG	0x1041
2583#define IEEE80211_WPS_SETUP_STATE	0x1044
2584#define IEEE80211_WPS_UUID_E		0x1047
2585#define IEEE80211_WPS_VERSION		0x104a
2586
2587#define BE_READ_2(p)					\
2588	((u_int16_t)					\
2589	 ((((const u_int8_t *)(p))[1]      ) |		\
2590	  (((const u_int8_t *)(p))[0] <<  8)))
2591
2592static void
2593printwpsie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2594{
2595#define	N(a)	(sizeof(a) / sizeof(a[0]))
2596	u_int8_t len = ie[1];
2597
2598	printf("%s", tag);
2599	if (verbose) {
2600		static const char *dev_pass_id[] = {
2601			"D",	/* Default (PIN) */
2602			"U",	/* User-specified */
2603			"M",	/* Machine-specified */
2604			"K",	/* Rekey */
2605			"P",	/* PushButton */
2606			"R"	/* Registrar-specified */
2607		};
2608		int n;
2609
2610		ie +=6, len -= 4;		/* NB: len is payload only */
2611
2612		/* WPS IE in Beacon and Probe Resp frames have different fields */
2613		printf("<");
2614		while (len) {
2615			uint16_t tlv_type = BE_READ_2(ie);
2616			uint16_t tlv_len  = BE_READ_2(ie + 2);
2617
2618			ie += 4, len -= 4;
2619
2620			switch (tlv_type) {
2621			case IEEE80211_WPS_VERSION:
2622				printf("v:%d.%d", *ie >> 4, *ie & 0xf);
2623				break;
2624			case IEEE80211_WPS_SETUP_STATE:
2625				/* Only 1 and 2 are valid */
2626				if (*ie == 0 || *ie >= 3)
2627					printf(" state:B");
2628				else
2629					printf(" st:%s", *ie == 1 ? "N" : "C");
2630				break;
2631			case IEEE80211_WPS_SELECTED_REG:
2632				printf(" sel:%s", *ie ? "T" : "F");
2633				break;
2634			case IEEE80211_WPS_DEV_PASS_ID:
2635				n = LE_READ_2(ie);
2636				if (n < N(dev_pass_id))
2637					printf(" dpi:%s", dev_pass_id[n]);
2638				break;
2639			case IEEE80211_WPS_UUID_E:
2640				printf(" uuid-e:");
2641				for (n = 0; n < (tlv_len - 1); n++)
2642					printf("%02x-", ie[n]);
2643				printf("%02x", ie[n]);
2644				break;
2645			}
2646			ie += tlv_len, len -= tlv_len;
2647		}
2648		printf(">");
2649	}
2650#undef N
2651}
2652
2653static void
2654printtdmaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2655{
2656	printf("%s", tag);
2657	if (verbose && ielen >= sizeof(struct ieee80211_tdma_param)) {
2658		const struct ieee80211_tdma_param *tdma =
2659		   (const struct ieee80211_tdma_param *) ie;
2660
2661		/* XXX tstamp */
2662		printf("<v%u slot:%u slotcnt:%u slotlen:%u bintval:%u inuse:0x%x>",
2663		    tdma->tdma_version, tdma->tdma_slot, tdma->tdma_slotcnt,
2664		    LE_READ_2(&tdma->tdma_slotlen), tdma->tdma_bintval,
2665		    tdma->tdma_inuse[0]);
2666	}
2667}
2668
2669/*
2670 * Copy the ssid string contents into buf, truncating to fit.  If the
2671 * ssid is entirely printable then just copy intact.  Otherwise convert
2672 * to hexadecimal.  If the result is truncated then replace the last
2673 * three characters with "...".
2674 */
2675static int
2676copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
2677{
2678	const u_int8_t *p;
2679	size_t maxlen;
2680	int i;
2681
2682	if (essid_len > bufsize)
2683		maxlen = bufsize;
2684	else
2685		maxlen = essid_len;
2686	/* determine printable or not */
2687	for (i = 0, p = essid; i < maxlen; i++, p++) {
2688		if (*p < ' ' || *p > 0x7e)
2689			break;
2690	}
2691	if (i != maxlen) {		/* not printable, print as hex */
2692		if (bufsize < 3)
2693			return 0;
2694		strlcpy(buf, "0x", bufsize);
2695		bufsize -= 2;
2696		p = essid;
2697		for (i = 0; i < maxlen && bufsize >= 2; i++) {
2698			sprintf(&buf[2+2*i], "%02x", p[i]);
2699			bufsize -= 2;
2700		}
2701		if (i != essid_len)
2702			memcpy(&buf[2+2*i-3], "...", 3);
2703	} else {			/* printable, truncate as needed */
2704		memcpy(buf, essid, maxlen);
2705		if (maxlen != essid_len)
2706			memcpy(&buf[maxlen-3], "...", 3);
2707	}
2708	return maxlen;
2709}
2710
2711static void
2712printssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2713{
2714	char ssid[2*IEEE80211_NWID_LEN+1];
2715
2716	printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid);
2717}
2718
2719static void
2720printrates(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2721{
2722	const char *sep;
2723	int i;
2724
2725	printf("%s", tag);
2726	sep = "<";
2727	for (i = 2; i < ielen; i++) {
2728		printf("%s%s%d", sep,
2729		    ie[i] & IEEE80211_RATE_BASIC ? "B" : "",
2730		    ie[i] & IEEE80211_RATE_VAL);
2731		sep = ",";
2732	}
2733	printf(">");
2734}
2735
2736static void
2737printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2738{
2739	const struct ieee80211_country_ie *cie =
2740	   (const struct ieee80211_country_ie *) ie;
2741	int i, nbands, schan, nchan;
2742
2743	printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]);
2744	nbands = (cie->len - 3) / sizeof(cie->band[0]);
2745	for (i = 0; i < nbands; i++) {
2746		schan = cie->band[i].schan;
2747		nchan = cie->band[i].nchan;
2748		if (nchan != 1)
2749			printf(" %u-%u,%u", schan, schan + nchan-1,
2750			    cie->band[i].maxtxpwr);
2751		else
2752			printf(" %u,%u", schan, cie->band[i].maxtxpwr);
2753	}
2754	printf(">");
2755}
2756
2757/* unaligned little endian access */
2758#define LE_READ_4(p)					\
2759	((u_int32_t)					\
2760	 ((((const u_int8_t *)(p))[0]      ) |		\
2761	  (((const u_int8_t *)(p))[1] <<  8) |		\
2762	  (((const u_int8_t *)(p))[2] << 16) |		\
2763	  (((const u_int8_t *)(p))[3] << 24)))
2764
2765static __inline int
2766iswpaoui(const u_int8_t *frm)
2767{
2768	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
2769}
2770
2771static __inline int
2772iswmeinfo(const u_int8_t *frm)
2773{
2774	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
2775		frm[6] == WME_INFO_OUI_SUBTYPE;
2776}
2777
2778static __inline int
2779iswmeparam(const u_int8_t *frm)
2780{
2781	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
2782		frm[6] == WME_PARAM_OUI_SUBTYPE;
2783}
2784
2785static __inline int
2786isatherosoui(const u_int8_t *frm)
2787{
2788	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
2789}
2790
2791static __inline int
2792istdmaoui(const uint8_t *frm)
2793{
2794	return frm[1] > 3 && LE_READ_4(frm+2) == ((TDMA_OUI_TYPE<<24)|TDMA_OUI);
2795}
2796
2797static __inline int
2798iswpsoui(const uint8_t *frm)
2799{
2800	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPS_OUI_TYPE<<24)|WPA_OUI);
2801}
2802
2803static const char *
2804iename(int elemid)
2805{
2806	switch (elemid) {
2807	case IEEE80211_ELEMID_FHPARMS:	return " FHPARMS";
2808	case IEEE80211_ELEMID_CFPARMS:	return " CFPARMS";
2809	case IEEE80211_ELEMID_TIM:	return " TIM";
2810	case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS";
2811	case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE";
2812	case IEEE80211_ELEMID_PWRCNSTR:	return " PWRCNSTR";
2813	case IEEE80211_ELEMID_PWRCAP:	return " PWRCAP";
2814	case IEEE80211_ELEMID_TPCREQ:	return " TPCREQ";
2815	case IEEE80211_ELEMID_TPCREP:	return " TPCREP";
2816	case IEEE80211_ELEMID_SUPPCHAN:	return " SUPPCHAN";
2817	case IEEE80211_ELEMID_CHANSWITCHANN:return " CSA";
2818	case IEEE80211_ELEMID_MEASREQ:	return " MEASREQ";
2819	case IEEE80211_ELEMID_MEASREP:	return " MEASREP";
2820	case IEEE80211_ELEMID_QUIET:	return " QUIET";
2821	case IEEE80211_ELEMID_IBSSDFS:	return " IBSSDFS";
2822	case IEEE80211_ELEMID_TPC:	return " TPC";
2823	case IEEE80211_ELEMID_CCKM:	return " CCKM";
2824	}
2825	return " ???";
2826}
2827
2828static void
2829printies(const u_int8_t *vp, int ielen, int maxcols)
2830{
2831	while (ielen > 0) {
2832		switch (vp[0]) {
2833		case IEEE80211_ELEMID_SSID:
2834			if (verbose)
2835				printssid(" SSID", vp, 2+vp[1], maxcols);
2836			break;
2837		case IEEE80211_ELEMID_RATES:
2838		case IEEE80211_ELEMID_XRATES:
2839			if (verbose)
2840				printrates(vp[0] == IEEE80211_ELEMID_RATES ?
2841				    " RATES" : " XRATES", vp, 2+vp[1], maxcols);
2842			break;
2843		case IEEE80211_ELEMID_DSPARMS:
2844			if (verbose)
2845				printf(" DSPARMS<%u>", vp[2]);
2846			break;
2847		case IEEE80211_ELEMID_COUNTRY:
2848			if (verbose)
2849				printcountry(" COUNTRY", vp, 2+vp[1], maxcols);
2850			break;
2851		case IEEE80211_ELEMID_ERP:
2852			if (verbose)
2853				printf(" ERP<0x%x>", vp[2]);
2854			break;
2855		case IEEE80211_ELEMID_VENDOR:
2856			if (iswpaoui(vp))
2857				printwpaie(" WPA", vp, 2+vp[1], maxcols);
2858			else if (iswmeinfo(vp))
2859				printwmeinfo(" WME", vp, 2+vp[1], maxcols);
2860			else if (iswmeparam(vp))
2861				printwmeparam(" WME", vp, 2+vp[1], maxcols);
2862			else if (isatherosoui(vp))
2863				printathie(" ATH", vp, 2+vp[1], maxcols);
2864			else if (iswpsoui(vp))
2865				printwpsie(" WPS", vp, 2+vp[1], maxcols);
2866			else if (istdmaoui(vp))
2867				printtdmaie(" TDMA", vp, 2+vp[1], maxcols);
2868			else if (verbose)
2869				printie(" VEN", vp, 2+vp[1], maxcols);
2870			break;
2871		case IEEE80211_ELEMID_RSN:
2872			printrsnie(" RSN", vp, 2+vp[1], maxcols);
2873			break;
2874		case IEEE80211_ELEMID_HTCAP:
2875			printhtcap(" HTCAP", vp, 2+vp[1], maxcols);
2876			break;
2877		case IEEE80211_ELEMID_HTINFO:
2878			if (verbose)
2879				printhtinfo(" HTINFO", vp, 2+vp[1], maxcols);
2880			break;
2881		default:
2882			if (verbose)
2883				printie(iename(vp[0]), vp, 2+vp[1], maxcols);
2884			break;
2885		}
2886		ielen -= 2+vp[1];
2887		vp += 2+vp[1];
2888	}
2889}
2890
2891static void
2892printmimo(const struct ieee80211_mimo_info *mi)
2893{
2894	/* NB: don't muddy display unless there's something to show */
2895	if (mi->rssi[0] != 0 || mi->rssi[1] != 0 || mi->rssi[2] != 0) {
2896		/* XXX ignore EVM for now */
2897		printf(" (rssi %d:%d:%d nf %d:%d:%d)",
2898		    mi->rssi[0], mi->rssi[1], mi->rssi[2],
2899		    mi->noise[0], mi->noise[1], mi->noise[2]);
2900	}
2901}
2902
2903static void
2904list_scan(int s)
2905{
2906	uint8_t buf[24*1024];
2907	char ssid[IEEE80211_NWID_LEN+1];
2908	const uint8_t *cp;
2909	int len, ssidmax;
2910
2911	if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0)
2912		errx(1, "unable to get scan results");
2913	if (len < sizeof(struct ieee80211req_scan_result))
2914		return;
2915
2916	getchaninfo(s);
2917
2918	ssidmax = verbose ? IEEE80211_NWID_LEN : 14;
2919	printf("%-*.*s  %-17.17s  %4s %4s  %-7s  %3s %4s\n"
2920		, ssidmax, ssidmax, "SSID"
2921		, "BSSID"
2922		, "CHAN"
2923		, "RATE"
2924		, " S:N"
2925		, "INT"
2926		, "CAPS"
2927	);
2928	cp = buf;
2929	do {
2930		const struct ieee80211req_scan_result *sr;
2931		const uint8_t *vp;
2932
2933		sr = (const struct ieee80211req_scan_result *) cp;
2934		vp = cp + sr->isr_ie_off;
2935		printf("%-*.*s  %s  %3d  %3dM %3d:%-3d  %3d %-4.4s"
2936			, ssidmax
2937			  , copy_essid(ssid, ssidmax, vp, sr->isr_ssid_len)
2938			  , ssid
2939			, ether_ntoa((const struct ether_addr *) sr->isr_bssid)
2940			, ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
2941			, getmaxrate(sr->isr_rates, sr->isr_nrates)
2942			, (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise
2943			, sr->isr_intval
2944			, getcaps(sr->isr_capinfo)
2945		);
2946		printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);
2947		printf("\n");
2948		cp += sr->isr_len, len -= sr->isr_len;
2949	} while (len >= sizeof(struct ieee80211req_scan_result));
2950}
2951
2952#ifdef __FreeBSD__
2953#include <net80211/ieee80211_freebsd.h>
2954#endif
2955#ifdef __NetBSD__
2956#include <net80211/ieee80211_netbsd.h>
2957#endif
2958
2959static void
2960scan_and_wait(int s)
2961{
2962	struct ieee80211_scan_req sr;
2963	struct ieee80211req ireq;
2964	int sroute;
2965
2966	sroute = socket(PF_ROUTE, SOCK_RAW, 0);
2967	if (sroute < 0) {
2968		perror("socket(PF_ROUTE,SOCK_RAW)");
2969		return;
2970	}
2971	(void) memset(&ireq, 0, sizeof(ireq));
2972	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2973	ireq.i_type = IEEE80211_IOC_SCAN_REQ;
2974
2975	memset(&sr, 0, sizeof(sr));
2976	sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
2977		    | IEEE80211_IOC_SCAN_NOPICK
2978		    | IEEE80211_IOC_SCAN_ONCE;
2979	sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
2980	sr.sr_nssid = 0;
2981
2982	ireq.i_data = &sr;
2983	ireq.i_len = sizeof(sr);
2984	/* NB: only root can trigger a scan so ignore errors */
2985	if (ioctl(s, SIOCS80211, &ireq) >= 0) {
2986		char buf[2048];
2987		struct if_announcemsghdr *ifan;
2988		struct rt_msghdr *rtm;
2989
2990		do {
2991			if (read(sroute, buf, sizeof(buf)) < 0) {
2992				perror("read(PF_ROUTE)");
2993				break;
2994			}
2995			rtm = (struct rt_msghdr *) buf;
2996			if (rtm->rtm_version != RTM_VERSION)
2997				break;
2998			ifan = (struct if_announcemsghdr *) rtm;
2999		} while (rtm->rtm_type != RTM_IEEE80211 ||
3000		    ifan->ifan_what != RTM_IEEE80211_SCAN);
3001	}
3002	close(sroute);
3003}
3004
3005static
3006DECL_CMD_FUNC(set80211scan, val, d)
3007{
3008	scan_and_wait(s);
3009	list_scan(s);
3010}
3011
3012static enum ieee80211_opmode get80211opmode(int s);
3013
3014static int
3015gettxseq(const struct ieee80211req_sta_info *si)
3016{
3017#define	IEEE80211_NODE_QOS	0x0002		/* QoS enabled */
3018
3019	int i, txseq;
3020
3021	if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3022		return si->isi_txseqs[0];
3023	/* XXX not right but usually what folks want */
3024	txseq = 0;
3025	for (i = 0; i < IEEE80211_TID_SIZE; i++)
3026		if (si->isi_txseqs[i] > txseq)
3027			txseq = si->isi_txseqs[i];
3028	return txseq;
3029#undef IEEE80211_NODE_QOS
3030}
3031
3032static int
3033getrxseq(const struct ieee80211req_sta_info *si)
3034{
3035#define	IEEE80211_NODE_QOS	0x0002		/* QoS enabled */
3036
3037	int i, rxseq;
3038
3039	if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3040		return si->isi_rxseqs[0];
3041	/* XXX not right but usually what folks want */
3042	rxseq = 0;
3043	for (i = 0; i < IEEE80211_TID_SIZE; i++)
3044		if (si->isi_rxseqs[i] > rxseq)
3045			rxseq = si->isi_rxseqs[i];
3046	return rxseq;
3047#undef IEEE80211_NODE_QOS
3048}
3049
3050static void
3051list_stations(int s)
3052{
3053	union {
3054		struct ieee80211req_sta_req req;
3055		uint8_t buf[24*1024];
3056	} u;
3057	enum ieee80211_opmode opmode = get80211opmode(s);
3058	const uint8_t *cp;
3059	int len;
3060
3061	/* broadcast address =>'s get all stations */
3062	(void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
3063	if (opmode == IEEE80211_M_STA) {
3064		/*
3065		 * Get information about the associated AP.
3066		 */
3067		(void) get80211(s, IEEE80211_IOC_BSSID,
3068		    u.req.is_u.macaddr, IEEE80211_ADDR_LEN);
3069	}
3070	if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0)
3071		errx(1, "unable to get station information");
3072	if (len < sizeof(struct ieee80211req_sta_info))
3073		return;
3074
3075	getchaninfo(s);
3076
3077	printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %4s\n"
3078		, "ADDR"
3079		, "AID"
3080		, "CHAN"
3081		, "RATE"
3082		, "RSSI"
3083		, "IDLE"
3084		, "TXSEQ"
3085		, "RXSEQ"
3086		, "CAPS"
3087		, "FLAG"
3088	);
3089	cp = (const uint8_t *) u.req.info;
3090	do {
3091		const struct ieee80211req_sta_info *si;
3092
3093		si = (const struct ieee80211req_sta_info *) cp;
3094		if (si->isi_len < sizeof(*si))
3095			break;
3096		printf("%s %4u %4d %3dM %3.1f %4d %6d %6d %-4.4s %-4.4s"
3097			, ether_ntoa((const struct ether_addr*) si->isi_macaddr)
3098			, IEEE80211_AID(si->isi_associd)
3099			, ieee80211_mhz2ieee(si->isi_freq, si->isi_flags)
3100			, si->isi_txmbps/2
3101			, si->isi_rssi/2.
3102			, si->isi_inact
3103			, gettxseq(si)
3104			, getrxseq(si)
3105			, getcaps(si->isi_capinfo)
3106			, getflags(si->isi_state)
3107		);
3108		printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
3109		printmimo(&si->isi_mimo);
3110		printf("\n");
3111		cp += si->isi_len, len -= si->isi_len;
3112	} while (len >= sizeof(struct ieee80211req_sta_info));
3113}
3114
3115static const char *
3116get_chaninfo(const struct ieee80211_channel *c, int precise,
3117	char buf[], size_t bsize)
3118{
3119	buf[0] = '\0';
3120	if (IEEE80211_IS_CHAN_FHSS(c))
3121		strlcat(buf, " FHSS", bsize);
3122	if (IEEE80211_IS_CHAN_A(c))
3123		strlcat(buf, " 11a", bsize);
3124	else if (IEEE80211_IS_CHAN_ANYG(c))
3125		strlcat(buf, " 11g", bsize);
3126	else if (IEEE80211_IS_CHAN_B(c))
3127		strlcat(buf, " 11b", bsize);
3128	if (IEEE80211_IS_CHAN_HALF(c))
3129		strlcat(buf, "/10Mhz", bsize);
3130	if (IEEE80211_IS_CHAN_QUARTER(c))
3131		strlcat(buf, "/5Mhz", bsize);
3132	if (IEEE80211_IS_CHAN_TURBO(c))
3133		strlcat(buf, " Turbo", bsize);
3134	if (precise) {
3135		if (IEEE80211_IS_CHAN_HT20(c))
3136			strlcat(buf, " ht/20", bsize);
3137		else if (IEEE80211_IS_CHAN_HT40D(c))
3138			strlcat(buf, " ht/40-", bsize);
3139		else if (IEEE80211_IS_CHAN_HT40U(c))
3140			strlcat(buf, " ht/40+", bsize);
3141	} else {
3142		if (IEEE80211_IS_CHAN_HT(c))
3143			strlcat(buf, " ht", bsize);
3144	}
3145	return buf;
3146}
3147
3148static void
3149print_chaninfo(const struct ieee80211_channel *c, int verb)
3150{
3151	char buf[14];
3152
3153	printf("Channel %3u : %u%c Mhz%-14.14s",
3154		ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
3155		IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
3156		get_chaninfo(c, verb, buf, sizeof(buf)));
3157}
3158
3159static int
3160chanpref(const struct ieee80211_channel *c)
3161{
3162	if (IEEE80211_IS_CHAN_HT40(c))
3163		return 40;
3164	if (IEEE80211_IS_CHAN_HT20(c))
3165		return 30;
3166	if (IEEE80211_IS_CHAN_HALF(c))
3167		return 10;
3168	if (IEEE80211_IS_CHAN_QUARTER(c))
3169		return 5;
3170	if (IEEE80211_IS_CHAN_TURBO(c))
3171		return 25;
3172	if (IEEE80211_IS_CHAN_A(c))
3173		return 20;
3174	if (IEEE80211_IS_CHAN_G(c))
3175		return 20;
3176	if (IEEE80211_IS_CHAN_B(c))
3177		return 15;
3178	if (IEEE80211_IS_CHAN_PUREG(c))
3179		return 15;
3180	return 0;
3181}
3182
3183static void
3184print_channels(int s, const struct ieee80211req_chaninfo *chans,
3185	int allchans, int verb)
3186{
3187	struct ieee80211req_chaninfo *achans;
3188	uint8_t reported[IEEE80211_CHAN_BYTES];
3189	const struct ieee80211_channel *c;
3190	int i, half;
3191
3192	achans = malloc(IEEE80211_CHANINFO_SPACE(chans));
3193	if (achans == NULL)
3194		errx(1, "no space for active channel list");
3195	achans->ic_nchans = 0;
3196	memset(reported, 0, sizeof(reported));
3197	if (!allchans) {
3198		struct ieee80211req_chanlist active;
3199
3200		if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0)
3201			errx(1, "unable to get active channel list");
3202		for (i = 0; i < chans->ic_nchans; i++) {
3203			c = &chans->ic_chans[i];
3204			if (!isset(active.ic_channels, c->ic_ieee))
3205				continue;
3206			/*
3207			 * Suppress compatible duplicates unless
3208			 * verbose.  The kernel gives us it's
3209			 * complete channel list which has separate
3210			 * entries for 11g/11b and 11a/turbo.
3211			 */
3212			if (isset(reported, c->ic_ieee) && !verb) {
3213				/* XXX we assume duplicates are adjacent */
3214				achans->ic_chans[achans->ic_nchans-1] = *c;
3215			} else {
3216				achans->ic_chans[achans->ic_nchans++] = *c;
3217				setbit(reported, c->ic_ieee);
3218			}
3219		}
3220	} else {
3221		for (i = 0; i < chans->ic_nchans; i++) {
3222			c = &chans->ic_chans[i];
3223			/* suppress duplicates as above */
3224			if (isset(reported, c->ic_ieee) && !verb) {
3225				/* XXX we assume duplicates are adjacent */
3226				struct ieee80211_channel *a =
3227				    &achans->ic_chans[achans->ic_nchans-1];
3228				if (chanpref(c) > chanpref(a))
3229					*a = *c;
3230			} else {
3231				achans->ic_chans[achans->ic_nchans++] = *c;
3232				setbit(reported, c->ic_ieee);
3233			}
3234		}
3235	}
3236	half = achans->ic_nchans / 2;
3237	if (achans->ic_nchans % 2)
3238		half++;
3239
3240	for (i = 0; i < achans->ic_nchans / 2; i++) {
3241		print_chaninfo(&achans->ic_chans[i], verb);
3242		print_chaninfo(&achans->ic_chans[half+i], verb);
3243		printf("\n");
3244	}
3245	if (achans->ic_nchans % 2) {
3246		print_chaninfo(&achans->ic_chans[i], verb);
3247		printf("\n");
3248	}
3249	free(achans);
3250}
3251
3252static void
3253list_channels(int s, int allchans)
3254{
3255	getchaninfo(s);
3256	print_channels(s, chaninfo, allchans, verbose);
3257}
3258
3259static void
3260print_txpow(const struct ieee80211_channel *c)
3261{
3262	printf("Channel %3u : %u Mhz %3.1f reg %2d  ",
3263	    c->ic_ieee, c->ic_freq,
3264	    c->ic_maxpower/2., c->ic_maxregpower);
3265}
3266
3267static void
3268print_txpow_verbose(const struct ieee80211_channel *c)
3269{
3270	print_chaninfo(c, 1);
3271	printf("min %4.1f dBm  max %3.1f dBm  reg %2d dBm",
3272	    c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
3273	/* indicate where regulatory cap limits power use */
3274	if (c->ic_maxpower > 2*c->ic_maxregpower)
3275		printf(" <");
3276}
3277
3278static void
3279list_txpow(int s)
3280{
3281	struct ieee80211req_chaninfo *achans;
3282	uint8_t reported[IEEE80211_CHAN_BYTES];
3283	struct ieee80211_channel *c, *prev;
3284	int i, half;
3285
3286	getchaninfo(s);
3287	achans = malloc(IEEE80211_CHANINFO_SPACE(chaninfo));
3288	if (achans == NULL)
3289		errx(1, "no space for active channel list");
3290	achans->ic_nchans = 0;
3291	memset(reported, 0, sizeof(reported));
3292	for (i = 0; i < chaninfo->ic_nchans; i++) {
3293		c = &chaninfo->ic_chans[i];
3294		/* suppress duplicates as above */
3295		if (isset(reported, c->ic_ieee) && !verbose) {
3296			/* XXX we assume duplicates are adjacent */
3297			prev = &achans->ic_chans[achans->ic_nchans-1];
3298			/* display highest power on channel */
3299			if (c->ic_maxpower > prev->ic_maxpower)
3300				*prev = *c;
3301		} else {
3302			achans->ic_chans[achans->ic_nchans++] = *c;
3303			setbit(reported, c->ic_ieee);
3304		}
3305	}
3306	if (!verbose) {
3307		half = achans->ic_nchans / 2;
3308		if (achans->ic_nchans % 2)
3309			half++;
3310
3311		for (i = 0; i < achans->ic_nchans / 2; i++) {
3312			print_txpow(&achans->ic_chans[i]);
3313			print_txpow(&achans->ic_chans[half+i]);
3314			printf("\n");
3315		}
3316		if (achans->ic_nchans % 2) {
3317			print_txpow(&achans->ic_chans[i]);
3318			printf("\n");
3319		}
3320	} else {
3321		for (i = 0; i < achans->ic_nchans; i++) {
3322			print_txpow_verbose(&achans->ic_chans[i]);
3323			printf("\n");
3324		}
3325	}
3326	free(achans);
3327}
3328
3329static void
3330list_keys(int s)
3331{
3332}
3333
3334#define	IEEE80211_C_BITS \
3335	"\20\1STA\7FF\10TURBOP\11IBSS\12PMGT" \
3336	"\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \
3337	"\21MONITOR\22DFS\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
3338	"\37TXFRAG\40TDMA"
3339
3340static void
3341list_capabilities(int s)
3342{
3343	struct ieee80211_devcaps_req *dc;
3344
3345	if (verbose)
3346		dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
3347	else
3348		dc = malloc(IEEE80211_DEVCAPS_SIZE(1));
3349	if (dc == NULL)
3350		errx(1, "no space for device capabilities");
3351	dc->dc_chaninfo.ic_nchans = verbose ? MAXCHAN : 1;
3352	getdevcaps(s, dc);
3353	printb("drivercaps", dc->dc_drivercaps, IEEE80211_C_BITS);
3354	if (dc->dc_cryptocaps != 0 || verbose) {
3355		putchar('\n');
3356		printb("cryptocaps", dc->dc_cryptocaps, IEEE80211_CRYPTO_BITS);
3357	}
3358	if (dc->dc_htcaps != 0 || verbose) {
3359		putchar('\n');
3360		printb("htcaps", dc->dc_htcaps, IEEE80211_HTCAP_BITS);
3361	}
3362	putchar('\n');
3363	if (verbose) {
3364		chaninfo = &dc->dc_chaninfo;	/* XXX */
3365		print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, verbose);
3366	}
3367	free(dc);
3368}
3369
3370static int
3371get80211wme(int s, int param, int ac, int *val)
3372{
3373	struct ieee80211req ireq;
3374
3375	(void) memset(&ireq, 0, sizeof(ireq));
3376	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3377	ireq.i_type = param;
3378	ireq.i_len = ac;
3379	if (ioctl(s, SIOCG80211, &ireq) < 0) {
3380		warn("cannot get WME parameter %d, ac %d%s",
3381		    param, ac & IEEE80211_WMEPARAM_VAL,
3382		    ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : "");
3383		return -1;
3384	}
3385	*val = ireq.i_val;
3386	return 0;
3387}
3388
3389static void
3390list_wme_aci(int s, const char *tag, int ac)
3391{
3392	int val;
3393
3394	printf("\t%s", tag);
3395
3396	/* show WME BSS parameters */
3397	if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1)
3398		printf(" cwmin %2u", val);
3399	if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1)
3400		printf(" cwmax %2u", val);
3401	if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1)
3402		printf(" aifs %2u", val);
3403	if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1)
3404		printf(" txopLimit %3u", val);
3405	if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) {
3406		if (val)
3407			printf(" acm");
3408		else if (verbose)
3409			printf(" -acm");
3410	}
3411	/* !BSS only */
3412	if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
3413		if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) {
3414			if (!val)
3415				printf(" -ack");
3416			else if (verbose)
3417				printf(" ack");
3418		}
3419	}
3420	printf("\n");
3421}
3422
3423static void
3424list_wme(int s)
3425{
3426	static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
3427	int ac;
3428
3429	if (verbose) {
3430		/* display both BSS and local settings */
3431		for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
3432	again:
3433			if (ac & IEEE80211_WMEPARAM_BSS)
3434				list_wme_aci(s, "     ", ac);
3435			else
3436				list_wme_aci(s, acnames[ac], ac);
3437			if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
3438				ac |= IEEE80211_WMEPARAM_BSS;
3439				goto again;
3440			} else
3441				ac &= ~IEEE80211_WMEPARAM_BSS;
3442		}
3443	} else {
3444		/* display only channel settings */
3445		for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++)
3446			list_wme_aci(s, acnames[ac], ac);
3447	}
3448}
3449
3450static void
3451list_roam(int s)
3452{
3453	const struct ieee80211_roamparam *rp;
3454	int mode;
3455
3456	getroam(s);
3457	for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_11NA; mode++) {
3458		rp = &roamparams.params[mode];
3459		if (rp->rssi == 0 && rp->rate == 0)
3460			continue;
3461		if (rp->rssi & 1)
3462			LINE_CHECK("roam:%-6.6s rssi %2u.5dBm rate %2u Mb/s",
3463			    modename[mode], rp->rssi/2, rp->rate/2);
3464		else
3465			LINE_CHECK("roam:%-6.6s rssi %4udBm rate %2u Mb/s",
3466			    modename[mode], rp->rssi/2, rp->rate/2);
3467	}
3468	for (; mode < IEEE80211_MODE_MAX; mode++) {
3469		rp = &roamparams.params[mode];
3470		if (rp->rssi == 0 && rp->rate == 0)
3471			continue;
3472		if (rp->rssi & 1)
3473			LINE_CHECK("roam:%-6.6s rssi %2u.5dBm  MCS %2u    ",
3474			    modename[mode], rp->rssi/2, rp->rate &~ 0x80);
3475		else
3476			LINE_CHECK("roam:%-6.6s rssi %4udBm  MCS %2u    ",
3477			    modename[mode], rp->rssi/2, rp->rate &~ 0x80);
3478	}
3479}
3480
3481static void
3482list_txparams(int s)
3483{
3484	const struct ieee80211_txparam *tp;
3485	int mode;
3486
3487	gettxparams(s);
3488	for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_11NA; mode++) {
3489		tp = &txparams.params[mode];
3490		if (tp->mgmtrate == 0 && tp->mcastrate == 0)
3491			continue;
3492		if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
3493			LINE_CHECK("%-6.6s ucast NONE    mgmt %2u Mb/s "
3494			    "mcast %2u Mb/s maxretry %u",
3495			    modename[mode], tp->mgmtrate/2,
3496			    tp->mcastrate/2, tp->maxretry);
3497		else
3498			LINE_CHECK("%-6.6s ucast %2u Mb/s mgmt %2u Mb/s "
3499			    "mcast %2u Mb/s maxretry %u",
3500			    modename[mode], tp->ucastrate/2, tp->mgmtrate/2,
3501			    tp->mcastrate/2, tp->maxretry);
3502	}
3503	for (; mode < IEEE80211_MODE_MAX; mode++) {
3504		tp = &txparams.params[mode];
3505		if (tp->mgmtrate == 0 && tp->mcastrate == 0)
3506			continue;
3507		if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
3508			LINE_CHECK("%-6.6s ucast NONE    mgmt %2u MCS  "
3509			    "mcast %2u MCS  maxretry %u",
3510			    modename[mode], tp->mgmtrate &~ 0x80,
3511			    tp->mcastrate &~ 0x80, tp->maxretry);
3512		else
3513			LINE_CHECK("%-6.6s ucast %2u MCS  mgmt %2u MCS  "
3514			    "mcast %2u MCS  maxretry %u",
3515			    modename[mode], tp->ucastrate &~ 0x80,
3516			    tp->mgmtrate &~ 0x80,
3517			    tp->mcastrate &~ 0x80, tp->maxretry);
3518	}
3519}
3520
3521static void
3522printpolicy(int policy)
3523{
3524	switch (policy) {
3525	case IEEE80211_MACCMD_POLICY_OPEN:
3526		printf("policy: open\n");
3527		break;
3528	case IEEE80211_MACCMD_POLICY_ALLOW:
3529		printf("policy: allow\n");
3530		break;
3531	case IEEE80211_MACCMD_POLICY_DENY:
3532		printf("policy: deny\n");
3533		break;
3534	case IEEE80211_MACCMD_POLICY_RADIUS:
3535		printf("policy: radius\n");
3536		break;
3537	default:
3538		printf("policy: unknown (%u)\n", policy);
3539		break;
3540	}
3541}
3542
3543static void
3544list_mac(int s)
3545{
3546	struct ieee80211req ireq;
3547	struct ieee80211req_maclist *acllist;
3548	int i, nacls, policy, len;
3549	uint8_t *data;
3550	char c;
3551
3552	(void) memset(&ireq, 0, sizeof(ireq));
3553	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
3554	ireq.i_type = IEEE80211_IOC_MACCMD;
3555	ireq.i_val = IEEE80211_MACCMD_POLICY;
3556	if (ioctl(s, SIOCG80211, &ireq) < 0) {
3557		if (errno == EINVAL) {
3558			printf("No acl policy loaded\n");
3559			return;
3560		}
3561		err(1, "unable to get mac policy");
3562	}
3563	policy = ireq.i_val;
3564	if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
3565		c = '*';
3566	} else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
3567		c = '+';
3568	} else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
3569		c = '-';
3570	} else if (policy == IEEE80211_MACCMD_POLICY_RADIUS) {
3571		c = 'r';		/* NB: should never have entries */
3572	} else {
3573		printf("policy: unknown (%u)\n", policy);
3574		c = '?';
3575	}
3576	if (verbose || c == '?')
3577		printpolicy(policy);
3578
3579	ireq.i_val = IEEE80211_MACCMD_LIST;
3580	ireq.i_len = 0;
3581	if (ioctl(s, SIOCG80211, &ireq) < 0)
3582		err(1, "unable to get mac acl list size");
3583	if (ireq.i_len == 0) {		/* NB: no acls */
3584		if (!(verbose || c == '?'))
3585			printpolicy(policy);
3586		return;
3587	}
3588	len = ireq.i_len;
3589
3590	data = malloc(len);
3591	if (data == NULL)
3592		err(1, "out of memory for acl list");
3593
3594	ireq.i_data = data;
3595	if (ioctl(s, SIOCG80211, &ireq) < 0)
3596		err(1, "unable to get mac acl list");
3597	nacls = len / sizeof(*acllist);
3598	acllist = (struct ieee80211req_maclist *) data;
3599	for (i = 0; i < nacls; i++)
3600		printf("%c%s\n", c, ether_ntoa(
3601			(const struct ether_addr *) acllist[i].ml_macaddr));
3602	free(data);
3603}
3604
3605static void
3606print_regdomain(const struct ieee80211_regdomain *reg, int verb)
3607{
3608	if ((reg->regdomain != 0 &&
3609	    reg->regdomain != reg->country) || verb) {
3610		const struct regdomain *rd =
3611		    lib80211_regdomain_findbysku(getregdata(), reg->regdomain);
3612		if (rd == NULL)
3613			LINE_CHECK("regdomain %d", reg->regdomain);
3614		else
3615			LINE_CHECK("regdomain %s", rd->name);
3616	}
3617	if (reg->country != 0 || verb) {
3618		const struct country *cc =
3619		    lib80211_country_findbycc(getregdata(), reg->country);
3620		if (cc == NULL)
3621			LINE_CHECK("country %d", reg->country);
3622		else
3623			LINE_CHECK("country %s", cc->isoname);
3624	}
3625	if (reg->location == 'I')
3626		LINE_CHECK("indoor");
3627	else if (reg->location == 'O')
3628		LINE_CHECK("outdoor");
3629	else if (verb)
3630		LINE_CHECK("anywhere");
3631	if (reg->ecm)
3632		LINE_CHECK("ecm");
3633	else if (verb)
3634		LINE_CHECK("-ecm");
3635}
3636
3637static void
3638list_regdomain(int s, int channelsalso)
3639{
3640	getregdomain(s);
3641	if (channelsalso) {
3642		getchaninfo(s);
3643		spacer = ':';
3644		print_regdomain(&regdomain, 1);
3645		LINE_BREAK();
3646		print_channels(s, chaninfo, 1/*allchans*/, 1/*verbose*/);
3647	} else
3648		print_regdomain(&regdomain, verbose);
3649}
3650
3651static
3652DECL_CMD_FUNC(set80211list, arg, d)
3653{
3654#define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
3655
3656	LINE_INIT('\t');
3657
3658	if (iseq(arg, "sta"))
3659		list_stations(s);
3660	else if (iseq(arg, "scan") || iseq(arg, "ap"))
3661		list_scan(s);
3662	else if (iseq(arg, "chan") || iseq(arg, "freq"))
3663		list_channels(s, 1);
3664	else if (iseq(arg, "active"))
3665		list_channels(s, 0);
3666	else if (iseq(arg, "keys"))
3667		list_keys(s);
3668	else if (iseq(arg, "caps"))
3669		list_capabilities(s);
3670	else if (iseq(arg, "wme") || iseq(arg, "wmm"))
3671		list_wme(s);
3672	else if (iseq(arg, "mac"))
3673		list_mac(s);
3674	else if (iseq(arg, "txpow"))
3675		list_txpow(s);
3676	else if (iseq(arg, "roam"))
3677		list_roam(s);
3678	else if (iseq(arg, "txparam") || iseq(arg, "txparm"))
3679		list_txparams(s);
3680	else if (iseq(arg, "regdomain"))
3681		list_regdomain(s, 1);
3682	else if (iseq(arg, "countries"))
3683		list_countries();
3684	else
3685		errx(1, "Don't know how to list %s for %s", arg, name);
3686	LINE_BREAK();
3687#undef iseq
3688}
3689
3690static enum ieee80211_opmode
3691get80211opmode(int s)
3692{
3693	struct ifmediareq ifmr;
3694
3695	(void) memset(&ifmr, 0, sizeof(ifmr));
3696	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
3697
3698	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
3699		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
3700			if (ifmr.ifm_current & IFM_FLAG0)
3701				return IEEE80211_M_AHDEMO;
3702			else
3703				return IEEE80211_M_IBSS;
3704		}
3705		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
3706			return IEEE80211_M_HOSTAP;
3707		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
3708			return IEEE80211_M_MONITOR;
3709	}
3710	return IEEE80211_M_STA;
3711}
3712
3713#if 0
3714static void
3715printcipher(int s, struct ieee80211req *ireq, int keylenop)
3716{
3717	switch (ireq->i_val) {
3718	case IEEE80211_CIPHER_WEP:
3719		ireq->i_type = keylenop;
3720		if (ioctl(s, SIOCG80211, ireq) != -1)
3721			printf("WEP-%s",
3722			    ireq->i_len <= 5 ? "40" :
3723			    ireq->i_len <= 13 ? "104" : "128");
3724		else
3725			printf("WEP");
3726		break;
3727	case IEEE80211_CIPHER_TKIP:
3728		printf("TKIP");
3729		break;
3730	case IEEE80211_CIPHER_AES_OCB:
3731		printf("AES-OCB");
3732		break;
3733	case IEEE80211_CIPHER_AES_CCM:
3734		printf("AES-CCM");
3735		break;
3736	case IEEE80211_CIPHER_CKIP:
3737		printf("CKIP");
3738		break;
3739	case IEEE80211_CIPHER_NONE:
3740		printf("NONE");
3741		break;
3742	default:
3743		printf("UNKNOWN (0x%x)", ireq->i_val);
3744		break;
3745	}
3746}
3747#endif
3748
3749static void
3750printkey(const struct ieee80211req_key *ik)
3751{
3752	static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
3753	int keylen = ik->ik_keylen;
3754	int printcontents;
3755
3756	printcontents = printkeys &&
3757		(memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
3758	if (printcontents)
3759		LINE_BREAK();
3760	switch (ik->ik_type) {
3761	case IEEE80211_CIPHER_WEP:
3762		/* compatibility */
3763		LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
3764		    keylen <= 5 ? "40-bit" :
3765		    keylen <= 13 ? "104-bit" : "128-bit");
3766		break;
3767	case IEEE80211_CIPHER_TKIP:
3768		if (keylen > 128/8)
3769			keylen -= 128/8;	/* ignore MIC for now */
3770		LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3771		break;
3772	case IEEE80211_CIPHER_AES_OCB:
3773		LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3774		break;
3775	case IEEE80211_CIPHER_AES_CCM:
3776		LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3777		break;
3778	case IEEE80211_CIPHER_CKIP:
3779		LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3780		break;
3781	case IEEE80211_CIPHER_NONE:
3782		LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3783		break;
3784	default:
3785		LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
3786			ik->ik_type, ik->ik_keyix+1, 8*keylen);
3787		break;
3788	}
3789	if (printcontents) {
3790		int i;
3791
3792		printf(" <");
3793		for (i = 0; i < keylen; i++)
3794			printf("%02x", ik->ik_keydata[i]);
3795		printf(">");
3796		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
3797		    (ik->ik_keyrsc != 0 || verbose))
3798			printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
3799		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
3800		    (ik->ik_keytsc != 0 || verbose))
3801			printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
3802		if (ik->ik_flags != 0 && verbose) {
3803			const char *sep = " ";
3804
3805			if (ik->ik_flags & IEEE80211_KEY_XMIT)
3806				printf("%stx", sep), sep = "+";
3807			if (ik->ik_flags & IEEE80211_KEY_RECV)
3808				printf("%srx", sep), sep = "+";
3809			if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
3810				printf("%sdef", sep), sep = "+";
3811		}
3812		LINE_BREAK();
3813	}
3814}
3815
3816static void
3817printrate(const char *tag, int v, int defrate, int defmcs)
3818{
3819	if (v == 11)
3820		LINE_CHECK("%s 5.5", tag);
3821	else if (v & 0x80) {
3822		if (v != defmcs)
3823			LINE_CHECK("%s %d", tag, v &~ 0x80);
3824	} else {
3825		if (v != defrate)
3826			LINE_CHECK("%s %d", tag, v/2);
3827	}
3828}
3829
3830static int
3831getssid(int s, int ix, void *data, size_t len, int *plen)
3832{
3833	struct ieee80211req ireq;
3834
3835	(void) memset(&ireq, 0, sizeof(ireq));
3836	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3837	ireq.i_type = IEEE80211_IOC_SSID;
3838	ireq.i_val = ix;
3839	ireq.i_data = data;
3840	ireq.i_len = len;
3841	if (ioctl(s, SIOCG80211, &ireq) < 0)
3842		return -1;
3843	*plen = ireq.i_len;
3844	return 0;
3845}
3846
3847static void
3848ieee80211_status(int s)
3849{
3850	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
3851	enum ieee80211_opmode opmode = get80211opmode(s);
3852	int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode;
3853	uint8_t data[32];
3854	const struct ieee80211_channel *c;
3855	const struct ieee80211_roamparam *rp;
3856	const struct ieee80211_txparam *tp;
3857
3858	if (getssid(s, -1, data, sizeof(data), &len) < 0) {
3859		/* If we can't get the SSID, this isn't an 802.11 device. */
3860		return;
3861	}
3862
3863	/*
3864	 * Invalidate cached state so printing status for multiple
3865	 * if's doesn't reuse the first interfaces' cached state.
3866	 */
3867	gotcurchan = 0;
3868	gotroam = 0;
3869	gottxparams = 0;
3870	gothtconf = 0;
3871	gotregdomain = 0;
3872
3873	if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
3874		num = 0;
3875	printf("\tssid ");
3876	if (num > 1) {
3877		for (i = 0; i < num; i++) {
3878			if (getssid(s, i, data, sizeof(data), &len) >= 0 && len > 0) {
3879				printf(" %d:", i + 1);
3880				print_string(data, len);
3881			}
3882		}
3883	} else
3884		print_string(data, len);
3885
3886	c = getcurchan(s);
3887	if (c->ic_freq != IEEE80211_CHAN_ANY) {
3888		char buf[14];
3889		printf(" channel %d (%u Mhz%s)", c->ic_ieee, c->ic_freq,
3890			get_chaninfo(c, 1, buf, sizeof(buf)));
3891	} else if (verbose)
3892		printf(" channel UNDEF");
3893
3894	if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 &&
3895	    (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
3896		printf(" bssid %s", ether_ntoa((struct ether_addr *)data));
3897
3898	if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) {
3899		printf("\n\tstationname ");
3900		print_string(data, len);
3901	}
3902
3903	spacer = ' ';		/* force first break */
3904	LINE_BREAK();
3905
3906	list_regdomain(s, 0);
3907
3908	wpa = 0;
3909	if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) {
3910		switch (val) {
3911		case IEEE80211_AUTH_NONE:
3912			LINE_CHECK("authmode NONE");
3913			break;
3914		case IEEE80211_AUTH_OPEN:
3915			LINE_CHECK("authmode OPEN");
3916			break;
3917		case IEEE80211_AUTH_SHARED:
3918			LINE_CHECK("authmode SHARED");
3919			break;
3920		case IEEE80211_AUTH_8021X:
3921			LINE_CHECK("authmode 802.1x");
3922			break;
3923		case IEEE80211_AUTH_WPA:
3924			if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0)
3925				wpa = 1;	/* default to WPA1 */
3926			switch (wpa) {
3927			case 2:
3928				LINE_CHECK("authmode WPA2/802.11i");
3929				break;
3930			case 3:
3931				LINE_CHECK("authmode WPA1+WPA2/802.11i");
3932				break;
3933			default:
3934				LINE_CHECK("authmode WPA");
3935				break;
3936			}
3937			break;
3938		case IEEE80211_AUTH_AUTO:
3939			LINE_CHECK("authmode AUTO");
3940			break;
3941		default:
3942			LINE_CHECK("authmode UNKNOWN (0x%x)", val);
3943			break;
3944		}
3945	}
3946
3947	if (wpa || verbose) {
3948		if (get80211val(s, IEEE80211_IOC_WPS, &val) != -1) {
3949			if (val)
3950				LINE_CHECK("wps");
3951			else if (verbose)
3952				LINE_CHECK("-wps");
3953		}
3954		if (get80211val(s, IEEE80211_IOC_TSN, &val) != -1) {
3955			if (val)
3956				LINE_CHECK("tsn");
3957			else if (verbose)
3958				LINE_CHECK("-tsn");
3959		}
3960		if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) {
3961			if (val)
3962				LINE_CHECK("countermeasures");
3963			else if (verbose)
3964				LINE_CHECK("-countermeasures");
3965		}
3966#if 0
3967		/* XXX not interesting with WPA done in user space */
3968		ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
3969		if (ioctl(s, SIOCG80211, &ireq) != -1) {
3970		}
3971
3972		ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
3973		if (ioctl(s, SIOCG80211, &ireq) != -1) {
3974			LINE_CHECK("mcastcipher ");
3975			printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
3976			spacer = ' ';
3977		}
3978
3979		ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
3980		if (ioctl(s, SIOCG80211, &ireq) != -1) {
3981			LINE_CHECK("ucastcipher ");
3982			printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
3983		}
3984
3985		if (wpa & 2) {
3986			ireq.i_type = IEEE80211_IOC_RSNCAPS;
3987			if (ioctl(s, SIOCG80211, &ireq) != -1) {
3988				LINE_CHECK("RSN caps 0x%x", ireq.i_val);
3989				spacer = ' ';
3990			}
3991		}
3992
3993		ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
3994		if (ioctl(s, SIOCG80211, &ireq) != -1) {
3995		}
3996#endif
3997	}
3998
3999	if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 &&
4000	    wepmode != IEEE80211_WEP_NOSUP) {
4001		int firstkey;
4002
4003		switch (wepmode) {
4004		case IEEE80211_WEP_OFF:
4005			LINE_CHECK("privacy OFF");
4006			break;
4007		case IEEE80211_WEP_ON:
4008			LINE_CHECK("privacy ON");
4009			break;
4010		case IEEE80211_WEP_MIXED:
4011			LINE_CHECK("privacy MIXED");
4012			break;
4013		default:
4014			LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
4015			break;
4016		}
4017
4018		/*
4019		 * If we get here then we've got WEP support so we need
4020		 * to print WEP status.
4021		 */
4022
4023		if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) {
4024			warn("WEP support, but no tx key!");
4025			goto end;
4026		}
4027		if (val != -1)
4028			LINE_CHECK("deftxkey %d", val+1);
4029		else if (wepmode != IEEE80211_WEP_OFF || verbose)
4030			LINE_CHECK("deftxkey UNDEF");
4031
4032		if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) {
4033			warn("WEP support, but no NUMWEPKEYS support!");
4034			goto end;
4035		}
4036
4037		firstkey = 1;
4038		for (i = 0; i < num; i++) {
4039			struct ieee80211req_key ik;
4040
4041			memset(&ik, 0, sizeof(ik));
4042			ik.ik_keyix = i;
4043			if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) {
4044				warn("WEP support, but can get keys!");
4045				goto end;
4046			}
4047			if (ik.ik_keylen != 0) {
4048				if (verbose)
4049					LINE_BREAK();
4050				printkey(&ik);
4051				firstkey = 0;
4052			}
4053		}
4054end:
4055		;
4056	}
4057
4058	if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 &&
4059	    val != IEEE80211_POWERSAVE_NOSUP ) {
4060		if (val != IEEE80211_POWERSAVE_OFF || verbose) {
4061			switch (val) {
4062			case IEEE80211_POWERSAVE_OFF:
4063				LINE_CHECK("powersavemode OFF");
4064				break;
4065			case IEEE80211_POWERSAVE_CAM:
4066				LINE_CHECK("powersavemode CAM");
4067				break;
4068			case IEEE80211_POWERSAVE_PSP:
4069				LINE_CHECK("powersavemode PSP");
4070				break;
4071			case IEEE80211_POWERSAVE_PSP_CAM:
4072				LINE_CHECK("powersavemode PSP-CAM");
4073				break;
4074			}
4075			if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1)
4076				LINE_CHECK("powersavesleep %d", val);
4077		}
4078	}
4079
4080	if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) {
4081		if (val & 1)
4082			LINE_CHECK("txpower %d.5", val/2);
4083		else
4084			LINE_CHECK("txpower %d", val/2);
4085	}
4086	if (verbose) {
4087		if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1)
4088			LINE_CHECK("txpowmax %.1f", val/2.);
4089	}
4090
4091	if (get80211val(s, IEEE80211_IOC_DOTD, &val) != -1) {
4092		if (val)
4093			LINE_CHECK("dotd");
4094		else if (verbose)
4095			LINE_CHECK("-dotd");
4096	}
4097
4098	if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) {
4099		if (val != IEEE80211_RTS_MAX || verbose)
4100			LINE_CHECK("rtsthreshold %d", val);
4101	}
4102
4103	if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) {
4104		if (val != IEEE80211_FRAG_MAX || verbose)
4105			LINE_CHECK("fragthreshold %d", val);
4106	}
4107	if (opmode == IEEE80211_M_STA || verbose) {
4108		if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) {
4109			if (val != IEEE80211_HWBMISS_MAX || verbose)
4110				LINE_CHECK("bmiss %d", val);
4111		}
4112	}
4113
4114	if (!verbose) {
4115		gettxparams(s);
4116		tp = &txparams.params[chan2mode(c)];
4117		printrate("ucastrate", tp->ucastrate,
4118		    IEEE80211_FIXED_RATE_NONE, IEEE80211_FIXED_RATE_NONE);
4119		printrate("mcastrate", tp->mcastrate, 2*1, 0x80|0);
4120		printrate("mgmtrate", tp->mgmtrate, 2*1, 0x80|0);
4121		if (tp->maxretry != 6)		/* XXX */
4122			LINE_CHECK("maxretry %d", tp->maxretry);
4123	} else {
4124		LINE_BREAK();
4125		list_txparams(s);
4126	}
4127
4128	bgscaninterval = -1;
4129	(void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval);
4130
4131	if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) {
4132		if (val != bgscaninterval || verbose)
4133			LINE_CHECK("scanvalid %u", val);
4134	}
4135
4136	bgscan = 0;
4137	if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) {
4138		if (bgscan)
4139			LINE_CHECK("bgscan");
4140		else if (verbose)
4141			LINE_CHECK("-bgscan");
4142	}
4143	if (bgscan || verbose) {
4144		if (bgscaninterval != -1)
4145			LINE_CHECK("bgscanintvl %u", bgscaninterval);
4146		if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1)
4147			LINE_CHECK("bgscanidle %u", val);
4148		if (!verbose) {
4149			getroam(s);
4150			rp = &roamparams.params[chan2mode(c)];
4151			if (rp->rssi & 1)
4152				LINE_CHECK("roam:rssi %u.5", rp->rssi/2);
4153			else
4154				LINE_CHECK("roam:rssi %u", rp->rssi/2);
4155			LINE_CHECK("roam:rate %u", rp->rate/2);
4156		} else {
4157			LINE_BREAK();
4158			list_roam(s);
4159		}
4160	}
4161
4162	if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
4163		if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) {
4164			if (val)
4165				LINE_CHECK("pureg");
4166			else if (verbose)
4167				LINE_CHECK("-pureg");
4168		}
4169		if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) {
4170			switch (val) {
4171			case IEEE80211_PROTMODE_OFF:
4172				LINE_CHECK("protmode OFF");
4173				break;
4174			case IEEE80211_PROTMODE_CTS:
4175				LINE_CHECK("protmode CTS");
4176				break;
4177			case IEEE80211_PROTMODE_RTSCTS:
4178				LINE_CHECK("protmode RTSCTS");
4179				break;
4180			default:
4181				LINE_CHECK("protmode UNKNOWN (0x%x)", val);
4182				break;
4183			}
4184		}
4185	}
4186
4187	if (IEEE80211_IS_CHAN_HT(c) || verbose) {
4188		gethtconf(s);
4189		switch (htconf & 3) {
4190		case 0:
4191		case 2:
4192			LINE_CHECK("-ht");
4193			break;
4194		case 1:
4195			LINE_CHECK("ht20");
4196			break;
4197		case 3:
4198			if (verbose)
4199				LINE_CHECK("ht");
4200			break;
4201		}
4202		if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) {
4203			if (!val)
4204				LINE_CHECK("-htcompat");
4205			else if (verbose)
4206				LINE_CHECK("htcompat");
4207		}
4208		if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) {
4209			switch (val) {
4210			case 0:
4211				LINE_CHECK("-ampdu");
4212				break;
4213			case 1:
4214				LINE_CHECK("ampdutx -ampdurx");
4215				break;
4216			case 2:
4217				LINE_CHECK("-ampdutx ampdurx");
4218				break;
4219			case 3:
4220				if (verbose)
4221					LINE_CHECK("ampdu");
4222				break;
4223			}
4224		}
4225		if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) {
4226			switch (val) {
4227			case IEEE80211_HTCAP_MAXRXAMPDU_8K:
4228				LINE_CHECK("ampdulimit 8k");
4229				break;
4230			case IEEE80211_HTCAP_MAXRXAMPDU_16K:
4231				LINE_CHECK("ampdulimit 16k");
4232				break;
4233			case IEEE80211_HTCAP_MAXRXAMPDU_32K:
4234				LINE_CHECK("ampdulimit 32k");
4235				break;
4236			case IEEE80211_HTCAP_MAXRXAMPDU_64K:
4237				LINE_CHECK("ampdulimit 64k");
4238				break;
4239			}
4240		}
4241		if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) {
4242			switch (val) {
4243			case IEEE80211_HTCAP_MPDUDENSITY_NA:
4244				if (verbose)
4245					LINE_CHECK("ampdudensity NA");
4246				break;
4247			case IEEE80211_HTCAP_MPDUDENSITY_025:
4248				LINE_CHECK("ampdudensity .25");
4249				break;
4250			case IEEE80211_HTCAP_MPDUDENSITY_05:
4251				LINE_CHECK("ampdudensity .5");
4252				break;
4253			case IEEE80211_HTCAP_MPDUDENSITY_1:
4254				LINE_CHECK("ampdudensity 1");
4255				break;
4256			case IEEE80211_HTCAP_MPDUDENSITY_2:
4257				LINE_CHECK("ampdudensity 2");
4258				break;
4259			case IEEE80211_HTCAP_MPDUDENSITY_4:
4260				LINE_CHECK("ampdudensity 4");
4261				break;
4262			case IEEE80211_HTCAP_MPDUDENSITY_8:
4263				LINE_CHECK("ampdudensity 8");
4264				break;
4265			case IEEE80211_HTCAP_MPDUDENSITY_16:
4266				LINE_CHECK("ampdudensity 16");
4267				break;
4268			}
4269		}
4270		if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) {
4271			switch (val) {
4272			case 0:
4273				LINE_CHECK("-amsdu");
4274				break;
4275			case 1:
4276				LINE_CHECK("amsdutx -amsdurx");
4277				break;
4278			case 2:
4279				LINE_CHECK("-amsdutx amsdurx");
4280				break;
4281			case 3:
4282				if (verbose)
4283					LINE_CHECK("amsdu");
4284				break;
4285			}
4286		}
4287		/* XXX amsdu limit */
4288		if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) {
4289			if (val)
4290				LINE_CHECK("shortgi");
4291			else if (verbose)
4292				LINE_CHECK("-shortgi");
4293		}
4294		if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) {
4295			if (val == IEEE80211_PROTMODE_OFF)
4296				LINE_CHECK("htprotmode OFF");
4297			else if (val != IEEE80211_PROTMODE_RTSCTS)
4298				LINE_CHECK("htprotmode UNKNOWN (0x%x)", val);
4299			else if (verbose)
4300				LINE_CHECK("htprotmode RTSCTS");
4301		}
4302		if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) {
4303			if (val)
4304				LINE_CHECK("puren");
4305			else if (verbose)
4306				LINE_CHECK("-puren");
4307		}
4308		if (get80211val(s, IEEE80211_IOC_SMPS, &val) != -1) {
4309			if (val == IEEE80211_HTCAP_SMPS_DYNAMIC)
4310				LINE_CHECK("smpsdyn");
4311			else if (val == IEEE80211_HTCAP_SMPS_ENA)
4312				LINE_CHECK("smps");
4313			else if (verbose)
4314				LINE_CHECK("-smps");
4315		}
4316		if (get80211val(s, IEEE80211_IOC_RIFS, &val) != -1) {
4317			if (val)
4318				LINE_CHECK("rifs");
4319			else if (verbose)
4320				LINE_CHECK("-rifs");
4321		}
4322	}
4323
4324	if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) {
4325		if (wme)
4326			LINE_CHECK("wme");
4327		else if (verbose)
4328			LINE_CHECK("-wme");
4329	} else
4330		wme = 0;
4331
4332	if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) {
4333		if (val)
4334			LINE_CHECK("burst");
4335		else if (verbose)
4336			LINE_CHECK("-burst");
4337	}
4338
4339	if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) {
4340		if (val)
4341			LINE_CHECK("ff");
4342		else if (verbose)
4343			LINE_CHECK("-ff");
4344	}
4345	if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) {
4346		if (val)
4347			LINE_CHECK("dturbo");
4348		else if (verbose)
4349			LINE_CHECK("-dturbo");
4350	}
4351	if (get80211val(s, IEEE80211_IOC_DWDS, &val) != -1) {
4352		if (val)
4353			LINE_CHECK("dwds");
4354		else if (verbose)
4355			LINE_CHECK("-dwds");
4356	}
4357
4358	if (opmode == IEEE80211_M_HOSTAP) {
4359		if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) {
4360			if (val)
4361				LINE_CHECK("hidessid");
4362			else if (verbose)
4363				LINE_CHECK("-hidessid");
4364		}
4365		if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) {
4366			if (!val)
4367				LINE_CHECK("-apbridge");
4368			else if (verbose)
4369				LINE_CHECK("apbridge");
4370		}
4371		if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1)
4372			LINE_CHECK("dtimperiod %u", val);
4373
4374		if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) {
4375			if (!val)
4376				LINE_CHECK("-doth");
4377			else if (verbose)
4378				LINE_CHECK("doth");
4379		}
4380		if (get80211val(s, IEEE80211_IOC_DFS, &val) != -1) {
4381			if (!val)
4382				LINE_CHECK("-dfs");
4383			else if (verbose)
4384				LINE_CHECK("dfs");
4385		}
4386		if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) {
4387			if (!val)
4388				LINE_CHECK("-inact");
4389			else if (verbose)
4390				LINE_CHECK("inact");
4391		}
4392	} else {
4393		if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) {
4394			if (val != IEEE80211_ROAMING_AUTO || verbose) {
4395				switch (val) {
4396				case IEEE80211_ROAMING_DEVICE:
4397					LINE_CHECK("roaming DEVICE");
4398					break;
4399				case IEEE80211_ROAMING_AUTO:
4400					LINE_CHECK("roaming AUTO");
4401					break;
4402				case IEEE80211_ROAMING_MANUAL:
4403					LINE_CHECK("roaming MANUAL");
4404					break;
4405				default:
4406					LINE_CHECK("roaming UNKNOWN (0x%x)",
4407						val);
4408					break;
4409				}
4410			}
4411		}
4412	}
4413
4414	if (opmode == IEEE80211_M_AHDEMO) {
4415		if (get80211val(s, IEEE80211_IOC_TDMA_SLOT, &val) != -1)
4416			LINE_CHECK("tdmaslot %u", val);
4417		if (get80211val(s, IEEE80211_IOC_TDMA_SLOTCNT, &val) != -1)
4418			LINE_CHECK("tdmaslotcnt %u", val);
4419		if (get80211val(s, IEEE80211_IOC_TDMA_SLOTLEN, &val) != -1)
4420			LINE_CHECK("tdmaslotlen %u", val);
4421		if (get80211val(s, IEEE80211_IOC_TDMA_BINTERVAL, &val) != -1)
4422			LINE_CHECK("tdmabintval %u", val);
4423	} else if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) {
4424		/* XXX default define not visible */
4425		if (val != 100 || verbose)
4426			LINE_CHECK("bintval %u", val);
4427	}
4428
4429	if (wme && verbose) {
4430		LINE_BREAK();
4431		list_wme(s);
4432	}
4433	LINE_BREAK();
4434}
4435
4436static int
4437get80211(int s, int type, void *data, int len)
4438{
4439	struct ieee80211req ireq;
4440
4441	(void) memset(&ireq, 0, sizeof(ireq));
4442	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4443	ireq.i_type = type;
4444	ireq.i_data = data;
4445	ireq.i_len = len;
4446	return ioctl(s, SIOCG80211, &ireq);
4447}
4448
4449static int
4450get80211len(int s, int type, void *data, int len, int *plen)
4451{
4452	struct ieee80211req ireq;
4453
4454	(void) memset(&ireq, 0, sizeof(ireq));
4455	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4456	ireq.i_type = type;
4457	ireq.i_len = len;
4458	assert(ireq.i_len == len);	/* NB: check for 16-bit truncation */
4459	ireq.i_data = data;
4460	if (ioctl(s, SIOCG80211, &ireq) < 0)
4461		return -1;
4462	*plen = ireq.i_len;
4463	return 0;
4464}
4465
4466static int
4467get80211val(int s, int type, int *val)
4468{
4469	struct ieee80211req ireq;
4470
4471	(void) memset(&ireq, 0, sizeof(ireq));
4472	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4473	ireq.i_type = type;
4474	if (ioctl(s, SIOCG80211, &ireq) < 0)
4475		return -1;
4476	*val = ireq.i_val;
4477	return 0;
4478}
4479
4480static void
4481set80211(int s, int type, int val, int len, void *data)
4482{
4483	struct ieee80211req	ireq;
4484
4485	(void) memset(&ireq, 0, sizeof(ireq));
4486	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4487	ireq.i_type = type;
4488	ireq.i_val = val;
4489	ireq.i_len = len;
4490	assert(ireq.i_len == len);	/* NB: check for 16-bit truncation */
4491	ireq.i_data = data;
4492	if (ioctl(s, SIOCS80211, &ireq) < 0)
4493		err(1, "SIOCS80211");
4494}
4495
4496static const char *
4497get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
4498{
4499	int len;
4500	int hexstr;
4501	u_int8_t *p;
4502
4503	len = *lenp;
4504	p = buf;
4505	hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
4506	if (hexstr)
4507		val += 2;
4508	for (;;) {
4509		if (*val == '\0')
4510			break;
4511		if (sep != NULL && strchr(sep, *val) != NULL) {
4512			val++;
4513			break;
4514		}
4515		if (hexstr) {
4516			if (!isxdigit((u_char)val[0])) {
4517				warnx("bad hexadecimal digits");
4518				return NULL;
4519			}
4520			if (!isxdigit((u_char)val[1])) {
4521				warnx("odd count hexadecimal digits");
4522				return NULL;
4523			}
4524		}
4525		if (p >= buf + len) {
4526			if (hexstr)
4527				warnx("hexadecimal digits too long");
4528			else
4529				warnx("string too long");
4530			return NULL;
4531		}
4532		if (hexstr) {
4533#define	tohex(x)	(isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
4534			*p++ = (tohex((u_char)val[0]) << 4) |
4535			    tohex((u_char)val[1]);
4536#undef tohex
4537			val += 2;
4538		} else
4539			*p++ = *val++;
4540	}
4541	len = p - buf;
4542	/* The string "-" is treated as the empty string. */
4543	if (!hexstr && len == 1 && buf[0] == '-') {
4544		len = 0;
4545		memset(buf, 0, *lenp);
4546	} else if (len < *lenp)
4547		memset(p, 0, *lenp - len);
4548	*lenp = len;
4549	return val;
4550}
4551
4552static void
4553print_string(const u_int8_t *buf, int len)
4554{
4555	int i;
4556	int hasspc;
4557
4558	i = 0;
4559	hasspc = 0;
4560	for (; i < len; i++) {
4561		if (!isprint(buf[i]) && buf[i] != '\0')
4562			break;
4563		if (isspace(buf[i]))
4564			hasspc++;
4565	}
4566	if (i == len) {
4567		if (hasspc || len == 0 || buf[0] == '\0')
4568			printf("\"%.*s\"", len, buf);
4569		else
4570			printf("%.*s", len, buf);
4571	} else {
4572		printf("0x");
4573		for (i = 0; i < len; i++)
4574			printf("%02x", buf[i]);
4575	}
4576}
4577
4578/*
4579 * Virtual AP cloning support.
4580 */
4581static struct ieee80211_clone_params params = {
4582	.icp_opmode	= IEEE80211_M_STA,	/* default to station mode */
4583};
4584
4585static void
4586wlan_create(int s, struct ifreq *ifr)
4587{
4588	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
4589
4590	if (params.icp_parent[0] == '\0')
4591		errx(1, "must specify a parent when creating a wlan device");
4592	if (params.icp_opmode == IEEE80211_M_WDS &&
4593	    memcmp(params.icp_bssid, zerobssid, sizeof(zerobssid)) == 0)
4594		errx(1, "no bssid specified for WDS (use wlanbssid)");
4595	ifr->ifr_data = (caddr_t) &params;
4596	if (ioctl(s, SIOCIFCREATE2, ifr) < 0)
4597		err(1, "SIOCIFCREATE2");
4598}
4599
4600static
4601DECL_CMD_FUNC(set80211clone_wlandev, arg, d)
4602{
4603	strlcpy(params.icp_parent, arg, IFNAMSIZ);
4604	clone_setcallback(wlan_create);
4605}
4606
4607static
4608DECL_CMD_FUNC(set80211clone_wlanbssid, arg, d)
4609{
4610	const struct ether_addr *ea;
4611
4612	ea = ether_aton(arg);
4613	if (ea == NULL)
4614		errx(1, "%s: cannot parse bssid", arg);
4615	memcpy(params.icp_bssid, ea->octet, IEEE80211_ADDR_LEN);
4616	clone_setcallback(wlan_create);
4617}
4618
4619static
4620DECL_CMD_FUNC(set80211clone_wlanaddr, arg, d)
4621{
4622	const struct ether_addr *ea;
4623
4624	ea = ether_aton(arg);
4625	if (ea == NULL)
4626		errx(1, "%s: cannot parse addres", arg);
4627	memcpy(params.icp_macaddr, ea->octet, IEEE80211_ADDR_LEN);
4628	params.icp_flags |= IEEE80211_CLONE_MACADDR;
4629	clone_setcallback(wlan_create);
4630}
4631
4632static
4633DECL_CMD_FUNC(set80211clone_wlanmode, arg, d)
4634{
4635#define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
4636	if (iseq(arg, "sta"))
4637		params.icp_opmode = IEEE80211_M_STA;
4638	else if (iseq(arg, "ahdemo") || iseq(arg, "adhoc-demo"))
4639		params.icp_opmode = IEEE80211_M_AHDEMO;
4640	else if (iseq(arg, "ibss") || iseq(arg, "adhoc"))
4641		params.icp_opmode = IEEE80211_M_IBSS;
4642	else if (iseq(arg, "ap") || iseq(arg, "host"))
4643		params.icp_opmode = IEEE80211_M_HOSTAP;
4644	else if (iseq(arg, "wds"))
4645		params.icp_opmode = IEEE80211_M_WDS;
4646	else if (iseq(arg, "monitor"))
4647		params.icp_opmode = IEEE80211_M_MONITOR;
4648	else if (iseq(arg, "tdma")) {
4649		params.icp_opmode = IEEE80211_M_AHDEMO;
4650		params.icp_flags |= IEEE80211_CLONE_TDMA;
4651	} else
4652		errx(1, "Don't know to create %s for %s", arg, name);
4653	clone_setcallback(wlan_create);
4654#undef iseq
4655}
4656
4657static void
4658set80211clone_beacons(const char *val, int d, int s, const struct afswtch *rafp)
4659{
4660	/* NB: inverted sense */
4661	if (d)
4662		params.icp_flags &= ~IEEE80211_CLONE_NOBEACONS;
4663	else
4664		params.icp_flags |= IEEE80211_CLONE_NOBEACONS;
4665	clone_setcallback(wlan_create);
4666}
4667
4668static void
4669set80211clone_bssid(const char *val, int d, int s, const struct afswtch *rafp)
4670{
4671	if (d)
4672		params.icp_flags |= IEEE80211_CLONE_BSSID;
4673	else
4674		params.icp_flags &= ~IEEE80211_CLONE_BSSID;
4675	clone_setcallback(wlan_create);
4676}
4677
4678static void
4679set80211clone_wdslegacy(const char *val, int d, int s, const struct afswtch *rafp)
4680{
4681	if (d)
4682		params.icp_flags |= IEEE80211_CLONE_WDSLEGACY;
4683	else
4684		params.icp_flags &= ~IEEE80211_CLONE_WDSLEGACY;
4685	clone_setcallback(wlan_create);
4686}
4687
4688static struct cmd ieee80211_cmds[] = {
4689	DEF_CMD_ARG("ssid",		set80211ssid),
4690	DEF_CMD_ARG("nwid",		set80211ssid),
4691	DEF_CMD_ARG("stationname",	set80211stationname),
4692	DEF_CMD_ARG("station",		set80211stationname),	/* BSD/OS */
4693	DEF_CMD_ARG("channel",		set80211channel),
4694	DEF_CMD_ARG("authmode",		set80211authmode),
4695	DEF_CMD_ARG("powersavemode",	set80211powersavemode),
4696	DEF_CMD("powersave",	1,	set80211powersave),
4697	DEF_CMD("-powersave",	0,	set80211powersave),
4698	DEF_CMD_ARG("powersavesleep", 	set80211powersavesleep),
4699	DEF_CMD_ARG("wepmode",		set80211wepmode),
4700	DEF_CMD("wep",		1,	set80211wep),
4701	DEF_CMD("-wep",		0,	set80211wep),
4702	DEF_CMD_ARG("deftxkey",		set80211weptxkey),
4703	DEF_CMD_ARG("weptxkey",		set80211weptxkey),
4704	DEF_CMD_ARG("wepkey",		set80211wepkey),
4705	DEF_CMD_ARG("nwkey",		set80211nwkey),		/* NetBSD */
4706	DEF_CMD("-nwkey",	0,	set80211wep),		/* NetBSD */
4707	DEF_CMD_ARG("rtsthreshold",	set80211rtsthreshold),
4708	DEF_CMD_ARG("protmode",		set80211protmode),
4709	DEF_CMD_ARG("txpower",		set80211txpower),
4710	DEF_CMD_ARG("roaming",		set80211roaming),
4711	DEF_CMD("wme",		1,	set80211wme),
4712	DEF_CMD("-wme",		0,	set80211wme),
4713	DEF_CMD("wmm",		1,	set80211wme),
4714	DEF_CMD("-wmm",		0,	set80211wme),
4715	DEF_CMD("hidessid",	1,	set80211hidessid),
4716	DEF_CMD("-hidessid",	0,	set80211hidessid),
4717	DEF_CMD("apbridge",	1,	set80211apbridge),
4718	DEF_CMD("-apbridge",	0,	set80211apbridge),
4719	DEF_CMD_ARG("chanlist",		set80211chanlist),
4720	DEF_CMD_ARG("bssid",		set80211bssid),
4721	DEF_CMD_ARG("ap",		set80211bssid),
4722	DEF_CMD("scan",	0,		set80211scan),
4723	DEF_CMD_ARG("list",		set80211list),
4724	DEF_CMD_ARG2("cwmin",		set80211cwmin),
4725	DEF_CMD_ARG2("cwmax",		set80211cwmax),
4726	DEF_CMD_ARG2("aifs",		set80211aifs),
4727	DEF_CMD_ARG2("txoplimit",	set80211txoplimit),
4728	DEF_CMD_ARG("acm",		set80211acm),
4729	DEF_CMD_ARG("-acm",		set80211noacm),
4730	DEF_CMD_ARG("ack",		set80211ackpolicy),
4731	DEF_CMD_ARG("-ack",		set80211noackpolicy),
4732	DEF_CMD_ARG2("bss:cwmin",	set80211bsscwmin),
4733	DEF_CMD_ARG2("bss:cwmax",	set80211bsscwmax),
4734	DEF_CMD_ARG2("bss:aifs",	set80211bssaifs),
4735	DEF_CMD_ARG2("bss:txoplimit",	set80211bsstxoplimit),
4736	DEF_CMD_ARG("dtimperiod",	set80211dtimperiod),
4737	DEF_CMD_ARG("bintval",		set80211bintval),
4738	DEF_CMD("mac:open",	IEEE80211_MACCMD_POLICY_OPEN,	set80211maccmd),
4739	DEF_CMD("mac:allow",	IEEE80211_MACCMD_POLICY_ALLOW,	set80211maccmd),
4740	DEF_CMD("mac:deny",	IEEE80211_MACCMD_POLICY_DENY,	set80211maccmd),
4741	DEF_CMD("mac:radius",	IEEE80211_MACCMD_POLICY_RADIUS,	set80211maccmd),
4742	DEF_CMD("mac:flush",	IEEE80211_MACCMD_FLUSH,		set80211maccmd),
4743	DEF_CMD("mac:detach",	IEEE80211_MACCMD_DETACH,	set80211maccmd),
4744	DEF_CMD_ARG("mac:add",		set80211addmac),
4745	DEF_CMD_ARG("mac:del",		set80211delmac),
4746	DEF_CMD_ARG("mac:kick",		set80211kickmac),
4747	DEF_CMD("pureg",	1,	set80211pureg),
4748	DEF_CMD("-pureg",	0,	set80211pureg),
4749	DEF_CMD("ff",		1,	set80211fastframes),
4750	DEF_CMD("-ff",		0,	set80211fastframes),
4751	DEF_CMD("dturbo",	1,	set80211dturbo),
4752	DEF_CMD("-dturbo",	0,	set80211dturbo),
4753	DEF_CMD("bgscan",	1,	set80211bgscan),
4754	DEF_CMD("-bgscan",	0,	set80211bgscan),
4755	DEF_CMD_ARG("bgscanidle",	set80211bgscanidle),
4756	DEF_CMD_ARG("bgscanintvl",	set80211bgscanintvl),
4757	DEF_CMD_ARG("scanvalid",	set80211scanvalid),
4758	DEF_CMD_ARG("roam:rssi",	set80211roamrssi),
4759	DEF_CMD_ARG("roam:rate",	set80211roamrate),
4760	DEF_CMD_ARG("mcastrate",	set80211mcastrate),
4761	DEF_CMD_ARG("ucastrate",	set80211ucastrate),
4762	DEF_CMD_ARG("mgtrate",		set80211mgtrate),
4763	DEF_CMD_ARG("mgmtrate",		set80211mgtrate),
4764	DEF_CMD_ARG("maxretry",		set80211maxretry),
4765	DEF_CMD_ARG("fragthreshold",	set80211fragthreshold),
4766	DEF_CMD("burst",	1,	set80211burst),
4767	DEF_CMD("-burst",	0,	set80211burst),
4768	DEF_CMD_ARG("bmiss",		set80211bmissthreshold),
4769	DEF_CMD_ARG("bmissthreshold",	set80211bmissthreshold),
4770	DEF_CMD("shortgi",	1,	set80211shortgi),
4771	DEF_CMD("-shortgi",	0,	set80211shortgi),
4772	DEF_CMD("ampdurx",	2,	set80211ampdu),
4773	DEF_CMD("-ampdurx",	-2,	set80211ampdu),
4774	DEF_CMD("ampdutx",	1,	set80211ampdu),
4775	DEF_CMD("-ampdutx",	-1,	set80211ampdu),
4776	DEF_CMD("ampdu",	3,	set80211ampdu),		/* NB: tx+rx */
4777	DEF_CMD("-ampdu",	-3,	set80211ampdu),
4778	DEF_CMD_ARG("ampdulimit",	set80211ampdulimit),
4779	DEF_CMD_ARG("ampdudensity",	set80211ampdudensity),
4780	DEF_CMD("amsdurx",	2,	set80211amsdu),
4781	DEF_CMD("-amsdurx",	-2,	set80211amsdu),
4782	DEF_CMD("amsdutx",	1,	set80211amsdu),
4783	DEF_CMD("-amsdutx",	-1,	set80211amsdu),
4784	DEF_CMD("amsdu",	3,	set80211amsdu),		/* NB: tx+rx */
4785	DEF_CMD("-amsdu",	-3,	set80211amsdu),
4786	DEF_CMD_ARG("amsdulimit",	set80211amsdulimit),
4787	DEF_CMD("puren",	1,	set80211puren),
4788	DEF_CMD("-puren",	0,	set80211puren),
4789	DEF_CMD("doth",		1,	set80211doth),
4790	DEF_CMD("-doth",	0,	set80211doth),
4791	DEF_CMD("dfs",		1,	set80211dfs),
4792	DEF_CMD("-dfs",		0,	set80211dfs),
4793	DEF_CMD("htcompat",	1,	set80211htcompat),
4794	DEF_CMD("-htcompat",	0,	set80211htcompat),
4795	DEF_CMD("dwds",		1,	set80211dwds),
4796	DEF_CMD("-dwds",	0,	set80211dwds),
4797	DEF_CMD("inact",	1,	set80211inact),
4798	DEF_CMD("-inact",	0,	set80211inact),
4799	DEF_CMD("tsn",		1,	set80211tsn),
4800	DEF_CMD("-tsn",		0,	set80211tsn),
4801	DEF_CMD_ARG("regdomain",	set80211regdomain),
4802	DEF_CMD_ARG("country",		set80211country),
4803	DEF_CMD("indoor",	'I',	set80211location),
4804	DEF_CMD("-indoor",	'O',	set80211location),
4805	DEF_CMD("outdoor",	'O',	set80211location),
4806	DEF_CMD("-outdoor",	'I',	set80211location),
4807	DEF_CMD("anywhere",	' ',	set80211location),
4808	DEF_CMD("ecm",		1,	set80211ecm),
4809	DEF_CMD("-ecm",		0,	set80211ecm),
4810	DEF_CMD("dotd",		1,	set80211dotd),
4811	DEF_CMD("-dotd",	0,	set80211dotd),
4812	DEF_CMD_ARG("htprotmode",	set80211htprotmode),
4813	DEF_CMD("ht20",		1,	set80211htconf),
4814	DEF_CMD("-ht20",	0,	set80211htconf),
4815	DEF_CMD("ht40",		3,	set80211htconf),	/* NB: 20+40 */
4816	DEF_CMD("-ht40",	0,	set80211htconf),
4817	DEF_CMD("ht",		3,	set80211htconf),	/* NB: 20+40 */
4818	DEF_CMD("-ht",		0,	set80211htconf),
4819	DEF_CMD("rifs",		1,	set80211rifs),
4820	DEF_CMD("-rifs",	0,	set80211rifs),
4821	DEF_CMD("smps",		IEEE80211_HTCAP_SMPS_ENA,	set80211smps),
4822	DEF_CMD("smpsdyn",	IEEE80211_HTCAP_SMPS_DYNAMIC,	set80211smps),
4823	DEF_CMD("-smps",	IEEE80211_HTCAP_SMPS_OFF,	set80211smps),
4824	/* XXX for testing */
4825	DEF_CMD_ARG("chanswitch",	set80211chanswitch),
4826
4827	DEF_CMD_ARG("tdmaslot",		set80211tdmaslot),
4828	DEF_CMD_ARG("tdmaslotcnt",	set80211tdmaslotcnt),
4829	DEF_CMD_ARG("tdmaslotlen",	set80211tdmaslotlen),
4830	DEF_CMD_ARG("tdmabintval",	set80211tdmabintval),
4831
4832	/* vap cloning support */
4833	DEF_CLONE_CMD_ARG("wlanaddr",	set80211clone_wlanaddr),
4834	DEF_CLONE_CMD_ARG("wlanbssid",	set80211clone_wlanbssid),
4835	DEF_CLONE_CMD_ARG("wlandev",	set80211clone_wlandev),
4836	DEF_CLONE_CMD_ARG("wlanmode",	set80211clone_wlanmode),
4837	DEF_CLONE_CMD("beacons", 1,	set80211clone_beacons),
4838	DEF_CLONE_CMD("-beacons", 0,	set80211clone_beacons),
4839	DEF_CLONE_CMD("bssid",	1,	set80211clone_bssid),
4840	DEF_CLONE_CMD("-bssid",	0,	set80211clone_bssid),
4841	DEF_CLONE_CMD("wdslegacy", 1,	set80211clone_wdslegacy),
4842	DEF_CLONE_CMD("-wdslegacy", 0,	set80211clone_wdslegacy),
4843};
4844static struct afswtch af_ieee80211 = {
4845	.af_name	= "af_ieee80211",
4846	.af_af		= AF_UNSPEC,
4847	.af_other_status = ieee80211_status,
4848};
4849
4850static __constructor void
4851ieee80211_ctor(void)
4852{
4853#define	N(a)	(sizeof(a) / sizeof(a[0]))
4854	int i;
4855
4856	for (i = 0; i < N(ieee80211_cmds);  i++)
4857		cmd_register(&ieee80211_cmds[i]);
4858	af_register(&af_ieee80211);
4859#undef N
4860}
4861