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