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