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