1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright 2001 The Aerospace Corporation.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of The Aerospace Corporation may not be used to endorse or
15 *    promote products derived from this software.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AEROSPACE CORPORATION BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD$
30 */
31
32/*-
33 * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
34 * All rights reserved.
35 *
36 * This code is derived from software contributed to The NetBSD Foundation
37 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
38 * NASA Ames Research Center.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
42 * are met:
43 * 1. Redistributions of source code must retain the above copyright
44 *    notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 *    notice, this list of conditions and the following disclaimer in the
47 *    documentation and/or other materials provided with the distribution.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
50 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
51 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
52 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
53 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
54 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
55 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
56 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
57 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
58 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
59 * POSSIBILITY OF SUCH DAMAGE.
60 */
61
62#include <sys/param.h>
63#include <sys/ioctl.h>
64#include <sys/socket.h>
65#include <sys/sysctl.h>
66#include <sys/time.h>
67
68#include <net/ethernet.h>
69#include <net/if.h>
70#include <net/if_dl.h>
71#include <net/if_types.h>
72#include <net/if_media.h>
73#include <net/route.h>
74
75#include <net80211/ieee80211_ioctl.h>
76#include <net80211/ieee80211_freebsd.h>
77#include <net80211/ieee80211_superg.h>
78#include <net80211/ieee80211_tdma.h>
79#include <net80211/ieee80211_mesh.h>
80#include <net80211/ieee80211_wps.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#include <locale.h>
95#include <langinfo.h>
96
97#include "ifconfig.h"
98
99#include <lib80211/lib80211_regdomain.h>
100#include <lib80211/lib80211_ioctl.h>
101
102#ifndef IEEE80211_FIXED_RATE_NONE
103#define	IEEE80211_FIXED_RATE_NONE	0xff
104#endif
105
106/* XXX need these publicly defined or similar */
107#ifndef IEEE80211_NODE_AUTH
108#define	IEEE80211_NODE_AUTH	0x000001	/* authorized for data */
109#define	IEEE80211_NODE_QOS	0x000002	/* QoS enabled */
110#define	IEEE80211_NODE_ERP	0x000004	/* ERP enabled */
111#define	IEEE80211_NODE_PWR_MGT	0x000010	/* power save mode enabled */
112#define	IEEE80211_NODE_AREF	0x000020	/* authentication ref held */
113#define	IEEE80211_NODE_HT	0x000040	/* HT enabled */
114#define	IEEE80211_NODE_HTCOMPAT	0x000080	/* HT setup w/ vendor OUI's */
115#define	IEEE80211_NODE_WPS	0x000100	/* WPS association */
116#define	IEEE80211_NODE_TSN	0x000200	/* TSN association */
117#define	IEEE80211_NODE_AMPDU_RX	0x000400	/* AMPDU rx enabled */
118#define	IEEE80211_NODE_AMPDU_TX	0x000800	/* AMPDU tx enabled */
119#define	IEEE80211_NODE_MIMO_PS	0x001000	/* MIMO power save enabled */
120#define	IEEE80211_NODE_MIMO_RTS	0x002000	/* send RTS in MIMO PS */
121#define	IEEE80211_NODE_RIFS	0x004000	/* RIFS enabled */
122#define	IEEE80211_NODE_SGI20	0x008000	/* Short GI in HT20 enabled */
123#define	IEEE80211_NODE_SGI40	0x010000	/* Short GI in HT40 enabled */
124#define	IEEE80211_NODE_ASSOCID	0x020000	/* xmit requires associd */
125#define	IEEE80211_NODE_AMSDU_RX	0x040000	/* AMSDU rx enabled */
126#define	IEEE80211_NODE_AMSDU_TX	0x080000	/* AMSDU tx enabled */
127#define	IEEE80211_NODE_VHT	0x100000	/* VHT enabled */
128#define	IEEE80211_NODE_LDPC	0x200000	/* LDPC enabled */
129#define	IEEE80211_NODE_UAPSD	0x400000	/* UAPSD enabled */
130#endif
131
132/* XXX should also figure out where to put these for k/u-space sharing. */
133#ifndef IEEE80211_FVHT_VHT
134#define	IEEE80211_FVHT_VHT	0x000000001	/* CONF: VHT supported */
135#define	IEEE80211_FVHT_USEVHT40	0x000000002	/* CONF: Use VHT40 */
136#define	IEEE80211_FVHT_USEVHT80	0x000000004	/* CONF: Use VHT80 */
137#define	IEEE80211_FVHT_USEVHT160 0x000000008	/* CONF: Use VHT160 */
138#define	IEEE80211_FVHT_USEVHT80P80 0x000000010	/* CONF: Use VHT 80+80 */
139#endif
140
141/* Helper macros unified. */
142#ifndef	_IEEE80211_MASKSHIFT
143#define	_IEEE80211_MASKSHIFT(_v, _f)	(((_v) & _f) >> _f##_S)
144#endif
145#ifndef	_IEEE80211_SHIFTMASK
146#define	_IEEE80211_SHIFTMASK(_v, _f)	(((_v) << _f##_S) & _f)
147#endif
148
149#define	MAXCHAN	1536		/* max 1.5K channels */
150
151#define	MAXCOL	78
152static	int col;
153static	char spacer;
154
155static void LINE_INIT(char c);
156static void LINE_BREAK(void);
157static void LINE_CHECK(const char *fmt, ...);
158
159static const char *modename[IEEE80211_MODE_MAX] = {
160	[IEEE80211_MODE_AUTO]	  = "auto",
161	[IEEE80211_MODE_11A]	  = "11a",
162	[IEEE80211_MODE_11B]	  = "11b",
163	[IEEE80211_MODE_11G]	  = "11g",
164	[IEEE80211_MODE_FH]	  = "fh",
165	[IEEE80211_MODE_TURBO_A]  = "turboA",
166	[IEEE80211_MODE_TURBO_G]  = "turboG",
167	[IEEE80211_MODE_STURBO_A] = "sturbo",
168	[IEEE80211_MODE_11NA]	  = "11na",
169	[IEEE80211_MODE_11NG]	  = "11ng",
170	[IEEE80211_MODE_HALF]	  = "half",
171	[IEEE80211_MODE_QUARTER]  = "quarter",
172	[IEEE80211_MODE_VHT_2GHZ] = "11acg",
173	[IEEE80211_MODE_VHT_5GHZ] = "11ac",
174};
175
176static void set80211(int s, int type, int val, int len, void *data);
177static int get80211(int s, int type, void *data, int len);
178static int get80211len(int s, int type, void *data, int len, int *plen);
179static int get80211val(int s, int type, int *val);
180static const char *get_string(const char *val, const char *sep,
181    u_int8_t *buf, int *lenp);
182static void print_string(const u_int8_t *buf, int len);
183static void print_regdomain(const struct ieee80211_regdomain *, int);
184static void print_channels(int, const struct ieee80211req_chaninfo *,
185    int allchans, int verbose);
186static void regdomain_makechannels(struct ieee80211_regdomain_req *,
187    const struct ieee80211_devcaps_req *);
188static const char *mesh_linkstate_string(uint8_t state);
189
190static struct ieee80211req_chaninfo *chaninfo;
191static struct ieee80211_regdomain regdomain;
192static int gotregdomain = 0;
193static struct ieee80211_roamparams_req roamparams;
194static int gotroam = 0;
195static struct ieee80211_txparams_req txparams;
196static int gottxparams = 0;
197static struct ieee80211_channel curchan;
198static int gotcurchan = 0;
199static struct ifmediareq *ifmr;
200static int htconf = 0;
201static	int gothtconf = 0;
202
203static void
204gethtconf(int s)
205{
206	if (gothtconf)
207		return;
208	if (get80211val(s, IEEE80211_IOC_HTCONF, &htconf) < 0)
209		warn("unable to get HT configuration information");
210	gothtconf = 1;
211}
212
213/* VHT */
214static int vhtconf = 0;
215static	int gotvhtconf = 0;
216
217static void
218getvhtconf(int s)
219{
220	if (gotvhtconf)
221		return;
222	if (get80211val(s, IEEE80211_IOC_VHTCONF, &vhtconf) < 0)
223		warn("unable to get VHT configuration information");
224	gotvhtconf = 1;
225}
226
227/*
228 * Collect channel info from the kernel.  We use this (mostly)
229 * to handle mapping between frequency and IEEE channel number.
230 */
231static void
232getchaninfo(int s)
233{
234	if (chaninfo != NULL)
235		return;
236	chaninfo = malloc(IEEE80211_CHANINFO_SIZE(MAXCHAN));
237	if (chaninfo == NULL)
238		errx(1, "no space for channel list");
239	if (get80211(s, IEEE80211_IOC_CHANINFO, chaninfo,
240	    IEEE80211_CHANINFO_SIZE(MAXCHAN)) < 0)
241		err(1, "unable to get channel information");
242	ifmr = ifmedia_getstate();
243	gethtconf(s);
244	getvhtconf(s);
245}
246
247static struct regdata *
248getregdata(void)
249{
250	static struct regdata *rdp = NULL;
251	if (rdp == NULL) {
252		rdp = lib80211_alloc_regdata();
253		if (rdp == NULL)
254			errx(-1, "missing or corrupted regdomain database");
255	}
256	return rdp;
257}
258
259/*
260 * Given the channel at index i with attributes from,
261 * check if there is a channel with attributes to in
262 * the channel table.  With suitable attributes this
263 * allows the caller to look for promotion; e.g. from
264 * 11b > 11g.
265 */
266static int
267canpromote(int i, int from, int to)
268{
269	const struct ieee80211_channel *fc = &chaninfo->ic_chans[i];
270	u_int j;
271
272	if ((fc->ic_flags & from) != from)
273		return i;
274	/* NB: quick check exploiting ordering of chans w/ same frequency */
275	if (i+1 < chaninfo->ic_nchans &&
276	    chaninfo->ic_chans[i+1].ic_freq == fc->ic_freq &&
277	    (chaninfo->ic_chans[i+1].ic_flags & to) == to)
278		return i+1;
279	/* brute force search in case channel list is not ordered */
280	for (j = 0; j < chaninfo->ic_nchans; j++) {
281		const struct ieee80211_channel *tc = &chaninfo->ic_chans[j];
282		if (j != i &&
283		    tc->ic_freq == fc->ic_freq && (tc->ic_flags & to) == to)
284		return j;
285	}
286	return i;
287}
288
289/*
290 * Handle channel promotion.  When a channel is specified with
291 * only a frequency we want to promote it to the ``best'' channel
292 * available.  The channel list has separate entries for 11b, 11g,
293 * 11a, and 11n[ga] channels so specifying a frequency w/o any
294 * attributes requires we upgrade, e.g. from 11b -> 11g.  This
295 * gets complicated when the channel is specified on the same
296 * command line with a media request that constrains the available
297 * channe list (e.g. mode 11a); we want to honor that to avoid
298 * confusing behaviour.
299 */
300/*
301 * XXX VHT
302 */
303static int
304promote(int i)
305{
306	/*
307	 * Query the current mode of the interface in case it's
308	 * constrained (e.g. to 11a).  We must do this carefully
309	 * as there may be a pending ifmedia request in which case
310	 * asking the kernel will give us the wrong answer.  This
311	 * is an unfortunate side-effect of the way ifconfig is
312	 * structure for modularity (yech).
313	 *
314	 * NB: ifmr is actually setup in getchaninfo (above); we
315	 *     assume it's called coincident with to this call so
316	 *     we have a ``current setting''; otherwise we must pass
317	 *     the socket descriptor down to here so we can make
318	 *     the ifmedia_getstate call ourselves.
319	 */
320	int chanmode = ifmr != NULL ? IFM_MODE(ifmr->ifm_current) : IFM_AUTO;
321
322	/* when ambiguous promote to ``best'' */
323	/* NB: we abitrarily pick HT40+ over HT40- */
324	if (chanmode != IFM_IEEE80211_11B)
325		i = canpromote(i, IEEE80211_CHAN_B, IEEE80211_CHAN_G);
326	if (chanmode != IFM_IEEE80211_11G && (htconf & 1)) {
327		i = canpromote(i, IEEE80211_CHAN_G,
328			IEEE80211_CHAN_G | IEEE80211_CHAN_HT20);
329		if (htconf & 2) {
330			i = canpromote(i, IEEE80211_CHAN_G,
331				IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D);
332			i = canpromote(i, IEEE80211_CHAN_G,
333				IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U);
334		}
335	}
336	if (chanmode != IFM_IEEE80211_11A && (htconf & 1)) {
337		i = canpromote(i, IEEE80211_CHAN_A,
338			IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
339		if (htconf & 2) {
340			i = canpromote(i, IEEE80211_CHAN_A,
341				IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
342			i = canpromote(i, IEEE80211_CHAN_A,
343				IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
344		}
345	}
346	return i;
347}
348
349static void
350mapfreq(struct ieee80211_channel *chan, int freq, int flags)
351{
352	u_int i;
353
354	for (i = 0; i < chaninfo->ic_nchans; i++) {
355		const struct ieee80211_channel *c = &chaninfo->ic_chans[i];
356
357		if (c->ic_freq == freq && (c->ic_flags & flags) == flags) {
358			if (flags == 0) {
359				/* when ambiguous promote to ``best'' */
360				c = &chaninfo->ic_chans[promote(i)];
361			}
362			*chan = *c;
363			return;
364		}
365	}
366	errx(1, "unknown/undefined frequency %u/0x%x", freq, flags);
367}
368
369static void
370mapchan(struct ieee80211_channel *chan, int ieee, int flags)
371{
372	u_int i;
373
374	for (i = 0; i < chaninfo->ic_nchans; i++) {
375		const struct ieee80211_channel *c = &chaninfo->ic_chans[i];
376
377		if (c->ic_ieee == ieee && (c->ic_flags & flags) == flags) {
378			if (flags == 0) {
379				/* when ambiguous promote to ``best'' */
380				c = &chaninfo->ic_chans[promote(i)];
381			}
382			*chan = *c;
383			return;
384		}
385	}
386	errx(1, "unknown/undefined channel number %d flags 0x%x", ieee, flags);
387}
388
389static const struct ieee80211_channel *
390getcurchan(int s)
391{
392	if (gotcurchan)
393		return &curchan;
394	if (get80211(s, IEEE80211_IOC_CURCHAN, &curchan, sizeof(curchan)) < 0) {
395		int val;
396		/* fall back to legacy ioctl */
397		if (get80211val(s, IEEE80211_IOC_CHANNEL, &val) < 0)
398			err(-1, "cannot figure out current channel");
399		getchaninfo(s);
400		mapchan(&curchan, val, 0);
401	}
402	gotcurchan = 1;
403	return &curchan;
404}
405
406static enum ieee80211_phymode
407chan2mode(const struct ieee80211_channel *c)
408{
409	if (IEEE80211_IS_CHAN_VHTA(c))
410		return IEEE80211_MODE_VHT_5GHZ;
411	if (IEEE80211_IS_CHAN_VHTG(c))
412		return IEEE80211_MODE_VHT_2GHZ;
413	if (IEEE80211_IS_CHAN_HTA(c))
414		return IEEE80211_MODE_11NA;
415	if (IEEE80211_IS_CHAN_HTG(c))
416		return IEEE80211_MODE_11NG;
417	if (IEEE80211_IS_CHAN_108A(c))
418		return IEEE80211_MODE_TURBO_A;
419	if (IEEE80211_IS_CHAN_108G(c))
420		return IEEE80211_MODE_TURBO_G;
421	if (IEEE80211_IS_CHAN_ST(c))
422		return IEEE80211_MODE_STURBO_A;
423	if (IEEE80211_IS_CHAN_FHSS(c))
424		return IEEE80211_MODE_FH;
425	if (IEEE80211_IS_CHAN_HALF(c))
426		return IEEE80211_MODE_HALF;
427	if (IEEE80211_IS_CHAN_QUARTER(c))
428		return IEEE80211_MODE_QUARTER;
429	if (IEEE80211_IS_CHAN_A(c))
430		return IEEE80211_MODE_11A;
431	if (IEEE80211_IS_CHAN_ANYG(c))
432		return IEEE80211_MODE_11G;
433	if (IEEE80211_IS_CHAN_B(c))
434		return IEEE80211_MODE_11B;
435	return IEEE80211_MODE_AUTO;
436}
437
438static void
439getroam(int s)
440{
441	if (gotroam)
442		return;
443	if (get80211(s, IEEE80211_IOC_ROAM,
444	    &roamparams, sizeof(roamparams)) < 0)
445		err(1, "unable to get roaming parameters");
446	gotroam = 1;
447}
448
449static void
450setroam_cb(int s, void *arg)
451{
452	struct ieee80211_roamparams_req *roam = arg;
453	set80211(s, IEEE80211_IOC_ROAM, 0, sizeof(*roam), roam);
454}
455
456static void
457gettxparams(int s)
458{
459	if (gottxparams)
460		return;
461	if (get80211(s, IEEE80211_IOC_TXPARAMS,
462	    &txparams, sizeof(txparams)) < 0)
463		err(1, "unable to get transmit parameters");
464	gottxparams = 1;
465}
466
467static void
468settxparams_cb(int s, void *arg)
469{
470	struct ieee80211_txparams_req *txp = arg;
471	set80211(s, IEEE80211_IOC_TXPARAMS, 0, sizeof(*txp), txp);
472}
473
474static void
475getregdomain(int s)
476{
477	if (gotregdomain)
478		return;
479	if (get80211(s, IEEE80211_IOC_REGDOMAIN,
480	    &regdomain, sizeof(regdomain)) < 0)
481		err(1, "unable to get regulatory domain info");
482	gotregdomain = 1;
483}
484
485static void
486getdevcaps(int s, struct ieee80211_devcaps_req *dc)
487{
488	if (get80211(s, IEEE80211_IOC_DEVCAPS, dc,
489	    IEEE80211_DEVCAPS_SPACE(dc)) < 0)
490		err(1, "unable to get device capabilities");
491}
492
493static void
494setregdomain_cb(int s, void *arg)
495{
496	struct ieee80211_regdomain_req *req;
497	struct ieee80211_regdomain *rd = arg;
498	struct ieee80211_devcaps_req *dc;
499	struct regdata *rdp = getregdata();
500
501	if (rd->country != NO_COUNTRY) {
502		const struct country *cc;
503		/*
504		 * Check current country seting to make sure it's
505		 * compatible with the new regdomain.  If not, then
506		 * override it with any default country for this
507		 * SKU.  If we cannot arrange a match, then abort.
508		 */
509		cc = lib80211_country_findbycc(rdp, rd->country);
510		if (cc == NULL)
511			errx(1, "unknown ISO country code %d", rd->country);
512		if (cc->rd->sku != rd->regdomain) {
513			const struct regdomain *rp;
514			/*
515			 * Check if country is incompatible with regdomain.
516			 * To enable multiple regdomains for a country code
517			 * we permit a mismatch between the regdomain and
518			 * the country's associated regdomain when the
519			 * regdomain is setup w/o a default country.  For
520			 * example, US is bound to the FCC regdomain but
521			 * we allow US to be combined with FCC3 because FCC3
522			 * has not default country.  This allows bogus
523			 * combinations like FCC3+DK which are resolved when
524			 * constructing the channel list by deferring to the
525			 * regdomain to construct the channel list.
526			 */
527			rp = lib80211_regdomain_findbysku(rdp, rd->regdomain);
528			if (rp == NULL)
529				errx(1, "country %s (%s) is not usable with "
530				    "regdomain %d", cc->isoname, cc->name,
531				    rd->regdomain);
532			else if (rp->cc != NULL && rp->cc != cc)
533				errx(1, "country %s (%s) is not usable with "
534				   "regdomain %s", cc->isoname, cc->name,
535				   rp->name);
536		}
537	}
538	/*
539	 * Fetch the device capabilities and calculate the
540	 * full set of netbands for which we request a new
541	 * channel list be constructed.  Once that's done we
542	 * push the regdomain info + channel list to the kernel.
543	 */
544	dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
545	if (dc == NULL)
546		errx(1, "no space for device capabilities");
547	dc->dc_chaninfo.ic_nchans = MAXCHAN;
548	getdevcaps(s, dc);
549#if 0
550	if (verbose) {
551		printf("drivercaps: 0x%x\n", dc->dc_drivercaps);
552		printf("cryptocaps: 0x%x\n", dc->dc_cryptocaps);
553		printf("htcaps    : 0x%x\n", dc->dc_htcaps);
554		printf("vhtcaps   : 0x%x\n", dc->dc_vhtcaps);
555#if 0
556		memcpy(chaninfo, &dc->dc_chaninfo,
557		    IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
558		print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, 1/*verbose*/);
559#endif
560	}
561#endif
562	req = malloc(IEEE80211_REGDOMAIN_SIZE(dc->dc_chaninfo.ic_nchans));
563	if (req == NULL)
564		errx(1, "no space for regdomain request");
565	req->rd = *rd;
566	regdomain_makechannels(req, dc);
567	if (verbose) {
568		LINE_INIT(':');
569		print_regdomain(rd, 1/*verbose*/);
570		LINE_BREAK();
571		/* blech, reallocate channel list for new data */
572		if (chaninfo != NULL)
573			free(chaninfo);
574		chaninfo = malloc(IEEE80211_CHANINFO_SPACE(&req->chaninfo));
575		if (chaninfo == NULL)
576			errx(1, "no space for channel list");
577		memcpy(chaninfo, &req->chaninfo,
578		    IEEE80211_CHANINFO_SPACE(&req->chaninfo));
579		print_channels(s, &req->chaninfo, 1/*allchans*/, 1/*verbose*/);
580	}
581	if (req->chaninfo.ic_nchans == 0)
582		errx(1, "no channels calculated");
583	set80211(s, IEEE80211_IOC_REGDOMAIN, 0,
584	    IEEE80211_REGDOMAIN_SPACE(req), req);
585	free(req);
586	free(dc);
587}
588
589static int
590ieee80211_mhz2ieee(int freq, int flags)
591{
592	struct ieee80211_channel chan;
593	mapfreq(&chan, freq, flags);
594	return chan.ic_ieee;
595}
596
597static int
598isanyarg(const char *arg)
599{
600	return (strncmp(arg, "-", 1) == 0 ||
601	    strncasecmp(arg, "any", 3) == 0 || strncasecmp(arg, "off", 3) == 0);
602}
603
604static void
605set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
606{
607	int		ssid;
608	int		len;
609	u_int8_t	data[IEEE80211_NWID_LEN];
610
611	ssid = 0;
612	len = strlen(val);
613	if (len > 2 && isdigit((int)val[0]) && val[1] == ':') {
614		ssid = atoi(val)-1;
615		val += 2;
616	}
617
618	bzero(data, sizeof(data));
619	len = sizeof(data);
620	if (get_string(val, NULL, data, &len) == NULL)
621		exit(1);
622
623	set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
624}
625
626static void
627set80211meshid(const char *val, int d, int s, const struct afswtch *rafp)
628{
629	int		len;
630	u_int8_t	data[IEEE80211_NWID_LEN];
631
632	memset(data, 0, sizeof(data));
633	len = sizeof(data);
634	if (get_string(val, NULL, data, &len) == NULL)
635		exit(1);
636
637	set80211(s, IEEE80211_IOC_MESH_ID, 0, len, data);
638}
639
640static void
641set80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
642{
643	int			len;
644	u_int8_t		data[33];
645
646	bzero(data, sizeof(data));
647	len = sizeof(data);
648	get_string(val, NULL, data, &len);
649
650	set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
651}
652
653/*
654 * Parse a channel specification for attributes/flags.
655 * The syntax is:
656 *	freq/xx		channel width (5,10,20,40,40+,40-)
657 *	freq:mode	channel mode (a,b,g,h,n,t,s,d)
658 *
659 * These can be combined in either order; e.g. 2437:ng/40.
660 * Modes are case insensitive.
661 *
662 * The result is not validated here; it's assumed to be
663 * checked against the channel table fetched from the kernel.
664 */
665static int
666getchannelflags(const char *val, int freq)
667{
668#define	_CHAN_HT	0x80000000
669	const char *cp;
670	int flags;
671	int is_vht = 0;
672
673	flags = 0;
674
675	cp = strchr(val, ':');
676	if (cp != NULL) {
677		for (cp++; isalpha((int) *cp); cp++) {
678			/* accept mixed case */
679			int c = *cp;
680			if (isupper(c))
681				c = tolower(c);
682			switch (c) {
683			case 'a':		/* 802.11a */
684				flags |= IEEE80211_CHAN_A;
685				break;
686			case 'b':		/* 802.11b */
687				flags |= IEEE80211_CHAN_B;
688				break;
689			case 'g':		/* 802.11g */
690				flags |= IEEE80211_CHAN_G;
691				break;
692			case 'v':		/* vht: 802.11ac */
693				is_vht = 1;
694				/* Fallthrough */
695			case 'h':		/* ht = 802.11n */
696			case 'n':		/* 802.11n */
697				flags |= _CHAN_HT;	/* NB: private */
698				break;
699			case 'd':		/* dt = Atheros Dynamic Turbo */
700				flags |= IEEE80211_CHAN_TURBO;
701				break;
702			case 't':		/* ht, dt, st, t */
703				/* dt and unadorned t specify Dynamic Turbo */
704				if ((flags & (IEEE80211_CHAN_STURBO|_CHAN_HT)) == 0)
705					flags |= IEEE80211_CHAN_TURBO;
706				break;
707			case 's':		/* st = Atheros Static Turbo */
708				flags |= IEEE80211_CHAN_STURBO;
709				break;
710			default:
711				errx(-1, "%s: Invalid channel attribute %c\n",
712				    val, *cp);
713			}
714		}
715	}
716	cp = strchr(val, '/');
717	if (cp != NULL) {
718		char *ep;
719		u_long cw = strtoul(cp+1, &ep, 10);
720
721		switch (cw) {
722		case 5:
723			flags |= IEEE80211_CHAN_QUARTER;
724			break;
725		case 10:
726			flags |= IEEE80211_CHAN_HALF;
727			break;
728		case 20:
729			/* NB: this may be removed below */
730			flags |= IEEE80211_CHAN_HT20;
731			break;
732		case 40:
733		case 80:
734		case 160:
735			/* Handle the 80/160 VHT flag */
736			if (cw == 80)
737				flags |= IEEE80211_CHAN_VHT80;
738			else if (cw == 160)
739				flags |= IEEE80211_CHAN_VHT160;
740
741			/* Fallthrough */
742			if (ep != NULL && *ep == '+')
743				flags |= IEEE80211_CHAN_HT40U;
744			else if (ep != NULL && *ep == '-')
745				flags |= IEEE80211_CHAN_HT40D;
746			break;
747		default:
748			errx(-1, "%s: Invalid channel width\n", val);
749		}
750	}
751
752	/*
753	 * Cleanup specifications.
754	 */
755	if ((flags & _CHAN_HT) == 0) {
756		/*
757		 * If user specified freq/20 or freq/40 quietly remove
758		 * HT cw attributes depending on channel use.  To give
759		 * an explicit 20/40 width for an HT channel you must
760		 * indicate it is an HT channel since all HT channels
761		 * are also usable for legacy operation; e.g. freq:n/40.
762		 */
763		flags &= ~IEEE80211_CHAN_HT;
764		flags &= ~IEEE80211_CHAN_VHT;
765	} else {
766		/*
767		 * Remove private indicator that this is an HT channel
768		 * and if no explicit channel width has been given
769		 * provide the default settings.
770		 */
771		flags &= ~_CHAN_HT;
772		if ((flags & IEEE80211_CHAN_HT) == 0) {
773			struct ieee80211_channel chan;
774			/*
775			 * Consult the channel list to see if we can use
776			 * HT40+ or HT40- (if both the map routines choose).
777			 */
778			if (freq > 255)
779				mapfreq(&chan, freq, 0);
780			else
781				mapchan(&chan, freq, 0);
782			flags |= (chan.ic_flags & IEEE80211_CHAN_HT);
783		}
784
785		/*
786		 * If VHT is enabled, then also set the VHT flag and the
787		 * relevant channel up/down.
788		 */
789		if (is_vht && (flags & IEEE80211_CHAN_HT)) {
790			/*
791			 * XXX yes, maybe we should just have VHT, and reuse
792			 * HT20/HT40U/HT40D
793			 */
794			if (flags & IEEE80211_CHAN_VHT80)
795				;
796			else if (flags & IEEE80211_CHAN_HT20)
797				flags |= IEEE80211_CHAN_VHT20;
798			else if (flags & IEEE80211_CHAN_HT40U)
799				flags |= IEEE80211_CHAN_VHT40U;
800			else if (flags & IEEE80211_CHAN_HT40D)
801				flags |= IEEE80211_CHAN_VHT40D;
802		}
803	}
804	return flags;
805#undef _CHAN_HT
806}
807
808static void
809getchannel(int s, struct ieee80211_channel *chan, const char *val)
810{
811	int v, flags;
812	char *eptr;
813
814	memset(chan, 0, sizeof(*chan));
815	if (isanyarg(val)) {
816		chan->ic_freq = IEEE80211_CHAN_ANY;
817		return;
818	}
819	getchaninfo(s);
820	errno = 0;
821	v = strtol(val, &eptr, 10);
822	if (val[0] == '\0' || val == eptr || errno == ERANGE ||
823	    /* channel may be suffixed with nothing, :flag, or /width */
824	    (eptr[0] != '\0' && eptr[0] != ':' && eptr[0] != '/'))
825		errx(1, "invalid channel specification%s",
826		    errno == ERANGE ? " (out of range)" : "");
827	flags = getchannelflags(val, v);
828	if (v > 255) {		/* treat as frequency */
829		mapfreq(chan, v, flags);
830	} else {
831		mapchan(chan, v, flags);
832	}
833}
834
835static void
836set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
837{
838	struct ieee80211_channel chan;
839
840	getchannel(s, &chan, val);
841	set80211(s, IEEE80211_IOC_CURCHAN, 0, sizeof(chan), &chan);
842}
843
844static void
845set80211chanswitch(const char *val, int d, int s, const struct afswtch *rafp)
846{
847	struct ieee80211_chanswitch_req csr;
848
849	getchannel(s, &csr.csa_chan, val);
850	csr.csa_mode = 1;
851	csr.csa_count = 5;
852	set80211(s, IEEE80211_IOC_CHANSWITCH, 0, sizeof(csr), &csr);
853}
854
855static void
856set80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
857{
858	int	mode;
859
860	if (strcasecmp(val, "none") == 0) {
861		mode = IEEE80211_AUTH_NONE;
862	} else if (strcasecmp(val, "open") == 0) {
863		mode = IEEE80211_AUTH_OPEN;
864	} else if (strcasecmp(val, "shared") == 0) {
865		mode = IEEE80211_AUTH_SHARED;
866	} else if (strcasecmp(val, "8021x") == 0) {
867		mode = IEEE80211_AUTH_8021X;
868	} else if (strcasecmp(val, "wpa") == 0) {
869		mode = IEEE80211_AUTH_WPA;
870	} else {
871		errx(1, "unknown authmode");
872	}
873
874	set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
875}
876
877static void
878set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
879{
880	int	mode;
881
882	if (strcasecmp(val, "off") == 0) {
883		mode = IEEE80211_POWERSAVE_OFF;
884	} else if (strcasecmp(val, "on") == 0) {
885		mode = IEEE80211_POWERSAVE_ON;
886	} else if (strcasecmp(val, "cam") == 0) {
887		mode = IEEE80211_POWERSAVE_CAM;
888	} else if (strcasecmp(val, "psp") == 0) {
889		mode = IEEE80211_POWERSAVE_PSP;
890	} else if (strcasecmp(val, "psp-cam") == 0) {
891		mode = IEEE80211_POWERSAVE_PSP_CAM;
892	} else {
893		errx(1, "unknown powersavemode");
894	}
895
896	set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
897}
898
899static void
900set80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
901{
902	if (d == 0)
903		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
904		    0, NULL);
905	else
906		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
907		    0, NULL);
908}
909
910static void
911set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
912{
913	set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
914}
915
916static void
917set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
918{
919	int	mode;
920
921	if (strcasecmp(val, "off") == 0) {
922		mode = IEEE80211_WEP_OFF;
923	} else if (strcasecmp(val, "on") == 0) {
924		mode = IEEE80211_WEP_ON;
925	} else if (strcasecmp(val, "mixed") == 0) {
926		mode = IEEE80211_WEP_MIXED;
927	} else {
928		errx(1, "unknown wep mode");
929	}
930
931	set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
932}
933
934static void
935set80211wep(const char *val, int d, int s, const struct afswtch *rafp)
936{
937	set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
938}
939
940static int
941isundefarg(const char *arg)
942{
943	return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0);
944}
945
946static void
947set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
948{
949	if (isundefarg(val))
950		set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
951	else
952		set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
953}
954
955static void
956set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
957{
958	int		key = 0;
959	int		len;
960	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
961
962	if (isdigit((int)val[0]) && val[1] == ':') {
963		key = atoi(val)-1;
964		val += 2;
965	}
966
967	bzero(data, sizeof(data));
968	len = sizeof(data);
969	get_string(val, NULL, data, &len);
970
971	set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
972}
973
974/*
975 * This function is purely a NetBSD compatibility interface.  The NetBSD
976 * interface is too inflexible, but it's there so we'll support it since
977 * it's not all that hard.
978 */
979static void
980set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
981{
982	int		txkey;
983	int		i, len;
984	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
985
986	set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
987
988	if (isdigit((int)val[0]) && val[1] == ':') {
989		txkey = val[0]-'0'-1;
990		val += 2;
991
992		for (i = 0; i < 4; i++) {
993			bzero(data, sizeof(data));
994			len = sizeof(data);
995			val = get_string(val, ",", data, &len);
996			if (val == NULL)
997				exit(1);
998
999			set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
1000		}
1001	} else {
1002		bzero(data, sizeof(data));
1003		len = sizeof(data);
1004		get_string(val, NULL, data, &len);
1005		txkey = 0;
1006
1007		set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
1008
1009		bzero(data, sizeof(data));
1010		for (i = 1; i < 4; i++)
1011			set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
1012	}
1013
1014	set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
1015}
1016
1017static void
1018set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
1019{
1020	set80211(s, IEEE80211_IOC_RTSTHRESHOLD,
1021		isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL);
1022}
1023
1024static void
1025set80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
1026{
1027	int	mode;
1028
1029	if (strcasecmp(val, "off") == 0) {
1030		mode = IEEE80211_PROTMODE_OFF;
1031	} else if (strcasecmp(val, "cts") == 0) {
1032		mode = IEEE80211_PROTMODE_CTS;
1033	} else if (strncasecmp(val, "rtscts", 3) == 0) {
1034		mode = IEEE80211_PROTMODE_RTSCTS;
1035	} else {
1036		errx(1, "unknown protection mode");
1037	}
1038
1039	set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
1040}
1041
1042static void
1043set80211htprotmode(const char *val, int d, int s, const struct afswtch *rafp)
1044{
1045	int	mode;
1046
1047	if (strcasecmp(val, "off") == 0) {
1048		mode = IEEE80211_PROTMODE_OFF;
1049	} else if (strncasecmp(val, "rts", 3) == 0) {
1050		mode = IEEE80211_PROTMODE_RTSCTS;
1051	} else {
1052		errx(1, "unknown protection mode");
1053	}
1054
1055	set80211(s, IEEE80211_IOC_HTPROTMODE, mode, 0, NULL);
1056}
1057
1058static void
1059set80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
1060{
1061	double v = atof(val);
1062	int txpow;
1063
1064	txpow = (int) (2*v);
1065	if (txpow != 2*v)
1066		errx(-1, "invalid tx power (must be .5 dBm units)");
1067	set80211(s, IEEE80211_IOC_TXPOWER, txpow, 0, NULL);
1068}
1069
1070#define	IEEE80211_ROAMING_DEVICE	0
1071#define	IEEE80211_ROAMING_AUTO		1
1072#define	IEEE80211_ROAMING_MANUAL	2
1073
1074static void
1075set80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
1076{
1077	int mode;
1078
1079	if (strcasecmp(val, "device") == 0) {
1080		mode = IEEE80211_ROAMING_DEVICE;
1081	} else if (strcasecmp(val, "auto") == 0) {
1082		mode = IEEE80211_ROAMING_AUTO;
1083	} else if (strcasecmp(val, "manual") == 0) {
1084		mode = IEEE80211_ROAMING_MANUAL;
1085	} else {
1086		errx(1, "unknown roaming mode");
1087	}
1088	set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
1089}
1090
1091static void
1092set80211wme(const char *val, int d, int s, const struct afswtch *rafp)
1093{
1094	set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
1095}
1096
1097static void
1098set80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
1099{
1100	set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
1101}
1102
1103static void
1104set80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
1105{
1106	set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
1107}
1108
1109static void
1110set80211fastframes(const char *val, int d, int s, const struct afswtch *rafp)
1111{
1112	set80211(s, IEEE80211_IOC_FF, d, 0, NULL);
1113}
1114
1115static void
1116set80211dturbo(const char *val, int d, int s, const struct afswtch *rafp)
1117{
1118	set80211(s, IEEE80211_IOC_TURBOP, d, 0, NULL);
1119}
1120
1121static void
1122set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
1123{
1124	struct ieee80211req_chanlist chanlist;
1125	char *temp, *cp, *tp;
1126
1127	temp = malloc(strlen(val) + 1);
1128	if (temp == NULL)
1129		errx(1, "malloc failed");
1130	strcpy(temp, val);
1131	memset(&chanlist, 0, sizeof(chanlist));
1132	cp = temp;
1133	for (;;) {
1134		int first, last, f, c;
1135
1136		tp = strchr(cp, ',');
1137		if (tp != NULL)
1138			*tp++ = '\0';
1139		switch (sscanf(cp, "%u-%u", &first, &last)) {
1140		case 1:
1141			if (first > IEEE80211_CHAN_MAX)
1142				errx(-1, "channel %u out of range, max %u",
1143					first, IEEE80211_CHAN_MAX);
1144			setbit(chanlist.ic_channels, first);
1145			break;
1146		case 2:
1147			if (first > IEEE80211_CHAN_MAX)
1148				errx(-1, "channel %u out of range, max %u",
1149					first, IEEE80211_CHAN_MAX);
1150			if (last > IEEE80211_CHAN_MAX)
1151				errx(-1, "channel %u out of range, max %u",
1152					last, IEEE80211_CHAN_MAX);
1153			if (first > last)
1154				errx(-1, "void channel range, %u > %u",
1155					first, last);
1156			for (f = first; f <= last; f++)
1157				setbit(chanlist.ic_channels, f);
1158			break;
1159		}
1160		if (tp == NULL)
1161			break;
1162		c = *tp;
1163		while (isspace(c))
1164			tp++;
1165		if (!isdigit(c))
1166			break;
1167		cp = tp;
1168	}
1169	set80211(s, IEEE80211_IOC_CHANLIST, 0, sizeof(chanlist), &chanlist);
1170	free(temp);
1171}
1172
1173static void
1174set80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
1175{
1176
1177	if (!isanyarg(val)) {
1178		char *temp;
1179		struct sockaddr_dl sdl;
1180
1181		temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1182		if (temp == NULL)
1183			errx(1, "malloc failed");
1184		temp[0] = ':';
1185		strcpy(temp + 1, val);
1186		sdl.sdl_len = sizeof(sdl);
1187		link_addr(temp, &sdl);
1188		free(temp);
1189		if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1190			errx(1, "malformed link-level address");
1191		set80211(s, IEEE80211_IOC_BSSID, 0,
1192			IEEE80211_ADDR_LEN, LLADDR(&sdl));
1193	} else {
1194		uint8_t zerobssid[IEEE80211_ADDR_LEN];
1195		memset(zerobssid, 0, sizeof(zerobssid));
1196		set80211(s, IEEE80211_IOC_BSSID, 0,
1197			IEEE80211_ADDR_LEN, zerobssid);
1198	}
1199}
1200
1201static int
1202getac(const char *ac)
1203{
1204	if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0)
1205		return WME_AC_BE;
1206	if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0)
1207		return WME_AC_BK;
1208	if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0)
1209		return WME_AC_VI;
1210	if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0)
1211		return WME_AC_VO;
1212	errx(1, "unknown wme access class %s", ac);
1213}
1214
1215static
1216DECL_CMD_FUNC2(set80211cwmin, ac, val)
1217{
1218	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
1219}
1220
1221static
1222DECL_CMD_FUNC2(set80211cwmax, ac, val)
1223{
1224	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
1225}
1226
1227static
1228DECL_CMD_FUNC2(set80211aifs, ac, val)
1229{
1230	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
1231}
1232
1233static
1234DECL_CMD_FUNC2(set80211txoplimit, ac, val)
1235{
1236	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
1237}
1238
1239static
1240DECL_CMD_FUNC(set80211acm, ac, d)
1241{
1242	set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL);
1243}
1244static
1245DECL_CMD_FUNC(set80211noacm, ac, d)
1246{
1247	set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL);
1248}
1249
1250static
1251DECL_CMD_FUNC(set80211ackpolicy, ac, d)
1252{
1253	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL);
1254}
1255static
1256DECL_CMD_FUNC(set80211noackpolicy, ac, d)
1257{
1258	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL);
1259}
1260
1261static
1262DECL_CMD_FUNC2(set80211bsscwmin, ac, val)
1263{
1264	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
1265		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1266}
1267
1268static
1269DECL_CMD_FUNC2(set80211bsscwmax, ac, val)
1270{
1271	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
1272		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1273}
1274
1275static
1276DECL_CMD_FUNC2(set80211bssaifs, ac, val)
1277{
1278	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
1279		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1280}
1281
1282static
1283DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
1284{
1285	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
1286		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1287}
1288
1289static
1290DECL_CMD_FUNC(set80211dtimperiod, val, d)
1291{
1292	set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
1293}
1294
1295static
1296DECL_CMD_FUNC(set80211bintval, val, d)
1297{
1298	set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
1299}
1300
1301static void
1302set80211macmac(int s, int op, const char *val)
1303{
1304	char *temp;
1305	struct sockaddr_dl sdl;
1306
1307	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1308	if (temp == NULL)
1309		errx(1, "malloc failed");
1310	temp[0] = ':';
1311	strcpy(temp + 1, val);
1312	sdl.sdl_len = sizeof(sdl);
1313	link_addr(temp, &sdl);
1314	free(temp);
1315	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1316		errx(1, "malformed link-level address");
1317	set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
1318}
1319
1320static
1321DECL_CMD_FUNC(set80211addmac, val, d)
1322{
1323	set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
1324}
1325
1326static
1327DECL_CMD_FUNC(set80211delmac, val, d)
1328{
1329	set80211macmac(s, IEEE80211_IOC_DELMAC, val);
1330}
1331
1332static
1333DECL_CMD_FUNC(set80211kickmac, val, d)
1334{
1335	char *temp;
1336	struct sockaddr_dl sdl;
1337	struct ieee80211req_mlme mlme;
1338
1339	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1340	if (temp == NULL)
1341		errx(1, "malloc failed");
1342	temp[0] = ':';
1343	strcpy(temp + 1, val);
1344	sdl.sdl_len = sizeof(sdl);
1345	link_addr(temp, &sdl);
1346	free(temp);
1347	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1348		errx(1, "malformed link-level address");
1349	memset(&mlme, 0, sizeof(mlme));
1350	mlme.im_op = IEEE80211_MLME_DEAUTH;
1351	mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE;
1352	memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN);
1353	set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), &mlme);
1354}
1355
1356static
1357DECL_CMD_FUNC(set80211maccmd, val, d)
1358{
1359	set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
1360}
1361
1362static void
1363set80211meshrtmac(int s, int req, const char *val)
1364{
1365	char *temp;
1366	struct sockaddr_dl sdl;
1367
1368	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1369	if (temp == NULL)
1370		errx(1, "malloc failed");
1371	temp[0] = ':';
1372	strcpy(temp + 1, val);
1373	sdl.sdl_len = sizeof(sdl);
1374	link_addr(temp, &sdl);
1375	free(temp);
1376	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1377		errx(1, "malformed link-level address");
1378	set80211(s, IEEE80211_IOC_MESH_RTCMD, req,
1379	    IEEE80211_ADDR_LEN, LLADDR(&sdl));
1380}
1381
1382static
1383DECL_CMD_FUNC(set80211addmeshrt, val, d)
1384{
1385	set80211meshrtmac(s, IEEE80211_MESH_RTCMD_ADD, val);
1386}
1387
1388static
1389DECL_CMD_FUNC(set80211delmeshrt, val, d)
1390{
1391	set80211meshrtmac(s, IEEE80211_MESH_RTCMD_DELETE, val);
1392}
1393
1394static
1395DECL_CMD_FUNC(set80211meshrtcmd, val, d)
1396{
1397	set80211(s, IEEE80211_IOC_MESH_RTCMD, d, 0, NULL);
1398}
1399
1400static
1401DECL_CMD_FUNC(set80211hwmprootmode, val, d)
1402{
1403	int mode;
1404
1405	if (strcasecmp(val, "normal") == 0)
1406		mode = IEEE80211_HWMP_ROOTMODE_NORMAL;
1407	else if (strcasecmp(val, "proactive") == 0)
1408		mode = IEEE80211_HWMP_ROOTMODE_PROACTIVE;
1409	else if (strcasecmp(val, "rann") == 0)
1410		mode = IEEE80211_HWMP_ROOTMODE_RANN;
1411	else
1412		mode = IEEE80211_HWMP_ROOTMODE_DISABLED;
1413	set80211(s, IEEE80211_IOC_HWMP_ROOTMODE, mode, 0, NULL);
1414}
1415
1416static
1417DECL_CMD_FUNC(set80211hwmpmaxhops, val, d)
1418{
1419	set80211(s, IEEE80211_IOC_HWMP_MAXHOPS, atoi(val), 0, NULL);
1420}
1421
1422static void
1423set80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
1424{
1425	set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
1426}
1427
1428static void
1429set80211quiet(const char *val, int d, int s, const struct afswtch *rafp)
1430{
1431	set80211(s, IEEE80211_IOC_QUIET, d, 0, NULL);
1432}
1433
1434static
1435DECL_CMD_FUNC(set80211quietperiod, val, d)
1436{
1437	set80211(s, IEEE80211_IOC_QUIET_PERIOD, atoi(val), 0, NULL);
1438}
1439
1440static
1441DECL_CMD_FUNC(set80211quietcount, val, d)
1442{
1443	set80211(s, IEEE80211_IOC_QUIET_COUNT, atoi(val), 0, NULL);
1444}
1445
1446static
1447DECL_CMD_FUNC(set80211quietduration, val, d)
1448{
1449	set80211(s, IEEE80211_IOC_QUIET_DUR, atoi(val), 0, NULL);
1450}
1451
1452static
1453DECL_CMD_FUNC(set80211quietoffset, val, d)
1454{
1455	set80211(s, IEEE80211_IOC_QUIET_OFFSET, atoi(val), 0, NULL);
1456}
1457
1458static void
1459set80211bgscan(const char *val, int d, int s, const struct afswtch *rafp)
1460{
1461	set80211(s, IEEE80211_IOC_BGSCAN, d, 0, NULL);
1462}
1463
1464static
1465DECL_CMD_FUNC(set80211bgscanidle, val, d)
1466{
1467	set80211(s, IEEE80211_IOC_BGSCAN_IDLE, atoi(val), 0, NULL);
1468}
1469
1470static
1471DECL_CMD_FUNC(set80211bgscanintvl, val, d)
1472{
1473	set80211(s, IEEE80211_IOC_BGSCAN_INTERVAL, atoi(val), 0, NULL);
1474}
1475
1476static
1477DECL_CMD_FUNC(set80211scanvalid, val, d)
1478{
1479	set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL);
1480}
1481
1482/*
1483 * Parse an optional trailing specification of which netbands
1484 * to apply a parameter to.  This is basically the same syntax
1485 * as used for channels but you can concatenate to specify
1486 * multiple.  For example:
1487 *	14:abg		apply to 11a, 11b, and 11g
1488 *	6:ht		apply to 11na and 11ng
1489 * We don't make a big effort to catch silly things; this is
1490 * really a convenience mechanism.
1491 */
1492static int
1493getmodeflags(const char *val)
1494{
1495	const char *cp;
1496	int flags;
1497
1498	flags = 0;
1499
1500	cp = strchr(val, ':');
1501	if (cp != NULL) {
1502		for (cp++; isalpha((int) *cp); cp++) {
1503			/* accept mixed case */
1504			int c = *cp;
1505			if (isupper(c))
1506				c = tolower(c);
1507			switch (c) {
1508			case 'a':		/* 802.11a */
1509				flags |= IEEE80211_CHAN_A;
1510				break;
1511			case 'b':		/* 802.11b */
1512				flags |= IEEE80211_CHAN_B;
1513				break;
1514			case 'g':		/* 802.11g */
1515				flags |= IEEE80211_CHAN_G;
1516				break;
1517			case 'n':		/* 802.11n */
1518				flags |= IEEE80211_CHAN_HT;
1519				break;
1520			case 'd':		/* dt = Atheros Dynamic Turbo */
1521				flags |= IEEE80211_CHAN_TURBO;
1522				break;
1523			case 't':		/* ht, dt, st, t */
1524				/* dt and unadorned t specify Dynamic Turbo */
1525				if ((flags & (IEEE80211_CHAN_STURBO|IEEE80211_CHAN_HT)) == 0)
1526					flags |= IEEE80211_CHAN_TURBO;
1527				break;
1528			case 's':		/* st = Atheros Static Turbo */
1529				flags |= IEEE80211_CHAN_STURBO;
1530				break;
1531			case 'h':		/* 1/2-width channels */
1532				flags |= IEEE80211_CHAN_HALF;
1533				break;
1534			case 'q':		/* 1/4-width channels */
1535				flags |= IEEE80211_CHAN_QUARTER;
1536				break;
1537			case 'v':
1538				/* XXX set HT too? */
1539				flags |= IEEE80211_CHAN_VHT;
1540				break;
1541			default:
1542				errx(-1, "%s: Invalid mode attribute %c\n",
1543				    val, *cp);
1544			}
1545		}
1546	}
1547	return flags;
1548}
1549
1550#define	_APPLY(_flags, _base, _param, _v) do {				\
1551    if (_flags & IEEE80211_CHAN_HT) {					\
1552	    if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1553		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1554		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1555	    } else if (_flags & IEEE80211_CHAN_5GHZ)			\
1556		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1557	    else							\
1558		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1559    }									\
1560    if (_flags & IEEE80211_CHAN_TURBO) {				\
1561	    if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1562		    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;	\
1563		    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;	\
1564	    } else if (_flags & IEEE80211_CHAN_5GHZ)			\
1565		    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;	\
1566	    else							\
1567		    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;	\
1568    }									\
1569    if (_flags & IEEE80211_CHAN_STURBO)					\
1570	    _base.params[IEEE80211_MODE_STURBO_A]._param = _v;		\
1571    if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)		\
1572	    _base.params[IEEE80211_MODE_11A]._param = _v;		\
1573    if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)		\
1574	    _base.params[IEEE80211_MODE_11G]._param = _v;		\
1575    if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)		\
1576	    _base.params[IEEE80211_MODE_11B]._param = _v;		\
1577    if (_flags & IEEE80211_CHAN_HALF)					\
1578	    _base.params[IEEE80211_MODE_HALF]._param = _v;		\
1579    if (_flags & IEEE80211_CHAN_QUARTER)				\
1580	    _base.params[IEEE80211_MODE_QUARTER]._param = _v;		\
1581} while (0)
1582#define	_APPLY1(_flags, _base, _param, _v) do {				\
1583    if (_flags & IEEE80211_CHAN_HT) {					\
1584	    if (_flags & IEEE80211_CHAN_5GHZ)				\
1585		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1586	    else							\
1587		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1588    } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A)	\
1589	    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;		\
1590    else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G)	\
1591	    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;		\
1592    else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST)		\
1593	    _base.params[IEEE80211_MODE_STURBO_A]._param = _v;		\
1594    else if (_flags & IEEE80211_CHAN_HALF)				\
1595	    _base.params[IEEE80211_MODE_HALF]._param = _v;		\
1596    else if (_flags & IEEE80211_CHAN_QUARTER)				\
1597	    _base.params[IEEE80211_MODE_QUARTER]._param = _v;		\
1598    else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)		\
1599	    _base.params[IEEE80211_MODE_11A]._param = _v;		\
1600    else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)		\
1601	    _base.params[IEEE80211_MODE_11G]._param = _v;		\
1602    else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)		\
1603	    _base.params[IEEE80211_MODE_11B]._param = _v;		\
1604} while (0)
1605#define	_APPLY_RATE(_flags, _base, _param, _v) do {			\
1606    if (_flags & IEEE80211_CHAN_HT) {					\
1607	(_v) = (_v / 2) | IEEE80211_RATE_MCS;				\
1608    }									\
1609    _APPLY(_flags, _base, _param, _v);					\
1610} while (0)
1611#define	_APPLY_RATE1(_flags, _base, _param, _v) do {			\
1612    if (_flags & IEEE80211_CHAN_HT) {					\
1613	(_v) = (_v / 2) | IEEE80211_RATE_MCS;				\
1614    }									\
1615    _APPLY1(_flags, _base, _param, _v);					\
1616} while (0)
1617
1618static
1619DECL_CMD_FUNC(set80211roamrssi, val, d)
1620{
1621	double v = atof(val);
1622	int rssi, flags;
1623
1624	rssi = (int) (2*v);
1625	if (rssi != 2*v)
1626		errx(-1, "invalid rssi (must be .5 dBm units)");
1627	flags = getmodeflags(val);
1628	getroam(s);
1629	if (flags == 0) {		/* NB: no flags => current channel */
1630		flags = getcurchan(s)->ic_flags;
1631		_APPLY1(flags, roamparams, rssi, rssi);
1632	} else
1633		_APPLY(flags, roamparams, rssi, rssi);
1634	callback_register(setroam_cb, &roamparams);
1635}
1636
1637static int
1638getrate(const char *val, const char *tag)
1639{
1640	double v = atof(val);
1641	int rate;
1642
1643	rate = (int) (2*v);
1644	if (rate != 2*v)
1645		errx(-1, "invalid %s rate (must be .5 Mb/s units)", tag);
1646	return rate;		/* NB: returns 2x the specified value */
1647}
1648
1649static
1650DECL_CMD_FUNC(set80211roamrate, val, d)
1651{
1652	int rate, flags;
1653
1654	rate = getrate(val, "roam");
1655	flags = getmodeflags(val);
1656	getroam(s);
1657	if (flags == 0) {		/* NB: no flags => current channel */
1658		flags = getcurchan(s)->ic_flags;
1659		_APPLY_RATE1(flags, roamparams, rate, rate);
1660	} else
1661		_APPLY_RATE(flags, roamparams, rate, rate);
1662	callback_register(setroam_cb, &roamparams);
1663}
1664
1665static
1666DECL_CMD_FUNC(set80211mcastrate, val, d)
1667{
1668	int rate, flags;
1669
1670	rate = getrate(val, "mcast");
1671	flags = getmodeflags(val);
1672	gettxparams(s);
1673	if (flags == 0) {		/* NB: no flags => current channel */
1674		flags = getcurchan(s)->ic_flags;
1675		_APPLY_RATE1(flags, txparams, mcastrate, rate);
1676	} else
1677		_APPLY_RATE(flags, txparams, mcastrate, rate);
1678	callback_register(settxparams_cb, &txparams);
1679}
1680
1681static
1682DECL_CMD_FUNC(set80211mgtrate, val, d)
1683{
1684	int rate, flags;
1685
1686	rate = getrate(val, "mgmt");
1687	flags = getmodeflags(val);
1688	gettxparams(s);
1689	if (flags == 0) {		/* NB: no flags => current channel */
1690		flags = getcurchan(s)->ic_flags;
1691		_APPLY_RATE1(flags, txparams, mgmtrate, rate);
1692	} else
1693		_APPLY_RATE(flags, txparams, mgmtrate, rate);
1694	callback_register(settxparams_cb, &txparams);
1695}
1696
1697static
1698DECL_CMD_FUNC(set80211ucastrate, val, d)
1699{
1700	int flags;
1701
1702	gettxparams(s);
1703	flags = getmodeflags(val);
1704	if (isanyarg(val)) {
1705		if (flags == 0) {	/* NB: no flags => current channel */
1706			flags = getcurchan(s)->ic_flags;
1707			_APPLY1(flags, txparams, ucastrate,
1708			    IEEE80211_FIXED_RATE_NONE);
1709		} else
1710			_APPLY(flags, txparams, ucastrate,
1711			    IEEE80211_FIXED_RATE_NONE);
1712	} else {
1713		int rate = getrate(val, "ucast");
1714		if (flags == 0) {	/* NB: no flags => current channel */
1715			flags = getcurchan(s)->ic_flags;
1716			_APPLY_RATE1(flags, txparams, ucastrate, rate);
1717		} else
1718			_APPLY_RATE(flags, txparams, ucastrate, rate);
1719	}
1720	callback_register(settxparams_cb, &txparams);
1721}
1722
1723static
1724DECL_CMD_FUNC(set80211maxretry, val, d)
1725{
1726	int v = atoi(val), flags;
1727
1728	flags = getmodeflags(val);
1729	gettxparams(s);
1730	if (flags == 0) {		/* NB: no flags => current channel */
1731		flags = getcurchan(s)->ic_flags;
1732		_APPLY1(flags, txparams, maxretry, v);
1733	} else
1734		_APPLY(flags, txparams, maxretry, v);
1735	callback_register(settxparams_cb, &txparams);
1736}
1737#undef _APPLY_RATE
1738#undef _APPLY
1739
1740static
1741DECL_CMD_FUNC(set80211fragthreshold, val, d)
1742{
1743	set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
1744		isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
1745}
1746
1747static
1748DECL_CMD_FUNC(set80211bmissthreshold, val, d)
1749{
1750	set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
1751		isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
1752}
1753
1754static void
1755set80211burst(const char *val, int d, int s, const struct afswtch *rafp)
1756{
1757	set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
1758}
1759
1760static void
1761set80211doth(const char *val, int d, int s, const struct afswtch *rafp)
1762{
1763	set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL);
1764}
1765
1766static void
1767set80211dfs(const char *val, int d, int s, const struct afswtch *rafp)
1768{
1769	set80211(s, IEEE80211_IOC_DFS, d, 0, NULL);
1770}
1771
1772static void
1773set80211shortgi(const char *val, int d, int s, const struct afswtch *rafp)
1774{
1775	set80211(s, IEEE80211_IOC_SHORTGI,
1776		d ? (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) : 0,
1777		0, NULL);
1778}
1779
1780/* XXX 11ac density/size is different */
1781static void
1782set80211ampdu(const char *val, int d, int s, const struct afswtch *rafp)
1783{
1784	int ampdu;
1785
1786	if (get80211val(s, IEEE80211_IOC_AMPDU, &ampdu) < 0)
1787		errx(-1, "cannot set AMPDU setting");
1788	if (d < 0) {
1789		d = -d;
1790		ampdu &= ~d;
1791	} else
1792		ampdu |= d;
1793	set80211(s, IEEE80211_IOC_AMPDU, ampdu, 0, NULL);
1794}
1795
1796static void
1797set80211stbc(const char *val, int d, int s, const struct afswtch *rafp)
1798{
1799	int stbc;
1800
1801	if (get80211val(s, IEEE80211_IOC_STBC, &stbc) < 0)
1802		errx(-1, "cannot set STBC setting");
1803	if (d < 0) {
1804		d = -d;
1805		stbc &= ~d;
1806	} else
1807		stbc |= d;
1808	set80211(s, IEEE80211_IOC_STBC, stbc, 0, NULL);
1809}
1810
1811static void
1812set80211ldpc(const char *val, int d, int s, const struct afswtch *rafp)
1813{
1814        int ldpc;
1815
1816        if (get80211val(s, IEEE80211_IOC_LDPC, &ldpc) < 0)
1817                errx(-1, "cannot set LDPC setting");
1818        if (d < 0) {
1819                d = -d;
1820                ldpc &= ~d;
1821        } else
1822                ldpc |= d;
1823        set80211(s, IEEE80211_IOC_LDPC, ldpc, 0, NULL);
1824}
1825
1826static void
1827set80211uapsd(const char *val, int d, int s, const struct afswtch *rafp)
1828{
1829	set80211(s, IEEE80211_IOC_UAPSD, d, 0, NULL);
1830}
1831
1832static
1833DECL_CMD_FUNC(set80211ampdulimit, val, d)
1834{
1835	int v;
1836
1837	switch (atoi(val)) {
1838	case 8:
1839	case 8*1024:
1840		v = IEEE80211_HTCAP_MAXRXAMPDU_8K;
1841		break;
1842	case 16:
1843	case 16*1024:
1844		v = IEEE80211_HTCAP_MAXRXAMPDU_16K;
1845		break;
1846	case 32:
1847	case 32*1024:
1848		v = IEEE80211_HTCAP_MAXRXAMPDU_32K;
1849		break;
1850	case 64:
1851	case 64*1024:
1852		v = IEEE80211_HTCAP_MAXRXAMPDU_64K;
1853		break;
1854	default:
1855		errx(-1, "invalid A-MPDU limit %s", val);
1856	}
1857	set80211(s, IEEE80211_IOC_AMPDU_LIMIT, v, 0, NULL);
1858}
1859
1860/* XXX 11ac density/size is different */
1861static
1862DECL_CMD_FUNC(set80211ampdudensity, val, d)
1863{
1864	int v;
1865
1866	if (isanyarg(val) || strcasecmp(val, "na") == 0)
1867		v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1868	else switch ((int)(atof(val)*4)) {
1869	case 0:
1870		v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1871		break;
1872	case 1:
1873		v = IEEE80211_HTCAP_MPDUDENSITY_025;
1874		break;
1875	case 2:
1876		v = IEEE80211_HTCAP_MPDUDENSITY_05;
1877		break;
1878	case 4:
1879		v = IEEE80211_HTCAP_MPDUDENSITY_1;
1880		break;
1881	case 8:
1882		v = IEEE80211_HTCAP_MPDUDENSITY_2;
1883		break;
1884	case 16:
1885		v = IEEE80211_HTCAP_MPDUDENSITY_4;
1886		break;
1887	case 32:
1888		v = IEEE80211_HTCAP_MPDUDENSITY_8;
1889		break;
1890	case 64:
1891		v = IEEE80211_HTCAP_MPDUDENSITY_16;
1892		break;
1893	default:
1894		errx(-1, "invalid A-MPDU density %s", val);
1895	}
1896	set80211(s, IEEE80211_IOC_AMPDU_DENSITY, v, 0, NULL);
1897}
1898
1899static void
1900set80211amsdu(const char *val, int d, int s, const struct afswtch *rafp)
1901{
1902	int amsdu;
1903
1904	if (get80211val(s, IEEE80211_IOC_AMSDU, &amsdu) < 0)
1905		err(-1, "cannot get AMSDU setting");
1906	if (d < 0) {
1907		d = -d;
1908		amsdu &= ~d;
1909	} else
1910		amsdu |= d;
1911	set80211(s, IEEE80211_IOC_AMSDU, amsdu, 0, NULL);
1912}
1913
1914static
1915DECL_CMD_FUNC(set80211amsdulimit, val, d)
1916{
1917	set80211(s, IEEE80211_IOC_AMSDU_LIMIT, atoi(val), 0, NULL);
1918}
1919
1920static void
1921set80211puren(const char *val, int d, int s, const struct afswtch *rafp)
1922{
1923	set80211(s, IEEE80211_IOC_PUREN, d, 0, NULL);
1924}
1925
1926static void
1927set80211htcompat(const char *val, int d, int s, const struct afswtch *rafp)
1928{
1929	set80211(s, IEEE80211_IOC_HTCOMPAT, d, 0, NULL);
1930}
1931
1932static void
1933set80211htconf(const char *val, int d, int s, const struct afswtch *rafp)
1934{
1935	set80211(s, IEEE80211_IOC_HTCONF, d, 0, NULL);
1936	htconf = d;
1937}
1938
1939static void
1940set80211dwds(const char *val, int d, int s, const struct afswtch *rafp)
1941{
1942	set80211(s, IEEE80211_IOC_DWDS, d, 0, NULL);
1943}
1944
1945static void
1946set80211inact(const char *val, int d, int s, const struct afswtch *rafp)
1947{
1948	set80211(s, IEEE80211_IOC_INACTIVITY, d, 0, NULL);
1949}
1950
1951static void
1952set80211tsn(const char *val, int d, int s, const struct afswtch *rafp)
1953{
1954	set80211(s, IEEE80211_IOC_TSN, d, 0, NULL);
1955}
1956
1957static void
1958set80211dotd(const char *val, int d, int s, const struct afswtch *rafp)
1959{
1960	set80211(s, IEEE80211_IOC_DOTD, d, 0, NULL);
1961}
1962
1963static void
1964set80211smps(const char *val, int d, int s, const struct afswtch *rafp)
1965{
1966	set80211(s, IEEE80211_IOC_SMPS, d, 0, NULL);
1967}
1968
1969static void
1970set80211rifs(const char *val, int d, int s, const struct afswtch *rafp)
1971{
1972	set80211(s, IEEE80211_IOC_RIFS, d, 0, NULL);
1973}
1974
1975static void
1976set80211vhtconf(const char *val, int d, int s, const struct afswtch *rafp)
1977{
1978	if (get80211val(s, IEEE80211_IOC_VHTCONF, &vhtconf) < 0)
1979		errx(-1, "cannot set VHT setting");
1980	printf("%s: vhtconf=0x%08x, d=%d\n", __func__, vhtconf, d);
1981	if (d < 0) {
1982		d = -d;
1983		vhtconf &= ~d;
1984	} else
1985		vhtconf |= d;
1986	printf("%s: vhtconf is now 0x%08x\n", __func__, vhtconf);
1987	set80211(s, IEEE80211_IOC_VHTCONF, vhtconf, 0, NULL);
1988}
1989
1990static
1991DECL_CMD_FUNC(set80211tdmaslot, val, d)
1992{
1993	set80211(s, IEEE80211_IOC_TDMA_SLOT, atoi(val), 0, NULL);
1994}
1995
1996static
1997DECL_CMD_FUNC(set80211tdmaslotcnt, val, d)
1998{
1999	set80211(s, IEEE80211_IOC_TDMA_SLOTCNT, atoi(val), 0, NULL);
2000}
2001
2002static
2003DECL_CMD_FUNC(set80211tdmaslotlen, val, d)
2004{
2005	set80211(s, IEEE80211_IOC_TDMA_SLOTLEN, atoi(val), 0, NULL);
2006}
2007
2008static
2009DECL_CMD_FUNC(set80211tdmabintval, val, d)
2010{
2011	set80211(s, IEEE80211_IOC_TDMA_BINTERVAL, atoi(val), 0, NULL);
2012}
2013
2014static
2015DECL_CMD_FUNC(set80211meshttl, val, d)
2016{
2017	set80211(s, IEEE80211_IOC_MESH_TTL, atoi(val), 0, NULL);
2018}
2019
2020static
2021DECL_CMD_FUNC(set80211meshforward, val, d)
2022{
2023	set80211(s, IEEE80211_IOC_MESH_FWRD, d, 0, NULL);
2024}
2025
2026static
2027DECL_CMD_FUNC(set80211meshgate, val, d)
2028{
2029	set80211(s, IEEE80211_IOC_MESH_GATE, d, 0, NULL);
2030}
2031
2032static
2033DECL_CMD_FUNC(set80211meshpeering, val, d)
2034{
2035	set80211(s, IEEE80211_IOC_MESH_AP, d, 0, NULL);
2036}
2037
2038static
2039DECL_CMD_FUNC(set80211meshmetric, val, d)
2040{
2041	char v[12];
2042
2043	memcpy(v, val, sizeof(v));
2044	set80211(s, IEEE80211_IOC_MESH_PR_METRIC, 0, 0, v);
2045}
2046
2047static
2048DECL_CMD_FUNC(set80211meshpath, val, d)
2049{
2050	char v[12];
2051
2052	memcpy(v, val, sizeof(v));
2053	set80211(s, IEEE80211_IOC_MESH_PR_PATH, 0, 0, v);
2054}
2055
2056static int
2057regdomain_sort(const void *a, const void *b)
2058{
2059#define	CHAN_ALL \
2060	(IEEE80211_CHAN_ALLTURBO|IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)
2061	const struct ieee80211_channel *ca = a;
2062	const struct ieee80211_channel *cb = b;
2063
2064	return ca->ic_freq == cb->ic_freq ?
2065	    (ca->ic_flags & CHAN_ALL) - (cb->ic_flags & CHAN_ALL) :
2066	    ca->ic_freq - cb->ic_freq;
2067#undef CHAN_ALL
2068}
2069
2070static const struct ieee80211_channel *
2071chanlookup(const struct ieee80211_channel chans[], int nchans,
2072	int freq, int flags)
2073{
2074	int i;
2075
2076	flags &= IEEE80211_CHAN_ALLTURBO;
2077	for (i = 0; i < nchans; i++) {
2078		const struct ieee80211_channel *c = &chans[i];
2079		if (c->ic_freq == freq &&
2080		    (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
2081			return c;
2082	}
2083	return NULL;
2084}
2085
2086static int
2087chanfind(const struct ieee80211_channel chans[], int nchans, int flags)
2088{
2089	int i;
2090
2091	for (i = 0; i < nchans; i++) {
2092		const struct ieee80211_channel *c = &chans[i];
2093		if ((c->ic_flags & flags) == flags)
2094			return 1;
2095	}
2096	return 0;
2097}
2098
2099/*
2100 * Check channel compatibility.
2101 */
2102static int
2103checkchan(const struct ieee80211req_chaninfo *avail, int freq, int flags)
2104{
2105	flags &= ~REQ_FLAGS;
2106	/*
2107	 * Check if exact channel is in the calibration table;
2108	 * everything below is to deal with channels that we
2109	 * want to include but that are not explicitly listed.
2110	 */
2111	if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, flags) != NULL)
2112		return 1;
2113	if (flags & IEEE80211_CHAN_GSM) {
2114		/*
2115		 * XXX GSM frequency mapping is handled in the kernel
2116		 * so we cannot find them in the calibration table;
2117		 * just accept the channel and the kernel will reject
2118		 * the channel list if it's wrong.
2119		 */
2120		return 1;
2121	}
2122	/*
2123	 * If this is a 1/2 or 1/4 width channel allow it if a full
2124	 * width channel is present for this frequency, and the device
2125	 * supports fractional channels on this band.  This is a hack
2126	 * that avoids bloating the calibration table; it may be better
2127	 * by per-band attributes though (we are effectively calculating
2128	 * this attribute by scanning the channel list ourself).
2129	 */
2130	if ((flags & (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) == 0)
2131		return 0;
2132	if (chanlookup(avail->ic_chans, avail->ic_nchans, freq,
2133	    flags &~ (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) == NULL)
2134		return 0;
2135	if (flags & IEEE80211_CHAN_HALF) {
2136		return chanfind(avail->ic_chans, avail->ic_nchans,
2137		    IEEE80211_CHAN_HALF |
2138		       (flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
2139	} else {
2140		return chanfind(avail->ic_chans, avail->ic_nchans,
2141		    IEEE80211_CHAN_QUARTER |
2142			(flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
2143	}
2144}
2145
2146static void
2147regdomain_addchans(struct ieee80211req_chaninfo *ci,
2148	const netband_head *bands,
2149	const struct ieee80211_regdomain *reg,
2150	uint32_t chanFlags,
2151	const struct ieee80211req_chaninfo *avail)
2152{
2153	const struct netband *nb;
2154	const struct freqband *b;
2155	struct ieee80211_channel *c, *prev;
2156	int freq, hi_adj, lo_adj, channelSep;
2157	uint32_t flags;
2158
2159	hi_adj = (chanFlags & IEEE80211_CHAN_HT40U) ? -20 : 0;
2160	lo_adj = (chanFlags & IEEE80211_CHAN_HT40D) ? 20 : 0;
2161	channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40;
2162
2163	LIST_FOREACH(nb, bands, next) {
2164		b = nb->band;
2165		if (verbose) {
2166			printf("%s:", __func__);
2167			printb(" chanFlags", chanFlags, IEEE80211_CHAN_BITS);
2168			printb(" bandFlags", nb->flags | b->flags,
2169			    IEEE80211_CHAN_BITS);
2170			putchar('\n');
2171		}
2172		prev = NULL;
2173
2174		for (freq = b->freqStart + lo_adj;
2175		     freq <= b->freqEnd + hi_adj; freq += b->chanSep) {
2176			/*
2177			 * Construct flags for the new channel.  We take
2178			 * the attributes from the band descriptions except
2179			 * for HT40 which is enabled generically (i.e. +/-
2180			 * extension channel) in the band description and
2181			 * then constrained according by channel separation.
2182			 */
2183			flags = nb->flags | b->flags;
2184
2185			/*
2186			 * VHT first - HT is a subset.
2187			 */
2188			if (flags & IEEE80211_CHAN_VHT) {
2189				if ((chanFlags & IEEE80211_CHAN_VHT20) &&
2190				    (flags & IEEE80211_CHAN_VHT20) == 0) {
2191					if (verbose)
2192						printf("%u: skip, not a "
2193						    "VHT20 channel\n", freq);
2194					continue;
2195				}
2196				if ((chanFlags & IEEE80211_CHAN_VHT40) &&
2197				    (flags & IEEE80211_CHAN_VHT40) == 0) {
2198					if (verbose)
2199						printf("%u: skip, not a "
2200						    "VHT40 channel\n", freq);
2201					continue;
2202				}
2203				if ((chanFlags & IEEE80211_CHAN_VHT80) &&
2204				    (flags & IEEE80211_CHAN_VHT80) == 0) {
2205					if (verbose)
2206						printf("%u: skip, not a "
2207						    "VHT80 channel\n", freq);
2208					continue;
2209				}
2210				if ((chanFlags & IEEE80211_CHAN_VHT160) &&
2211				    (flags & IEEE80211_CHAN_VHT160) == 0) {
2212					if (verbose)
2213						printf("%u: skip, not a "
2214						    "VHT160 channel\n", freq);
2215					continue;
2216				}
2217				if ((chanFlags & IEEE80211_CHAN_VHT80P80) &&
2218				    (flags & IEEE80211_CHAN_VHT80P80) == 0) {
2219					if (verbose)
2220						printf("%u: skip, not a "
2221						    "VHT80+80 channel\n", freq);
2222					continue;
2223				}
2224				flags &= ~IEEE80211_CHAN_VHT;
2225				flags |= chanFlags & IEEE80211_CHAN_VHT;
2226			}
2227
2228			/* Now, constrain HT */
2229			if (flags & IEEE80211_CHAN_HT) {
2230				/*
2231				 * HT channels are generated specially; we're
2232				 * called to add HT20, HT40+, and HT40- chan's
2233				 * so we need to expand only band specs for
2234				 * the HT channel type being added.
2235				 */
2236				if ((chanFlags & IEEE80211_CHAN_HT20) &&
2237				    (flags & IEEE80211_CHAN_HT20) == 0) {
2238					if (verbose)
2239						printf("%u: skip, not an "
2240						    "HT20 channel\n", freq);
2241					continue;
2242				}
2243				if ((chanFlags & IEEE80211_CHAN_HT40) &&
2244				    (flags & IEEE80211_CHAN_HT40) == 0) {
2245					if (verbose)
2246						printf("%u: skip, not an "
2247						    "HT40 channel\n", freq);
2248					continue;
2249				}
2250				/* NB: HT attribute comes from caller */
2251				flags &= ~IEEE80211_CHAN_HT;
2252				flags |= chanFlags & IEEE80211_CHAN_HT;
2253			}
2254			/*
2255			 * Check if device can operate on this frequency.
2256			 */
2257			if (!checkchan(avail, freq, flags)) {
2258				if (verbose) {
2259					printf("%u: skip, ", freq);
2260					printb("flags", flags,
2261					    IEEE80211_CHAN_BITS);
2262					printf(" not available\n");
2263				}
2264				continue;
2265			}
2266			if ((flags & REQ_ECM) && !reg->ecm) {
2267				if (verbose)
2268					printf("%u: skip, ECM channel\n", freq);
2269				continue;
2270			}
2271			if ((flags & REQ_INDOOR) && reg->location == 'O') {
2272				if (verbose)
2273					printf("%u: skip, indoor channel\n",
2274					    freq);
2275				continue;
2276			}
2277			if ((flags & REQ_OUTDOOR) && reg->location == 'I') {
2278				if (verbose)
2279					printf("%u: skip, outdoor channel\n",
2280					    freq);
2281				continue;
2282			}
2283			if ((flags & IEEE80211_CHAN_HT40) &&
2284			    prev != NULL && (freq - prev->ic_freq) < channelSep) {
2285				if (verbose)
2286					printf("%u: skip, only %u channel "
2287					    "separation, need %d\n", freq,
2288					    freq - prev->ic_freq, channelSep);
2289				continue;
2290			}
2291			if (ci->ic_nchans == IEEE80211_CHAN_MAX) {
2292				if (verbose)
2293					printf("%u: skip, channel table full\n",
2294					    freq);
2295				break;
2296			}
2297			c = &ci->ic_chans[ci->ic_nchans++];
2298			memset(c, 0, sizeof(*c));
2299			c->ic_freq = freq;
2300			c->ic_flags = flags;
2301		if (c->ic_flags & IEEE80211_CHAN_DFS)
2302				c->ic_maxregpower = nb->maxPowerDFS;
2303			else
2304				c->ic_maxregpower = nb->maxPower;
2305			if (verbose) {
2306				printf("[%3d] add freq %u ",
2307				    ci->ic_nchans-1, c->ic_freq);
2308				printb("flags", c->ic_flags, IEEE80211_CHAN_BITS);
2309				printf(" power %u\n", c->ic_maxregpower);
2310			}
2311			/* NB: kernel fills in other fields */
2312			prev = c;
2313		}
2314	}
2315}
2316
2317static void
2318regdomain_makechannels(
2319	struct ieee80211_regdomain_req *req,
2320	const struct ieee80211_devcaps_req *dc)
2321{
2322	struct regdata *rdp = getregdata();
2323	const struct country *cc;
2324	const struct ieee80211_regdomain *reg = &req->rd;
2325	struct ieee80211req_chaninfo *ci = &req->chaninfo;
2326	const struct regdomain *rd;
2327
2328	/*
2329	 * Locate construction table for new channel list.  We treat
2330	 * the regdomain/SKU as definitive so a country can be in
2331	 * multiple with different properties (e.g. US in FCC+FCC3).
2332	 * If no regdomain is specified then we fallback on the country
2333	 * code to find the associated regdomain since countries always
2334	 * belong to at least one regdomain.
2335	 */
2336	if (reg->regdomain == 0) {
2337		cc = lib80211_country_findbycc(rdp, reg->country);
2338		if (cc == NULL)
2339			errx(1, "internal error, country %d not found",
2340			    reg->country);
2341		rd = cc->rd;
2342	} else
2343		rd = lib80211_regdomain_findbysku(rdp, reg->regdomain);
2344	if (rd == NULL)
2345		errx(1, "internal error, regdomain %d not found",
2346			    reg->regdomain);
2347	if (rd->sku != SKU_DEBUG) {
2348		/*
2349		 * regdomain_addchans incrememnts the channel count for
2350		 * each channel it adds so initialize ic_nchans to zero.
2351		 * Note that we know we have enough space to hold all possible
2352		 * channels because the devcaps list size was used to
2353		 * allocate our request.
2354		 */
2355		ci->ic_nchans = 0;
2356		if (!LIST_EMPTY(&rd->bands_11b))
2357			regdomain_addchans(ci, &rd->bands_11b, reg,
2358			    IEEE80211_CHAN_B, &dc->dc_chaninfo);
2359		if (!LIST_EMPTY(&rd->bands_11g))
2360			regdomain_addchans(ci, &rd->bands_11g, reg,
2361			    IEEE80211_CHAN_G, &dc->dc_chaninfo);
2362		if (!LIST_EMPTY(&rd->bands_11a))
2363			regdomain_addchans(ci, &rd->bands_11a, reg,
2364			    IEEE80211_CHAN_A, &dc->dc_chaninfo);
2365		if (!LIST_EMPTY(&rd->bands_11na) && dc->dc_htcaps != 0) {
2366			regdomain_addchans(ci, &rd->bands_11na, reg,
2367			    IEEE80211_CHAN_A | IEEE80211_CHAN_HT20,
2368			    &dc->dc_chaninfo);
2369			if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
2370				regdomain_addchans(ci, &rd->bands_11na, reg,
2371				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U,
2372				    &dc->dc_chaninfo);
2373				regdomain_addchans(ci, &rd->bands_11na, reg,
2374				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D,
2375				    &dc->dc_chaninfo);
2376			}
2377		}
2378		if (!LIST_EMPTY(&rd->bands_11ac) && dc->dc_vhtcaps != 0) {
2379			regdomain_addchans(ci, &rd->bands_11ac, reg,
2380			    IEEE80211_CHAN_A | IEEE80211_CHAN_HT20 |
2381			    IEEE80211_CHAN_VHT20,
2382			    &dc->dc_chaninfo);
2383
2384			/* VHT40 is a function of HT40.. */
2385			if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
2386				regdomain_addchans(ci, &rd->bands_11ac, reg,
2387				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U |
2388				    IEEE80211_CHAN_VHT40U,
2389				    &dc->dc_chaninfo);
2390				regdomain_addchans(ci, &rd->bands_11ac, reg,
2391				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D |
2392				    IEEE80211_CHAN_VHT40D,
2393				    &dc->dc_chaninfo);
2394			}
2395
2396			/* VHT80 is mandatory (and so should be VHT40 above). */
2397			if (1) {
2398				regdomain_addchans(ci, &rd->bands_11ac, reg,
2399				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U |
2400				    IEEE80211_CHAN_VHT80,
2401				    &dc->dc_chaninfo);
2402				regdomain_addchans(ci, &rd->bands_11ac, reg,
2403				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D |
2404				    IEEE80211_CHAN_VHT80,
2405				    &dc->dc_chaninfo);
2406			}
2407
2408			/* VHT160 */
2409			if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160MHZ(
2410			    dc->dc_vhtcaps)) {
2411				regdomain_addchans(ci, &rd->bands_11ac, reg,
2412				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U |
2413				    IEEE80211_CHAN_VHT160,
2414				    &dc->dc_chaninfo);
2415				regdomain_addchans(ci, &rd->bands_11ac, reg,
2416				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D |
2417				    IEEE80211_CHAN_VHT160,
2418				    &dc->dc_chaninfo);
2419			}
2420
2421			/* VHT80P80 */
2422			if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160_80P80MHZ(
2423			    dc->dc_vhtcaps)) {
2424				regdomain_addchans(ci, &rd->bands_11ac, reg,
2425				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U |
2426				    IEEE80211_CHAN_VHT80P80,
2427				    &dc->dc_chaninfo);
2428				regdomain_addchans(ci, &rd->bands_11ac, reg,
2429				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D |
2430				    IEEE80211_CHAN_VHT80P80,
2431				    &dc->dc_chaninfo);
2432			}
2433		}
2434
2435		if (!LIST_EMPTY(&rd->bands_11ng) && dc->dc_htcaps != 0) {
2436			regdomain_addchans(ci, &rd->bands_11ng, reg,
2437			    IEEE80211_CHAN_G | IEEE80211_CHAN_HT20,
2438			    &dc->dc_chaninfo);
2439			if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
2440				regdomain_addchans(ci, &rd->bands_11ng, reg,
2441				    IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U,
2442				    &dc->dc_chaninfo);
2443				regdomain_addchans(ci, &rd->bands_11ng, reg,
2444				    IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D,
2445				    &dc->dc_chaninfo);
2446			}
2447		}
2448		qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]),
2449		    regdomain_sort);
2450	} else
2451		memcpy(ci, &dc->dc_chaninfo,
2452		    IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
2453}
2454
2455static void
2456list_countries(void)
2457{
2458	struct regdata *rdp = getregdata();
2459	const struct country *cp;
2460	const struct regdomain *dp;
2461	int i;
2462
2463	i = 0;
2464	printf("\nCountry codes:\n");
2465	LIST_FOREACH(cp, &rdp->countries, next) {
2466		printf("%2s %-15.15s%s", cp->isoname,
2467		    cp->name, ((i+1)%4) == 0 ? "\n" : " ");
2468		i++;
2469	}
2470	i = 0;
2471	printf("\nRegulatory domains:\n");
2472	LIST_FOREACH(dp, &rdp->domains, next) {
2473		printf("%-15.15s%s", dp->name, ((i+1)%4) == 0 ? "\n" : " ");
2474		i++;
2475	}
2476	printf("\n");
2477}
2478
2479static void
2480defaultcountry(const struct regdomain *rd)
2481{
2482	struct regdata *rdp = getregdata();
2483	const struct country *cc;
2484
2485	cc = lib80211_country_findbycc(rdp, rd->cc->code);
2486	if (cc == NULL)
2487		errx(1, "internal error, ISO country code %d not "
2488		    "defined for regdomain %s", rd->cc->code, rd->name);
2489	regdomain.country = cc->code;
2490	regdomain.isocc[0] = cc->isoname[0];
2491	regdomain.isocc[1] = cc->isoname[1];
2492}
2493
2494static
2495DECL_CMD_FUNC(set80211regdomain, val, d)
2496{
2497	struct regdata *rdp = getregdata();
2498	const struct regdomain *rd;
2499
2500	rd = lib80211_regdomain_findbyname(rdp, val);
2501	if (rd == NULL) {
2502		char *eptr;
2503		long sku = strtol(val, &eptr, 0);
2504
2505		if (eptr != val)
2506			rd = lib80211_regdomain_findbysku(rdp, sku);
2507		if (eptr == val || rd == NULL)
2508			errx(1, "unknown regdomain %s", val);
2509	}
2510	getregdomain(s);
2511	regdomain.regdomain = rd->sku;
2512	if (regdomain.country == 0 && rd->cc != NULL) {
2513		/*
2514		 * No country code setup and there's a default
2515		 * one for this regdomain fill it in.
2516		 */
2517		defaultcountry(rd);
2518	}
2519	callback_register(setregdomain_cb, &regdomain);
2520}
2521
2522static
2523DECL_CMD_FUNC(set80211country, val, d)
2524{
2525	struct regdata *rdp = getregdata();
2526	const struct country *cc;
2527
2528	cc = lib80211_country_findbyname(rdp, val);
2529	if (cc == NULL) {
2530		char *eptr;
2531		long code = strtol(val, &eptr, 0);
2532
2533		if (eptr != val)
2534			cc = lib80211_country_findbycc(rdp, code);
2535		if (eptr == val || cc == NULL)
2536			errx(1, "unknown ISO country code %s", val);
2537	}
2538	getregdomain(s);
2539	regdomain.regdomain = cc->rd->sku;
2540	regdomain.country = cc->code;
2541	regdomain.isocc[0] = cc->isoname[0];
2542	regdomain.isocc[1] = cc->isoname[1];
2543	callback_register(setregdomain_cb, &regdomain);
2544}
2545
2546static void
2547set80211location(const char *val, int d, int s, const struct afswtch *rafp)
2548{
2549	getregdomain(s);
2550	regdomain.location = d;
2551	callback_register(setregdomain_cb, &regdomain);
2552}
2553
2554static void
2555set80211ecm(const char *val, int d, int s, const struct afswtch *rafp)
2556{
2557	getregdomain(s);
2558	regdomain.ecm = d;
2559	callback_register(setregdomain_cb, &regdomain);
2560}
2561
2562static void
2563LINE_INIT(char c)
2564{
2565	spacer = c;
2566	if (c == '\t')
2567		col = 8;
2568	else
2569		col = 1;
2570}
2571
2572static void
2573LINE_BREAK(void)
2574{
2575	if (spacer != '\t') {
2576		printf("\n");
2577		spacer = '\t';
2578	}
2579	col = 8;		/* 8-col tab */
2580}
2581
2582static void
2583LINE_CHECK(const char *fmt, ...)
2584{
2585	char buf[80];
2586	va_list ap;
2587	int n;
2588
2589	va_start(ap, fmt);
2590	n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
2591	va_end(ap);
2592	col += 1+n;
2593	if (col > MAXCOL) {
2594		LINE_BREAK();
2595		col += n;
2596	}
2597	buf[0] = spacer;
2598	printf("%s", buf);
2599	spacer = ' ';
2600}
2601
2602static int
2603getmaxrate(const uint8_t rates[15], uint8_t nrates)
2604{
2605	int i, maxrate = -1;
2606
2607	for (i = 0; i < nrates; i++) {
2608		int rate = rates[i] & IEEE80211_RATE_VAL;
2609		if (rate > maxrate)
2610			maxrate = rate;
2611	}
2612	return maxrate / 2;
2613}
2614
2615static const char *
2616getcaps(int capinfo)
2617{
2618	static char capstring[32];
2619	char *cp = capstring;
2620
2621	if (capinfo & IEEE80211_CAPINFO_ESS)
2622		*cp++ = 'E';
2623	if (capinfo & IEEE80211_CAPINFO_IBSS)
2624		*cp++ = 'I';
2625	if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
2626		*cp++ = 'c';
2627	if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
2628		*cp++ = 'C';
2629	if (capinfo & IEEE80211_CAPINFO_PRIVACY)
2630		*cp++ = 'P';
2631	if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
2632		*cp++ = 'S';
2633	if (capinfo & IEEE80211_CAPINFO_PBCC)
2634		*cp++ = 'B';
2635	if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
2636		*cp++ = 'A';
2637	if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
2638		*cp++ = 's';
2639	if (capinfo & IEEE80211_CAPINFO_RSN)
2640		*cp++ = 'R';
2641	if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
2642		*cp++ = 'D';
2643	*cp = '\0';
2644	return capstring;
2645}
2646
2647static const char *
2648getflags(int flags)
2649{
2650	static char flagstring[32];
2651	char *cp = flagstring;
2652
2653	if (flags & IEEE80211_NODE_AUTH)
2654		*cp++ = 'A';
2655	if (flags & IEEE80211_NODE_QOS)
2656		*cp++ = 'Q';
2657	if (flags & IEEE80211_NODE_ERP)
2658		*cp++ = 'E';
2659	if (flags & IEEE80211_NODE_PWR_MGT)
2660		*cp++ = 'P';
2661	if (flags & IEEE80211_NODE_HT) {
2662		*cp++ = 'H';
2663		if (flags & IEEE80211_NODE_HTCOMPAT)
2664			*cp++ = '+';
2665	}
2666	if (flags & IEEE80211_NODE_VHT)
2667		*cp++ = 'V';
2668	if (flags & IEEE80211_NODE_WPS)
2669		*cp++ = 'W';
2670	if (flags & IEEE80211_NODE_TSN)
2671		*cp++ = 'N';
2672	if (flags & IEEE80211_NODE_AMPDU_TX)
2673		*cp++ = 'T';
2674	if (flags & IEEE80211_NODE_AMPDU_RX)
2675		*cp++ = 'R';
2676	if (flags & IEEE80211_NODE_MIMO_PS) {
2677		*cp++ = 'M';
2678		if (flags & IEEE80211_NODE_MIMO_RTS)
2679			*cp++ = '+';
2680	}
2681	if (flags & IEEE80211_NODE_RIFS)
2682		*cp++ = 'I';
2683	if (flags & IEEE80211_NODE_SGI40) {
2684		*cp++ = 'S';
2685		if (flags & IEEE80211_NODE_SGI20)
2686			*cp++ = '+';
2687	} else if (flags & IEEE80211_NODE_SGI20)
2688		*cp++ = 's';
2689	if (flags & IEEE80211_NODE_AMSDU_TX)
2690		*cp++ = 't';
2691	if (flags & IEEE80211_NODE_AMSDU_RX)
2692		*cp++ = 'r';
2693	if (flags & IEEE80211_NODE_UAPSD)
2694		*cp++ = 'U';
2695	if (flags & IEEE80211_NODE_LDPC)
2696		*cp++ = 'L';
2697	*cp = '\0';
2698	return flagstring;
2699}
2700
2701static void
2702printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
2703{
2704	printf("%s", tag);
2705	if (verbose) {
2706		maxlen -= strlen(tag)+2;
2707		if (2*ielen > maxlen)
2708			maxlen--;
2709		printf("<");
2710		for (; ielen > 0; ie++, ielen--) {
2711			if (maxlen-- <= 0)
2712				break;
2713			printf("%02x", *ie);
2714		}
2715		if (ielen != 0)
2716			printf("-");
2717		printf(">");
2718	}
2719}
2720
2721#define LE_READ_2(p)					\
2722	((u_int16_t)					\
2723	 ((((const u_int8_t *)(p))[0]      ) |		\
2724	  (((const u_int8_t *)(p))[1] <<  8)))
2725#define LE_READ_4(p)					\
2726	((u_int32_t)					\
2727	 ((((const u_int8_t *)(p))[0]      ) |		\
2728	  (((const u_int8_t *)(p))[1] <<  8) |		\
2729	  (((const u_int8_t *)(p))[2] << 16) |		\
2730	  (((const u_int8_t *)(p))[3] << 24)))
2731
2732/*
2733 * NB: The decoding routines assume a properly formatted ie
2734 *     which should be safe as the kernel only retains them
2735 *     if they parse ok.
2736 */
2737
2738static void
2739printwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2740{
2741	static const char *acnames[] = { "BE", "BK", "VO", "VI" };
2742	const struct ieee80211_wme_param *wme =
2743	    (const struct ieee80211_wme_param *) ie;
2744	int i;
2745
2746	printf("%s", tag);
2747	if (!verbose)
2748		return;
2749	printf("<qosinfo 0x%x", wme->param_qosInfo);
2750	ie += offsetof(struct ieee80211_wme_param, params_acParams);
2751	for (i = 0; i < WME_NUM_AC; i++) {
2752		const struct ieee80211_wme_acparams *ac =
2753		    &wme->params_acParams[i];
2754
2755		printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]", acnames[i],
2756		    _IEEE80211_MASKSHIFT(ac->acp_aci_aifsn, WME_PARAM_ACM) ?
2757			"acm " : "",
2758		    _IEEE80211_MASKSHIFT(ac->acp_aci_aifsn, WME_PARAM_AIFSN),
2759		    _IEEE80211_MASKSHIFT(ac->acp_logcwminmax,
2760			WME_PARAM_LOGCWMIN),
2761		    _IEEE80211_MASKSHIFT(ac->acp_logcwminmax,
2762			WME_PARAM_LOGCWMAX),
2763		    LE_READ_2(&ac->acp_txop));
2764	}
2765	printf(">");
2766}
2767
2768static void
2769printwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2770{
2771	printf("%s", tag);
2772	if (verbose) {
2773		const struct ieee80211_wme_info *wme =
2774		    (const struct ieee80211_wme_info *) ie;
2775		printf("<version 0x%x info 0x%x>",
2776		    wme->wme_version, wme->wme_info);
2777	}
2778}
2779
2780static void
2781printvhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2782{
2783	printf("%s", tag);
2784	if (verbose) {
2785		const struct ieee80211_ie_vhtcap *vhtcap =
2786		    (const struct ieee80211_ie_vhtcap *) ie;
2787		uint32_t vhtcap_info = LE_READ_4(&vhtcap->vht_cap_info);
2788
2789		printf("<cap 0x%08x", vhtcap_info);
2790		printf(" rx_mcs_map 0x%x",
2791		    LE_READ_2(&vhtcap->supp_mcs.rx_mcs_map));
2792		printf(" rx_highest %d",
2793		    LE_READ_2(&vhtcap->supp_mcs.rx_highest) & 0x1fff);
2794		printf(" tx_mcs_map 0x%x",
2795		    LE_READ_2(&vhtcap->supp_mcs.tx_mcs_map));
2796		printf(" tx_highest %d",
2797		    LE_READ_2(&vhtcap->supp_mcs.tx_highest) & 0x1fff);
2798
2799		printf(">");
2800	}
2801}
2802
2803static void
2804printvhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2805{
2806	printf("%s", tag);
2807	if (verbose) {
2808		const struct ieee80211_ie_vht_operation *vhtinfo =
2809		    (const struct ieee80211_ie_vht_operation *) ie;
2810
2811		printf("<chw %d freq1_idx %d freq2_idx %d basic_mcs_set 0x%04x>",
2812		    vhtinfo->chan_width,
2813		    vhtinfo->center_freq_seg1_idx,
2814		    vhtinfo->center_freq_seg2_idx,
2815		    LE_READ_2(&vhtinfo->basic_mcs_set));
2816	}
2817}
2818
2819static void
2820printvhtpwrenv(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2821{
2822	printf("%s", tag);
2823	static const char *txpwrmap[] = {
2824		"20",
2825		"40",
2826		"80",
2827		"160",
2828	};
2829	if (verbose) {
2830		const struct ieee80211_ie_vht_txpwrenv *vhtpwr =
2831		    (const struct ieee80211_ie_vht_txpwrenv *) ie;
2832		int i, n;
2833		const char *sep = "";
2834
2835		/* Get count; trim at ielen */
2836		n = (vhtpwr->tx_info &
2837		    IEEE80211_VHT_TXPWRENV_INFO_COUNT_MASK) + 1;
2838		/* Trim at ielen */
2839		if (n > ielen - 3)
2840			n = ielen - 3;
2841		printf("<tx_info 0x%02x pwr:[", vhtpwr->tx_info);
2842		for (i = 0; i < n; i++) {
2843			printf("%s%s:%.2f", sep, txpwrmap[i],
2844			    ((float) ((int8_t) ie[i+3])) / 2.0);
2845			sep = " ";
2846		}
2847
2848		printf("]>");
2849	}
2850}
2851
2852static void
2853printhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2854{
2855	printf("%s", tag);
2856	if (verbose) {
2857		const struct ieee80211_ie_htcap *htcap =
2858		    (const struct ieee80211_ie_htcap *) ie;
2859		const char *sep;
2860		int i, j;
2861
2862		printf("<cap 0x%x param 0x%x",
2863		    LE_READ_2(&htcap->hc_cap), htcap->hc_param);
2864		printf(" mcsset[");
2865		sep = "";
2866		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2867			if (isset(htcap->hc_mcsset, i)) {
2868				for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2869					if (isclr(htcap->hc_mcsset, j))
2870						break;
2871				j--;
2872				if (i == j)
2873					printf("%s%u", sep, i);
2874				else
2875					printf("%s%u-%u", sep, i, j);
2876				i += j-i;
2877				sep = ",";
2878			}
2879		printf("] extcap 0x%x txbf 0x%x antenna 0x%x>",
2880		    LE_READ_2(&htcap->hc_extcap),
2881		    LE_READ_4(&htcap->hc_txbf),
2882		    htcap->hc_antenna);
2883	}
2884}
2885
2886static void
2887printhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2888{
2889	printf("%s", tag);
2890	if (verbose) {
2891		const struct ieee80211_ie_htinfo *htinfo =
2892		    (const struct ieee80211_ie_htinfo *) ie;
2893		const char *sep;
2894		int i, j;
2895
2896		printf("<ctl %u, %x,%x,%x,%x", htinfo->hi_ctrlchannel,
2897		    htinfo->hi_byte1, htinfo->hi_byte2, htinfo->hi_byte3,
2898		    LE_READ_2(&htinfo->hi_byte45));
2899		printf(" basicmcs[");
2900		sep = "";
2901		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2902			if (isset(htinfo->hi_basicmcsset, i)) {
2903				for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2904					if (isclr(htinfo->hi_basicmcsset, j))
2905						break;
2906				j--;
2907				if (i == j)
2908					printf("%s%u", sep, i);
2909				else
2910					printf("%s%u-%u", sep, i, j);
2911				i += j-i;
2912				sep = ",";
2913			}
2914		printf("]>");
2915	}
2916}
2917
2918static void
2919printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2920{
2921
2922	printf("%s", tag);
2923	if (verbose) {
2924		const struct ieee80211_ath_ie *ath =
2925			(const struct ieee80211_ath_ie *)ie;
2926
2927		printf("<");
2928		if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME)
2929			printf("DTURBO,");
2930		if (ath->ath_capability & ATHEROS_CAP_COMPRESSION)
2931			printf("COMP,");
2932		if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME)
2933			printf("FF,");
2934		if (ath->ath_capability & ATHEROS_CAP_XR)
2935			printf("XR,");
2936		if (ath->ath_capability & ATHEROS_CAP_AR)
2937			printf("AR,");
2938		if (ath->ath_capability & ATHEROS_CAP_BURST)
2939			printf("BURST,");
2940		if (ath->ath_capability & ATHEROS_CAP_WME)
2941			printf("WME,");
2942		if (ath->ath_capability & ATHEROS_CAP_BOOST)
2943			printf("BOOST,");
2944		printf("0x%x>", LE_READ_2(ath->ath_defkeyix));
2945	}
2946}
2947
2948
2949static void
2950printmeshconf(const char *tag, const uint8_t *ie, size_t ielen, int maxlen)
2951{
2952
2953	printf("%s", tag);
2954	if (verbose) {
2955		const struct ieee80211_meshconf_ie *mconf =
2956			(const struct ieee80211_meshconf_ie *)ie;
2957		printf("<PATH:");
2958		if (mconf->conf_pselid == IEEE80211_MESHCONF_PATH_HWMP)
2959			printf("HWMP");
2960		else
2961			printf("UNKNOWN");
2962		printf(" LINK:");
2963		if (mconf->conf_pmetid == IEEE80211_MESHCONF_METRIC_AIRTIME)
2964			printf("AIRTIME");
2965		else
2966			printf("UNKNOWN");
2967		printf(" CONGESTION:");
2968		if (mconf->conf_ccid == IEEE80211_MESHCONF_CC_DISABLED)
2969			printf("DISABLED");
2970		else
2971			printf("UNKNOWN");
2972		printf(" SYNC:");
2973		if (mconf->conf_syncid == IEEE80211_MESHCONF_SYNC_NEIGHOFF)
2974			printf("NEIGHOFF");
2975		else
2976			printf("UNKNOWN");
2977		printf(" AUTH:");
2978		if (mconf->conf_authid == IEEE80211_MESHCONF_AUTH_DISABLED)
2979			printf("DISABLED");
2980		else
2981			printf("UNKNOWN");
2982		printf(" FORM:0x%x CAPS:0x%x>", mconf->conf_form,
2983		    mconf->conf_cap);
2984	}
2985}
2986
2987static void
2988printbssload(const char *tag, const uint8_t *ie, size_t ielen, int maxlen)
2989{
2990	printf("%s", tag);
2991	if (verbose) {
2992		const struct ieee80211_bss_load_ie *bssload =
2993		    (const struct ieee80211_bss_load_ie *) ie;
2994		printf("<sta count %d, chan load %d, aac %d>",
2995		    LE_READ_2(&bssload->sta_count),
2996		    bssload->chan_load,
2997		    bssload->aac);
2998	}
2999}
3000
3001static void
3002printapchanrep(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3003{
3004	printf("%s", tag);
3005	if (verbose) {
3006		const struct ieee80211_ap_chan_report_ie *ap =
3007		    (const struct ieee80211_ap_chan_report_ie *) ie;
3008		const char *sep = "";
3009		int i;
3010
3011		printf("<class %u, chan:[", ap->i_class);
3012
3013		for (i = 3; i < ielen; i++) {
3014			printf("%s%u", sep, ie[i]);
3015			sep = ",";
3016		}
3017		printf("]>");
3018	}
3019}
3020
3021static const char *
3022wpa_cipher(const u_int8_t *sel)
3023{
3024#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
3025	u_int32_t w = LE_READ_4(sel);
3026
3027	switch (w) {
3028	case WPA_SEL(WPA_CSE_NULL):
3029		return "NONE";
3030	case WPA_SEL(WPA_CSE_WEP40):
3031		return "WEP40";
3032	case WPA_SEL(WPA_CSE_WEP104):
3033		return "WEP104";
3034	case WPA_SEL(WPA_CSE_TKIP):
3035		return "TKIP";
3036	case WPA_SEL(WPA_CSE_CCMP):
3037		return "AES-CCMP";
3038	}
3039	return "?";		/* NB: so 1<< is discarded */
3040#undef WPA_SEL
3041}
3042
3043static const char *
3044wpa_keymgmt(const u_int8_t *sel)
3045{
3046#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
3047	u_int32_t w = LE_READ_4(sel);
3048
3049	switch (w) {
3050	case WPA_SEL(WPA_ASE_8021X_UNSPEC):
3051		return "8021X-UNSPEC";
3052	case WPA_SEL(WPA_ASE_8021X_PSK):
3053		return "8021X-PSK";
3054	case WPA_SEL(WPA_ASE_NONE):
3055		return "NONE";
3056	}
3057	return "?";
3058#undef WPA_SEL
3059}
3060
3061static void
3062printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3063{
3064	u_int8_t len = ie[1];
3065
3066	printf("%s", tag);
3067	if (verbose) {
3068		const char *sep;
3069		int n;
3070
3071		ie += 6, len -= 4;		/* NB: len is payload only */
3072
3073		printf("<v%u", LE_READ_2(ie));
3074		ie += 2, len -= 2;
3075
3076		printf(" mc:%s", wpa_cipher(ie));
3077		ie += 4, len -= 4;
3078
3079		/* unicast ciphers */
3080		n = LE_READ_2(ie);
3081		ie += 2, len -= 2;
3082		sep = " uc:";
3083		for (; n > 0; n--) {
3084			printf("%s%s", sep, wpa_cipher(ie));
3085			ie += 4, len -= 4;
3086			sep = "+";
3087		}
3088
3089		/* key management algorithms */
3090		n = LE_READ_2(ie);
3091		ie += 2, len -= 2;
3092		sep = " km:";
3093		for (; n > 0; n--) {
3094			printf("%s%s", sep, wpa_keymgmt(ie));
3095			ie += 4, len -= 4;
3096			sep = "+";
3097		}
3098
3099		if (len > 2)		/* optional capabilities */
3100			printf(", caps 0x%x", LE_READ_2(ie));
3101		printf(">");
3102	}
3103}
3104
3105static const char *
3106rsn_cipher(const u_int8_t *sel)
3107{
3108#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
3109	u_int32_t w = LE_READ_4(sel);
3110
3111	switch (w) {
3112	case RSN_SEL(RSN_CSE_NULL):
3113		return "NONE";
3114	case RSN_SEL(RSN_CSE_WEP40):
3115		return "WEP40";
3116	case RSN_SEL(RSN_CSE_WEP104):
3117		return "WEP104";
3118	case RSN_SEL(RSN_CSE_TKIP):
3119		return "TKIP";
3120	case RSN_SEL(RSN_CSE_CCMP):
3121		return "AES-CCMP";
3122	case RSN_SEL(RSN_CSE_WRAP):
3123		return "AES-OCB";
3124	}
3125	return "?";
3126#undef WPA_SEL
3127}
3128
3129static const char *
3130rsn_keymgmt(const u_int8_t *sel)
3131{
3132#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
3133	u_int32_t w = LE_READ_4(sel);
3134
3135	switch (w) {
3136	case RSN_SEL(RSN_ASE_8021X_UNSPEC):
3137		return "8021X-UNSPEC";
3138	case RSN_SEL(RSN_ASE_8021X_PSK):
3139		return "8021X-PSK";
3140	case RSN_SEL(RSN_ASE_NONE):
3141		return "NONE";
3142	}
3143	return "?";
3144#undef RSN_SEL
3145}
3146
3147static void
3148printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3149{
3150	printf("%s", tag);
3151	if (verbose) {
3152		const char *sep;
3153		int n;
3154
3155		ie += 2, ielen -= 2;
3156
3157		printf("<v%u", LE_READ_2(ie));
3158		ie += 2, ielen -= 2;
3159
3160		printf(" mc:%s", rsn_cipher(ie));
3161		ie += 4, ielen -= 4;
3162
3163		/* unicast ciphers */
3164		n = LE_READ_2(ie);
3165		ie += 2, ielen -= 2;
3166		sep = " uc:";
3167		for (; n > 0; n--) {
3168			printf("%s%s", sep, rsn_cipher(ie));
3169			ie += 4, ielen -= 4;
3170			sep = "+";
3171		}
3172
3173		/* key management algorithms */
3174		n = LE_READ_2(ie);
3175		ie += 2, ielen -= 2;
3176		sep = " km:";
3177		for (; n > 0; n--) {
3178			printf("%s%s", sep, rsn_keymgmt(ie));
3179			ie += 4, ielen -= 4;
3180			sep = "+";
3181		}
3182
3183		if (ielen > 2)		/* optional capabilities */
3184			printf(", caps 0x%x", LE_READ_2(ie));
3185		/* XXXPMKID */
3186		printf(">");
3187	}
3188}
3189
3190#define BE_READ_2(p)					\
3191	((u_int16_t)					\
3192	 ((((const u_int8_t *)(p))[1]      ) |		\
3193	  (((const u_int8_t *)(p))[0] <<  8)))
3194
3195static void
3196printwpsie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3197{
3198	u_int8_t len = ie[1];
3199
3200	printf("%s", tag);
3201	if (verbose) {
3202		static const char *dev_pass_id[] = {
3203			"D",	/* Default (PIN) */
3204			"U",	/* User-specified */
3205			"M",	/* Machine-specified */
3206			"K",	/* Rekey */
3207			"P",	/* PushButton */
3208			"R"	/* Registrar-specified */
3209		};
3210		int n;
3211		int f;
3212
3213		ie +=6, len -= 4;		/* NB: len is payload only */
3214
3215		/* WPS IE in Beacon and Probe Resp frames have different fields */
3216		printf("<");
3217		while (len) {
3218			uint16_t tlv_type = BE_READ_2(ie);
3219			uint16_t tlv_len  = BE_READ_2(ie + 2);
3220			uint16_t cfg_mthd;
3221
3222			/* some devices broadcast invalid WPS frames */
3223			if (tlv_len > len) {
3224				printf("bad frame length tlv_type=0x%02x "
3225				    "tlv_len=%d len=%d", tlv_type, tlv_len,
3226				    len);
3227				break;
3228			}
3229
3230			ie += 4, len -= 4;
3231
3232			switch (tlv_type) {
3233			case IEEE80211_WPS_ATTR_VERSION:
3234				printf("v:%d.%d", *ie >> 4, *ie & 0xf);
3235				break;
3236			case IEEE80211_WPS_ATTR_AP_SETUP_LOCKED:
3237				printf(" ap_setup:%s", *ie ? "locked" :
3238				    "unlocked");
3239				break;
3240			case IEEE80211_WPS_ATTR_CONFIG_METHODS:
3241			case IEEE80211_WPS_ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
3242				if (tlv_type == IEEE80211_WPS_ATTR_SELECTED_REGISTRAR_CONFIG_METHODS)
3243					printf(" sel_reg_cfg_mthd:");
3244				else
3245					printf(" cfg_mthd:" );
3246				cfg_mthd = BE_READ_2(ie);
3247				f = 0;
3248				for (n = 15; n >= 0; n--) {
3249					if (f) {
3250						printf(",");
3251						f = 0;
3252					}
3253					switch (cfg_mthd & (1 << n)) {
3254					case 0:
3255						break;
3256					case IEEE80211_WPS_CONFIG_USBA:
3257						printf("usba");
3258						f++;
3259						break;
3260					case IEEE80211_WPS_CONFIG_ETHERNET:
3261						printf("ethernet");
3262						f++;
3263						break;
3264					case IEEE80211_WPS_CONFIG_LABEL:
3265						printf("label");
3266						f++;
3267						break;
3268					case IEEE80211_WPS_CONFIG_DISPLAY:
3269						if (!(cfg_mthd &
3270						    (IEEE80211_WPS_CONFIG_VIRT_DISPLAY |
3271						    IEEE80211_WPS_CONFIG_PHY_DISPLAY)))
3272						    {
3273							printf("display");
3274							f++;
3275						}
3276						break;
3277					case IEEE80211_WPS_CONFIG_EXT_NFC_TOKEN:
3278						printf("ext_nfc_tokenk");
3279						f++;
3280						break;
3281					case IEEE80211_WPS_CONFIG_INT_NFC_TOKEN:
3282						printf("int_nfc_token");
3283						f++;
3284						break;
3285					case IEEE80211_WPS_CONFIG_NFC_INTERFACE:
3286						printf("nfc_interface");
3287						f++;
3288						break;
3289					case IEEE80211_WPS_CONFIG_PUSHBUTTON:
3290						if (!(cfg_mthd &
3291						    (IEEE80211_WPS_CONFIG_VIRT_PUSHBUTTON |
3292						    IEEE80211_WPS_CONFIG_PHY_PUSHBUTTON))) {
3293							printf("push_button");
3294							f++;
3295						}
3296						break;
3297					case IEEE80211_WPS_CONFIG_KEYPAD:
3298						printf("keypad");
3299						f++;
3300						break;
3301					case IEEE80211_WPS_CONFIG_VIRT_PUSHBUTTON:
3302						printf("virtual_push_button");
3303						f++;
3304						break;
3305					case IEEE80211_WPS_CONFIG_PHY_PUSHBUTTON:
3306						printf("physical_push_button");
3307						f++;
3308						break;
3309					case IEEE80211_WPS_CONFIG_P2PS:
3310						printf("p2ps");
3311						f++;
3312						break;
3313					case IEEE80211_WPS_CONFIG_VIRT_DISPLAY:
3314						printf("virtual_display");
3315						f++;
3316						break;
3317					case IEEE80211_WPS_CONFIG_PHY_DISPLAY:
3318						printf("physical_display");
3319						f++;
3320						break;
3321					default:
3322						printf("unknown_wps_config<%04x>",
3323						    cfg_mthd & (1 << n));
3324						f++;
3325						break;
3326					}
3327				}
3328				break;
3329			case IEEE80211_WPS_ATTR_DEV_NAME:
3330				printf(" device_name:<%.*s>", tlv_len, ie);
3331				break;
3332			case IEEE80211_WPS_ATTR_DEV_PASSWORD_ID:
3333				n = LE_READ_2(ie);
3334				if (n < nitems(dev_pass_id))
3335					printf(" dpi:%s", dev_pass_id[n]);
3336				break;
3337			case IEEE80211_WPS_ATTR_MANUFACTURER:
3338				printf(" manufacturer:<%.*s>", tlv_len, ie);
3339				break;
3340			case IEEE80211_WPS_ATTR_MODEL_NAME:
3341				printf(" model_name:<%.*s>", tlv_len, ie);
3342				break;
3343			case IEEE80211_WPS_ATTR_MODEL_NUMBER:
3344				printf(" model_number:<%.*s>", tlv_len, ie);
3345				break;
3346			case IEEE80211_WPS_ATTR_PRIMARY_DEV_TYPE:
3347				printf(" prim_dev:");
3348				for (n = 0; n < tlv_len; n++)
3349					printf("%02x", ie[n]);
3350				break;
3351			case IEEE80211_WPS_ATTR_RF_BANDS:
3352				printf(" rf:");
3353				f = 0;
3354				for (n = 7; n >= 0; n--) {
3355					if (f) {
3356						printf(",");
3357						f = 0;
3358					}
3359					switch (*ie & (1 << n)) {
3360					case 0:
3361						break;
3362					case IEEE80211_WPS_RF_BAND_24GHZ:
3363						printf("2.4Ghz");
3364						f++;
3365						break;
3366					case IEEE80211_WPS_RF_BAND_50GHZ:
3367						printf("5Ghz");
3368						f++;
3369						break;
3370					case IEEE80211_WPS_RF_BAND_600GHZ:
3371						printf("60Ghz");
3372						f++;
3373						break;
3374					default:
3375						printf("unknown<%02x>",
3376						    *ie & (1 << n));
3377						f++;
3378						break;
3379					}
3380				}
3381				break;
3382			case IEEE80211_WPS_ATTR_RESPONSE_TYPE:
3383				printf(" resp_type:0x%02x", *ie);
3384				break;
3385			case IEEE80211_WPS_ATTR_SELECTED_REGISTRAR:
3386				printf(" sel:%s", *ie ? "T" : "F");
3387				break;
3388			case IEEE80211_WPS_ATTR_SERIAL_NUMBER:
3389				printf(" serial_number:<%.*s>", tlv_len, ie);
3390				break;
3391			case IEEE80211_WPS_ATTR_UUID_E:
3392				printf(" uuid-e:");
3393				for (n = 0; n < (tlv_len - 1); n++)
3394					printf("%02x-", ie[n]);
3395				printf("%02x", ie[n]);
3396				break;
3397			case IEEE80211_WPS_ATTR_VENDOR_EXT:
3398				printf(" vendor:");
3399				for (n = 0; n < tlv_len; n++)
3400					printf("%02x", ie[n]);
3401				break;
3402			case IEEE80211_WPS_ATTR_WPS_STATE:
3403				switch (*ie) {
3404				case IEEE80211_WPS_STATE_NOT_CONFIGURED:
3405					printf(" state:N");
3406					break;
3407				case IEEE80211_WPS_STATE_CONFIGURED:
3408					printf(" state:C");
3409					break;
3410				default:
3411					printf(" state:B<%02x>", *ie);
3412					break;
3413				}
3414				break;
3415			default:
3416				printf(" unknown_wps_attr:0x%x", tlv_type);
3417				break;
3418			}
3419			ie += tlv_len, len -= tlv_len;
3420		}
3421		printf(">");
3422	}
3423}
3424
3425static void
3426printtdmaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3427{
3428	printf("%s", tag);
3429	if (verbose && ielen >= sizeof(struct ieee80211_tdma_param)) {
3430		const struct ieee80211_tdma_param *tdma =
3431		   (const struct ieee80211_tdma_param *) ie;
3432
3433		/* XXX tstamp */
3434		printf("<v%u slot:%u slotcnt:%u slotlen:%u bintval:%u inuse:0x%x>",
3435		    tdma->tdma_version, tdma->tdma_slot, tdma->tdma_slotcnt,
3436		    LE_READ_2(&tdma->tdma_slotlen), tdma->tdma_bintval,
3437		    tdma->tdma_inuse[0]);
3438	}
3439}
3440
3441/*
3442 * Copy the ssid string contents into buf, truncating to fit.  If the
3443 * ssid is entirely printable then just copy intact.  Otherwise convert
3444 * to hexadecimal.  If the result is truncated then replace the last
3445 * three characters with "...".
3446 */
3447static int
3448copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
3449{
3450	const u_int8_t *p;
3451	size_t maxlen;
3452	u_int i;
3453
3454	if (essid_len > bufsize)
3455		maxlen = bufsize;
3456	else
3457		maxlen = essid_len;
3458	/* determine printable or not */
3459	for (i = 0, p = essid; i < maxlen; i++, p++) {
3460		if (*p < ' ' || *p > 0x7e)
3461			break;
3462	}
3463	if (i != maxlen) {		/* not printable, print as hex */
3464		if (bufsize < 3)
3465			return 0;
3466		strlcpy(buf, "0x", bufsize);
3467		bufsize -= 2;
3468		p = essid;
3469		for (i = 0; i < maxlen && bufsize >= 2; i++) {
3470			sprintf(&buf[2+2*i], "%02x", p[i]);
3471			bufsize -= 2;
3472		}
3473		if (i != essid_len)
3474			memcpy(&buf[2+2*i-3], "...", 3);
3475	} else {			/* printable, truncate as needed */
3476		memcpy(buf, essid, maxlen);
3477		if (maxlen != essid_len)
3478			memcpy(&buf[maxlen-3], "...", 3);
3479	}
3480	return maxlen;
3481}
3482
3483static void
3484printssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3485{
3486	char ssid[2*IEEE80211_NWID_LEN+1];
3487
3488	printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid);
3489}
3490
3491static void
3492printrates(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3493{
3494	const char *sep;
3495	int i;
3496
3497	printf("%s", tag);
3498	sep = "<";
3499	for (i = 2; i < ielen; i++) {
3500		printf("%s%s%d", sep,
3501		    ie[i] & IEEE80211_RATE_BASIC ? "B" : "",
3502		    ie[i] & IEEE80211_RATE_VAL);
3503		sep = ",";
3504	}
3505	printf(">");
3506}
3507
3508static void
3509printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3510{
3511	const struct ieee80211_country_ie *cie =
3512	   (const struct ieee80211_country_ie *) ie;
3513	int i, nbands, schan, nchan;
3514
3515	printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]);
3516	nbands = (cie->len - 3) / sizeof(cie->band[0]);
3517	for (i = 0; i < nbands; i++) {
3518		schan = cie->band[i].schan;
3519		nchan = cie->band[i].nchan;
3520		if (nchan != 1)
3521			printf(" %u-%u,%u", schan, schan + nchan-1,
3522			    cie->band[i].maxtxpwr);
3523		else
3524			printf(" %u,%u", schan, cie->band[i].maxtxpwr);
3525	}
3526	printf(">");
3527}
3528
3529static __inline int
3530iswpaoui(const u_int8_t *frm)
3531{
3532	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
3533}
3534
3535static __inline int
3536iswmeinfo(const u_int8_t *frm)
3537{
3538	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
3539		frm[6] == WME_INFO_OUI_SUBTYPE;
3540}
3541
3542static __inline int
3543iswmeparam(const u_int8_t *frm)
3544{
3545	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
3546		frm[6] == WME_PARAM_OUI_SUBTYPE;
3547}
3548
3549static __inline int
3550isatherosoui(const u_int8_t *frm)
3551{
3552	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
3553}
3554
3555static __inline int
3556istdmaoui(const uint8_t *frm)
3557{
3558	return frm[1] > 3 && LE_READ_4(frm+2) == ((TDMA_OUI_TYPE<<24)|TDMA_OUI);
3559}
3560
3561static __inline int
3562iswpsoui(const uint8_t *frm)
3563{
3564	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPS_OUI_TYPE<<24)|WPA_OUI);
3565}
3566
3567static const char *
3568iename(int elemid)
3569{
3570	static char iename_buf[64];
3571	switch (elemid) {
3572	case IEEE80211_ELEMID_FHPARMS:	return " FHPARMS";
3573	case IEEE80211_ELEMID_CFPARMS:	return " CFPARMS";
3574	case IEEE80211_ELEMID_TIM:	return " TIM";
3575	case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS";
3576	case IEEE80211_ELEMID_BSSLOAD:	return " BSSLOAD";
3577	case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE";
3578	case IEEE80211_ELEMID_PWRCNSTR:	return " PWRCNSTR";
3579	case IEEE80211_ELEMID_PWRCAP:	return " PWRCAP";
3580	case IEEE80211_ELEMID_TPCREQ:	return " TPCREQ";
3581	case IEEE80211_ELEMID_TPCREP:	return " TPCREP";
3582	case IEEE80211_ELEMID_SUPPCHAN:	return " SUPPCHAN";
3583	case IEEE80211_ELEMID_CSA:	return " CSA";
3584	case IEEE80211_ELEMID_MEASREQ:	return " MEASREQ";
3585	case IEEE80211_ELEMID_MEASREP:	return " MEASREP";
3586	case IEEE80211_ELEMID_QUIET:	return " QUIET";
3587	case IEEE80211_ELEMID_IBSSDFS:	return " IBSSDFS";
3588	case IEEE80211_ELEMID_RESERVED_47:
3589					return " RESERVED_47";
3590	case IEEE80211_ELEMID_MOBILITY_DOMAIN:
3591					return " MOBILITY_DOMAIN";
3592	case IEEE80211_ELEMID_RRM_ENACAPS:
3593					return " RRM_ENCAPS";
3594	case IEEE80211_ELEMID_OVERLAP_BSS_SCAN_PARAM:
3595					return " OVERLAP_BSS";
3596	case IEEE80211_ELEMID_TPC:	return " TPC";
3597	case IEEE80211_ELEMID_CCKM:	return " CCKM";
3598	case IEEE80211_ELEMID_EXTCAP:	return " EXTCAP";
3599	}
3600	snprintf(iename_buf, sizeof(iename_buf), " UNKNOWN_ELEMID_%d",
3601	    elemid);
3602	return (const char *) iename_buf;
3603}
3604
3605static void
3606printies(const u_int8_t *vp, int ielen, int maxcols)
3607{
3608	while (ielen > 0) {
3609		switch (vp[0]) {
3610		case IEEE80211_ELEMID_SSID:
3611			if (verbose)
3612				printssid(" SSID", vp, 2+vp[1], maxcols);
3613			break;
3614		case IEEE80211_ELEMID_RATES:
3615		case IEEE80211_ELEMID_XRATES:
3616			if (verbose)
3617				printrates(vp[0] == IEEE80211_ELEMID_RATES ?
3618				    " RATES" : " XRATES", vp, 2+vp[1], maxcols);
3619			break;
3620		case IEEE80211_ELEMID_DSPARMS:
3621			if (verbose)
3622				printf(" DSPARMS<%u>", vp[2]);
3623			break;
3624		case IEEE80211_ELEMID_COUNTRY:
3625			if (verbose)
3626				printcountry(" COUNTRY", vp, 2+vp[1], maxcols);
3627			break;
3628		case IEEE80211_ELEMID_ERP:
3629			if (verbose)
3630				printf(" ERP<0x%x>", vp[2]);
3631			break;
3632		case IEEE80211_ELEMID_VENDOR:
3633			if (iswpaoui(vp))
3634				printwpaie(" WPA", vp, 2+vp[1], maxcols);
3635			else if (iswmeinfo(vp))
3636				printwmeinfo(" WME", vp, 2+vp[1], maxcols);
3637			else if (iswmeparam(vp))
3638				printwmeparam(" WME", vp, 2+vp[1], maxcols);
3639			else if (isatherosoui(vp))
3640				printathie(" ATH", vp, 2+vp[1], maxcols);
3641			else if (iswpsoui(vp))
3642				printwpsie(" WPS", vp, 2+vp[1], maxcols);
3643			else if (istdmaoui(vp))
3644				printtdmaie(" TDMA", vp, 2+vp[1], maxcols);
3645			else if (verbose)
3646				printie(" VEN", vp, 2+vp[1], maxcols);
3647			break;
3648		case IEEE80211_ELEMID_RSN:
3649			printrsnie(" RSN", vp, 2+vp[1], maxcols);
3650			break;
3651		case IEEE80211_ELEMID_HTCAP:
3652			printhtcap(" HTCAP", vp, 2+vp[1], maxcols);
3653			break;
3654		case IEEE80211_ELEMID_HTINFO:
3655			if (verbose)
3656				printhtinfo(" HTINFO", vp, 2+vp[1], maxcols);
3657			break;
3658		case IEEE80211_ELEMID_MESHID:
3659			if (verbose)
3660				printssid(" MESHID", vp, 2+vp[1], maxcols);
3661			break;
3662		case IEEE80211_ELEMID_MESHCONF:
3663			printmeshconf(" MESHCONF", vp, 2+vp[1], maxcols);
3664			break;
3665		case IEEE80211_ELEMID_VHT_CAP:
3666			printvhtcap(" VHTCAP", vp, 2+vp[1], maxcols);
3667			break;
3668		case IEEE80211_ELEMID_VHT_OPMODE:
3669			printvhtinfo(" VHTOPMODE", vp, 2+vp[1], maxcols);
3670			break;
3671		case IEEE80211_ELEMID_VHT_PWR_ENV:
3672			printvhtpwrenv(" VHTPWRENV", vp, 2+vp[1], maxcols);
3673			break;
3674		case IEEE80211_ELEMID_BSSLOAD:
3675			printbssload(" BSSLOAD", vp, 2+vp[1], maxcols);
3676			break;
3677		case IEEE80211_ELEMID_APCHANREP:
3678			printapchanrep(" APCHANREP", vp, 2+vp[1], maxcols);
3679			break;
3680		default:
3681			if (verbose)
3682				printie(iename(vp[0]), vp, 2+vp[1], maxcols);
3683			break;
3684		}
3685		ielen -= 2+vp[1];
3686		vp += 2+vp[1];
3687	}
3688}
3689
3690static void
3691printmimo(const struct ieee80211_mimo_info *mi)
3692{
3693	int i;
3694	int r = 0;
3695
3696	for (i = 0; i < IEEE80211_MAX_CHAINS; i++) {
3697		if (mi->ch[i].rssi != 0) {
3698			r = 1;
3699			break;
3700		}
3701	}
3702
3703	/* NB: don't muddy display unless there's something to show */
3704	if (r == 0)
3705		return;
3706
3707	/* XXX TODO: ignore EVM; secondary channels for now */
3708	printf(" (rssi %.1f:%.1f:%.1f:%.1f nf %d:%d:%d:%d)",
3709	    mi->ch[0].rssi[0] / 2.0,
3710	    mi->ch[1].rssi[0] / 2.0,
3711	    mi->ch[2].rssi[0] / 2.0,
3712	    mi->ch[3].rssi[0] / 2.0,
3713	    mi->ch[0].noise[0],
3714	    mi->ch[1].noise[0],
3715	    mi->ch[2].noise[0],
3716	    mi->ch[3].noise[0]);
3717}
3718
3719static void
3720list_scan(int s)
3721{
3722	uint8_t buf[24*1024];
3723	char ssid[IEEE80211_NWID_LEN+1];
3724	const uint8_t *cp;
3725	int len, idlen;
3726
3727	if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0)
3728		errx(1, "unable to get scan results");
3729	if (len < sizeof(struct ieee80211req_scan_result))
3730		return;
3731
3732	getchaninfo(s);
3733
3734	printf("%-*.*s  %-17.17s  %4s %4s   %-7s  %3s %4s\n"
3735		, IEEE80211_NWID_LEN, IEEE80211_NWID_LEN, "SSID/MESH ID"
3736		, "BSSID"
3737		, "CHAN"
3738		, "RATE"
3739		, " S:N"
3740		, "INT"
3741		, "CAPS"
3742	);
3743	cp = buf;
3744	do {
3745		const struct ieee80211req_scan_result *sr;
3746		const uint8_t *vp, *idp;
3747
3748		sr = (const struct ieee80211req_scan_result *) cp;
3749		vp = cp + sr->isr_ie_off;
3750		if (sr->isr_meshid_len) {
3751			idp = vp + sr->isr_ssid_len;
3752			idlen = sr->isr_meshid_len;
3753		} else {
3754			idp = vp;
3755			idlen = sr->isr_ssid_len;
3756		}
3757		printf("%-*.*s  %s  %3d  %3dM %4d:%-4d %4d %-4.4s"
3758			, IEEE80211_NWID_LEN
3759			  , copy_essid(ssid, IEEE80211_NWID_LEN, idp, idlen)
3760			  , ssid
3761			, ether_ntoa((const struct ether_addr *) sr->isr_bssid)
3762			, ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
3763			, getmaxrate(sr->isr_rates, sr->isr_nrates)
3764			, (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise
3765			, sr->isr_intval
3766			, getcaps(sr->isr_capinfo)
3767		);
3768		printies(vp + sr->isr_ssid_len + sr->isr_meshid_len,
3769		    sr->isr_ie_len, 24);
3770		printf("\n");
3771		cp += sr->isr_len, len -= sr->isr_len;
3772	} while (len >= sizeof(struct ieee80211req_scan_result));
3773}
3774
3775static void
3776scan_and_wait(int s)
3777{
3778	struct ieee80211_scan_req sr;
3779	struct ieee80211req ireq;
3780	int sroute;
3781
3782	sroute = socket(PF_ROUTE, SOCK_RAW, 0);
3783	if (sroute < 0) {
3784		perror("socket(PF_ROUTE,SOCK_RAW)");
3785		return;
3786	}
3787	(void) memset(&ireq, 0, sizeof(ireq));
3788	(void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
3789	ireq.i_type = IEEE80211_IOC_SCAN_REQ;
3790
3791	memset(&sr, 0, sizeof(sr));
3792	sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
3793		    | IEEE80211_IOC_SCAN_BGSCAN
3794		    | IEEE80211_IOC_SCAN_NOPICK
3795		    | IEEE80211_IOC_SCAN_ONCE;
3796	sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
3797	sr.sr_nssid = 0;
3798
3799	ireq.i_data = &sr;
3800	ireq.i_len = sizeof(sr);
3801	/*
3802	 * NB: only root can trigger a scan so ignore errors. Also ignore
3803	 * possible errors from net80211, even if no new scan could be
3804	 * started there might still be a valid scan cache.
3805	 */
3806	if (ioctl(s, SIOCS80211, &ireq) == 0) {
3807		char buf[2048];
3808		struct if_announcemsghdr *ifan;
3809		struct rt_msghdr *rtm;
3810
3811		do {
3812			if (read(sroute, buf, sizeof(buf)) < 0) {
3813				perror("read(PF_ROUTE)");
3814				break;
3815			}
3816			rtm = (struct rt_msghdr *) buf;
3817			if (rtm->rtm_version != RTM_VERSION)
3818				break;
3819			ifan = (struct if_announcemsghdr *) rtm;
3820		} while (rtm->rtm_type != RTM_IEEE80211 ||
3821		    ifan->ifan_what != RTM_IEEE80211_SCAN);
3822	}
3823	close(sroute);
3824}
3825
3826static
3827DECL_CMD_FUNC(set80211scan, val, d)
3828{
3829	scan_and_wait(s);
3830	list_scan(s);
3831}
3832
3833static enum ieee80211_opmode get80211opmode(int s);
3834
3835static int
3836gettxseq(const struct ieee80211req_sta_info *si)
3837{
3838	int i, txseq;
3839
3840	if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3841		return si->isi_txseqs[0];
3842	/* XXX not right but usually what folks want */
3843	txseq = 0;
3844	for (i = 0; i < IEEE80211_TID_SIZE; i++)
3845		if (si->isi_txseqs[i] > txseq)
3846			txseq = si->isi_txseqs[i];
3847	return txseq;
3848}
3849
3850static int
3851getrxseq(const struct ieee80211req_sta_info *si)
3852{
3853	int i, rxseq;
3854
3855	if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3856		return si->isi_rxseqs[0];
3857	/* XXX not right but usually what folks want */
3858	rxseq = 0;
3859	for (i = 0; i < IEEE80211_TID_SIZE; i++)
3860		if (si->isi_rxseqs[i] > rxseq)
3861			rxseq = si->isi_rxseqs[i];
3862	return rxseq;
3863}
3864
3865static void
3866list_stations(int s)
3867{
3868	union {
3869		struct ieee80211req_sta_req req;
3870		uint8_t buf[24*1024];
3871	} u;
3872	enum ieee80211_opmode opmode = get80211opmode(s);
3873	const uint8_t *cp;
3874	int len;
3875
3876	/* broadcast address =>'s get all stations */
3877	(void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
3878	if (opmode == IEEE80211_M_STA) {
3879		/*
3880		 * Get information about the associated AP.
3881		 */
3882		(void) get80211(s, IEEE80211_IOC_BSSID,
3883		    u.req.is_u.macaddr, IEEE80211_ADDR_LEN);
3884	}
3885	if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0)
3886		errx(1, "unable to get station information");
3887	if (len < sizeof(struct ieee80211req_sta_info))
3888		return;
3889
3890	getchaninfo(s);
3891
3892	if (opmode == IEEE80211_M_MBSS)
3893		printf("%-17.17s %4s %5s %5s %7s %4s %4s %4s %6s %6s\n"
3894			, "ADDR"
3895			, "CHAN"
3896			, "LOCAL"
3897			, "PEER"
3898			, "STATE"
3899			, "RATE"
3900			, "RSSI"
3901			, "IDLE"
3902			, "TXSEQ"
3903			, "RXSEQ"
3904		);
3905	else
3906		printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %-12s\n"
3907			, "ADDR"
3908			, "AID"
3909			, "CHAN"
3910			, "RATE"
3911			, "RSSI"
3912			, "IDLE"
3913			, "TXSEQ"
3914			, "RXSEQ"
3915			, "CAPS"
3916			, "FLAG"
3917		);
3918	cp = (const uint8_t *) u.req.info;
3919	do {
3920		const struct ieee80211req_sta_info *si;
3921
3922		si = (const struct ieee80211req_sta_info *) cp;
3923		if (si->isi_len < sizeof(*si))
3924			break;
3925		if (opmode == IEEE80211_M_MBSS)
3926			printf("%s %4d %5x %5x %7.7s %3dM %4.1f %4d %6d %6d"
3927				, ether_ntoa((const struct ether_addr*)
3928				    si->isi_macaddr)
3929				, ieee80211_mhz2ieee(si->isi_freq,
3930				    si->isi_flags)
3931				, si->isi_localid
3932				, si->isi_peerid
3933				, mesh_linkstate_string(si->isi_peerstate)
3934				, si->isi_txmbps/2
3935				, si->isi_rssi/2.
3936				, si->isi_inact
3937				, gettxseq(si)
3938				, getrxseq(si)
3939			);
3940		else
3941			printf("%s %4u %4d %3dM %4.1f %4d %6d %6d %-4.4s %-12.12s"
3942				, ether_ntoa((const struct ether_addr*)
3943				    si->isi_macaddr)
3944				, IEEE80211_AID(si->isi_associd)
3945				, ieee80211_mhz2ieee(si->isi_freq,
3946				    si->isi_flags)
3947				, si->isi_txmbps/2
3948				, si->isi_rssi/2.
3949				, si->isi_inact
3950				, gettxseq(si)
3951				, getrxseq(si)
3952				, getcaps(si->isi_capinfo)
3953				, getflags(si->isi_state)
3954			);
3955		printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
3956		printmimo(&si->isi_mimo);
3957		printf("\n");
3958		cp += si->isi_len, len -= si->isi_len;
3959	} while (len >= sizeof(struct ieee80211req_sta_info));
3960}
3961
3962static const char *
3963mesh_linkstate_string(uint8_t state)
3964{
3965	static const char *state_names[] = {
3966	    [0] = "IDLE",
3967	    [1] = "OPEN-TX",
3968	    [2] = "OPEN-RX",
3969	    [3] = "CONF-RX",
3970	    [4] = "ESTAB",
3971	    [5] = "HOLDING",
3972	};
3973
3974	if (state >= nitems(state_names)) {
3975		static char buf[10];
3976		snprintf(buf, sizeof(buf), "#%u", state);
3977		return buf;
3978	} else
3979		return state_names[state];
3980}
3981
3982static const char *
3983get_chaninfo(const struct ieee80211_channel *c, int precise,
3984	char buf[], size_t bsize)
3985{
3986	buf[0] = '\0';
3987	if (IEEE80211_IS_CHAN_FHSS(c))
3988		strlcat(buf, " FHSS", bsize);
3989	if (IEEE80211_IS_CHAN_A(c))
3990		strlcat(buf, " 11a", bsize);
3991	else if (IEEE80211_IS_CHAN_ANYG(c))
3992		strlcat(buf, " 11g", bsize);
3993	else if (IEEE80211_IS_CHAN_B(c))
3994		strlcat(buf, " 11b", bsize);
3995	if (IEEE80211_IS_CHAN_HALF(c))
3996		strlcat(buf, "/10MHz", bsize);
3997	if (IEEE80211_IS_CHAN_QUARTER(c))
3998		strlcat(buf, "/5MHz", bsize);
3999	if (IEEE80211_IS_CHAN_TURBO(c))
4000		strlcat(buf, " Turbo", bsize);
4001	if (precise) {
4002		if (IEEE80211_IS_CHAN_VHT80P80(c))
4003			strlcat(buf, " vht/80p80", bsize);
4004		else if (IEEE80211_IS_CHAN_VHT160(c))
4005			strlcat(buf, " vht/160", bsize);
4006		else if (IEEE80211_IS_CHAN_VHT80(c) &&
4007		    IEEE80211_IS_CHAN_HT40D(c))
4008			strlcat(buf, " vht/80-", bsize);
4009		else if (IEEE80211_IS_CHAN_VHT80(c) &&
4010		    IEEE80211_IS_CHAN_HT40U(c))
4011			strlcat(buf, " vht/80+", bsize);
4012		else if (IEEE80211_IS_CHAN_VHT80(c))
4013			strlcat(buf, " vht/80", bsize);
4014		else if (IEEE80211_IS_CHAN_VHT40D(c))
4015			strlcat(buf, " vht/40-", bsize);
4016		else if (IEEE80211_IS_CHAN_VHT40U(c))
4017			strlcat(buf, " vht/40+", bsize);
4018		else if (IEEE80211_IS_CHAN_VHT20(c))
4019			strlcat(buf, " vht/20", bsize);
4020		else if (IEEE80211_IS_CHAN_HT20(c))
4021			strlcat(buf, " ht/20", bsize);
4022		else if (IEEE80211_IS_CHAN_HT40D(c))
4023			strlcat(buf, " ht/40-", bsize);
4024		else if (IEEE80211_IS_CHAN_HT40U(c))
4025			strlcat(buf, " ht/40+", bsize);
4026	} else {
4027		if (IEEE80211_IS_CHAN_VHT(c))
4028			strlcat(buf, " vht", bsize);
4029		else if (IEEE80211_IS_CHAN_HT(c))
4030			strlcat(buf, " ht", bsize);
4031	}
4032	return buf;
4033}
4034
4035static void
4036print_chaninfo(const struct ieee80211_channel *c, int verb)
4037{
4038	char buf[14];
4039
4040	if (verb)
4041		printf("Channel %3u : %u%c%c%c%c%c MHz%-14.14s",
4042		    ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
4043		    IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
4044		    IEEE80211_IS_CHAN_DFS(c) ? 'D' : ' ',
4045		    IEEE80211_IS_CHAN_RADAR(c) ? 'R' : ' ',
4046		    IEEE80211_IS_CHAN_CWINT(c) ? 'I' : ' ',
4047		    IEEE80211_IS_CHAN_CACDONE(c) ? 'C' : ' ',
4048		    get_chaninfo(c, verb, buf, sizeof(buf)));
4049	else
4050	printf("Channel %3u : %u%c MHz%-14.14s",
4051	    ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
4052	    IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
4053	    get_chaninfo(c, verb, buf, sizeof(buf)));
4054
4055}
4056
4057static int
4058chanpref(const struct ieee80211_channel *c)
4059{
4060
4061	if (IEEE80211_IS_CHAN_VHT80P80(c))
4062		return 90;
4063	if (IEEE80211_IS_CHAN_VHT160(c))
4064		return 80;
4065	if (IEEE80211_IS_CHAN_VHT80(c))
4066		return 70;
4067	if (IEEE80211_IS_CHAN_VHT40(c))
4068		return 60;
4069	if (IEEE80211_IS_CHAN_VHT20(c))
4070		return 50;
4071	if (IEEE80211_IS_CHAN_HT40(c))
4072		return 40;
4073	if (IEEE80211_IS_CHAN_HT20(c))
4074		return 30;
4075	if (IEEE80211_IS_CHAN_HALF(c))
4076		return 10;
4077	if (IEEE80211_IS_CHAN_QUARTER(c))
4078		return 5;
4079	if (IEEE80211_IS_CHAN_TURBO(c))
4080		return 25;
4081	if (IEEE80211_IS_CHAN_A(c))
4082		return 20;
4083	if (IEEE80211_IS_CHAN_G(c))
4084		return 20;
4085	if (IEEE80211_IS_CHAN_B(c))
4086		return 15;
4087	if (IEEE80211_IS_CHAN_PUREG(c))
4088		return 15;
4089	return 0;
4090}
4091
4092static void
4093print_channels(int s, const struct ieee80211req_chaninfo *chans,
4094	int allchans, int verb)
4095{
4096	struct ieee80211req_chaninfo *achans;
4097	uint8_t reported[IEEE80211_CHAN_BYTES];
4098	const struct ieee80211_channel *c;
4099	int i, half;
4100
4101	achans = malloc(IEEE80211_CHANINFO_SPACE(chans));
4102	if (achans == NULL)
4103		errx(1, "no space for active channel list");
4104	achans->ic_nchans = 0;
4105	memset(reported, 0, sizeof(reported));
4106	if (!allchans) {
4107		struct ieee80211req_chanlist active;
4108
4109		if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0)
4110			errx(1, "unable to get active channel list");
4111		for (i = 0; i < chans->ic_nchans; i++) {
4112			c = &chans->ic_chans[i];
4113			if (!isset(active.ic_channels, c->ic_ieee))
4114				continue;
4115			/*
4116			 * Suppress compatible duplicates unless
4117			 * verbose.  The kernel gives us it's
4118			 * complete channel list which has separate
4119			 * entries for 11g/11b and 11a/turbo.
4120			 */
4121			if (isset(reported, c->ic_ieee) && !verb) {
4122				/* XXX we assume duplicates are adjacent */
4123				achans->ic_chans[achans->ic_nchans-1] = *c;
4124			} else {
4125				achans->ic_chans[achans->ic_nchans++] = *c;
4126				setbit(reported, c->ic_ieee);
4127			}
4128		}
4129	} else {
4130		for (i = 0; i < chans->ic_nchans; i++) {
4131			c = &chans->ic_chans[i];
4132			/* suppress duplicates as above */
4133			if (isset(reported, c->ic_ieee) && !verb) {
4134				/* XXX we assume duplicates are adjacent */
4135				struct ieee80211_channel *a =
4136				    &achans->ic_chans[achans->ic_nchans-1];
4137				if (chanpref(c) > chanpref(a))
4138					*a = *c;
4139			} else {
4140				achans->ic_chans[achans->ic_nchans++] = *c;
4141				setbit(reported, c->ic_ieee);
4142			}
4143		}
4144	}
4145	half = achans->ic_nchans / 2;
4146	if (achans->ic_nchans % 2)
4147		half++;
4148
4149	for (i = 0; i < achans->ic_nchans / 2; i++) {
4150		print_chaninfo(&achans->ic_chans[i], verb);
4151		print_chaninfo(&achans->ic_chans[half+i], verb);
4152		printf("\n");
4153	}
4154	if (achans->ic_nchans % 2) {
4155		print_chaninfo(&achans->ic_chans[i], verb);
4156		printf("\n");
4157	}
4158	free(achans);
4159}
4160
4161static void
4162list_channels(int s, int allchans)
4163{
4164	getchaninfo(s);
4165	print_channels(s, chaninfo, allchans, verbose);
4166}
4167
4168static void
4169print_txpow(const struct ieee80211_channel *c)
4170{
4171	printf("Channel %3u : %u MHz %3.1f reg %2d  ",
4172	    c->ic_ieee, c->ic_freq,
4173	    c->ic_maxpower/2., c->ic_maxregpower);
4174}
4175
4176static void
4177print_txpow_verbose(const struct ieee80211_channel *c)
4178{
4179	print_chaninfo(c, 1);
4180	printf("min %4.1f dBm  max %3.1f dBm  reg %2d dBm",
4181	    c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
4182	/* indicate where regulatory cap limits power use */
4183	if (c->ic_maxpower > 2*c->ic_maxregpower)
4184		printf(" <");
4185}
4186
4187static void
4188list_txpow(int s)
4189{
4190	struct ieee80211req_chaninfo *achans;
4191	uint8_t reported[IEEE80211_CHAN_BYTES];
4192	struct ieee80211_channel *c, *prev;
4193	int i, half;
4194
4195	getchaninfo(s);
4196	achans = malloc(IEEE80211_CHANINFO_SPACE(chaninfo));
4197	if (achans == NULL)
4198		errx(1, "no space for active channel list");
4199	achans->ic_nchans = 0;
4200	memset(reported, 0, sizeof(reported));
4201	for (i = 0; i < chaninfo->ic_nchans; i++) {
4202		c = &chaninfo->ic_chans[i];
4203		/* suppress duplicates as above */
4204		if (isset(reported, c->ic_ieee) && !verbose) {
4205			/* XXX we assume duplicates are adjacent */
4206			assert(achans->ic_nchans > 0);
4207			prev = &achans->ic_chans[achans->ic_nchans-1];
4208			/* display highest power on channel */
4209			if (c->ic_maxpower > prev->ic_maxpower)
4210				*prev = *c;
4211		} else {
4212			achans->ic_chans[achans->ic_nchans++] = *c;
4213			setbit(reported, c->ic_ieee);
4214		}
4215	}
4216	if (!verbose) {
4217		half = achans->ic_nchans / 2;
4218		if (achans->ic_nchans % 2)
4219			half++;
4220
4221		for (i = 0; i < achans->ic_nchans / 2; i++) {
4222			print_txpow(&achans->ic_chans[i]);
4223			print_txpow(&achans->ic_chans[half+i]);
4224			printf("\n");
4225		}
4226		if (achans->ic_nchans % 2) {
4227			print_txpow(&achans->ic_chans[i]);
4228			printf("\n");
4229		}
4230	} else {
4231		for (i = 0; i < achans->ic_nchans; i++) {
4232			print_txpow_verbose(&achans->ic_chans[i]);
4233			printf("\n");
4234		}
4235	}
4236	free(achans);
4237}
4238
4239static void
4240list_keys(int s)
4241{
4242}
4243
4244static void
4245list_capabilities(int s)
4246{
4247	struct ieee80211_devcaps_req *dc;
4248
4249	if (verbose)
4250		dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
4251	else
4252		dc = malloc(IEEE80211_DEVCAPS_SIZE(1));
4253	if (dc == NULL)
4254		errx(1, "no space for device capabilities");
4255	dc->dc_chaninfo.ic_nchans = verbose ? MAXCHAN : 1;
4256	getdevcaps(s, dc);
4257	printb("drivercaps", dc->dc_drivercaps, IEEE80211_C_BITS);
4258	if (dc->dc_cryptocaps != 0 || verbose) {
4259		putchar('\n');
4260		printb("cryptocaps", dc->dc_cryptocaps, IEEE80211_CRYPTO_BITS);
4261	}
4262	if (dc->dc_htcaps != 0 || verbose) {
4263		putchar('\n');
4264		printb("htcaps", dc->dc_htcaps, IEEE80211_HTCAP_BITS);
4265	}
4266	if (dc->dc_vhtcaps != 0 || verbose) {
4267		putchar('\n');
4268		printb("vhtcaps", dc->dc_vhtcaps, IEEE80211_VHTCAP_BITS);
4269	}
4270
4271	putchar('\n');
4272	if (verbose) {
4273		chaninfo = &dc->dc_chaninfo;	/* XXX */
4274		print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, verbose);
4275	}
4276	free(dc);
4277}
4278
4279static int
4280get80211wme(int s, int param, int ac, int *val)
4281{
4282	struct ieee80211req ireq;
4283
4284	(void) memset(&ireq, 0, sizeof(ireq));
4285	(void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
4286	ireq.i_type = param;
4287	ireq.i_len = ac;
4288	if (ioctl(s, SIOCG80211, &ireq) < 0) {
4289		warn("cannot get WME parameter %d, ac %d%s",
4290		    param, ac & IEEE80211_WMEPARAM_VAL,
4291		    ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : "");
4292		return -1;
4293	}
4294	*val = ireq.i_val;
4295	return 0;
4296}
4297
4298static void
4299list_wme_aci(int s, const char *tag, int ac)
4300{
4301	int val;
4302
4303	printf("\t%s", tag);
4304
4305	/* show WME BSS parameters */
4306	if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1)
4307		printf(" cwmin %2u", val);
4308	if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1)
4309		printf(" cwmax %2u", val);
4310	if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1)
4311		printf(" aifs %2u", val);
4312	if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1)
4313		printf(" txopLimit %3u", val);
4314	if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) {
4315		if (val)
4316			printf(" acm");
4317		else if (verbose)
4318			printf(" -acm");
4319	}
4320	/* !BSS only */
4321	if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
4322		if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) {
4323			if (!val)
4324				printf(" -ack");
4325			else if (verbose)
4326				printf(" ack");
4327		}
4328	}
4329	printf("\n");
4330}
4331
4332static void
4333list_wme(int s)
4334{
4335	static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
4336	int ac;
4337
4338	if (verbose) {
4339		/* display both BSS and local settings */
4340		for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
4341	again:
4342			if (ac & IEEE80211_WMEPARAM_BSS)
4343				list_wme_aci(s, "     ", ac);
4344			else
4345				list_wme_aci(s, acnames[ac], ac);
4346			if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
4347				ac |= IEEE80211_WMEPARAM_BSS;
4348				goto again;
4349			} else
4350				ac &= ~IEEE80211_WMEPARAM_BSS;
4351		}
4352	} else {
4353		/* display only channel settings */
4354		for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++)
4355			list_wme_aci(s, acnames[ac], ac);
4356	}
4357}
4358
4359static void
4360list_roam(int s)
4361{
4362	const struct ieee80211_roamparam *rp;
4363	int mode;
4364
4365	getroam(s);
4366	for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
4367		rp = &roamparams.params[mode];
4368		if (rp->rssi == 0 && rp->rate == 0)
4369			continue;
4370		if (mode == IEEE80211_MODE_11NA ||
4371		    mode == IEEE80211_MODE_11NG ||
4372		    mode == IEEE80211_MODE_VHT_2GHZ ||
4373		    mode == IEEE80211_MODE_VHT_5GHZ) {
4374			if (rp->rssi & 1)
4375				LINE_CHECK("roam:%-7.7s rssi %2u.5dBm  MCS %2u    ",
4376				    modename[mode], rp->rssi/2,
4377				    rp->rate &~ IEEE80211_RATE_MCS);
4378			else
4379				LINE_CHECK("roam:%-7.7s rssi %4udBm  MCS %2u    ",
4380				    modename[mode], rp->rssi/2,
4381				    rp->rate &~ IEEE80211_RATE_MCS);
4382		} else {
4383			if (rp->rssi & 1)
4384				LINE_CHECK("roam:%-7.7s rssi %2u.5dBm rate %2u Mb/s",
4385				    modename[mode], rp->rssi/2, rp->rate/2);
4386			else
4387				LINE_CHECK("roam:%-7.7s rssi %4udBm rate %2u Mb/s",
4388				    modename[mode], rp->rssi/2, rp->rate/2);
4389		}
4390	}
4391}
4392
4393/* XXX TODO: rate-to-string method... */
4394static const char*
4395get_mcs_mbs_rate_str(uint8_t rate)
4396{
4397	return (rate & IEEE80211_RATE_MCS) ? "MCS " : "Mb/s";
4398}
4399
4400static uint8_t
4401get_rate_value(uint8_t rate)
4402{
4403	if (rate & IEEE80211_RATE_MCS)
4404		return (rate &~ IEEE80211_RATE_MCS);
4405	return (rate / 2);
4406}
4407
4408static void
4409list_txparams(int s)
4410{
4411	const struct ieee80211_txparam *tp;
4412	int mode;
4413
4414	gettxparams(s);
4415	for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
4416		tp = &txparams.params[mode];
4417		if (tp->mgmtrate == 0 && tp->mcastrate == 0)
4418			continue;
4419		if (mode == IEEE80211_MODE_11NA ||
4420		    mode == IEEE80211_MODE_11NG ||
4421		    mode == IEEE80211_MODE_VHT_2GHZ ||
4422		    mode == IEEE80211_MODE_VHT_5GHZ) {
4423			if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
4424				LINE_CHECK("%-7.7s ucast NONE    mgmt %2u %s "
4425				    "mcast %2u %s maxretry %u",
4426				    modename[mode],
4427				    get_rate_value(tp->mgmtrate),
4428				    get_mcs_mbs_rate_str(tp->mgmtrate),
4429				    get_rate_value(tp->mcastrate),
4430				    get_mcs_mbs_rate_str(tp->mcastrate),
4431				    tp->maxretry);
4432			else
4433				LINE_CHECK("%-7.7s ucast %2u MCS  mgmt %2u %s "
4434				    "mcast %2u %s maxretry %u",
4435				    modename[mode],
4436				    tp->ucastrate &~ IEEE80211_RATE_MCS,
4437				    get_rate_value(tp->mgmtrate),
4438				    get_mcs_mbs_rate_str(tp->mgmtrate),
4439				    get_rate_value(tp->mcastrate),
4440				    get_mcs_mbs_rate_str(tp->mcastrate),
4441				    tp->maxretry);
4442		} else {
4443			if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
4444				LINE_CHECK("%-7.7s ucast NONE    mgmt %2u Mb/s "
4445				    "mcast %2u Mb/s maxretry %u",
4446				    modename[mode],
4447				    tp->mgmtrate/2,
4448				    tp->mcastrate/2, tp->maxretry);
4449			else
4450				LINE_CHECK("%-7.7s ucast %2u Mb/s mgmt %2u Mb/s "
4451				    "mcast %2u Mb/s maxretry %u",
4452				    modename[mode],
4453				    tp->ucastrate/2, tp->mgmtrate/2,
4454				    tp->mcastrate/2, tp->maxretry);
4455		}
4456	}
4457}
4458
4459static void
4460printpolicy(int policy)
4461{
4462	switch (policy) {
4463	case IEEE80211_MACCMD_POLICY_OPEN:
4464		printf("policy: open\n");
4465		break;
4466	case IEEE80211_MACCMD_POLICY_ALLOW:
4467		printf("policy: allow\n");
4468		break;
4469	case IEEE80211_MACCMD_POLICY_DENY:
4470		printf("policy: deny\n");
4471		break;
4472	case IEEE80211_MACCMD_POLICY_RADIUS:
4473		printf("policy: radius\n");
4474		break;
4475	default:
4476		printf("policy: unknown (%u)\n", policy);
4477		break;
4478	}
4479}
4480
4481static void
4482list_mac(int s)
4483{
4484	struct ieee80211req ireq;
4485	struct ieee80211req_maclist *acllist;
4486	int i, nacls, policy, len;
4487	uint8_t *data;
4488	char c;
4489
4490	(void) memset(&ireq, 0, sizeof(ireq));
4491	(void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
4492	ireq.i_type = IEEE80211_IOC_MACCMD;
4493	ireq.i_val = IEEE80211_MACCMD_POLICY;
4494	if (ioctl(s, SIOCG80211, &ireq) < 0) {
4495		if (errno == EINVAL) {
4496			printf("No acl policy loaded\n");
4497			return;
4498		}
4499		err(1, "unable to get mac policy");
4500	}
4501	policy = ireq.i_val;
4502	if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
4503		c = '*';
4504	} else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
4505		c = '+';
4506	} else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
4507		c = '-';
4508	} else if (policy == IEEE80211_MACCMD_POLICY_RADIUS) {
4509		c = 'r';		/* NB: should never have entries */
4510	} else {
4511		printf("policy: unknown (%u)\n", policy);
4512		c = '?';
4513	}
4514	if (verbose || c == '?')
4515		printpolicy(policy);
4516
4517	ireq.i_val = IEEE80211_MACCMD_LIST;
4518	ireq.i_len = 0;
4519	if (ioctl(s, SIOCG80211, &ireq) < 0)
4520		err(1, "unable to get mac acl list size");
4521	if (ireq.i_len == 0) {		/* NB: no acls */
4522		if (!(verbose || c == '?'))
4523			printpolicy(policy);
4524		return;
4525	}
4526	len = ireq.i_len;
4527
4528	data = malloc(len);
4529	if (data == NULL)
4530		err(1, "out of memory for acl list");
4531
4532	ireq.i_data = data;
4533	if (ioctl(s, SIOCG80211, &ireq) < 0)
4534		err(1, "unable to get mac acl list");
4535	nacls = len / sizeof(*acllist);
4536	acllist = (struct ieee80211req_maclist *) data;
4537	for (i = 0; i < nacls; i++)
4538		printf("%c%s\n", c, ether_ntoa(
4539			(const struct ether_addr *) acllist[i].ml_macaddr));
4540	free(data);
4541}
4542
4543static void
4544print_regdomain(const struct ieee80211_regdomain *reg, int verb)
4545{
4546	if ((reg->regdomain != 0 &&
4547	    reg->regdomain != reg->country) || verb) {
4548		const struct regdomain *rd =
4549		    lib80211_regdomain_findbysku(getregdata(), reg->regdomain);
4550		if (rd == NULL)
4551			LINE_CHECK("regdomain %d", reg->regdomain);
4552		else
4553			LINE_CHECK("regdomain %s", rd->name);
4554	}
4555	if (reg->country != 0 || verb) {
4556		const struct country *cc =
4557		    lib80211_country_findbycc(getregdata(), reg->country);
4558		if (cc == NULL)
4559			LINE_CHECK("country %d", reg->country);
4560		else
4561			LINE_CHECK("country %s", cc->isoname);
4562	}
4563	if (reg->location == 'I')
4564		LINE_CHECK("indoor");
4565	else if (reg->location == 'O')
4566		LINE_CHECK("outdoor");
4567	else if (verb)
4568		LINE_CHECK("anywhere");
4569	if (reg->ecm)
4570		LINE_CHECK("ecm");
4571	else if (verb)
4572		LINE_CHECK("-ecm");
4573}
4574
4575static void
4576list_regdomain(int s, int channelsalso)
4577{
4578	getregdomain(s);
4579	if (channelsalso) {
4580		getchaninfo(s);
4581		spacer = ':';
4582		print_regdomain(&regdomain, 1);
4583		LINE_BREAK();
4584		print_channels(s, chaninfo, 1/*allchans*/, 1/*verbose*/);
4585	} else
4586		print_regdomain(&regdomain, verbose);
4587}
4588
4589static void
4590list_mesh(int s)
4591{
4592	struct ieee80211req ireq;
4593	struct ieee80211req_mesh_route routes[128];
4594	struct ieee80211req_mesh_route *rt;
4595
4596	(void) memset(&ireq, 0, sizeof(ireq));
4597	(void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
4598	ireq.i_type = IEEE80211_IOC_MESH_RTCMD;
4599	ireq.i_val = IEEE80211_MESH_RTCMD_LIST;
4600	ireq.i_data = &routes;
4601	ireq.i_len = sizeof(routes);
4602	if (ioctl(s, SIOCG80211, &ireq) < 0)
4603	 	err(1, "unable to get the Mesh routing table");
4604
4605	printf("%-17.17s %-17.17s %4s %4s %4s %6s %s\n"
4606		, "DEST"
4607		, "NEXT HOP"
4608		, "HOPS"
4609		, "METRIC"
4610		, "LIFETIME"
4611		, "MSEQ"
4612		, "FLAGS");
4613
4614	for (rt = &routes[0]; rt - &routes[0] < ireq.i_len / sizeof(*rt); rt++){
4615		printf("%s ",
4616		    ether_ntoa((const struct ether_addr *)rt->imr_dest));
4617		printf("%s %4u   %4u   %6u %6u    %c%c\n",
4618			ether_ntoa((const struct ether_addr *)rt->imr_nexthop),
4619			rt->imr_nhops, rt->imr_metric, rt->imr_lifetime,
4620			rt->imr_lastmseq,
4621			(rt->imr_flags & IEEE80211_MESHRT_FLAGS_DISCOVER) ?
4622			    'D' :
4623			(rt->imr_flags & IEEE80211_MESHRT_FLAGS_VALID) ?
4624			    'V' : '!',
4625			(rt->imr_flags & IEEE80211_MESHRT_FLAGS_PROXY) ?
4626			    'P' :
4627			(rt->imr_flags & IEEE80211_MESHRT_FLAGS_GATE) ?
4628			    'G' :' ');
4629	}
4630}
4631
4632static
4633DECL_CMD_FUNC(set80211list, arg, d)
4634{
4635#define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
4636
4637	LINE_INIT('\t');
4638
4639	if (iseq(arg, "sta"))
4640		list_stations(s);
4641	else if (iseq(arg, "scan") || iseq(arg, "ap"))
4642		list_scan(s);
4643	else if (iseq(arg, "chan") || iseq(arg, "freq"))
4644		list_channels(s, 1);
4645	else if (iseq(arg, "active"))
4646		list_channels(s, 0);
4647	else if (iseq(arg, "keys"))
4648		list_keys(s);
4649	else if (iseq(arg, "caps"))
4650		list_capabilities(s);
4651	else if (iseq(arg, "wme") || iseq(arg, "wmm"))
4652		list_wme(s);
4653	else if (iseq(arg, "mac"))
4654		list_mac(s);
4655	else if (iseq(arg, "txpow"))
4656		list_txpow(s);
4657	else if (iseq(arg, "roam"))
4658		list_roam(s);
4659	else if (iseq(arg, "txparam") || iseq(arg, "txparm"))
4660		list_txparams(s);
4661	else if (iseq(arg, "regdomain"))
4662		list_regdomain(s, 1);
4663	else if (iseq(arg, "countries"))
4664		list_countries();
4665	else if (iseq(arg, "mesh"))
4666		list_mesh(s);
4667	else
4668		errx(1, "Don't know how to list %s for %s", arg, name);
4669	LINE_BREAK();
4670#undef iseq
4671}
4672
4673static enum ieee80211_opmode
4674get80211opmode(int s)
4675{
4676	struct ifmediareq ifmr;
4677
4678	(void) memset(&ifmr, 0, sizeof(ifmr));
4679	(void) strlcpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
4680
4681	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
4682		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
4683			if (ifmr.ifm_current & IFM_FLAG0)
4684				return IEEE80211_M_AHDEMO;
4685			else
4686				return IEEE80211_M_IBSS;
4687		}
4688		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
4689			return IEEE80211_M_HOSTAP;
4690		if (ifmr.ifm_current & IFM_IEEE80211_IBSS)
4691			return IEEE80211_M_IBSS;
4692		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
4693			return IEEE80211_M_MONITOR;
4694		if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
4695			return IEEE80211_M_MBSS;
4696	}
4697	return IEEE80211_M_STA;
4698}
4699
4700#if 0
4701static void
4702printcipher(int s, struct ieee80211req *ireq, int keylenop)
4703{
4704	switch (ireq->i_val) {
4705	case IEEE80211_CIPHER_WEP:
4706		ireq->i_type = keylenop;
4707		if (ioctl(s, SIOCG80211, ireq) != -1)
4708			printf("WEP-%s",
4709			    ireq->i_len <= 5 ? "40" :
4710			    ireq->i_len <= 13 ? "104" : "128");
4711		else
4712			printf("WEP");
4713		break;
4714	case IEEE80211_CIPHER_TKIP:
4715		printf("TKIP");
4716		break;
4717	case IEEE80211_CIPHER_AES_OCB:
4718		printf("AES-OCB");
4719		break;
4720	case IEEE80211_CIPHER_AES_CCM:
4721		printf("AES-CCM");
4722		break;
4723	case IEEE80211_CIPHER_CKIP:
4724		printf("CKIP");
4725		break;
4726	case IEEE80211_CIPHER_NONE:
4727		printf("NONE");
4728		break;
4729	default:
4730		printf("UNKNOWN (0x%x)", ireq->i_val);
4731		break;
4732	}
4733}
4734#endif
4735
4736static void
4737printkey(const struct ieee80211req_key *ik)
4738{
4739	static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
4740	u_int keylen = ik->ik_keylen;
4741	int printcontents;
4742
4743	printcontents = printkeys &&
4744		(memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
4745	if (printcontents)
4746		LINE_BREAK();
4747	switch (ik->ik_type) {
4748	case IEEE80211_CIPHER_WEP:
4749		/* compatibility */
4750		LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
4751		    keylen <= 5 ? "40-bit" :
4752		    keylen <= 13 ? "104-bit" : "128-bit");
4753		break;
4754	case IEEE80211_CIPHER_TKIP:
4755		if (keylen > 128/8)
4756			keylen -= 128/8;	/* ignore MIC for now */
4757		LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4758		break;
4759	case IEEE80211_CIPHER_AES_OCB:
4760		LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4761		break;
4762	case IEEE80211_CIPHER_AES_CCM:
4763		LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4764		break;
4765	case IEEE80211_CIPHER_CKIP:
4766		LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4767		break;
4768	case IEEE80211_CIPHER_NONE:
4769		LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4770		break;
4771	default:
4772		LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
4773			ik->ik_type, ik->ik_keyix+1, 8*keylen);
4774		break;
4775	}
4776	if (printcontents) {
4777		u_int i;
4778
4779		printf(" <");
4780		for (i = 0; i < keylen; i++)
4781			printf("%02x", ik->ik_keydata[i]);
4782		printf(">");
4783		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
4784		    (ik->ik_keyrsc != 0 || verbose))
4785			printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
4786		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
4787		    (ik->ik_keytsc != 0 || verbose))
4788			printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
4789		if (ik->ik_flags != 0 && verbose) {
4790			const char *sep = " ";
4791
4792			if (ik->ik_flags & IEEE80211_KEY_XMIT)
4793				printf("%stx", sep), sep = "+";
4794			if (ik->ik_flags & IEEE80211_KEY_RECV)
4795				printf("%srx", sep), sep = "+";
4796			if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
4797				printf("%sdef", sep), sep = "+";
4798		}
4799		LINE_BREAK();
4800	}
4801}
4802
4803static void
4804printrate(const char *tag, int v, int defrate, int defmcs)
4805{
4806	if ((v & IEEE80211_RATE_MCS) == 0) {
4807		if (v != defrate) {
4808			if (v & 1)
4809				LINE_CHECK("%s %d.5", tag, v/2);
4810			else
4811				LINE_CHECK("%s %d", tag, v/2);
4812		}
4813	} else {
4814		if (v != defmcs)
4815			LINE_CHECK("%s %d", tag, v &~ 0x80);
4816	}
4817}
4818
4819static int
4820getid(int s, int ix, void *data, size_t len, int *plen, int mesh)
4821{
4822	struct ieee80211req ireq;
4823
4824	(void) memset(&ireq, 0, sizeof(ireq));
4825	(void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
4826	ireq.i_type = (!mesh) ? IEEE80211_IOC_SSID : IEEE80211_IOC_MESH_ID;
4827	ireq.i_val = ix;
4828	ireq.i_data = data;
4829	ireq.i_len = len;
4830	if (ioctl(s, SIOCG80211, &ireq) < 0)
4831		return -1;
4832	*plen = ireq.i_len;
4833	return 0;
4834}
4835
4836static int
4837getdevicename(int s, void *data, size_t len, int *plen)
4838{
4839	struct ieee80211req ireq;
4840
4841	(void) memset(&ireq, 0, sizeof(ireq));
4842	(void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
4843	ireq.i_type = IEEE80211_IOC_IC_NAME;
4844	ireq.i_val = -1;
4845	ireq.i_data = data;
4846	ireq.i_len = len;
4847	if (ioctl(s, SIOCG80211, &ireq) < 0)
4848		return (-1);
4849	*plen = ireq.i_len;
4850	return (0);
4851}
4852
4853static void
4854ieee80211_status(int s)
4855{
4856	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
4857	enum ieee80211_opmode opmode = get80211opmode(s);
4858	int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode;
4859	uint8_t data[32];
4860	const struct ieee80211_channel *c;
4861	const struct ieee80211_roamparam *rp;
4862	const struct ieee80211_txparam *tp;
4863
4864	if (getid(s, -1, data, sizeof(data), &len, 0) < 0) {
4865		/* If we can't get the SSID, this isn't an 802.11 device. */
4866		return;
4867	}
4868
4869	/*
4870	 * Invalidate cached state so printing status for multiple
4871	 * if's doesn't reuse the first interfaces' cached state.
4872	 */
4873	gotcurchan = 0;
4874	gotroam = 0;
4875	gottxparams = 0;
4876	gothtconf = 0;
4877	gotregdomain = 0;
4878
4879	printf("\t");
4880	if (opmode == IEEE80211_M_MBSS) {
4881		printf("meshid ");
4882		getid(s, 0, data, sizeof(data), &len, 1);
4883		print_string(data, len);
4884	} else {
4885		if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
4886			num = 0;
4887		printf("ssid ");
4888		if (num > 1) {
4889			for (i = 0; i < num; i++) {
4890				if (getid(s, i, data, sizeof(data), &len, 0) >= 0 && len > 0) {
4891					printf(" %d:", i + 1);
4892					print_string(data, len);
4893				}
4894			}
4895		} else
4896			print_string(data, len);
4897	}
4898	c = getcurchan(s);
4899	if (c->ic_freq != IEEE80211_CHAN_ANY) {
4900		char buf[14];
4901		printf(" channel %d (%u MHz%s)", c->ic_ieee, c->ic_freq,
4902			get_chaninfo(c, 1, buf, sizeof(buf)));
4903	} else if (verbose)
4904		printf(" channel UNDEF");
4905
4906	if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 &&
4907	    (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
4908		printf(" bssid %s", ether_ntoa((struct ether_addr *)data));
4909
4910	if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) {
4911		printf("\n\tstationname ");
4912		print_string(data, len);
4913	}
4914
4915	spacer = ' ';		/* force first break */
4916	LINE_BREAK();
4917
4918	list_regdomain(s, 0);
4919
4920	wpa = 0;
4921	if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) {
4922		switch (val) {
4923		case IEEE80211_AUTH_NONE:
4924			LINE_CHECK("authmode NONE");
4925			break;
4926		case IEEE80211_AUTH_OPEN:
4927			LINE_CHECK("authmode OPEN");
4928			break;
4929		case IEEE80211_AUTH_SHARED:
4930			LINE_CHECK("authmode SHARED");
4931			break;
4932		case IEEE80211_AUTH_8021X:
4933			LINE_CHECK("authmode 802.1x");
4934			break;
4935		case IEEE80211_AUTH_WPA:
4936			if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0)
4937				wpa = 1;	/* default to WPA1 */
4938			switch (wpa) {
4939			case 2:
4940				LINE_CHECK("authmode WPA2/802.11i");
4941				break;
4942			case 3:
4943				LINE_CHECK("authmode WPA1+WPA2/802.11i");
4944				break;
4945			default:
4946				LINE_CHECK("authmode WPA");
4947				break;
4948			}
4949			break;
4950		case IEEE80211_AUTH_AUTO:
4951			LINE_CHECK("authmode AUTO");
4952			break;
4953		default:
4954			LINE_CHECK("authmode UNKNOWN (0x%x)", val);
4955			break;
4956		}
4957	}
4958
4959	if (wpa || verbose) {
4960		if (get80211val(s, IEEE80211_IOC_WPS, &val) != -1) {
4961			if (val)
4962				LINE_CHECK("wps");
4963			else if (verbose)
4964				LINE_CHECK("-wps");
4965		}
4966		if (get80211val(s, IEEE80211_IOC_TSN, &val) != -1) {
4967			if (val)
4968				LINE_CHECK("tsn");
4969			else if (verbose)
4970				LINE_CHECK("-tsn");
4971		}
4972		if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) {
4973			if (val)
4974				LINE_CHECK("countermeasures");
4975			else if (verbose)
4976				LINE_CHECK("-countermeasures");
4977		}
4978#if 0
4979		/* XXX not interesting with WPA done in user space */
4980		ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
4981		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4982		}
4983
4984		ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
4985		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4986			LINE_CHECK("mcastcipher ");
4987			printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
4988			spacer = ' ';
4989		}
4990
4991		ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
4992		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4993			LINE_CHECK("ucastcipher ");
4994			printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
4995		}
4996
4997		if (wpa & 2) {
4998			ireq.i_type = IEEE80211_IOC_RSNCAPS;
4999			if (ioctl(s, SIOCG80211, &ireq) != -1) {
5000				LINE_CHECK("RSN caps 0x%x", ireq.i_val);
5001				spacer = ' ';
5002			}
5003		}
5004
5005		ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
5006		if (ioctl(s, SIOCG80211, &ireq) != -1) {
5007		}
5008#endif
5009	}
5010
5011	if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 &&
5012	    wepmode != IEEE80211_WEP_NOSUP) {
5013
5014		switch (wepmode) {
5015		case IEEE80211_WEP_OFF:
5016			LINE_CHECK("privacy OFF");
5017			break;
5018		case IEEE80211_WEP_ON:
5019			LINE_CHECK("privacy ON");
5020			break;
5021		case IEEE80211_WEP_MIXED:
5022			LINE_CHECK("privacy MIXED");
5023			break;
5024		default:
5025			LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
5026			break;
5027		}
5028
5029		/*
5030		 * If we get here then we've got WEP support so we need
5031		 * to print WEP status.
5032		 */
5033
5034		if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) {
5035			warn("WEP support, but no tx key!");
5036			goto end;
5037		}
5038		if (val != -1)
5039			LINE_CHECK("deftxkey %d", val+1);
5040		else if (wepmode != IEEE80211_WEP_OFF || verbose)
5041			LINE_CHECK("deftxkey UNDEF");
5042
5043		if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) {
5044			warn("WEP support, but no NUMWEPKEYS support!");
5045			goto end;
5046		}
5047
5048		for (i = 0; i < num; i++) {
5049			struct ieee80211req_key ik;
5050
5051			memset(&ik, 0, sizeof(ik));
5052			ik.ik_keyix = i;
5053			if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) {
5054				warn("WEP support, but can get keys!");
5055				goto end;
5056			}
5057			if (ik.ik_keylen != 0) {
5058				if (verbose)
5059					LINE_BREAK();
5060				printkey(&ik);
5061			}
5062		}
5063end:
5064		;
5065	}
5066
5067	if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 &&
5068	    val != IEEE80211_POWERSAVE_NOSUP ) {
5069		if (val != IEEE80211_POWERSAVE_OFF || verbose) {
5070			switch (val) {
5071			case IEEE80211_POWERSAVE_OFF:
5072				LINE_CHECK("powersavemode OFF");
5073				break;
5074			case IEEE80211_POWERSAVE_CAM:
5075				LINE_CHECK("powersavemode CAM");
5076				break;
5077			case IEEE80211_POWERSAVE_PSP:
5078				LINE_CHECK("powersavemode PSP");
5079				break;
5080			case IEEE80211_POWERSAVE_PSP_CAM:
5081				LINE_CHECK("powersavemode PSP-CAM");
5082				break;
5083			}
5084			if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1)
5085				LINE_CHECK("powersavesleep %d", val);
5086		}
5087	}
5088
5089	if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) {
5090		if (val & 1)
5091			LINE_CHECK("txpower %d.5", val/2);
5092		else
5093			LINE_CHECK("txpower %d", val/2);
5094	}
5095	if (verbose) {
5096		if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1)
5097			LINE_CHECK("txpowmax %.1f", val/2.);
5098	}
5099
5100	if (get80211val(s, IEEE80211_IOC_DOTD, &val) != -1) {
5101		if (val)
5102			LINE_CHECK("dotd");
5103		else if (verbose)
5104			LINE_CHECK("-dotd");
5105	}
5106
5107	if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) {
5108		if (val != IEEE80211_RTS_MAX || verbose)
5109			LINE_CHECK("rtsthreshold %d", val);
5110	}
5111
5112	if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) {
5113		if (val != IEEE80211_FRAG_MAX || verbose)
5114			LINE_CHECK("fragthreshold %d", val);
5115	}
5116	if (opmode == IEEE80211_M_STA || verbose) {
5117		if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) {
5118			if (val != IEEE80211_HWBMISS_MAX || verbose)
5119				LINE_CHECK("bmiss %d", val);
5120		}
5121	}
5122
5123	if (!verbose) {
5124		gettxparams(s);
5125		tp = &txparams.params[chan2mode(c)];
5126		printrate("ucastrate", tp->ucastrate,
5127		    IEEE80211_FIXED_RATE_NONE, IEEE80211_FIXED_RATE_NONE);
5128		printrate("mcastrate", tp->mcastrate, 2*1,
5129		    IEEE80211_RATE_MCS|0);
5130		printrate("mgmtrate", tp->mgmtrate, 2*1,
5131		    IEEE80211_RATE_MCS|0);
5132		if (tp->maxretry != 6)		/* XXX */
5133			LINE_CHECK("maxretry %d", tp->maxretry);
5134	} else {
5135		LINE_BREAK();
5136		list_txparams(s);
5137	}
5138
5139	bgscaninterval = -1;
5140	(void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval);
5141
5142	if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) {
5143		if (val != bgscaninterval || verbose)
5144			LINE_CHECK("scanvalid %u", val);
5145	}
5146
5147	bgscan = 0;
5148	if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) {
5149		if (bgscan)
5150			LINE_CHECK("bgscan");
5151		else if (verbose)
5152			LINE_CHECK("-bgscan");
5153	}
5154	if (bgscan || verbose) {
5155		if (bgscaninterval != -1)
5156			LINE_CHECK("bgscanintvl %u", bgscaninterval);
5157		if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1)
5158			LINE_CHECK("bgscanidle %u", val);
5159		if (!verbose) {
5160			getroam(s);
5161			rp = &roamparams.params[chan2mode(c)];
5162			if (rp->rssi & 1)
5163				LINE_CHECK("roam:rssi %u.5", rp->rssi/2);
5164			else
5165				LINE_CHECK("roam:rssi %u", rp->rssi/2);
5166			LINE_CHECK("roam:rate %s%u",
5167			    (rp->rate & IEEE80211_RATE_MCS) ? "MCS " : "",
5168			    get_rate_value(rp->rate));
5169		} else {
5170			LINE_BREAK();
5171			list_roam(s);
5172			LINE_BREAK();
5173		}
5174	}
5175
5176	if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
5177		if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) {
5178			if (val)
5179				LINE_CHECK("pureg");
5180			else if (verbose)
5181				LINE_CHECK("-pureg");
5182		}
5183		if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) {
5184			switch (val) {
5185			case IEEE80211_PROTMODE_OFF:
5186				LINE_CHECK("protmode OFF");
5187				break;
5188			case IEEE80211_PROTMODE_CTS:
5189				LINE_CHECK("protmode CTS");
5190				break;
5191			case IEEE80211_PROTMODE_RTSCTS:
5192				LINE_CHECK("protmode RTSCTS");
5193				break;
5194			default:
5195				LINE_CHECK("protmode UNKNOWN (0x%x)", val);
5196				break;
5197			}
5198		}
5199	}
5200
5201	if (IEEE80211_IS_CHAN_HT(c) || verbose) {
5202		gethtconf(s);
5203		switch (htconf & 3) {
5204		case 0:
5205		case 2:
5206			LINE_CHECK("-ht");
5207			break;
5208		case 1:
5209			LINE_CHECK("ht20");
5210			break;
5211		case 3:
5212			if (verbose)
5213				LINE_CHECK("ht");
5214			break;
5215		}
5216		if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) {
5217			if (!val)
5218				LINE_CHECK("-htcompat");
5219			else if (verbose)
5220				LINE_CHECK("htcompat");
5221		}
5222		if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) {
5223			switch (val) {
5224			case 0:
5225				LINE_CHECK("-ampdu");
5226				break;
5227			case 1:
5228				LINE_CHECK("ampdutx -ampdurx");
5229				break;
5230			case 2:
5231				LINE_CHECK("-ampdutx ampdurx");
5232				break;
5233			case 3:
5234				if (verbose)
5235					LINE_CHECK("ampdu");
5236				break;
5237			}
5238		}
5239		/* XXX 11ac density/size is different */
5240		if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) {
5241			switch (val) {
5242			case IEEE80211_HTCAP_MAXRXAMPDU_8K:
5243				LINE_CHECK("ampdulimit 8k");
5244				break;
5245			case IEEE80211_HTCAP_MAXRXAMPDU_16K:
5246				LINE_CHECK("ampdulimit 16k");
5247				break;
5248			case IEEE80211_HTCAP_MAXRXAMPDU_32K:
5249				LINE_CHECK("ampdulimit 32k");
5250				break;
5251			case IEEE80211_HTCAP_MAXRXAMPDU_64K:
5252				LINE_CHECK("ampdulimit 64k");
5253				break;
5254			}
5255		}
5256		/* XXX 11ac density/size is different */
5257		if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) {
5258			switch (val) {
5259			case IEEE80211_HTCAP_MPDUDENSITY_NA:
5260				if (verbose)
5261					LINE_CHECK("ampdudensity NA");
5262				break;
5263			case IEEE80211_HTCAP_MPDUDENSITY_025:
5264				LINE_CHECK("ampdudensity .25");
5265				break;
5266			case IEEE80211_HTCAP_MPDUDENSITY_05:
5267				LINE_CHECK("ampdudensity .5");
5268				break;
5269			case IEEE80211_HTCAP_MPDUDENSITY_1:
5270				LINE_CHECK("ampdudensity 1");
5271				break;
5272			case IEEE80211_HTCAP_MPDUDENSITY_2:
5273				LINE_CHECK("ampdudensity 2");
5274				break;
5275			case IEEE80211_HTCAP_MPDUDENSITY_4:
5276				LINE_CHECK("ampdudensity 4");
5277				break;
5278			case IEEE80211_HTCAP_MPDUDENSITY_8:
5279				LINE_CHECK("ampdudensity 8");
5280				break;
5281			case IEEE80211_HTCAP_MPDUDENSITY_16:
5282				LINE_CHECK("ampdudensity 16");
5283				break;
5284			}
5285		}
5286		if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) {
5287			switch (val) {
5288			case 0:
5289				LINE_CHECK("-amsdu");
5290				break;
5291			case 1:
5292				LINE_CHECK("amsdutx -amsdurx");
5293				break;
5294			case 2:
5295				LINE_CHECK("-amsdutx amsdurx");
5296				break;
5297			case 3:
5298				if (verbose)
5299					LINE_CHECK("amsdu");
5300				break;
5301			}
5302		}
5303		/* XXX amsdu limit */
5304		if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) {
5305			if (val)
5306				LINE_CHECK("shortgi");
5307			else if (verbose)
5308				LINE_CHECK("-shortgi");
5309		}
5310		if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) {
5311			if (val == IEEE80211_PROTMODE_OFF)
5312				LINE_CHECK("htprotmode OFF");
5313			else if (val != IEEE80211_PROTMODE_RTSCTS)
5314				LINE_CHECK("htprotmode UNKNOWN (0x%x)", val);
5315			else if (verbose)
5316				LINE_CHECK("htprotmode RTSCTS");
5317		}
5318		if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) {
5319			if (val)
5320				LINE_CHECK("puren");
5321			else if (verbose)
5322				LINE_CHECK("-puren");
5323		}
5324		if (get80211val(s, IEEE80211_IOC_SMPS, &val) != -1) {
5325			if (val == IEEE80211_HTCAP_SMPS_DYNAMIC)
5326				LINE_CHECK("smpsdyn");
5327			else if (val == IEEE80211_HTCAP_SMPS_ENA)
5328				LINE_CHECK("smps");
5329			else if (verbose)
5330				LINE_CHECK("-smps");
5331		}
5332		if (get80211val(s, IEEE80211_IOC_RIFS, &val) != -1) {
5333			if (val)
5334				LINE_CHECK("rifs");
5335			else if (verbose)
5336				LINE_CHECK("-rifs");
5337		}
5338
5339		/* XXX VHT STBC? */
5340		if (get80211val(s, IEEE80211_IOC_STBC, &val) != -1) {
5341			switch (val) {
5342			case 0:
5343				LINE_CHECK("-stbc");
5344				break;
5345			case 1:
5346				LINE_CHECK("stbctx -stbcrx");
5347				break;
5348			case 2:
5349				LINE_CHECK("-stbctx stbcrx");
5350				break;
5351			case 3:
5352				if (verbose)
5353					LINE_CHECK("stbc");
5354				break;
5355			}
5356		}
5357		if (get80211val(s, IEEE80211_IOC_LDPC, &val) != -1) {
5358			switch (val) {
5359			case 0:
5360				LINE_CHECK("-ldpc");
5361				break;
5362			case 1:
5363				LINE_CHECK("ldpctx -ldpcrx");
5364				break;
5365			case 2:
5366				LINE_CHECK("-ldpctx ldpcrx");
5367				break;
5368			case 3:
5369				if (verbose)
5370					LINE_CHECK("ldpc");
5371				break;
5372			}
5373		}
5374		if (get80211val(s, IEEE80211_IOC_UAPSD, &val) != -1) {
5375			switch (val) {
5376			case 0:
5377				LINE_CHECK("-uapsd");
5378				break;
5379			case 1:
5380				LINE_CHECK("uapsd");
5381				break;
5382			}
5383		}
5384	}
5385
5386	if (IEEE80211_IS_CHAN_VHT(c) || verbose) {
5387		getvhtconf(s);
5388		if (vhtconf & IEEE80211_FVHT_VHT)
5389			LINE_CHECK("vht");
5390		else
5391			LINE_CHECK("-vht");
5392		if (vhtconf & IEEE80211_FVHT_USEVHT40)
5393			LINE_CHECK("vht40");
5394		else
5395			LINE_CHECK("-vht40");
5396		if (vhtconf & IEEE80211_FVHT_USEVHT80)
5397			LINE_CHECK("vht80");
5398		else
5399			LINE_CHECK("-vht80");
5400		if (vhtconf & IEEE80211_FVHT_USEVHT160)
5401			LINE_CHECK("vht160");
5402		else
5403			LINE_CHECK("-vht160");
5404		if (vhtconf & IEEE80211_FVHT_USEVHT80P80)
5405			LINE_CHECK("vht80p80");
5406		else
5407			LINE_CHECK("-vht80p80");
5408	}
5409
5410	if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) {
5411		if (wme)
5412			LINE_CHECK("wme");
5413		else if (verbose)
5414			LINE_CHECK("-wme");
5415	} else
5416		wme = 0;
5417
5418	if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) {
5419		if (val)
5420			LINE_CHECK("burst");
5421		else if (verbose)
5422			LINE_CHECK("-burst");
5423	}
5424
5425	if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) {
5426		if (val)
5427			LINE_CHECK("ff");
5428		else if (verbose)
5429			LINE_CHECK("-ff");
5430	}
5431	if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) {
5432		if (val)
5433			LINE_CHECK("dturbo");
5434		else if (verbose)
5435			LINE_CHECK("-dturbo");
5436	}
5437	if (get80211val(s, IEEE80211_IOC_DWDS, &val) != -1) {
5438		if (val)
5439			LINE_CHECK("dwds");
5440		else if (verbose)
5441			LINE_CHECK("-dwds");
5442	}
5443
5444	if (opmode == IEEE80211_M_HOSTAP) {
5445		if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) {
5446			if (val)
5447				LINE_CHECK("hidessid");
5448			else if (verbose)
5449				LINE_CHECK("-hidessid");
5450		}
5451		if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) {
5452			if (!val)
5453				LINE_CHECK("-apbridge");
5454			else if (verbose)
5455				LINE_CHECK("apbridge");
5456		}
5457		if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1)
5458			LINE_CHECK("dtimperiod %u", val);
5459
5460		if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) {
5461			if (!val)
5462				LINE_CHECK("-doth");
5463			else if (verbose)
5464				LINE_CHECK("doth");
5465		}
5466		if (get80211val(s, IEEE80211_IOC_DFS, &val) != -1) {
5467			if (!val)
5468				LINE_CHECK("-dfs");
5469			else if (verbose)
5470				LINE_CHECK("dfs");
5471		}
5472		if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) {
5473			if (!val)
5474				LINE_CHECK("-inact");
5475			else if (verbose)
5476				LINE_CHECK("inact");
5477		}
5478	} else {
5479		if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) {
5480			if (val != IEEE80211_ROAMING_AUTO || verbose) {
5481				switch (val) {
5482				case IEEE80211_ROAMING_DEVICE:
5483					LINE_CHECK("roaming DEVICE");
5484					break;
5485				case IEEE80211_ROAMING_AUTO:
5486					LINE_CHECK("roaming AUTO");
5487					break;
5488				case IEEE80211_ROAMING_MANUAL:
5489					LINE_CHECK("roaming MANUAL");
5490					break;
5491				default:
5492					LINE_CHECK("roaming UNKNOWN (0x%x)",
5493						val);
5494					break;
5495				}
5496			}
5497		}
5498	}
5499
5500	if (opmode == IEEE80211_M_AHDEMO) {
5501		if (get80211val(s, IEEE80211_IOC_TDMA_SLOT, &val) != -1)
5502			LINE_CHECK("tdmaslot %u", val);
5503		if (get80211val(s, IEEE80211_IOC_TDMA_SLOTCNT, &val) != -1)
5504			LINE_CHECK("tdmaslotcnt %u", val);
5505		if (get80211val(s, IEEE80211_IOC_TDMA_SLOTLEN, &val) != -1)
5506			LINE_CHECK("tdmaslotlen %u", val);
5507		if (get80211val(s, IEEE80211_IOC_TDMA_BINTERVAL, &val) != -1)
5508			LINE_CHECK("tdmabintval %u", val);
5509	} else if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) {
5510		/* XXX default define not visible */
5511		if (val != 100 || verbose)
5512			LINE_CHECK("bintval %u", val);
5513	}
5514
5515	if (wme && verbose) {
5516		LINE_BREAK();
5517		list_wme(s);
5518	}
5519
5520	if (opmode == IEEE80211_M_MBSS) {
5521		if (get80211val(s, IEEE80211_IOC_MESH_TTL, &val) != -1) {
5522			LINE_CHECK("meshttl %u", val);
5523		}
5524		if (get80211val(s, IEEE80211_IOC_MESH_AP, &val) != -1) {
5525			if (val)
5526				LINE_CHECK("meshpeering");
5527			else
5528				LINE_CHECK("-meshpeering");
5529		}
5530		if (get80211val(s, IEEE80211_IOC_MESH_FWRD, &val) != -1) {
5531			if (val)
5532				LINE_CHECK("meshforward");
5533			else
5534				LINE_CHECK("-meshforward");
5535		}
5536		if (get80211val(s, IEEE80211_IOC_MESH_GATE, &val) != -1) {
5537			if (val)
5538				LINE_CHECK("meshgate");
5539			else
5540				LINE_CHECK("-meshgate");
5541		}
5542		if (get80211len(s, IEEE80211_IOC_MESH_PR_METRIC, data, 12,
5543		    &len) != -1) {
5544			data[len] = '\0';
5545			LINE_CHECK("meshmetric %s", data);
5546		}
5547		if (get80211len(s, IEEE80211_IOC_MESH_PR_PATH, data, 12,
5548		    &len) != -1) {
5549			data[len] = '\0';
5550			LINE_CHECK("meshpath %s", data);
5551		}
5552		if (get80211val(s, IEEE80211_IOC_HWMP_ROOTMODE, &val) != -1) {
5553			switch (val) {
5554			case IEEE80211_HWMP_ROOTMODE_DISABLED:
5555				LINE_CHECK("hwmprootmode DISABLED");
5556				break;
5557			case IEEE80211_HWMP_ROOTMODE_NORMAL:
5558				LINE_CHECK("hwmprootmode NORMAL");
5559				break;
5560			case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
5561				LINE_CHECK("hwmprootmode PROACTIVE");
5562				break;
5563			case IEEE80211_HWMP_ROOTMODE_RANN:
5564				LINE_CHECK("hwmprootmode RANN");
5565				break;
5566			default:
5567				LINE_CHECK("hwmprootmode UNKNOWN(%d)", val);
5568				break;
5569			}
5570		}
5571		if (get80211val(s, IEEE80211_IOC_HWMP_MAXHOPS, &val) != -1) {
5572			LINE_CHECK("hwmpmaxhops %u", val);
5573		}
5574	}
5575
5576	LINE_BREAK();
5577
5578	if (getdevicename(s, data, sizeof(data), &len) < 0)
5579		return;
5580	LINE_CHECK("parent interface: %s", data);
5581
5582	LINE_BREAK();
5583}
5584
5585static int
5586get80211(int s, int type, void *data, int len)
5587{
5588
5589	return (lib80211_get80211(s, name, type, data, len));
5590}
5591
5592static int
5593get80211len(int s, int type, void *data, int len, int *plen)
5594{
5595
5596	return (lib80211_get80211len(s, name, type, data, len, plen));
5597}
5598
5599static int
5600get80211val(int s, int type, int *val)
5601{
5602
5603	return (lib80211_get80211val(s, name, type, val));
5604}
5605
5606static void
5607set80211(int s, int type, int val, int len, void *data)
5608{
5609	int ret;
5610
5611	ret = lib80211_set80211(s, name, type, val, len, data);
5612	if (ret < 0)
5613		err(1, "SIOCS80211");
5614}
5615
5616static const char *
5617get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
5618{
5619	int len;
5620	int hexstr;
5621	u_int8_t *p;
5622
5623	len = *lenp;
5624	p = buf;
5625	hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
5626	if (hexstr)
5627		val += 2;
5628	for (;;) {
5629		if (*val == '\0')
5630			break;
5631		if (sep != NULL && strchr(sep, *val) != NULL) {
5632			val++;
5633			break;
5634		}
5635		if (hexstr) {
5636			if (!isxdigit((u_char)val[0])) {
5637				warnx("bad hexadecimal digits");
5638				return NULL;
5639			}
5640			if (!isxdigit((u_char)val[1])) {
5641				warnx("odd count hexadecimal digits");
5642				return NULL;
5643			}
5644		}
5645		if (p >= buf + len) {
5646			if (hexstr)
5647				warnx("hexadecimal digits too long");
5648			else
5649				warnx("string too long");
5650			return NULL;
5651		}
5652		if (hexstr) {
5653#define	tohex(x)	(isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
5654			*p++ = (tohex((u_char)val[0]) << 4) |
5655			    tohex((u_char)val[1]);
5656#undef tohex
5657			val += 2;
5658		} else
5659			*p++ = *val++;
5660	}
5661	len = p - buf;
5662	/* The string "-" is treated as the empty string. */
5663	if (!hexstr && len == 1 && buf[0] == '-') {
5664		len = 0;
5665		memset(buf, 0, *lenp);
5666	} else if (len < *lenp)
5667		memset(p, 0, *lenp - len);
5668	*lenp = len;
5669	return val;
5670}
5671
5672static void
5673print_string(const u_int8_t *buf, int len)
5674{
5675	int i;
5676	int hasspc;
5677	int utf8;
5678
5679	i = 0;
5680	hasspc = 0;
5681
5682	setlocale(LC_CTYPE, "");
5683	utf8 = strncmp("UTF-8", nl_langinfo(CODESET), 5) == 0;
5684
5685	for (; i < len; i++) {
5686		if (!isprint(buf[i]) && buf[i] != '\0' && !utf8)
5687			break;
5688		if (isspace(buf[i]))
5689			hasspc++;
5690	}
5691	if (i == len || utf8) {
5692		if (hasspc || len == 0 || buf[0] == '\0')
5693			printf("\"%.*s\"", len, buf);
5694		else
5695			printf("%.*s", len, buf);
5696	} else {
5697		printf("0x");
5698		for (i = 0; i < len; i++)
5699			printf("%02x", buf[i]);
5700	}
5701}
5702
5703static void
5704setdefregdomain(int s)
5705{
5706	struct regdata *rdp = getregdata();
5707	const struct regdomain *rd;
5708
5709	/* Check if regdomain/country was already set by a previous call. */
5710	/* XXX is it possible? */
5711	if (regdomain.regdomain != 0 ||
5712	    regdomain.country != CTRY_DEFAULT)
5713		return;
5714
5715	getregdomain(s);
5716
5717	/* Check if it was already set by the driver. */
5718	if (regdomain.regdomain != 0 ||
5719	    regdomain.country != CTRY_DEFAULT)
5720		return;
5721
5722	/* Set FCC/US as default. */
5723	rd = lib80211_regdomain_findbysku(rdp, SKU_FCC);
5724	if (rd == NULL)
5725		errx(1, "FCC regdomain was not found");
5726
5727	regdomain.regdomain = rd->sku;
5728	if (rd->cc != NULL)
5729		defaultcountry(rd);
5730
5731	/* Send changes to net80211. */
5732	setregdomain_cb(s, &regdomain);
5733
5734	/* Cleanup (so it can be overriden by subsequent parameters). */
5735	regdomain.regdomain = 0;
5736	regdomain.country = CTRY_DEFAULT;
5737	regdomain.isocc[0] = 0;
5738	regdomain.isocc[1] = 0;
5739}
5740
5741/*
5742 * Virtual AP cloning support.
5743 */
5744static struct ieee80211_clone_params params = {
5745	.icp_opmode	= IEEE80211_M_STA,	/* default to station mode */
5746};
5747
5748static void
5749wlan_create(int s, struct ifreq *ifr)
5750{
5751	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
5752	char orig_name[IFNAMSIZ];
5753
5754	if (params.icp_parent[0] == '\0')
5755		errx(1, "must specify a parent device (wlandev) when creating "
5756		    "a wlan device");
5757	if (params.icp_opmode == IEEE80211_M_WDS &&
5758	    memcmp(params.icp_bssid, zerobssid, sizeof(zerobssid)) == 0)
5759		errx(1, "no bssid specified for WDS (use wlanbssid)");
5760	ifr->ifr_data = (caddr_t) &params;
5761	ioctl_ifcreate(s, ifr);
5762
5763	/* XXX preserve original name for ifclonecreate(). */
5764	strlcpy(orig_name, name, sizeof(orig_name));
5765	strlcpy(name, ifr->ifr_name, sizeof(name));
5766
5767	setdefregdomain(s);
5768
5769	strlcpy(name, orig_name, sizeof(name));
5770}
5771
5772static
5773DECL_CMD_FUNC(set80211clone_wlandev, arg, d)
5774{
5775	strlcpy(params.icp_parent, arg, IFNAMSIZ);
5776}
5777
5778static
5779DECL_CMD_FUNC(set80211clone_wlanbssid, arg, d)
5780{
5781	const struct ether_addr *ea;
5782
5783	ea = ether_aton(arg);
5784	if (ea == NULL)
5785		errx(1, "%s: cannot parse bssid", arg);
5786	memcpy(params.icp_bssid, ea->octet, IEEE80211_ADDR_LEN);
5787}
5788
5789static
5790DECL_CMD_FUNC(set80211clone_wlanaddr, arg, d)
5791{
5792	const struct ether_addr *ea;
5793
5794	ea = ether_aton(arg);
5795	if (ea == NULL)
5796		errx(1, "%s: cannot parse address", arg);
5797	memcpy(params.icp_macaddr, ea->octet, IEEE80211_ADDR_LEN);
5798	params.icp_flags |= IEEE80211_CLONE_MACADDR;
5799}
5800
5801static
5802DECL_CMD_FUNC(set80211clone_wlanmode, arg, d)
5803{
5804#define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
5805	if (iseq(arg, "sta"))
5806		params.icp_opmode = IEEE80211_M_STA;
5807	else if (iseq(arg, "ahdemo") || iseq(arg, "adhoc-demo"))
5808		params.icp_opmode = IEEE80211_M_AHDEMO;
5809	else if (iseq(arg, "ibss") || iseq(arg, "adhoc"))
5810		params.icp_opmode = IEEE80211_M_IBSS;
5811	else if (iseq(arg, "ap") || iseq(arg, "host"))
5812		params.icp_opmode = IEEE80211_M_HOSTAP;
5813	else if (iseq(arg, "wds"))
5814		params.icp_opmode = IEEE80211_M_WDS;
5815	else if (iseq(arg, "monitor"))
5816		params.icp_opmode = IEEE80211_M_MONITOR;
5817	else if (iseq(arg, "tdma")) {
5818		params.icp_opmode = IEEE80211_M_AHDEMO;
5819		params.icp_flags |= IEEE80211_CLONE_TDMA;
5820	} else if (iseq(arg, "mesh") || iseq(arg, "mp")) /* mesh point */
5821		params.icp_opmode = IEEE80211_M_MBSS;
5822	else
5823		errx(1, "Don't know to create %s for %s", arg, name);
5824#undef iseq
5825}
5826
5827static void
5828set80211clone_beacons(const char *val, int d, int s, const struct afswtch *rafp)
5829{
5830	/* NB: inverted sense */
5831	if (d)
5832		params.icp_flags &= ~IEEE80211_CLONE_NOBEACONS;
5833	else
5834		params.icp_flags |= IEEE80211_CLONE_NOBEACONS;
5835}
5836
5837static void
5838set80211clone_bssid(const char *val, int d, int s, const struct afswtch *rafp)
5839{
5840	if (d)
5841		params.icp_flags |= IEEE80211_CLONE_BSSID;
5842	else
5843		params.icp_flags &= ~IEEE80211_CLONE_BSSID;
5844}
5845
5846static void
5847set80211clone_wdslegacy(const char *val, int d, int s, const struct afswtch *rafp)
5848{
5849	if (d)
5850		params.icp_flags |= IEEE80211_CLONE_WDSLEGACY;
5851	else
5852		params.icp_flags &= ~IEEE80211_CLONE_WDSLEGACY;
5853}
5854
5855static struct cmd ieee80211_cmds[] = {
5856	DEF_CMD_ARG("ssid",		set80211ssid),
5857	DEF_CMD_ARG("nwid",		set80211ssid),
5858	DEF_CMD_ARG("meshid",		set80211meshid),
5859	DEF_CMD_ARG("stationname",	set80211stationname),
5860	DEF_CMD_ARG("station",		set80211stationname),	/* BSD/OS */
5861	DEF_CMD_ARG("channel",		set80211channel),
5862	DEF_CMD_ARG("authmode",		set80211authmode),
5863	DEF_CMD_ARG("powersavemode",	set80211powersavemode),
5864	DEF_CMD("powersave",	1,	set80211powersave),
5865	DEF_CMD("-powersave",	0,	set80211powersave),
5866	DEF_CMD_ARG("powersavesleep", 	set80211powersavesleep),
5867	DEF_CMD_ARG("wepmode",		set80211wepmode),
5868	DEF_CMD("wep",		1,	set80211wep),
5869	DEF_CMD("-wep",		0,	set80211wep),
5870	DEF_CMD_ARG("deftxkey",		set80211weptxkey),
5871	DEF_CMD_ARG("weptxkey",		set80211weptxkey),
5872	DEF_CMD_ARG("wepkey",		set80211wepkey),
5873	DEF_CMD_ARG("nwkey",		set80211nwkey),		/* NetBSD */
5874	DEF_CMD("-nwkey",	0,	set80211wep),		/* NetBSD */
5875	DEF_CMD_ARG("rtsthreshold",	set80211rtsthreshold),
5876	DEF_CMD_ARG("protmode",		set80211protmode),
5877	DEF_CMD_ARG("txpower",		set80211txpower),
5878	DEF_CMD_ARG("roaming",		set80211roaming),
5879	DEF_CMD("wme",		1,	set80211wme),
5880	DEF_CMD("-wme",		0,	set80211wme),
5881	DEF_CMD("wmm",		1,	set80211wme),
5882	DEF_CMD("-wmm",		0,	set80211wme),
5883	DEF_CMD("hidessid",	1,	set80211hidessid),
5884	DEF_CMD("-hidessid",	0,	set80211hidessid),
5885	DEF_CMD("apbridge",	1,	set80211apbridge),
5886	DEF_CMD("-apbridge",	0,	set80211apbridge),
5887	DEF_CMD_ARG("chanlist",		set80211chanlist),
5888	DEF_CMD_ARG("bssid",		set80211bssid),
5889	DEF_CMD_ARG("ap",		set80211bssid),
5890	DEF_CMD("scan",	0,		set80211scan),
5891	DEF_CMD_ARG("list",		set80211list),
5892	DEF_CMD_ARG2("cwmin",		set80211cwmin),
5893	DEF_CMD_ARG2("cwmax",		set80211cwmax),
5894	DEF_CMD_ARG2("aifs",		set80211aifs),
5895	DEF_CMD_ARG2("txoplimit",	set80211txoplimit),
5896	DEF_CMD_ARG("acm",		set80211acm),
5897	DEF_CMD_ARG("-acm",		set80211noacm),
5898	DEF_CMD_ARG("ack",		set80211ackpolicy),
5899	DEF_CMD_ARG("-ack",		set80211noackpolicy),
5900	DEF_CMD_ARG2("bss:cwmin",	set80211bsscwmin),
5901	DEF_CMD_ARG2("bss:cwmax",	set80211bsscwmax),
5902	DEF_CMD_ARG2("bss:aifs",	set80211bssaifs),
5903	DEF_CMD_ARG2("bss:txoplimit",	set80211bsstxoplimit),
5904	DEF_CMD_ARG("dtimperiod",	set80211dtimperiod),
5905	DEF_CMD_ARG("bintval",		set80211bintval),
5906	DEF_CMD("mac:open",	IEEE80211_MACCMD_POLICY_OPEN,	set80211maccmd),
5907	DEF_CMD("mac:allow",	IEEE80211_MACCMD_POLICY_ALLOW,	set80211maccmd),
5908	DEF_CMD("mac:deny",	IEEE80211_MACCMD_POLICY_DENY,	set80211maccmd),
5909	DEF_CMD("mac:radius",	IEEE80211_MACCMD_POLICY_RADIUS,	set80211maccmd),
5910	DEF_CMD("mac:flush",	IEEE80211_MACCMD_FLUSH,		set80211maccmd),
5911	DEF_CMD("mac:detach",	IEEE80211_MACCMD_DETACH,	set80211maccmd),
5912	DEF_CMD_ARG("mac:add",		set80211addmac),
5913	DEF_CMD_ARG("mac:del",		set80211delmac),
5914	DEF_CMD_ARG("mac:kick",		set80211kickmac),
5915	DEF_CMD("pureg",	1,	set80211pureg),
5916	DEF_CMD("-pureg",	0,	set80211pureg),
5917	DEF_CMD("ff",		1,	set80211fastframes),
5918	DEF_CMD("-ff",		0,	set80211fastframes),
5919	DEF_CMD("dturbo",	1,	set80211dturbo),
5920	DEF_CMD("-dturbo",	0,	set80211dturbo),
5921	DEF_CMD("bgscan",	1,	set80211bgscan),
5922	DEF_CMD("-bgscan",	0,	set80211bgscan),
5923	DEF_CMD_ARG("bgscanidle",	set80211bgscanidle),
5924	DEF_CMD_ARG("bgscanintvl",	set80211bgscanintvl),
5925	DEF_CMD_ARG("scanvalid",	set80211scanvalid),
5926	DEF_CMD("quiet",	1,	set80211quiet),
5927	DEF_CMD("-quiet",	0,	set80211quiet),
5928	DEF_CMD_ARG("quiet_count",	set80211quietcount),
5929	DEF_CMD_ARG("quiet_period",	set80211quietperiod),
5930	DEF_CMD_ARG("quiet_duration",	set80211quietduration),
5931	DEF_CMD_ARG("quiet_offset",	set80211quietoffset),
5932	DEF_CMD_ARG("roam:rssi",	set80211roamrssi),
5933	DEF_CMD_ARG("roam:rate",	set80211roamrate),
5934	DEF_CMD_ARG("mcastrate",	set80211mcastrate),
5935	DEF_CMD_ARG("ucastrate",	set80211ucastrate),
5936	DEF_CMD_ARG("mgtrate",		set80211mgtrate),
5937	DEF_CMD_ARG("mgmtrate",		set80211mgtrate),
5938	DEF_CMD_ARG("maxretry",		set80211maxretry),
5939	DEF_CMD_ARG("fragthreshold",	set80211fragthreshold),
5940	DEF_CMD("burst",	1,	set80211burst),
5941	DEF_CMD("-burst",	0,	set80211burst),
5942	DEF_CMD_ARG("bmiss",		set80211bmissthreshold),
5943	DEF_CMD_ARG("bmissthreshold",	set80211bmissthreshold),
5944	DEF_CMD("shortgi",	1,	set80211shortgi),
5945	DEF_CMD("-shortgi",	0,	set80211shortgi),
5946	DEF_CMD("ampdurx",	2,	set80211ampdu),
5947	DEF_CMD("-ampdurx",	-2,	set80211ampdu),
5948	DEF_CMD("ampdutx",	1,	set80211ampdu),
5949	DEF_CMD("-ampdutx",	-1,	set80211ampdu),
5950	DEF_CMD("ampdu",	3,	set80211ampdu),		/* NB: tx+rx */
5951	DEF_CMD("-ampdu",	-3,	set80211ampdu),
5952	DEF_CMD_ARG("ampdulimit",	set80211ampdulimit),
5953	DEF_CMD_ARG("ampdudensity",	set80211ampdudensity),
5954	DEF_CMD("amsdurx",	2,	set80211amsdu),
5955	DEF_CMD("-amsdurx",	-2,	set80211amsdu),
5956	DEF_CMD("amsdutx",	1,	set80211amsdu),
5957	DEF_CMD("-amsdutx",	-1,	set80211amsdu),
5958	DEF_CMD("amsdu",	3,	set80211amsdu),		/* NB: tx+rx */
5959	DEF_CMD("-amsdu",	-3,	set80211amsdu),
5960	DEF_CMD_ARG("amsdulimit",	set80211amsdulimit),
5961	DEF_CMD("stbcrx",	2,	set80211stbc),
5962	DEF_CMD("-stbcrx",	-2,	set80211stbc),
5963	DEF_CMD("stbctx",	1,	set80211stbc),
5964	DEF_CMD("-stbctx",	-1,	set80211stbc),
5965	DEF_CMD("stbc",		3,	set80211stbc),		/* NB: tx+rx */
5966	DEF_CMD("-stbc",	-3,	set80211stbc),
5967	DEF_CMD("ldpcrx",	2,	set80211ldpc),
5968	DEF_CMD("-ldpcrx",	-2,	set80211ldpc),
5969	DEF_CMD("ldpctx",	1,	set80211ldpc),
5970	DEF_CMD("-ldpctx",	-1,	set80211ldpc),
5971	DEF_CMD("ldpc",		3,	set80211ldpc),		/* NB: tx+rx */
5972	DEF_CMD("-ldpc",	-3,	set80211ldpc),
5973	DEF_CMD("uapsd",	1,	set80211uapsd),
5974	DEF_CMD("-uapsd",	0,	set80211uapsd),
5975	DEF_CMD("puren",	1,	set80211puren),
5976	DEF_CMD("-puren",	0,	set80211puren),
5977	DEF_CMD("doth",		1,	set80211doth),
5978	DEF_CMD("-doth",	0,	set80211doth),
5979	DEF_CMD("dfs",		1,	set80211dfs),
5980	DEF_CMD("-dfs",		0,	set80211dfs),
5981	DEF_CMD("htcompat",	1,	set80211htcompat),
5982	DEF_CMD("-htcompat",	0,	set80211htcompat),
5983	DEF_CMD("dwds",		1,	set80211dwds),
5984	DEF_CMD("-dwds",	0,	set80211dwds),
5985	DEF_CMD("inact",	1,	set80211inact),
5986	DEF_CMD("-inact",	0,	set80211inact),
5987	DEF_CMD("tsn",		1,	set80211tsn),
5988	DEF_CMD("-tsn",		0,	set80211tsn),
5989	DEF_CMD_ARG("regdomain",	set80211regdomain),
5990	DEF_CMD_ARG("country",		set80211country),
5991	DEF_CMD("indoor",	'I',	set80211location),
5992	DEF_CMD("-indoor",	'O',	set80211location),
5993	DEF_CMD("outdoor",	'O',	set80211location),
5994	DEF_CMD("-outdoor",	'I',	set80211location),
5995	DEF_CMD("anywhere",	' ',	set80211location),
5996	DEF_CMD("ecm",		1,	set80211ecm),
5997	DEF_CMD("-ecm",		0,	set80211ecm),
5998	DEF_CMD("dotd",		1,	set80211dotd),
5999	DEF_CMD("-dotd",	0,	set80211dotd),
6000	DEF_CMD_ARG("htprotmode",	set80211htprotmode),
6001	DEF_CMD("ht20",		1,	set80211htconf),
6002	DEF_CMD("-ht20",	0,	set80211htconf),
6003	DEF_CMD("ht40",		3,	set80211htconf),	/* NB: 20+40 */
6004	DEF_CMD("-ht40",	0,	set80211htconf),
6005	DEF_CMD("ht",		3,	set80211htconf),	/* NB: 20+40 */
6006	DEF_CMD("-ht",		0,	set80211htconf),
6007	DEF_CMD("vht",		IEEE80211_FVHT_VHT,		set80211vhtconf),
6008	DEF_CMD("-vht",		0,				set80211vhtconf),
6009	DEF_CMD("vht40",	IEEE80211_FVHT_USEVHT40,	set80211vhtconf),
6010	DEF_CMD("-vht40",	-IEEE80211_FVHT_USEVHT40,	set80211vhtconf),
6011	DEF_CMD("vht80",	IEEE80211_FVHT_USEVHT80,	set80211vhtconf),
6012	DEF_CMD("-vht80",	-IEEE80211_FVHT_USEVHT80,	set80211vhtconf),
6013	DEF_CMD("vht160",	IEEE80211_FVHT_USEVHT160,	set80211vhtconf),
6014	DEF_CMD("-vht160",	-IEEE80211_FVHT_USEVHT160,	set80211vhtconf),
6015	DEF_CMD("vht80p80",	IEEE80211_FVHT_USEVHT80P80,	set80211vhtconf),
6016	DEF_CMD("-vht80p80",	-IEEE80211_FVHT_USEVHT80P80,	set80211vhtconf),
6017	DEF_CMD("rifs",		1,	set80211rifs),
6018	DEF_CMD("-rifs",	0,	set80211rifs),
6019	DEF_CMD("smps",		IEEE80211_HTCAP_SMPS_ENA,	set80211smps),
6020	DEF_CMD("smpsdyn",	IEEE80211_HTCAP_SMPS_DYNAMIC,	set80211smps),
6021	DEF_CMD("-smps",	IEEE80211_HTCAP_SMPS_OFF,	set80211smps),
6022	/* XXX for testing */
6023	DEF_CMD_ARG("chanswitch",	set80211chanswitch),
6024
6025	DEF_CMD_ARG("tdmaslot",		set80211tdmaslot),
6026	DEF_CMD_ARG("tdmaslotcnt",	set80211tdmaslotcnt),
6027	DEF_CMD_ARG("tdmaslotlen",	set80211tdmaslotlen),
6028	DEF_CMD_ARG("tdmabintval",	set80211tdmabintval),
6029
6030	DEF_CMD_ARG("meshttl",		set80211meshttl),
6031	DEF_CMD("meshforward",	1,	set80211meshforward),
6032	DEF_CMD("-meshforward",	0,	set80211meshforward),
6033	DEF_CMD("meshgate",	1,	set80211meshgate),
6034	DEF_CMD("-meshgate",	0,	set80211meshgate),
6035	DEF_CMD("meshpeering",	1,	set80211meshpeering),
6036	DEF_CMD("-meshpeering",	0,	set80211meshpeering),
6037	DEF_CMD_ARG("meshmetric",	set80211meshmetric),
6038	DEF_CMD_ARG("meshpath",		set80211meshpath),
6039	DEF_CMD("meshrt:flush",	IEEE80211_MESH_RTCMD_FLUSH,	set80211meshrtcmd),
6040	DEF_CMD_ARG("meshrt:add",	set80211addmeshrt),
6041	DEF_CMD_ARG("meshrt:del",	set80211delmeshrt),
6042	DEF_CMD_ARG("hwmprootmode",	set80211hwmprootmode),
6043	DEF_CMD_ARG("hwmpmaxhops",	set80211hwmpmaxhops),
6044
6045	/* vap cloning support */
6046	DEF_CLONE_CMD_ARG("wlanaddr",	set80211clone_wlanaddr),
6047	DEF_CLONE_CMD_ARG("wlanbssid",	set80211clone_wlanbssid),
6048	DEF_CLONE_CMD_ARG("wlandev",	set80211clone_wlandev),
6049	DEF_CLONE_CMD_ARG("wlanmode",	set80211clone_wlanmode),
6050	DEF_CLONE_CMD("beacons", 1,	set80211clone_beacons),
6051	DEF_CLONE_CMD("-beacons", 0,	set80211clone_beacons),
6052	DEF_CLONE_CMD("bssid",	1,	set80211clone_bssid),
6053	DEF_CLONE_CMD("-bssid",	0,	set80211clone_bssid),
6054	DEF_CLONE_CMD("wdslegacy", 1,	set80211clone_wdslegacy),
6055	DEF_CLONE_CMD("-wdslegacy", 0,	set80211clone_wdslegacy),
6056};
6057static struct afswtch af_ieee80211 = {
6058	.af_name	= "af_ieee80211",
6059	.af_af		= AF_UNSPEC,
6060	.af_other_status = ieee80211_status,
6061};
6062
6063static __constructor void
6064ieee80211_ctor(void)
6065{
6066	int i;
6067
6068	for (i = 0; i < nitems(ieee80211_cmds);  i++)
6069		cmd_register(&ieee80211_cmds[i]);
6070	af_register(&af_ieee80211);
6071	clone_setdefcallback_prefix("wlan", wlan_create);
6072}
6073