ifieee80211.c revision 234893
1/*
2 * Copyright 2001 The Aerospace Corporation.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. The name of The Aerospace Corporation may not be used to endorse or
13 *    promote products derived from this software.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AEROSPACE CORPORATION BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sbin/ifconfig/ifieee80211.c 234893 2012-05-01 16:15:34Z monthadar $
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	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	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	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	IEEE80211_CHAN_HTA	(IEEE80211_CHAN_HT|IEEE80211_CHAN_5GHZ)
1458#define	IEEE80211_CHAN_HTG	(IEEE80211_CHAN_HT|IEEE80211_CHAN_2GHZ)
1459
1460#define	_APPLY(_flags, _base, _param, _v) do {				\
1461    if (_flags & IEEE80211_CHAN_HT) {					\
1462	    if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1463		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1464		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1465	    } else if (_flags & IEEE80211_CHAN_5GHZ)			\
1466		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1467	    else							\
1468		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1469    }									\
1470    if (_flags & IEEE80211_CHAN_TURBO) {				\
1471	    if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1472		    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;	\
1473		    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;	\
1474	    } else if (_flags & IEEE80211_CHAN_5GHZ)			\
1475		    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;	\
1476	    else							\
1477		    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;	\
1478    }									\
1479    if (_flags & IEEE80211_CHAN_STURBO)					\
1480	    _base.params[IEEE80211_MODE_STURBO_A]._param = _v;		\
1481    if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)		\
1482	    _base.params[IEEE80211_MODE_11A]._param = _v;		\
1483    if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)		\
1484	    _base.params[IEEE80211_MODE_11G]._param = _v;		\
1485    if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)		\
1486	    _base.params[IEEE80211_MODE_11B]._param = _v;		\
1487    if (_flags & IEEE80211_CHAN_HALF)					\
1488	    _base.params[IEEE80211_MODE_HALF]._param = _v;		\
1489    if (_flags & IEEE80211_CHAN_QUARTER)				\
1490	    _base.params[IEEE80211_MODE_QUARTER]._param = _v;		\
1491} while (0)
1492#define	_APPLY1(_flags, _base, _param, _v) do {				\
1493    if (_flags & IEEE80211_CHAN_HT) {					\
1494	    if (_flags & IEEE80211_CHAN_5GHZ)				\
1495		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1496	    else							\
1497		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1498    } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A)	\
1499	    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;		\
1500    else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G)	\
1501	    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;		\
1502    else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST)		\
1503	    _base.params[IEEE80211_MODE_STURBO_A]._param = _v;		\
1504    else if (_flags & IEEE80211_CHAN_HALF)				\
1505	    _base.params[IEEE80211_MODE_HALF]._param = _v;		\
1506    else if (_flags & IEEE80211_CHAN_QUARTER)				\
1507	    _base.params[IEEE80211_MODE_QUARTER]._param = _v;		\
1508    else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)		\
1509	    _base.params[IEEE80211_MODE_11A]._param = _v;		\
1510    else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)		\
1511	    _base.params[IEEE80211_MODE_11G]._param = _v;		\
1512    else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)		\
1513	    _base.params[IEEE80211_MODE_11B]._param = _v;		\
1514} while (0)
1515#define	_APPLY_RATE(_flags, _base, _param, _v) do {			\
1516    if (_flags & IEEE80211_CHAN_HT) {					\
1517	(_v) = (_v / 2) | IEEE80211_RATE_MCS;				\
1518    }									\
1519    _APPLY(_flags, _base, _param, _v);					\
1520} while (0)
1521#define	_APPLY_RATE1(_flags, _base, _param, _v) do {			\
1522    if (_flags & IEEE80211_CHAN_HT) {					\
1523	(_v) = (_v / 2) | IEEE80211_RATE_MCS;				\
1524    }									\
1525    _APPLY1(_flags, _base, _param, _v);					\
1526} while (0)
1527
1528static
1529DECL_CMD_FUNC(set80211roamrssi, val, d)
1530{
1531	double v = atof(val);
1532	int rssi, flags;
1533
1534	rssi = (int) (2*v);
1535	if (rssi != 2*v)
1536		errx(-1, "invalid rssi (must be .5 dBm units)");
1537	flags = getmodeflags(val);
1538	getroam(s);
1539	if (flags == 0) {		/* NB: no flags => current channel */
1540		flags = getcurchan(s)->ic_flags;
1541		_APPLY1(flags, roamparams, rssi, rssi);
1542	} else
1543		_APPLY(flags, roamparams, rssi, rssi);
1544	callback_register(setroam_cb, &roamparams);
1545}
1546
1547static int
1548getrate(const char *val, const char *tag)
1549{
1550	double v = atof(val);
1551	int rate;
1552
1553	rate = (int) (2*v);
1554	if (rate != 2*v)
1555		errx(-1, "invalid %s rate (must be .5 Mb/s units)", tag);
1556	return rate;		/* NB: returns 2x the specified value */
1557}
1558
1559static
1560DECL_CMD_FUNC(set80211roamrate, val, d)
1561{
1562	int rate, flags;
1563
1564	rate = getrate(val, "roam");
1565	flags = getmodeflags(val);
1566	getroam(s);
1567	if (flags == 0) {		/* NB: no flags => current channel */
1568		flags = getcurchan(s)->ic_flags;
1569		_APPLY_RATE1(flags, roamparams, rate, rate);
1570	} else
1571		_APPLY_RATE(flags, roamparams, rate, rate);
1572	callback_register(setroam_cb, &roamparams);
1573}
1574
1575static
1576DECL_CMD_FUNC(set80211mcastrate, val, d)
1577{
1578	int rate, flags;
1579
1580	rate = getrate(val, "mcast");
1581	flags = getmodeflags(val);
1582	gettxparams(s);
1583	if (flags == 0) {		/* NB: no flags => current channel */
1584		flags = getcurchan(s)->ic_flags;
1585		_APPLY_RATE1(flags, txparams, mcastrate, rate);
1586	} else
1587		_APPLY_RATE(flags, txparams, mcastrate, rate);
1588	callback_register(settxparams_cb, &txparams);
1589}
1590
1591static
1592DECL_CMD_FUNC(set80211mgtrate, val, d)
1593{
1594	int rate, flags;
1595
1596	rate = getrate(val, "mgmt");
1597	flags = getmodeflags(val);
1598	gettxparams(s);
1599	if (flags == 0) {		/* NB: no flags => current channel */
1600		flags = getcurchan(s)->ic_flags;
1601		_APPLY_RATE1(flags, txparams, mgmtrate, rate);
1602	} else
1603		_APPLY_RATE(flags, txparams, mgmtrate, rate);
1604	callback_register(settxparams_cb, &txparams);
1605}
1606
1607static
1608DECL_CMD_FUNC(set80211ucastrate, val, d)
1609{
1610	int flags;
1611
1612	gettxparams(s);
1613	flags = getmodeflags(val);
1614	if (isanyarg(val)) {
1615		if (flags == 0) {	/* NB: no flags => current channel */
1616			flags = getcurchan(s)->ic_flags;
1617			_APPLY1(flags, txparams, ucastrate,
1618			    IEEE80211_FIXED_RATE_NONE);
1619		} else
1620			_APPLY(flags, txparams, ucastrate,
1621			    IEEE80211_FIXED_RATE_NONE);
1622	} else {
1623		int rate = getrate(val, "ucast");
1624		if (flags == 0) {	/* NB: no flags => current channel */
1625			flags = getcurchan(s)->ic_flags;
1626			_APPLY_RATE1(flags, txparams, ucastrate, rate);
1627		} else
1628			_APPLY_RATE(flags, txparams, ucastrate, rate);
1629	}
1630	callback_register(settxparams_cb, &txparams);
1631}
1632
1633static
1634DECL_CMD_FUNC(set80211maxretry, val, d)
1635{
1636	int v = atoi(val), flags;
1637
1638	flags = getmodeflags(val);
1639	gettxparams(s);
1640	if (flags == 0) {		/* NB: no flags => current channel */
1641		flags = getcurchan(s)->ic_flags;
1642		_APPLY1(flags, txparams, maxretry, v);
1643	} else
1644		_APPLY(flags, txparams, maxretry, v);
1645	callback_register(settxparams_cb, &txparams);
1646}
1647#undef _APPLY_RATE
1648#undef _APPLY
1649#undef IEEE80211_CHAN_HTA
1650#undef IEEE80211_CHAN_HTG
1651
1652static
1653DECL_CMD_FUNC(set80211fragthreshold, val, d)
1654{
1655	set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
1656		isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
1657}
1658
1659static
1660DECL_CMD_FUNC(set80211bmissthreshold, val, d)
1661{
1662	set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
1663		isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
1664}
1665
1666static void
1667set80211burst(const char *val, int d, int s, const struct afswtch *rafp)
1668{
1669	set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
1670}
1671
1672static void
1673set80211doth(const char *val, int d, int s, const struct afswtch *rafp)
1674{
1675	set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL);
1676}
1677
1678static void
1679set80211dfs(const char *val, int d, int s, const struct afswtch *rafp)
1680{
1681	set80211(s, IEEE80211_IOC_DFS, d, 0, NULL);
1682}
1683
1684static void
1685set80211shortgi(const char *val, int d, int s, const struct afswtch *rafp)
1686{
1687	set80211(s, IEEE80211_IOC_SHORTGI,
1688		d ? (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) : 0,
1689		0, NULL);
1690}
1691
1692static void
1693set80211ampdu(const char *val, int d, int s, const struct afswtch *rafp)
1694{
1695	int ampdu;
1696
1697	if (get80211val(s, IEEE80211_IOC_AMPDU, &ampdu) < 0)
1698		errx(-1, "cannot get AMPDU setting");
1699	if (d < 0) {
1700		d = -d;
1701		ampdu &= ~d;
1702	} else
1703		ampdu |= d;
1704	set80211(s, IEEE80211_IOC_AMPDU, ampdu, 0, NULL);
1705}
1706
1707static
1708DECL_CMD_FUNC(set80211ampdulimit, val, d)
1709{
1710	int v;
1711
1712	switch (atoi(val)) {
1713	case 8:
1714	case 8*1024:
1715		v = IEEE80211_HTCAP_MAXRXAMPDU_8K;
1716		break;
1717	case 16:
1718	case 16*1024:
1719		v = IEEE80211_HTCAP_MAXRXAMPDU_16K;
1720		break;
1721	case 32:
1722	case 32*1024:
1723		v = IEEE80211_HTCAP_MAXRXAMPDU_32K;
1724		break;
1725	case 64:
1726	case 64*1024:
1727		v = IEEE80211_HTCAP_MAXRXAMPDU_64K;
1728		break;
1729	default:
1730		errx(-1, "invalid A-MPDU limit %s", val);
1731	}
1732	set80211(s, IEEE80211_IOC_AMPDU_LIMIT, v, 0, NULL);
1733}
1734
1735static
1736DECL_CMD_FUNC(set80211ampdudensity, val, d)
1737{
1738	int v;
1739
1740	if (isanyarg(val) || strcasecmp(val, "na") == 0)
1741		v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1742	else switch ((int)(atof(val)*4)) {
1743	case 0:
1744		v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1745		break;
1746	case 1:
1747		v = IEEE80211_HTCAP_MPDUDENSITY_025;
1748		break;
1749	case 2:
1750		v = IEEE80211_HTCAP_MPDUDENSITY_05;
1751		break;
1752	case 4:
1753		v = IEEE80211_HTCAP_MPDUDENSITY_1;
1754		break;
1755	case 8:
1756		v = IEEE80211_HTCAP_MPDUDENSITY_2;
1757		break;
1758	case 16:
1759		v = IEEE80211_HTCAP_MPDUDENSITY_4;
1760		break;
1761	case 32:
1762		v = IEEE80211_HTCAP_MPDUDENSITY_8;
1763		break;
1764	case 64:
1765		v = IEEE80211_HTCAP_MPDUDENSITY_16;
1766		break;
1767	default:
1768		errx(-1, "invalid A-MPDU density %s", val);
1769	}
1770	set80211(s, IEEE80211_IOC_AMPDU_DENSITY, v, 0, NULL);
1771}
1772
1773static void
1774set80211amsdu(const char *val, int d, int s, const struct afswtch *rafp)
1775{
1776	int amsdu;
1777
1778	if (get80211val(s, IEEE80211_IOC_AMSDU, &amsdu) < 0)
1779		err(-1, "cannot get AMSDU setting");
1780	if (d < 0) {
1781		d = -d;
1782		amsdu &= ~d;
1783	} else
1784		amsdu |= d;
1785	set80211(s, IEEE80211_IOC_AMSDU, amsdu, 0, NULL);
1786}
1787
1788static
1789DECL_CMD_FUNC(set80211amsdulimit, val, d)
1790{
1791	set80211(s, IEEE80211_IOC_AMSDU_LIMIT, atoi(val), 0, NULL);
1792}
1793
1794static void
1795set80211puren(const char *val, int d, int s, const struct afswtch *rafp)
1796{
1797	set80211(s, IEEE80211_IOC_PUREN, d, 0, NULL);
1798}
1799
1800static void
1801set80211htcompat(const char *val, int d, int s, const struct afswtch *rafp)
1802{
1803	set80211(s, IEEE80211_IOC_HTCOMPAT, d, 0, NULL);
1804}
1805
1806static void
1807set80211htconf(const char *val, int d, int s, const struct afswtch *rafp)
1808{
1809	set80211(s, IEEE80211_IOC_HTCONF, d, 0, NULL);
1810	htconf = d;
1811}
1812
1813static void
1814set80211dwds(const char *val, int d, int s, const struct afswtch *rafp)
1815{
1816	set80211(s, IEEE80211_IOC_DWDS, d, 0, NULL);
1817}
1818
1819static void
1820set80211inact(const char *val, int d, int s, const struct afswtch *rafp)
1821{
1822	set80211(s, IEEE80211_IOC_INACTIVITY, d, 0, NULL);
1823}
1824
1825static void
1826set80211tsn(const char *val, int d, int s, const struct afswtch *rafp)
1827{
1828	set80211(s, IEEE80211_IOC_TSN, d, 0, NULL);
1829}
1830
1831static void
1832set80211dotd(const char *val, int d, int s, const struct afswtch *rafp)
1833{
1834	set80211(s, IEEE80211_IOC_DOTD, d, 0, NULL);
1835}
1836
1837static void
1838set80211smps(const char *val, int d, int s, const struct afswtch *rafp)
1839{
1840	set80211(s, IEEE80211_IOC_SMPS, d, 0, NULL);
1841}
1842
1843static void
1844set80211rifs(const char *val, int d, int s, const struct afswtch *rafp)
1845{
1846	set80211(s, IEEE80211_IOC_RIFS, d, 0, NULL);
1847}
1848
1849static
1850DECL_CMD_FUNC(set80211tdmaslot, val, d)
1851{
1852	set80211(s, IEEE80211_IOC_TDMA_SLOT, atoi(val), 0, NULL);
1853}
1854
1855static
1856DECL_CMD_FUNC(set80211tdmaslotcnt, val, d)
1857{
1858	set80211(s, IEEE80211_IOC_TDMA_SLOTCNT, atoi(val), 0, NULL);
1859}
1860
1861static
1862DECL_CMD_FUNC(set80211tdmaslotlen, val, d)
1863{
1864	set80211(s, IEEE80211_IOC_TDMA_SLOTLEN, atoi(val), 0, NULL);
1865}
1866
1867static
1868DECL_CMD_FUNC(set80211tdmabintval, val, d)
1869{
1870	set80211(s, IEEE80211_IOC_TDMA_BINTERVAL, atoi(val), 0, NULL);
1871}
1872
1873static
1874DECL_CMD_FUNC(set80211meshttl, val, d)
1875{
1876	set80211(s, IEEE80211_IOC_MESH_TTL, atoi(val), 0, NULL);
1877}
1878
1879static
1880DECL_CMD_FUNC(set80211meshforward, val, d)
1881{
1882	set80211(s, IEEE80211_IOC_MESH_FWRD, d, 0, NULL);
1883}
1884
1885static
1886DECL_CMD_FUNC(set80211meshgate, val, d)
1887{
1888	set80211(s, IEEE80211_IOC_MESH_GATE, d, 0, NULL);
1889}
1890
1891static
1892DECL_CMD_FUNC(set80211meshpeering, val, d)
1893{
1894	set80211(s, IEEE80211_IOC_MESH_AP, d, 0, NULL);
1895}
1896
1897static
1898DECL_CMD_FUNC(set80211meshmetric, val, d)
1899{
1900	char v[12];
1901
1902	memcpy(v, val, sizeof(v));
1903	set80211(s, IEEE80211_IOC_MESH_PR_METRIC, 0, 0, v);
1904}
1905
1906static
1907DECL_CMD_FUNC(set80211meshpath, val, d)
1908{
1909	char v[12];
1910
1911	memcpy(v, val, sizeof(v));
1912	set80211(s, IEEE80211_IOC_MESH_PR_PATH, 0, 0, v);
1913}
1914
1915static int
1916regdomain_sort(const void *a, const void *b)
1917{
1918#define	CHAN_ALL \
1919	(IEEE80211_CHAN_ALLTURBO|IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)
1920	const struct ieee80211_channel *ca = a;
1921	const struct ieee80211_channel *cb = b;
1922
1923	return ca->ic_freq == cb->ic_freq ?
1924	    (ca->ic_flags & CHAN_ALL) - (cb->ic_flags & CHAN_ALL) :
1925	    ca->ic_freq - cb->ic_freq;
1926#undef CHAN_ALL
1927}
1928
1929static const struct ieee80211_channel *
1930chanlookup(const struct ieee80211_channel chans[], int nchans,
1931	int freq, int flags)
1932{
1933	int i;
1934
1935	flags &= IEEE80211_CHAN_ALLTURBO;
1936	for (i = 0; i < nchans; i++) {
1937		const struct ieee80211_channel *c = &chans[i];
1938		if (c->ic_freq == freq &&
1939		    (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
1940			return c;
1941	}
1942	return NULL;
1943}
1944
1945static int
1946chanfind(const struct ieee80211_channel chans[], int nchans, int flags)
1947{
1948	int i;
1949
1950	for (i = 0; i < nchans; i++) {
1951		const struct ieee80211_channel *c = &chans[i];
1952		if ((c->ic_flags & flags) == flags)
1953			return 1;
1954	}
1955	return 0;
1956}
1957
1958/*
1959 * Check channel compatibility.
1960 */
1961static int
1962checkchan(const struct ieee80211req_chaninfo *avail, int freq, int flags)
1963{
1964	flags &= ~REQ_FLAGS;
1965	/*
1966	 * Check if exact channel is in the calibration table;
1967	 * everything below is to deal with channels that we
1968	 * want to include but that are not explicitly listed.
1969	 */
1970	if (flags & IEEE80211_CHAN_HT40) {
1971		/* NB: we use an HT40 channel center that matches HT20 */
1972		flags = (flags &~ IEEE80211_CHAN_HT40) | IEEE80211_CHAN_HT20;
1973	}
1974	if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, flags) != NULL)
1975		return 1;
1976	if (flags & IEEE80211_CHAN_GSM) {
1977		/*
1978		 * XXX GSM frequency mapping is handled in the kernel
1979		 * so we cannot find them in the calibration table;
1980		 * just accept the channel and the kernel will reject
1981		 * the channel list if it's wrong.
1982		 */
1983		return 1;
1984	}
1985	/*
1986	 * If this is a 1/2 or 1/4 width channel allow it if a full
1987	 * width channel is present for this frequency, and the device
1988	 * supports fractional channels on this band.  This is a hack
1989	 * that avoids bloating the calibration table; it may be better
1990	 * by per-band attributes though (we are effectively calculating
1991	 * this attribute by scanning the channel list ourself).
1992	 */
1993	if ((flags & (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) == 0)
1994		return 0;
1995	if (chanlookup(avail->ic_chans, avail->ic_nchans, freq,
1996	    flags &~ (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) == NULL)
1997		return 0;
1998	if (flags & IEEE80211_CHAN_HALF) {
1999		return chanfind(avail->ic_chans, avail->ic_nchans,
2000		    IEEE80211_CHAN_HALF |
2001		       (flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
2002	} else {
2003		return chanfind(avail->ic_chans, avail->ic_nchans,
2004		    IEEE80211_CHAN_QUARTER |
2005			(flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
2006	}
2007}
2008
2009static void
2010regdomain_addchans(struct ieee80211req_chaninfo *ci,
2011	const netband_head *bands,
2012	const struct ieee80211_regdomain *reg,
2013	uint32_t chanFlags,
2014	const struct ieee80211req_chaninfo *avail)
2015{
2016	const struct netband *nb;
2017	const struct freqband *b;
2018	struct ieee80211_channel *c, *prev;
2019	int freq, hi_adj, lo_adj, channelSep;
2020	uint32_t flags;
2021
2022	hi_adj = (chanFlags & IEEE80211_CHAN_HT40U) ? -20 : 0;
2023	lo_adj = (chanFlags & IEEE80211_CHAN_HT40D) ? 20 : 0;
2024	channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40;
2025	LIST_FOREACH(nb, bands, next) {
2026		b = nb->band;
2027		if (verbose) {
2028			printf("%s:", __func__);
2029			printb(" chanFlags", chanFlags, IEEE80211_CHAN_BITS);
2030			printb(" bandFlags", nb->flags | b->flags,
2031			    IEEE80211_CHAN_BITS);
2032			putchar('\n');
2033		}
2034		prev = NULL;
2035		for (freq = b->freqStart + lo_adj;
2036		     freq <= b->freqEnd + hi_adj; freq += b->chanSep) {
2037			/*
2038			 * Construct flags for the new channel.  We take
2039			 * the attributes from the band descriptions except
2040			 * for HT40 which is enabled generically (i.e. +/-
2041			 * extension channel) in the band description and
2042			 * then constrained according by channel separation.
2043			 */
2044			flags = nb->flags | b->flags;
2045			if (flags & IEEE80211_CHAN_HT) {
2046				/*
2047				 * HT channels are generated specially; we're
2048				 * called to add HT20, HT40+, and HT40- chan's
2049				 * so we need to expand only band specs for
2050				 * the HT channel type being added.
2051				 */
2052				if ((chanFlags & IEEE80211_CHAN_HT20) &&
2053				    (flags & IEEE80211_CHAN_HT20) == 0) {
2054					if (verbose)
2055						printf("%u: skip, not an "
2056						    "HT20 channel\n", freq);
2057					continue;
2058				}
2059				if ((chanFlags & IEEE80211_CHAN_HT40) &&
2060				    (flags & IEEE80211_CHAN_HT40) == 0) {
2061					if (verbose)
2062						printf("%u: skip, not an "
2063						    "HT40 channel\n", freq);
2064					continue;
2065				}
2066				/* NB: HT attribute comes from caller */
2067				flags &= ~IEEE80211_CHAN_HT;
2068				flags |= chanFlags & IEEE80211_CHAN_HT;
2069			}
2070			/*
2071			 * Check if device can operate on this frequency.
2072			 */
2073			if (!checkchan(avail, freq, flags)) {
2074				if (verbose) {
2075					printf("%u: skip, ", freq);
2076					printb("flags", flags,
2077					    IEEE80211_CHAN_BITS);
2078					printf(" not available\n");
2079				}
2080				continue;
2081			}
2082			if ((flags & REQ_ECM) && !reg->ecm) {
2083				if (verbose)
2084					printf("%u: skip, ECM channel\n", freq);
2085				continue;
2086			}
2087			if ((flags & REQ_INDOOR) && reg->location == 'O') {
2088				if (verbose)
2089					printf("%u: skip, indoor channel\n",
2090					    freq);
2091				continue;
2092			}
2093			if ((flags & REQ_OUTDOOR) && reg->location == 'I') {
2094				if (verbose)
2095					printf("%u: skip, outdoor channel\n",
2096					    freq);
2097				continue;
2098			}
2099			if ((flags & IEEE80211_CHAN_HT40) &&
2100			    prev != NULL && (freq - prev->ic_freq) < channelSep) {
2101				if (verbose)
2102					printf("%u: skip, only %u channel "
2103					    "separation, need %d\n", freq,
2104					    freq - prev->ic_freq, channelSep);
2105				continue;
2106			}
2107			if (ci->ic_nchans == IEEE80211_CHAN_MAX) {
2108				if (verbose)
2109					printf("%u: skip, channel table full\n",
2110					    freq);
2111				break;
2112			}
2113			c = &ci->ic_chans[ci->ic_nchans++];
2114			memset(c, 0, sizeof(*c));
2115			c->ic_freq = freq;
2116			c->ic_flags = flags;
2117			if (c->ic_flags & IEEE80211_CHAN_DFS)
2118				c->ic_maxregpower = nb->maxPowerDFS;
2119			else
2120				c->ic_maxregpower = nb->maxPower;
2121			if (verbose) {
2122				printf("[%3d] add freq %u ",
2123				    ci->ic_nchans-1, c->ic_freq);
2124				printb("flags", c->ic_flags, IEEE80211_CHAN_BITS);
2125				printf(" power %u\n", c->ic_maxregpower);
2126			}
2127			/* NB: kernel fills in other fields */
2128			prev = c;
2129		}
2130	}
2131}
2132
2133static void
2134regdomain_makechannels(
2135	struct ieee80211_regdomain_req *req,
2136	const struct ieee80211_devcaps_req *dc)
2137{
2138	struct regdata *rdp = getregdata();
2139	const struct country *cc;
2140	const struct ieee80211_regdomain *reg = &req->rd;
2141	struct ieee80211req_chaninfo *ci = &req->chaninfo;
2142	const struct regdomain *rd;
2143
2144	/*
2145	 * Locate construction table for new channel list.  We treat
2146	 * the regdomain/SKU as definitive so a country can be in
2147	 * multiple with different properties (e.g. US in FCC+FCC3).
2148	 * If no regdomain is specified then we fallback on the country
2149	 * code to find the associated regdomain since countries always
2150	 * belong to at least one regdomain.
2151	 */
2152	if (reg->regdomain == 0) {
2153		cc = lib80211_country_findbycc(rdp, reg->country);
2154		if (cc == NULL)
2155			errx(1, "internal error, country %d not found",
2156			    reg->country);
2157		rd = cc->rd;
2158	} else
2159		rd = lib80211_regdomain_findbysku(rdp, reg->regdomain);
2160	if (rd == NULL)
2161		errx(1, "internal error, regdomain %d not found",
2162			    reg->regdomain);
2163	if (rd->sku != SKU_DEBUG) {
2164		/*
2165		 * regdomain_addchans incrememnts the channel count for
2166		 * each channel it adds so initialize ic_nchans to zero.
2167		 * Note that we know we have enough space to hold all possible
2168		 * channels because the devcaps list size was used to
2169		 * allocate our request.
2170		 */
2171		ci->ic_nchans = 0;
2172		if (!LIST_EMPTY(&rd->bands_11b))
2173			regdomain_addchans(ci, &rd->bands_11b, reg,
2174			    IEEE80211_CHAN_B, &dc->dc_chaninfo);
2175		if (!LIST_EMPTY(&rd->bands_11g))
2176			regdomain_addchans(ci, &rd->bands_11g, reg,
2177			    IEEE80211_CHAN_G, &dc->dc_chaninfo);
2178		if (!LIST_EMPTY(&rd->bands_11a))
2179			regdomain_addchans(ci, &rd->bands_11a, reg,
2180			    IEEE80211_CHAN_A, &dc->dc_chaninfo);
2181		if (!LIST_EMPTY(&rd->bands_11na) && dc->dc_htcaps != 0) {
2182			regdomain_addchans(ci, &rd->bands_11na, reg,
2183			    IEEE80211_CHAN_A | IEEE80211_CHAN_HT20,
2184			    &dc->dc_chaninfo);
2185			if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
2186				regdomain_addchans(ci, &rd->bands_11na, reg,
2187				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U,
2188				    &dc->dc_chaninfo);
2189				regdomain_addchans(ci, &rd->bands_11na, reg,
2190				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D,
2191				    &dc->dc_chaninfo);
2192			}
2193		}
2194		if (!LIST_EMPTY(&rd->bands_11ng) && dc->dc_htcaps != 0) {
2195			regdomain_addchans(ci, &rd->bands_11ng, reg,
2196			    IEEE80211_CHAN_G | IEEE80211_CHAN_HT20,
2197			    &dc->dc_chaninfo);
2198			if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
2199				regdomain_addchans(ci, &rd->bands_11ng, reg,
2200				    IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U,
2201				    &dc->dc_chaninfo);
2202				regdomain_addchans(ci, &rd->bands_11ng, reg,
2203				    IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D,
2204				    &dc->dc_chaninfo);
2205			}
2206		}
2207		qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]),
2208		    regdomain_sort);
2209	} else
2210		memcpy(ci, &dc->dc_chaninfo,
2211		    IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
2212}
2213
2214static void
2215list_countries(void)
2216{
2217	struct regdata *rdp = getregdata();
2218	const struct country *cp;
2219	const struct regdomain *dp;
2220	int i;
2221
2222	i = 0;
2223	printf("\nCountry codes:\n");
2224	LIST_FOREACH(cp, &rdp->countries, next) {
2225		printf("%2s %-15.15s%s", cp->isoname,
2226		    cp->name, ((i+1)%4) == 0 ? "\n" : " ");
2227		i++;
2228	}
2229	i = 0;
2230	printf("\nRegulatory domains:\n");
2231	LIST_FOREACH(dp, &rdp->domains, next) {
2232		printf("%-15.15s%s", dp->name, ((i+1)%4) == 0 ? "\n" : " ");
2233		i++;
2234	}
2235	printf("\n");
2236}
2237
2238static void
2239defaultcountry(const struct regdomain *rd)
2240{
2241	struct regdata *rdp = getregdata();
2242	const struct country *cc;
2243
2244	cc = lib80211_country_findbycc(rdp, rd->cc->code);
2245	if (cc == NULL)
2246		errx(1, "internal error, ISO country code %d not "
2247		    "defined for regdomain %s", rd->cc->code, rd->name);
2248	regdomain.country = cc->code;
2249	regdomain.isocc[0] = cc->isoname[0];
2250	regdomain.isocc[1] = cc->isoname[1];
2251}
2252
2253static
2254DECL_CMD_FUNC(set80211regdomain, val, d)
2255{
2256	struct regdata *rdp = getregdata();
2257	const struct regdomain *rd;
2258
2259	rd = lib80211_regdomain_findbyname(rdp, val);
2260	if (rd == NULL) {
2261		char *eptr;
2262		long sku = strtol(val, &eptr, 0);
2263
2264		if (eptr != val)
2265			rd = lib80211_regdomain_findbysku(rdp, sku);
2266		if (eptr == val || rd == NULL)
2267			errx(1, "unknown regdomain %s", val);
2268	}
2269	getregdomain(s);
2270	regdomain.regdomain = rd->sku;
2271	if (regdomain.country == 0 && rd->cc != NULL) {
2272		/*
2273		 * No country code setup and there's a default
2274		 * one for this regdomain fill it in.
2275		 */
2276		defaultcountry(rd);
2277	}
2278	callback_register(setregdomain_cb, &regdomain);
2279}
2280
2281static
2282DECL_CMD_FUNC(set80211country, val, d)
2283{
2284	struct regdata *rdp = getregdata();
2285	const struct country *cc;
2286
2287	cc = lib80211_country_findbyname(rdp, val);
2288	if (cc == NULL) {
2289		char *eptr;
2290		long code = strtol(val, &eptr, 0);
2291
2292		if (eptr != val)
2293			cc = lib80211_country_findbycc(rdp, code);
2294		if (eptr == val || cc == NULL)
2295			errx(1, "unknown ISO country code %s", val);
2296	}
2297	getregdomain(s);
2298	regdomain.regdomain = cc->rd->sku;
2299	regdomain.country = cc->code;
2300	regdomain.isocc[0] = cc->isoname[0];
2301	regdomain.isocc[1] = cc->isoname[1];
2302	callback_register(setregdomain_cb, &regdomain);
2303}
2304
2305static void
2306set80211location(const char *val, int d, int s, const struct afswtch *rafp)
2307{
2308	getregdomain(s);
2309	regdomain.location = d;
2310	callback_register(setregdomain_cb, &regdomain);
2311}
2312
2313static void
2314set80211ecm(const char *val, int d, int s, const struct afswtch *rafp)
2315{
2316	getregdomain(s);
2317	regdomain.ecm = d;
2318	callback_register(setregdomain_cb, &regdomain);
2319}
2320
2321static void
2322LINE_INIT(char c)
2323{
2324	spacer = c;
2325	if (c == '\t')
2326		col = 8;
2327	else
2328		col = 1;
2329}
2330
2331static void
2332LINE_BREAK(void)
2333{
2334	if (spacer != '\t') {
2335		printf("\n");
2336		spacer = '\t';
2337	}
2338	col = 8;		/* 8-col tab */
2339}
2340
2341static void
2342LINE_CHECK(const char *fmt, ...)
2343{
2344	char buf[80];
2345	va_list ap;
2346	int n;
2347
2348	va_start(ap, fmt);
2349	n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
2350	va_end(ap);
2351	col += 1+n;
2352	if (col > MAXCOL) {
2353		LINE_BREAK();
2354		col += n;
2355	}
2356	buf[0] = spacer;
2357	printf("%s", buf);
2358	spacer = ' ';
2359}
2360
2361static int
2362getmaxrate(const uint8_t rates[15], uint8_t nrates)
2363{
2364	int i, maxrate = -1;
2365
2366	for (i = 0; i < nrates; i++) {
2367		int rate = rates[i] & IEEE80211_RATE_VAL;
2368		if (rate > maxrate)
2369			maxrate = rate;
2370	}
2371	return maxrate / 2;
2372}
2373
2374static const char *
2375getcaps(int capinfo)
2376{
2377	static char capstring[32];
2378	char *cp = capstring;
2379
2380	if (capinfo & IEEE80211_CAPINFO_ESS)
2381		*cp++ = 'E';
2382	if (capinfo & IEEE80211_CAPINFO_IBSS)
2383		*cp++ = 'I';
2384	if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
2385		*cp++ = 'c';
2386	if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
2387		*cp++ = 'C';
2388	if (capinfo & IEEE80211_CAPINFO_PRIVACY)
2389		*cp++ = 'P';
2390	if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
2391		*cp++ = 'S';
2392	if (capinfo & IEEE80211_CAPINFO_PBCC)
2393		*cp++ = 'B';
2394	if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
2395		*cp++ = 'A';
2396	if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
2397		*cp++ = 's';
2398	if (capinfo & IEEE80211_CAPINFO_RSN)
2399		*cp++ = 'R';
2400	if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
2401		*cp++ = 'D';
2402	*cp = '\0';
2403	return capstring;
2404}
2405
2406static const char *
2407getflags(int flags)
2408{
2409	static char flagstring[32];
2410	char *cp = flagstring;
2411
2412	if (flags & IEEE80211_NODE_AUTH)
2413		*cp++ = 'A';
2414	if (flags & IEEE80211_NODE_QOS)
2415		*cp++ = 'Q';
2416	if (flags & IEEE80211_NODE_ERP)
2417		*cp++ = 'E';
2418	if (flags & IEEE80211_NODE_PWR_MGT)
2419		*cp++ = 'P';
2420	if (flags & IEEE80211_NODE_HT) {
2421		*cp++ = 'H';
2422		if (flags & IEEE80211_NODE_HTCOMPAT)
2423			*cp++ = '+';
2424	}
2425	if (flags & IEEE80211_NODE_WPS)
2426		*cp++ = 'W';
2427	if (flags & IEEE80211_NODE_TSN)
2428		*cp++ = 'N';
2429	if (flags & IEEE80211_NODE_AMPDU_TX)
2430		*cp++ = 'T';
2431	if (flags & IEEE80211_NODE_AMPDU_RX)
2432		*cp++ = 'R';
2433	if (flags & IEEE80211_NODE_MIMO_PS) {
2434		*cp++ = 'M';
2435		if (flags & IEEE80211_NODE_MIMO_RTS)
2436			*cp++ = '+';
2437	}
2438	if (flags & IEEE80211_NODE_RIFS)
2439		*cp++ = 'I';
2440	if (flags & IEEE80211_NODE_SGI40) {
2441		*cp++ = 'S';
2442		if (flags & IEEE80211_NODE_SGI20)
2443			*cp++ = '+';
2444	} else if (flags & IEEE80211_NODE_SGI20)
2445		*cp++ = 's';
2446	if (flags & IEEE80211_NODE_AMSDU_TX)
2447		*cp++ = 't';
2448	if (flags & IEEE80211_NODE_AMSDU_RX)
2449		*cp++ = 'r';
2450	*cp = '\0';
2451	return flagstring;
2452}
2453
2454static void
2455printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
2456{
2457	printf("%s", tag);
2458	if (verbose) {
2459		maxlen -= strlen(tag)+2;
2460		if (2*ielen > maxlen)
2461			maxlen--;
2462		printf("<");
2463		for (; ielen > 0; ie++, ielen--) {
2464			if (maxlen-- <= 0)
2465				break;
2466			printf("%02x", *ie);
2467		}
2468		if (ielen != 0)
2469			printf("-");
2470		printf(">");
2471	}
2472}
2473
2474#define LE_READ_2(p)					\
2475	((u_int16_t)					\
2476	 ((((const u_int8_t *)(p))[0]      ) |		\
2477	  (((const u_int8_t *)(p))[1] <<  8)))
2478#define LE_READ_4(p)					\
2479	((u_int32_t)					\
2480	 ((((const u_int8_t *)(p))[0]      ) |		\
2481	  (((const u_int8_t *)(p))[1] <<  8) |		\
2482	  (((const u_int8_t *)(p))[2] << 16) |		\
2483	  (((const u_int8_t *)(p))[3] << 24)))
2484
2485/*
2486 * NB: The decoding routines assume a properly formatted ie
2487 *     which should be safe as the kernel only retains them
2488 *     if they parse ok.
2489 */
2490
2491static void
2492printwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2493{
2494#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
2495	static const char *acnames[] = { "BE", "BK", "VO", "VI" };
2496	const struct ieee80211_wme_param *wme =
2497	    (const struct ieee80211_wme_param *) ie;
2498	int i;
2499
2500	printf("%s", tag);
2501	if (!verbose)
2502		return;
2503	printf("<qosinfo 0x%x", wme->param_qosInfo);
2504	ie += offsetof(struct ieee80211_wme_param, params_acParams);
2505	for (i = 0; i < WME_NUM_AC; i++) {
2506		const struct ieee80211_wme_acparams *ac =
2507		    &wme->params_acParams[i];
2508
2509		printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]"
2510			, acnames[i]
2511			, MS(ac->acp_aci_aifsn, WME_PARAM_ACM) ? "acm " : ""
2512			, MS(ac->acp_aci_aifsn, WME_PARAM_AIFSN)
2513			, MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMIN)
2514			, MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMAX)
2515			, LE_READ_2(&ac->acp_txop)
2516		);
2517	}
2518	printf(">");
2519#undef MS
2520}
2521
2522static void
2523printwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2524{
2525	printf("%s", tag);
2526	if (verbose) {
2527		const struct ieee80211_wme_info *wme =
2528		    (const struct ieee80211_wme_info *) ie;
2529		printf("<version 0x%x info 0x%x>",
2530		    wme->wme_version, wme->wme_info);
2531	}
2532}
2533
2534static void
2535printhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2536{
2537	printf("%s", tag);
2538	if (verbose) {
2539		const struct ieee80211_ie_htcap *htcap =
2540		    (const struct ieee80211_ie_htcap *) ie;
2541		const char *sep;
2542		int i, j;
2543
2544		printf("<cap 0x%x param 0x%x",
2545		    LE_READ_2(&htcap->hc_cap), htcap->hc_param);
2546		printf(" mcsset[");
2547		sep = "";
2548		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2549			if (isset(htcap->hc_mcsset, i)) {
2550				for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2551					if (isclr(htcap->hc_mcsset, j))
2552						break;
2553				j--;
2554				if (i == j)
2555					printf("%s%u", sep, i);
2556				else
2557					printf("%s%u-%u", sep, i, j);
2558				i += j-i;
2559				sep = ",";
2560			}
2561		printf("] extcap 0x%x txbf 0x%x antenna 0x%x>",
2562		    LE_READ_2(&htcap->hc_extcap),
2563		    LE_READ_4(&htcap->hc_txbf),
2564		    htcap->hc_antenna);
2565	}
2566}
2567
2568static void
2569printhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2570{
2571	printf("%s", tag);
2572	if (verbose) {
2573		const struct ieee80211_ie_htinfo *htinfo =
2574		    (const struct ieee80211_ie_htinfo *) ie;
2575		const char *sep;
2576		int i, j;
2577
2578		printf("<ctl %u, %x,%x,%x,%x", htinfo->hi_ctrlchannel,
2579		    htinfo->hi_byte1, htinfo->hi_byte2, htinfo->hi_byte3,
2580		    LE_READ_2(&htinfo->hi_byte45));
2581		printf(" basicmcs[");
2582		sep = "";
2583		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2584			if (isset(htinfo->hi_basicmcsset, i)) {
2585				for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2586					if (isclr(htinfo->hi_basicmcsset, j))
2587						break;
2588				j--;
2589				if (i == j)
2590					printf("%s%u", sep, i);
2591				else
2592					printf("%s%u-%u", sep, i, j);
2593				i += j-i;
2594				sep = ",";
2595			}
2596		printf("]>");
2597	}
2598}
2599
2600static void
2601printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2602{
2603
2604	printf("%s", tag);
2605	if (verbose) {
2606		const struct ieee80211_ath_ie *ath =
2607			(const struct ieee80211_ath_ie *)ie;
2608
2609		printf("<");
2610		if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME)
2611			printf("DTURBO,");
2612		if (ath->ath_capability & ATHEROS_CAP_COMPRESSION)
2613			printf("COMP,");
2614		if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME)
2615			printf("FF,");
2616		if (ath->ath_capability & ATHEROS_CAP_XR)
2617			printf("XR,");
2618		if (ath->ath_capability & ATHEROS_CAP_AR)
2619			printf("AR,");
2620		if (ath->ath_capability & ATHEROS_CAP_BURST)
2621			printf("BURST,");
2622		if (ath->ath_capability & ATHEROS_CAP_WME)
2623			printf("WME,");
2624		if (ath->ath_capability & ATHEROS_CAP_BOOST)
2625			printf("BOOST,");
2626		printf("0x%x>", LE_READ_2(ath->ath_defkeyix));
2627	}
2628}
2629
2630
2631static void
2632printmeshconf(const char *tag, const uint8_t *ie, size_t ielen, int maxlen)
2633{
2634#define MATCHOUI(field, oui, string)					\
2635do {									\
2636	if (memcmp(field, oui, 4) == 0)					\
2637		printf("%s", string);					\
2638} while (0)
2639
2640	printf("%s", tag);
2641	if (verbose) {
2642		const struct ieee80211_meshconf_ie *mconf =
2643			(const struct ieee80211_meshconf_ie *)ie;
2644		printf("<PATH:");
2645		if (mconf->conf_pselid == IEEE80211_MESHCONF_PATH_HWMP)
2646			printf("HWMP");
2647		else
2648			printf("UNKNOWN");
2649		printf(" LINK:");
2650		if (mconf->conf_pmetid == IEEE80211_MESHCONF_METRIC_AIRTIME)
2651			printf("AIRTIME");
2652		else
2653			printf("UNKNOWN");
2654		printf(" CONGESTION:");
2655		if (mconf->conf_ccid == IEEE80211_MESHCONF_CC_DISABLED)
2656			printf("DISABLED");
2657		else
2658			printf("UNKNOWN");
2659		printf(" SYNC:");
2660		if (mconf->conf_syncid == IEEE80211_MESHCONF_SYNC_NEIGHOFF)
2661			printf("NEIGHOFF");
2662		else
2663			printf("UNKNOWN");
2664		printf(" AUTH:");
2665		if (mconf->conf_authid == IEEE80211_MESHCONF_AUTH_DISABLED)
2666			printf("DISABLED");
2667		else
2668			printf("UNKNOWN");
2669		printf(" FORM:0x%x CAPS:0x%x>", mconf->conf_form,
2670		    mconf->conf_cap);
2671	}
2672#undef MATCHOUI
2673}
2674
2675static const char *
2676wpa_cipher(const u_int8_t *sel)
2677{
2678#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
2679	u_int32_t w = LE_READ_4(sel);
2680
2681	switch (w) {
2682	case WPA_SEL(WPA_CSE_NULL):
2683		return "NONE";
2684	case WPA_SEL(WPA_CSE_WEP40):
2685		return "WEP40";
2686	case WPA_SEL(WPA_CSE_WEP104):
2687		return "WEP104";
2688	case WPA_SEL(WPA_CSE_TKIP):
2689		return "TKIP";
2690	case WPA_SEL(WPA_CSE_CCMP):
2691		return "AES-CCMP";
2692	}
2693	return "?";		/* NB: so 1<< is discarded */
2694#undef WPA_SEL
2695}
2696
2697static const char *
2698wpa_keymgmt(const u_int8_t *sel)
2699{
2700#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
2701	u_int32_t w = LE_READ_4(sel);
2702
2703	switch (w) {
2704	case WPA_SEL(WPA_ASE_8021X_UNSPEC):
2705		return "8021X-UNSPEC";
2706	case WPA_SEL(WPA_ASE_8021X_PSK):
2707		return "8021X-PSK";
2708	case WPA_SEL(WPA_ASE_NONE):
2709		return "NONE";
2710	}
2711	return "?";
2712#undef WPA_SEL
2713}
2714
2715static void
2716printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2717{
2718	u_int8_t len = ie[1];
2719
2720	printf("%s", tag);
2721	if (verbose) {
2722		const char *sep;
2723		int n;
2724
2725		ie += 6, len -= 4;		/* NB: len is payload only */
2726
2727		printf("<v%u", LE_READ_2(ie));
2728		ie += 2, len -= 2;
2729
2730		printf(" mc:%s", wpa_cipher(ie));
2731		ie += 4, len -= 4;
2732
2733		/* unicast ciphers */
2734		n = LE_READ_2(ie);
2735		ie += 2, len -= 2;
2736		sep = " uc:";
2737		for (; n > 0; n--) {
2738			printf("%s%s", sep, wpa_cipher(ie));
2739			ie += 4, len -= 4;
2740			sep = "+";
2741		}
2742
2743		/* key management algorithms */
2744		n = LE_READ_2(ie);
2745		ie += 2, len -= 2;
2746		sep = " km:";
2747		for (; n > 0; n--) {
2748			printf("%s%s", sep, wpa_keymgmt(ie));
2749			ie += 4, len -= 4;
2750			sep = "+";
2751		}
2752
2753		if (len > 2)		/* optional capabilities */
2754			printf(", caps 0x%x", LE_READ_2(ie));
2755		printf(">");
2756	}
2757}
2758
2759static const char *
2760rsn_cipher(const u_int8_t *sel)
2761{
2762#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
2763	u_int32_t w = LE_READ_4(sel);
2764
2765	switch (w) {
2766	case RSN_SEL(RSN_CSE_NULL):
2767		return "NONE";
2768	case RSN_SEL(RSN_CSE_WEP40):
2769		return "WEP40";
2770	case RSN_SEL(RSN_CSE_WEP104):
2771		return "WEP104";
2772	case RSN_SEL(RSN_CSE_TKIP):
2773		return "TKIP";
2774	case RSN_SEL(RSN_CSE_CCMP):
2775		return "AES-CCMP";
2776	case RSN_SEL(RSN_CSE_WRAP):
2777		return "AES-OCB";
2778	}
2779	return "?";
2780#undef WPA_SEL
2781}
2782
2783static const char *
2784rsn_keymgmt(const u_int8_t *sel)
2785{
2786#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
2787	u_int32_t w = LE_READ_4(sel);
2788
2789	switch (w) {
2790	case RSN_SEL(RSN_ASE_8021X_UNSPEC):
2791		return "8021X-UNSPEC";
2792	case RSN_SEL(RSN_ASE_8021X_PSK):
2793		return "8021X-PSK";
2794	case RSN_SEL(RSN_ASE_NONE):
2795		return "NONE";
2796	}
2797	return "?";
2798#undef RSN_SEL
2799}
2800
2801static void
2802printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2803{
2804	printf("%s", tag);
2805	if (verbose) {
2806		const char *sep;
2807		int n;
2808
2809		ie += 2, ielen -= 2;
2810
2811		printf("<v%u", LE_READ_2(ie));
2812		ie += 2, ielen -= 2;
2813
2814		printf(" mc:%s", rsn_cipher(ie));
2815		ie += 4, ielen -= 4;
2816
2817		/* unicast ciphers */
2818		n = LE_READ_2(ie);
2819		ie += 2, ielen -= 2;
2820		sep = " uc:";
2821		for (; n > 0; n--) {
2822			printf("%s%s", sep, rsn_cipher(ie));
2823			ie += 4, ielen -= 4;
2824			sep = "+";
2825		}
2826
2827		/* key management algorithms */
2828		n = LE_READ_2(ie);
2829		ie += 2, ielen -= 2;
2830		sep = " km:";
2831		for (; n > 0; n--) {
2832			printf("%s%s", sep, rsn_keymgmt(ie));
2833			ie += 4, ielen -= 4;
2834			sep = "+";
2835		}
2836
2837		if (ielen > 2)		/* optional capabilities */
2838			printf(", caps 0x%x", LE_READ_2(ie));
2839		/* XXXPMKID */
2840		printf(">");
2841	}
2842}
2843
2844/* XXX move to a public include file */
2845#define IEEE80211_WPS_DEV_PASS_ID	0x1012
2846#define IEEE80211_WPS_SELECTED_REG	0x1041
2847#define IEEE80211_WPS_SETUP_STATE	0x1044
2848#define IEEE80211_WPS_UUID_E		0x1047
2849#define IEEE80211_WPS_VERSION		0x104a
2850
2851#define BE_READ_2(p)					\
2852	((u_int16_t)					\
2853	 ((((const u_int8_t *)(p))[1]      ) |		\
2854	  (((const u_int8_t *)(p))[0] <<  8)))
2855
2856static void
2857printwpsie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2858{
2859#define	N(a)	(sizeof(a) / sizeof(a[0]))
2860	u_int8_t len = ie[1];
2861
2862	printf("%s", tag);
2863	if (verbose) {
2864		static const char *dev_pass_id[] = {
2865			"D",	/* Default (PIN) */
2866			"U",	/* User-specified */
2867			"M",	/* Machine-specified */
2868			"K",	/* Rekey */
2869			"P",	/* PushButton */
2870			"R"	/* Registrar-specified */
2871		};
2872		int n;
2873
2874		ie +=6, len -= 4;		/* NB: len is payload only */
2875
2876		/* WPS IE in Beacon and Probe Resp frames have different fields */
2877		printf("<");
2878		while (len) {
2879			uint16_t tlv_type = BE_READ_2(ie);
2880			uint16_t tlv_len  = BE_READ_2(ie + 2);
2881
2882			ie += 4, len -= 4;
2883
2884			switch (tlv_type) {
2885			case IEEE80211_WPS_VERSION:
2886				printf("v:%d.%d", *ie >> 4, *ie & 0xf);
2887				break;
2888			case IEEE80211_WPS_SETUP_STATE:
2889				/* Only 1 and 2 are valid */
2890				if (*ie == 0 || *ie >= 3)
2891					printf(" state:B");
2892				else
2893					printf(" st:%s", *ie == 1 ? "N" : "C");
2894				break;
2895			case IEEE80211_WPS_SELECTED_REG:
2896				printf(" sel:%s", *ie ? "T" : "F");
2897				break;
2898			case IEEE80211_WPS_DEV_PASS_ID:
2899				n = LE_READ_2(ie);
2900				if (n < N(dev_pass_id))
2901					printf(" dpi:%s", dev_pass_id[n]);
2902				break;
2903			case IEEE80211_WPS_UUID_E:
2904				printf(" uuid-e:");
2905				for (n = 0; n < (tlv_len - 1); n++)
2906					printf("%02x-", ie[n]);
2907				printf("%02x", ie[n]);
2908				break;
2909			}
2910			ie += tlv_len, len -= tlv_len;
2911		}
2912		printf(">");
2913	}
2914#undef N
2915}
2916
2917static void
2918printtdmaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2919{
2920	printf("%s", tag);
2921	if (verbose && ielen >= sizeof(struct ieee80211_tdma_param)) {
2922		const struct ieee80211_tdma_param *tdma =
2923		   (const struct ieee80211_tdma_param *) ie;
2924
2925		/* XXX tstamp */
2926		printf("<v%u slot:%u slotcnt:%u slotlen:%u bintval:%u inuse:0x%x>",
2927		    tdma->tdma_version, tdma->tdma_slot, tdma->tdma_slotcnt,
2928		    LE_READ_2(&tdma->tdma_slotlen), tdma->tdma_bintval,
2929		    tdma->tdma_inuse[0]);
2930	}
2931}
2932
2933/*
2934 * Copy the ssid string contents into buf, truncating to fit.  If the
2935 * ssid is entirely printable then just copy intact.  Otherwise convert
2936 * to hexadecimal.  If the result is truncated then replace the last
2937 * three characters with "...".
2938 */
2939static int
2940copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
2941{
2942	const u_int8_t *p;
2943	size_t maxlen;
2944	int i;
2945
2946	if (essid_len > bufsize)
2947		maxlen = bufsize;
2948	else
2949		maxlen = essid_len;
2950	/* determine printable or not */
2951	for (i = 0, p = essid; i < maxlen; i++, p++) {
2952		if (*p < ' ' || *p > 0x7e)
2953			break;
2954	}
2955	if (i != maxlen) {		/* not printable, print as hex */
2956		if (bufsize < 3)
2957			return 0;
2958		strlcpy(buf, "0x", bufsize);
2959		bufsize -= 2;
2960		p = essid;
2961		for (i = 0; i < maxlen && bufsize >= 2; i++) {
2962			sprintf(&buf[2+2*i], "%02x", p[i]);
2963			bufsize -= 2;
2964		}
2965		if (i != essid_len)
2966			memcpy(&buf[2+2*i-3], "...", 3);
2967	} else {			/* printable, truncate as needed */
2968		memcpy(buf, essid, maxlen);
2969		if (maxlen != essid_len)
2970			memcpy(&buf[maxlen-3], "...", 3);
2971	}
2972	return maxlen;
2973}
2974
2975static void
2976printssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2977{
2978	char ssid[2*IEEE80211_NWID_LEN+1];
2979
2980	printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid);
2981}
2982
2983static void
2984printrates(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2985{
2986	const char *sep;
2987	int i;
2988
2989	printf("%s", tag);
2990	sep = "<";
2991	for (i = 2; i < ielen; i++) {
2992		printf("%s%s%d", sep,
2993		    ie[i] & IEEE80211_RATE_BASIC ? "B" : "",
2994		    ie[i] & IEEE80211_RATE_VAL);
2995		sep = ",";
2996	}
2997	printf(">");
2998}
2999
3000static void
3001printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3002{
3003	const struct ieee80211_country_ie *cie =
3004	   (const struct ieee80211_country_ie *) ie;
3005	int i, nbands, schan, nchan;
3006
3007	printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]);
3008	nbands = (cie->len - 3) / sizeof(cie->band[0]);
3009	for (i = 0; i < nbands; i++) {
3010		schan = cie->band[i].schan;
3011		nchan = cie->band[i].nchan;
3012		if (nchan != 1)
3013			printf(" %u-%u,%u", schan, schan + nchan-1,
3014			    cie->band[i].maxtxpwr);
3015		else
3016			printf(" %u,%u", schan, cie->band[i].maxtxpwr);
3017	}
3018	printf(">");
3019}
3020
3021/* unaligned little endian access */
3022#define LE_READ_4(p)					\
3023	((u_int32_t)					\
3024	 ((((const u_int8_t *)(p))[0]      ) |		\
3025	  (((const u_int8_t *)(p))[1] <<  8) |		\
3026	  (((const u_int8_t *)(p))[2] << 16) |		\
3027	  (((const u_int8_t *)(p))[3] << 24)))
3028
3029static __inline int
3030iswpaoui(const u_int8_t *frm)
3031{
3032	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
3033}
3034
3035static __inline int
3036iswmeinfo(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_INFO_OUI_SUBTYPE;
3040}
3041
3042static __inline int
3043iswmeparam(const u_int8_t *frm)
3044{
3045	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
3046		frm[6] == WME_PARAM_OUI_SUBTYPE;
3047}
3048
3049static __inline int
3050isatherosoui(const u_int8_t *frm)
3051{
3052	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
3053}
3054
3055static __inline int
3056istdmaoui(const uint8_t *frm)
3057{
3058	return frm[1] > 3 && LE_READ_4(frm+2) == ((TDMA_OUI_TYPE<<24)|TDMA_OUI);
3059}
3060
3061static __inline int
3062iswpsoui(const uint8_t *frm)
3063{
3064	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPS_OUI_TYPE<<24)|WPA_OUI);
3065}
3066
3067static const char *
3068iename(int elemid)
3069{
3070	switch (elemid) {
3071	case IEEE80211_ELEMID_FHPARMS:	return " FHPARMS";
3072	case IEEE80211_ELEMID_CFPARMS:	return " CFPARMS";
3073	case IEEE80211_ELEMID_TIM:	return " TIM";
3074	case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS";
3075	case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE";
3076	case IEEE80211_ELEMID_PWRCNSTR:	return " PWRCNSTR";
3077	case IEEE80211_ELEMID_PWRCAP:	return " PWRCAP";
3078	case IEEE80211_ELEMID_TPCREQ:	return " TPCREQ";
3079	case IEEE80211_ELEMID_TPCREP:	return " TPCREP";
3080	case IEEE80211_ELEMID_SUPPCHAN:	return " SUPPCHAN";
3081	case IEEE80211_ELEMID_CSA:	return " CSA";
3082	case IEEE80211_ELEMID_MEASREQ:	return " MEASREQ";
3083	case IEEE80211_ELEMID_MEASREP:	return " MEASREP";
3084	case IEEE80211_ELEMID_QUIET:	return " QUIET";
3085	case IEEE80211_ELEMID_IBSSDFS:	return " IBSSDFS";
3086	case IEEE80211_ELEMID_TPC:	return " TPC";
3087	case IEEE80211_ELEMID_CCKM:	return " CCKM";
3088	}
3089	return " ???";
3090}
3091
3092static void
3093printies(const u_int8_t *vp, int ielen, int maxcols)
3094{
3095	while (ielen > 0) {
3096		switch (vp[0]) {
3097		case IEEE80211_ELEMID_SSID:
3098			if (verbose)
3099				printssid(" SSID", vp, 2+vp[1], maxcols);
3100			break;
3101		case IEEE80211_ELEMID_RATES:
3102		case IEEE80211_ELEMID_XRATES:
3103			if (verbose)
3104				printrates(vp[0] == IEEE80211_ELEMID_RATES ?
3105				    " RATES" : " XRATES", vp, 2+vp[1], maxcols);
3106			break;
3107		case IEEE80211_ELEMID_DSPARMS:
3108			if (verbose)
3109				printf(" DSPARMS<%u>", vp[2]);
3110			break;
3111		case IEEE80211_ELEMID_COUNTRY:
3112			if (verbose)
3113				printcountry(" COUNTRY", vp, 2+vp[1], maxcols);
3114			break;
3115		case IEEE80211_ELEMID_ERP:
3116			if (verbose)
3117				printf(" ERP<0x%x>", vp[2]);
3118			break;
3119		case IEEE80211_ELEMID_VENDOR:
3120			if (iswpaoui(vp))
3121				printwpaie(" WPA", vp, 2+vp[1], maxcols);
3122			else if (iswmeinfo(vp))
3123				printwmeinfo(" WME", vp, 2+vp[1], maxcols);
3124			else if (iswmeparam(vp))
3125				printwmeparam(" WME", vp, 2+vp[1], maxcols);
3126			else if (isatherosoui(vp))
3127				printathie(" ATH", vp, 2+vp[1], maxcols);
3128			else if (iswpsoui(vp))
3129				printwpsie(" WPS", vp, 2+vp[1], maxcols);
3130			else if (istdmaoui(vp))
3131				printtdmaie(" TDMA", vp, 2+vp[1], maxcols);
3132			else if (verbose)
3133				printie(" VEN", vp, 2+vp[1], maxcols);
3134			break;
3135		case IEEE80211_ELEMID_RSN:
3136			printrsnie(" RSN", vp, 2+vp[1], maxcols);
3137			break;
3138		case IEEE80211_ELEMID_HTCAP:
3139			printhtcap(" HTCAP", vp, 2+vp[1], maxcols);
3140			break;
3141		case IEEE80211_ELEMID_HTINFO:
3142			if (verbose)
3143				printhtinfo(" HTINFO", vp, 2+vp[1], maxcols);
3144			break;
3145		case IEEE80211_ELEMID_MESHID:
3146			if (verbose)
3147				printssid(" MESHID", vp, 2+vp[1], maxcols);
3148			break;
3149		case IEEE80211_ELEMID_MESHCONF:
3150			printmeshconf(" MESHCONF", vp, 2+vp[1], maxcols);
3151			break;
3152		default:
3153			if (verbose)
3154				printie(iename(vp[0]), vp, 2+vp[1], maxcols);
3155			break;
3156		}
3157		ielen -= 2+vp[1];
3158		vp += 2+vp[1];
3159	}
3160}
3161
3162static void
3163printmimo(const struct ieee80211_mimo_info *mi)
3164{
3165	/* NB: don't muddy display unless there's something to show */
3166	if (mi->rssi[0] != 0 || mi->rssi[1] != 0 || mi->rssi[2] != 0) {
3167		/* XXX ignore EVM for now */
3168		printf(" (rssi %d:%d:%d nf %d:%d:%d)",
3169		    mi->rssi[0], mi->rssi[1], mi->rssi[2],
3170		    mi->noise[0], mi->noise[1], mi->noise[2]);
3171	}
3172}
3173
3174static void
3175list_scan(int s)
3176{
3177	uint8_t buf[24*1024];
3178	char ssid[IEEE80211_NWID_LEN+1];
3179	const uint8_t *cp;
3180	int len, ssidmax, idlen;
3181
3182	if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0)
3183		errx(1, "unable to get scan results");
3184	if (len < sizeof(struct ieee80211req_scan_result))
3185		return;
3186
3187	getchaninfo(s);
3188
3189	ssidmax = verbose ? IEEE80211_NWID_LEN - 1 : 14;
3190	printf("%-*.*s  %-17.17s  %4s %4s  %-7s  %3s %4s\n"
3191		, ssidmax, ssidmax, "SSID/MESH ID"
3192		, "BSSID"
3193		, "CHAN"
3194		, "RATE"
3195		, " S:N"
3196		, "INT"
3197		, "CAPS"
3198	);
3199	cp = buf;
3200	do {
3201		const struct ieee80211req_scan_result *sr;
3202		const uint8_t *vp, *idp;
3203
3204		sr = (const struct ieee80211req_scan_result *) cp;
3205		vp = cp + sr->isr_ie_off;
3206		if (sr->isr_meshid_len) {
3207			idp = vp + sr->isr_ssid_len;
3208			idlen = sr->isr_meshid_len;
3209		} else {
3210			idp = vp;
3211			idlen = sr->isr_ssid_len;
3212		}
3213		printf("%-*.*s  %s  %3d  %3dM %3d:%-3d  %3d %-4.4s"
3214			, ssidmax
3215			  , copy_essid(ssid, ssidmax, idp, idlen)
3216			  , ssid
3217			, ether_ntoa((const struct ether_addr *) sr->isr_bssid)
3218			, ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
3219			, getmaxrate(sr->isr_rates, sr->isr_nrates)
3220			, (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise
3221			, sr->isr_intval
3222			, getcaps(sr->isr_capinfo)
3223		);
3224		printies(vp + sr->isr_ssid_len + sr->isr_meshid_len,
3225		    sr->isr_ie_len, 24);
3226		printf("\n");
3227		cp += sr->isr_len, len -= sr->isr_len;
3228	} while (len >= sizeof(struct ieee80211req_scan_result));
3229}
3230
3231static void
3232scan_and_wait(int s)
3233{
3234	struct ieee80211_scan_req sr;
3235	struct ieee80211req ireq;
3236	int sroute;
3237
3238	sroute = socket(PF_ROUTE, SOCK_RAW, 0);
3239	if (sroute < 0) {
3240		perror("socket(PF_ROUTE,SOCK_RAW)");
3241		return;
3242	}
3243	(void) memset(&ireq, 0, sizeof(ireq));
3244	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3245	ireq.i_type = IEEE80211_IOC_SCAN_REQ;
3246
3247	memset(&sr, 0, sizeof(sr));
3248	sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
3249		    | IEEE80211_IOC_SCAN_BGSCAN
3250		    | IEEE80211_IOC_SCAN_NOPICK
3251		    | IEEE80211_IOC_SCAN_ONCE;
3252	sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
3253	sr.sr_nssid = 0;
3254
3255	ireq.i_data = &sr;
3256	ireq.i_len = sizeof(sr);
3257	/*
3258	 * NB: only root can trigger a scan so ignore errors. Also ignore
3259	 * possible errors from net80211, even if no new scan could be
3260	 * started there might still be a valid scan cache.
3261	 */
3262	if (ioctl(s, SIOCS80211, &ireq) == 0) {
3263		char buf[2048];
3264		struct if_announcemsghdr *ifan;
3265		struct rt_msghdr *rtm;
3266
3267		do {
3268			if (read(sroute, buf, sizeof(buf)) < 0) {
3269				perror("read(PF_ROUTE)");
3270				break;
3271			}
3272			rtm = (struct rt_msghdr *) buf;
3273			if (rtm->rtm_version != RTM_VERSION)
3274				break;
3275			ifan = (struct if_announcemsghdr *) rtm;
3276		} while (rtm->rtm_type != RTM_IEEE80211 ||
3277		    ifan->ifan_what != RTM_IEEE80211_SCAN);
3278	}
3279	close(sroute);
3280}
3281
3282static
3283DECL_CMD_FUNC(set80211scan, val, d)
3284{
3285	scan_and_wait(s);
3286	list_scan(s);
3287}
3288
3289static enum ieee80211_opmode get80211opmode(int s);
3290
3291static int
3292gettxseq(const struct ieee80211req_sta_info *si)
3293{
3294	int i, txseq;
3295
3296	if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3297		return si->isi_txseqs[0];
3298	/* XXX not right but usually what folks want */
3299	txseq = 0;
3300	for (i = 0; i < IEEE80211_TID_SIZE; i++)
3301		if (si->isi_txseqs[i] > txseq)
3302			txseq = si->isi_txseqs[i];
3303	return txseq;
3304}
3305
3306static int
3307getrxseq(const struct ieee80211req_sta_info *si)
3308{
3309	int i, rxseq;
3310
3311	if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3312		return si->isi_rxseqs[0];
3313	/* XXX not right but usually what folks want */
3314	rxseq = 0;
3315	for (i = 0; i < IEEE80211_TID_SIZE; i++)
3316		if (si->isi_rxseqs[i] > rxseq)
3317			rxseq = si->isi_rxseqs[i];
3318	return rxseq;
3319}
3320
3321static void
3322list_stations(int s)
3323{
3324	union {
3325		struct ieee80211req_sta_req req;
3326		uint8_t buf[24*1024];
3327	} u;
3328	enum ieee80211_opmode opmode = get80211opmode(s);
3329	const uint8_t *cp;
3330	int len;
3331
3332	/* broadcast address =>'s get all stations */
3333	(void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
3334	if (opmode == IEEE80211_M_STA) {
3335		/*
3336		 * Get information about the associated AP.
3337		 */
3338		(void) get80211(s, IEEE80211_IOC_BSSID,
3339		    u.req.is_u.macaddr, IEEE80211_ADDR_LEN);
3340	}
3341	if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0)
3342		errx(1, "unable to get station information");
3343	if (len < sizeof(struct ieee80211req_sta_info))
3344		return;
3345
3346	getchaninfo(s);
3347
3348	if (opmode == IEEE80211_M_MBSS)
3349		printf("%-17.17s %4s %5s %5s %7s %4s %4s %4s %6s %6s\n"
3350			, "ADDR"
3351			, "CHAN"
3352			, "LOCAL"
3353			, "PEER"
3354			, "STATE"
3355			, "RATE"
3356			, "RSSI"
3357			, "IDLE"
3358			, "TXSEQ"
3359			, "RXSEQ"
3360		);
3361	else
3362		printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %-7s\n"
3363			, "ADDR"
3364			, "AID"
3365			, "CHAN"
3366			, "RATE"
3367			, "RSSI"
3368			, "IDLE"
3369			, "TXSEQ"
3370			, "RXSEQ"
3371			, "CAPS"
3372			, "FLAG"
3373		);
3374	cp = (const uint8_t *) u.req.info;
3375	do {
3376		const struct ieee80211req_sta_info *si;
3377
3378		si = (const struct ieee80211req_sta_info *) cp;
3379		if (si->isi_len < sizeof(*si))
3380			break;
3381		if (opmode == IEEE80211_M_MBSS)
3382			printf("%s %4d %5x %5x %7.7s %3dM %4.1f %4d %6d %6d"
3383				, ether_ntoa((const struct ether_addr*)
3384				    si->isi_macaddr)
3385				, ieee80211_mhz2ieee(si->isi_freq,
3386				    si->isi_flags)
3387				, si->isi_localid
3388				, si->isi_peerid
3389				, mesh_linkstate_string(si->isi_peerstate)
3390				, si->isi_txmbps/2
3391				, si->isi_rssi/2.
3392				, si->isi_inact
3393				, gettxseq(si)
3394				, getrxseq(si)
3395			);
3396		else
3397			printf("%s %4u %4d %3dM %4.1f %4d %6d %6d %-4.4s %-7.7s"
3398				, ether_ntoa((const struct ether_addr*)
3399				    si->isi_macaddr)
3400				, IEEE80211_AID(si->isi_associd)
3401				, ieee80211_mhz2ieee(si->isi_freq,
3402				    si->isi_flags)
3403				, si->isi_txmbps/2
3404				, si->isi_rssi/2.
3405				, si->isi_inact
3406				, gettxseq(si)
3407				, getrxseq(si)
3408				, getcaps(si->isi_capinfo)
3409				, getflags(si->isi_state)
3410			);
3411		printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
3412		printmimo(&si->isi_mimo);
3413		printf("\n");
3414		cp += si->isi_len, len -= si->isi_len;
3415	} while (len >= sizeof(struct ieee80211req_sta_info));
3416}
3417
3418static const char *
3419mesh_linkstate_string(uint8_t state)
3420{
3421#define	N(a)	(sizeof(a) / sizeof(a[0]))
3422	static const char *state_names[] = {
3423	    [0] = "IDLE",
3424	    [1] = "OPEN-TX",
3425	    [2] = "OPEN-RX",
3426	    [3] = "CONF-RX",
3427	    [4] = "ESTAB",
3428	    [5] = "HOLDING",
3429	};
3430
3431	if (state >= N(state_names)) {
3432		static char buf[10];
3433		snprintf(buf, sizeof(buf), "#%u", state);
3434		return buf;
3435	} else
3436		return state_names[state];
3437#undef N
3438}
3439
3440static const char *
3441get_chaninfo(const struct ieee80211_channel *c, int precise,
3442	char buf[], size_t bsize)
3443{
3444	buf[0] = '\0';
3445	if (IEEE80211_IS_CHAN_FHSS(c))
3446		strlcat(buf, " FHSS", bsize);
3447	if (IEEE80211_IS_CHAN_A(c))
3448		strlcat(buf, " 11a", bsize);
3449	else if (IEEE80211_IS_CHAN_ANYG(c))
3450		strlcat(buf, " 11g", bsize);
3451	else if (IEEE80211_IS_CHAN_B(c))
3452		strlcat(buf, " 11b", bsize);
3453	if (IEEE80211_IS_CHAN_HALF(c))
3454		strlcat(buf, "/10MHz", bsize);
3455	if (IEEE80211_IS_CHAN_QUARTER(c))
3456		strlcat(buf, "/5MHz", bsize);
3457	if (IEEE80211_IS_CHAN_TURBO(c))
3458		strlcat(buf, " Turbo", bsize);
3459	if (precise) {
3460		if (IEEE80211_IS_CHAN_HT20(c))
3461			strlcat(buf, " ht/20", bsize);
3462		else if (IEEE80211_IS_CHAN_HT40D(c))
3463			strlcat(buf, " ht/40-", bsize);
3464		else if (IEEE80211_IS_CHAN_HT40U(c))
3465			strlcat(buf, " ht/40+", bsize);
3466	} else {
3467		if (IEEE80211_IS_CHAN_HT(c))
3468			strlcat(buf, " ht", bsize);
3469	}
3470	return buf;
3471}
3472
3473static void
3474print_chaninfo(const struct ieee80211_channel *c, int verb)
3475{
3476	char buf[14];
3477
3478	if (verb)
3479		printf("Channel %3u : %u%c%c%c%c%c MHz%-14.14s",
3480		    ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
3481		    IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
3482		    IEEE80211_IS_CHAN_DFS(c) ? 'D' : ' ',
3483		    IEEE80211_IS_CHAN_RADAR(c) ? 'R' : ' ',
3484		    IEEE80211_IS_CHAN_CWINT(c) ? 'I' : ' ',
3485		    IEEE80211_IS_CHAN_CACDONE(c) ? 'C' : ' ',
3486		    get_chaninfo(c, verb, buf, sizeof(buf)));
3487	else
3488	printf("Channel %3u : %u%c MHz%-14.14s",
3489	    ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
3490	    IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
3491	    get_chaninfo(c, verb, buf, sizeof(buf)));
3492
3493}
3494
3495static int
3496chanpref(const struct ieee80211_channel *c)
3497{
3498	if (IEEE80211_IS_CHAN_HT40(c))
3499		return 40;
3500	if (IEEE80211_IS_CHAN_HT20(c))
3501		return 30;
3502	if (IEEE80211_IS_CHAN_HALF(c))
3503		return 10;
3504	if (IEEE80211_IS_CHAN_QUARTER(c))
3505		return 5;
3506	if (IEEE80211_IS_CHAN_TURBO(c))
3507		return 25;
3508	if (IEEE80211_IS_CHAN_A(c))
3509		return 20;
3510	if (IEEE80211_IS_CHAN_G(c))
3511		return 20;
3512	if (IEEE80211_IS_CHAN_B(c))
3513		return 15;
3514	if (IEEE80211_IS_CHAN_PUREG(c))
3515		return 15;
3516	return 0;
3517}
3518
3519static void
3520print_channels(int s, const struct ieee80211req_chaninfo *chans,
3521	int allchans, int verb)
3522{
3523	struct ieee80211req_chaninfo *achans;
3524	uint8_t reported[IEEE80211_CHAN_BYTES];
3525	const struct ieee80211_channel *c;
3526	int i, half;
3527
3528	achans = malloc(IEEE80211_CHANINFO_SPACE(chans));
3529	if (achans == NULL)
3530		errx(1, "no space for active channel list");
3531	achans->ic_nchans = 0;
3532	memset(reported, 0, sizeof(reported));
3533	if (!allchans) {
3534		struct ieee80211req_chanlist active;
3535
3536		if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0)
3537			errx(1, "unable to get active channel list");
3538		for (i = 0; i < chans->ic_nchans; i++) {
3539			c = &chans->ic_chans[i];
3540			if (!isset(active.ic_channels, c->ic_ieee))
3541				continue;
3542			/*
3543			 * Suppress compatible duplicates unless
3544			 * verbose.  The kernel gives us it's
3545			 * complete channel list which has separate
3546			 * entries for 11g/11b and 11a/turbo.
3547			 */
3548			if (isset(reported, c->ic_ieee) && !verb) {
3549				/* XXX we assume duplicates are adjacent */
3550				achans->ic_chans[achans->ic_nchans-1] = *c;
3551			} else {
3552				achans->ic_chans[achans->ic_nchans++] = *c;
3553				setbit(reported, c->ic_ieee);
3554			}
3555		}
3556	} else {
3557		for (i = 0; i < chans->ic_nchans; i++) {
3558			c = &chans->ic_chans[i];
3559			/* suppress duplicates as above */
3560			if (isset(reported, c->ic_ieee) && !verb) {
3561				/* XXX we assume duplicates are adjacent */
3562				struct ieee80211_channel *a =
3563				    &achans->ic_chans[achans->ic_nchans-1];
3564				if (chanpref(c) > chanpref(a))
3565					*a = *c;
3566			} else {
3567				achans->ic_chans[achans->ic_nchans++] = *c;
3568				setbit(reported, c->ic_ieee);
3569			}
3570		}
3571	}
3572	half = achans->ic_nchans / 2;
3573	if (achans->ic_nchans % 2)
3574		half++;
3575
3576	for (i = 0; i < achans->ic_nchans / 2; i++) {
3577		print_chaninfo(&achans->ic_chans[i], verb);
3578		print_chaninfo(&achans->ic_chans[half+i], verb);
3579		printf("\n");
3580	}
3581	if (achans->ic_nchans % 2) {
3582		print_chaninfo(&achans->ic_chans[i], verb);
3583		printf("\n");
3584	}
3585	free(achans);
3586}
3587
3588static void
3589list_channels(int s, int allchans)
3590{
3591	getchaninfo(s);
3592	print_channels(s, chaninfo, allchans, verbose);
3593}
3594
3595static void
3596print_txpow(const struct ieee80211_channel *c)
3597{
3598	printf("Channel %3u : %u MHz %3.1f reg %2d  ",
3599	    c->ic_ieee, c->ic_freq,
3600	    c->ic_maxpower/2., c->ic_maxregpower);
3601}
3602
3603static void
3604print_txpow_verbose(const struct ieee80211_channel *c)
3605{
3606	print_chaninfo(c, 1);
3607	printf("min %4.1f dBm  max %3.1f dBm  reg %2d dBm",
3608	    c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
3609	/* indicate where regulatory cap limits power use */
3610	if (c->ic_maxpower > 2*c->ic_maxregpower)
3611		printf(" <");
3612}
3613
3614static void
3615list_txpow(int s)
3616{
3617	struct ieee80211req_chaninfo *achans;
3618	uint8_t reported[IEEE80211_CHAN_BYTES];
3619	struct ieee80211_channel *c, *prev;
3620	int i, half;
3621
3622	getchaninfo(s);
3623	achans = malloc(IEEE80211_CHANINFO_SPACE(chaninfo));
3624	if (achans == NULL)
3625		errx(1, "no space for active channel list");
3626	achans->ic_nchans = 0;
3627	memset(reported, 0, sizeof(reported));
3628	for (i = 0; i < chaninfo->ic_nchans; i++) {
3629		c = &chaninfo->ic_chans[i];
3630		/* suppress duplicates as above */
3631		if (isset(reported, c->ic_ieee) && !verbose) {
3632			/* XXX we assume duplicates are adjacent */
3633			prev = &achans->ic_chans[achans->ic_nchans-1];
3634			/* display highest power on channel */
3635			if (c->ic_maxpower > prev->ic_maxpower)
3636				*prev = *c;
3637		} else {
3638			achans->ic_chans[achans->ic_nchans++] = *c;
3639			setbit(reported, c->ic_ieee);
3640		}
3641	}
3642	if (!verbose) {
3643		half = achans->ic_nchans / 2;
3644		if (achans->ic_nchans % 2)
3645			half++;
3646
3647		for (i = 0; i < achans->ic_nchans / 2; i++) {
3648			print_txpow(&achans->ic_chans[i]);
3649			print_txpow(&achans->ic_chans[half+i]);
3650			printf("\n");
3651		}
3652		if (achans->ic_nchans % 2) {
3653			print_txpow(&achans->ic_chans[i]);
3654			printf("\n");
3655		}
3656	} else {
3657		for (i = 0; i < achans->ic_nchans; i++) {
3658			print_txpow_verbose(&achans->ic_chans[i]);
3659			printf("\n");
3660		}
3661	}
3662	free(achans);
3663}
3664
3665static void
3666list_keys(int s)
3667{
3668}
3669
3670#define	IEEE80211_C_BITS \
3671	"\20\1STA\002803ENCAP\7FF\10TURBOP\11IBSS\12PMGT" \
3672	"\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \
3673	"\21MONITOR\22DFS\23MBSS\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
3674	"\37TXFRAG\40TDMA"
3675
3676static void
3677list_capabilities(int s)
3678{
3679	struct ieee80211_devcaps_req *dc;
3680
3681	if (verbose)
3682		dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
3683	else
3684		dc = malloc(IEEE80211_DEVCAPS_SIZE(1));
3685	if (dc == NULL)
3686		errx(1, "no space for device capabilities");
3687	dc->dc_chaninfo.ic_nchans = verbose ? MAXCHAN : 1;
3688	getdevcaps(s, dc);
3689	printb("drivercaps", dc->dc_drivercaps, IEEE80211_C_BITS);
3690	if (dc->dc_cryptocaps != 0 || verbose) {
3691		putchar('\n');
3692		printb("cryptocaps", dc->dc_cryptocaps, IEEE80211_CRYPTO_BITS);
3693	}
3694	if (dc->dc_htcaps != 0 || verbose) {
3695		putchar('\n');
3696		printb("htcaps", dc->dc_htcaps, IEEE80211_HTCAP_BITS);
3697	}
3698	putchar('\n');
3699	if (verbose) {
3700		chaninfo = &dc->dc_chaninfo;	/* XXX */
3701		print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, verbose);
3702	}
3703	free(dc);
3704}
3705
3706static int
3707get80211wme(int s, int param, int ac, int *val)
3708{
3709	struct ieee80211req ireq;
3710
3711	(void) memset(&ireq, 0, sizeof(ireq));
3712	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3713	ireq.i_type = param;
3714	ireq.i_len = ac;
3715	if (ioctl(s, SIOCG80211, &ireq) < 0) {
3716		warn("cannot get WME parameter %d, ac %d%s",
3717		    param, ac & IEEE80211_WMEPARAM_VAL,
3718		    ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : "");
3719		return -1;
3720	}
3721	*val = ireq.i_val;
3722	return 0;
3723}
3724
3725static void
3726list_wme_aci(int s, const char *tag, int ac)
3727{
3728	int val;
3729
3730	printf("\t%s", tag);
3731
3732	/* show WME BSS parameters */
3733	if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1)
3734		printf(" cwmin %2u", val);
3735	if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1)
3736		printf(" cwmax %2u", val);
3737	if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1)
3738		printf(" aifs %2u", val);
3739	if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1)
3740		printf(" txopLimit %3u", val);
3741	if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) {
3742		if (val)
3743			printf(" acm");
3744		else if (verbose)
3745			printf(" -acm");
3746	}
3747	/* !BSS only */
3748	if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
3749		if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) {
3750			if (!val)
3751				printf(" -ack");
3752			else if (verbose)
3753				printf(" ack");
3754		}
3755	}
3756	printf("\n");
3757}
3758
3759static void
3760list_wme(int s)
3761{
3762	static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
3763	int ac;
3764
3765	if (verbose) {
3766		/* display both BSS and local settings */
3767		for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
3768	again:
3769			if (ac & IEEE80211_WMEPARAM_BSS)
3770				list_wme_aci(s, "     ", ac);
3771			else
3772				list_wme_aci(s, acnames[ac], ac);
3773			if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
3774				ac |= IEEE80211_WMEPARAM_BSS;
3775				goto again;
3776			} else
3777				ac &= ~IEEE80211_WMEPARAM_BSS;
3778		}
3779	} else {
3780		/* display only channel settings */
3781		for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++)
3782			list_wme_aci(s, acnames[ac], ac);
3783	}
3784}
3785
3786static void
3787list_roam(int s)
3788{
3789	const struct ieee80211_roamparam *rp;
3790	int mode;
3791
3792	getroam(s);
3793	for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
3794		rp = &roamparams.params[mode];
3795		if (rp->rssi == 0 && rp->rate == 0)
3796			continue;
3797		if (mode == IEEE80211_MODE_11NA || mode == IEEE80211_MODE_11NG) {
3798			if (rp->rssi & 1)
3799				LINE_CHECK("roam:%-7.7s rssi %2u.5dBm  MCS %2u    ",
3800				    modename[mode], rp->rssi/2,
3801				    rp->rate &~ IEEE80211_RATE_MCS);
3802			else
3803				LINE_CHECK("roam:%-7.7s rssi %4udBm  MCS %2u    ",
3804				    modename[mode], rp->rssi/2,
3805				    rp->rate &~ IEEE80211_RATE_MCS);
3806		} else {
3807			if (rp->rssi & 1)
3808				LINE_CHECK("roam:%-7.7s rssi %2u.5dBm rate %2u Mb/s",
3809				    modename[mode], rp->rssi/2, rp->rate/2);
3810			else
3811				LINE_CHECK("roam:%-7.7s rssi %4udBm rate %2u Mb/s",
3812				    modename[mode], rp->rssi/2, rp->rate/2);
3813		}
3814	}
3815}
3816
3817static void
3818list_txparams(int s)
3819{
3820	const struct ieee80211_txparam *tp;
3821	int mode;
3822
3823	gettxparams(s);
3824	for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
3825		tp = &txparams.params[mode];
3826		if (tp->mgmtrate == 0 && tp->mcastrate == 0)
3827			continue;
3828		if (mode == IEEE80211_MODE_11NA || mode == IEEE80211_MODE_11NG) {
3829			if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
3830				LINE_CHECK("%-7.7s ucast NONE    mgmt %2u MCS  "
3831				    "mcast %2u MCS  maxretry %u",
3832				    modename[mode],
3833				    tp->mgmtrate &~ IEEE80211_RATE_MCS,
3834				    tp->mcastrate &~ IEEE80211_RATE_MCS,
3835				    tp->maxretry);
3836			else
3837				LINE_CHECK("%-7.7s ucast %2u MCS  mgmt %2u MCS  "
3838				    "mcast %2u MCS  maxretry %u",
3839				    modename[mode],
3840				    tp->ucastrate &~ IEEE80211_RATE_MCS,
3841				    tp->mgmtrate &~ IEEE80211_RATE_MCS,
3842				    tp->mcastrate &~ IEEE80211_RATE_MCS,
3843				    tp->maxretry);
3844		} else {
3845			if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
3846				LINE_CHECK("%-7.7s ucast NONE    mgmt %2u Mb/s "
3847				    "mcast %2u Mb/s maxretry %u",
3848				    modename[mode],
3849				    tp->mgmtrate/2,
3850				    tp->mcastrate/2, tp->maxretry);
3851			else
3852				LINE_CHECK("%-7.7s ucast %2u Mb/s mgmt %2u Mb/s "
3853				    "mcast %2u Mb/s maxretry %u",
3854				    modename[mode],
3855				    tp->ucastrate/2, tp->mgmtrate/2,
3856				    tp->mcastrate/2, tp->maxretry);
3857		}
3858	}
3859}
3860
3861static void
3862printpolicy(int policy)
3863{
3864	switch (policy) {
3865	case IEEE80211_MACCMD_POLICY_OPEN:
3866		printf("policy: open\n");
3867		break;
3868	case IEEE80211_MACCMD_POLICY_ALLOW:
3869		printf("policy: allow\n");
3870		break;
3871	case IEEE80211_MACCMD_POLICY_DENY:
3872		printf("policy: deny\n");
3873		break;
3874	case IEEE80211_MACCMD_POLICY_RADIUS:
3875		printf("policy: radius\n");
3876		break;
3877	default:
3878		printf("policy: unknown (%u)\n", policy);
3879		break;
3880	}
3881}
3882
3883static void
3884list_mac(int s)
3885{
3886	struct ieee80211req ireq;
3887	struct ieee80211req_maclist *acllist;
3888	int i, nacls, policy, len;
3889	uint8_t *data;
3890	char c;
3891
3892	(void) memset(&ireq, 0, sizeof(ireq));
3893	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
3894	ireq.i_type = IEEE80211_IOC_MACCMD;
3895	ireq.i_val = IEEE80211_MACCMD_POLICY;
3896	if (ioctl(s, SIOCG80211, &ireq) < 0) {
3897		if (errno == EINVAL) {
3898			printf("No acl policy loaded\n");
3899			return;
3900		}
3901		err(1, "unable to get mac policy");
3902	}
3903	policy = ireq.i_val;
3904	if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
3905		c = '*';
3906	} else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
3907		c = '+';
3908	} else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
3909		c = '-';
3910	} else if (policy == IEEE80211_MACCMD_POLICY_RADIUS) {
3911		c = 'r';		/* NB: should never have entries */
3912	} else {
3913		printf("policy: unknown (%u)\n", policy);
3914		c = '?';
3915	}
3916	if (verbose || c == '?')
3917		printpolicy(policy);
3918
3919	ireq.i_val = IEEE80211_MACCMD_LIST;
3920	ireq.i_len = 0;
3921	if (ioctl(s, SIOCG80211, &ireq) < 0)
3922		err(1, "unable to get mac acl list size");
3923	if (ireq.i_len == 0) {		/* NB: no acls */
3924		if (!(verbose || c == '?'))
3925			printpolicy(policy);
3926		return;
3927	}
3928	len = ireq.i_len;
3929
3930	data = malloc(len);
3931	if (data == NULL)
3932		err(1, "out of memory for acl list");
3933
3934	ireq.i_data = data;
3935	if (ioctl(s, SIOCG80211, &ireq) < 0)
3936		err(1, "unable to get mac acl list");
3937	nacls = len / sizeof(*acllist);
3938	acllist = (struct ieee80211req_maclist *) data;
3939	for (i = 0; i < nacls; i++)
3940		printf("%c%s\n", c, ether_ntoa(
3941			(const struct ether_addr *) acllist[i].ml_macaddr));
3942	free(data);
3943}
3944
3945static void
3946print_regdomain(const struct ieee80211_regdomain *reg, int verb)
3947{
3948	if ((reg->regdomain != 0 &&
3949	    reg->regdomain != reg->country) || verb) {
3950		const struct regdomain *rd =
3951		    lib80211_regdomain_findbysku(getregdata(), reg->regdomain);
3952		if (rd == NULL)
3953			LINE_CHECK("regdomain %d", reg->regdomain);
3954		else
3955			LINE_CHECK("regdomain %s", rd->name);
3956	}
3957	if (reg->country != 0 || verb) {
3958		const struct country *cc =
3959		    lib80211_country_findbycc(getregdata(), reg->country);
3960		if (cc == NULL)
3961			LINE_CHECK("country %d", reg->country);
3962		else
3963			LINE_CHECK("country %s", cc->isoname);
3964	}
3965	if (reg->location == 'I')
3966		LINE_CHECK("indoor");
3967	else if (reg->location == 'O')
3968		LINE_CHECK("outdoor");
3969	else if (verb)
3970		LINE_CHECK("anywhere");
3971	if (reg->ecm)
3972		LINE_CHECK("ecm");
3973	else if (verb)
3974		LINE_CHECK("-ecm");
3975}
3976
3977static void
3978list_regdomain(int s, int channelsalso)
3979{
3980	getregdomain(s);
3981	if (channelsalso) {
3982		getchaninfo(s);
3983		spacer = ':';
3984		print_regdomain(&regdomain, 1);
3985		LINE_BREAK();
3986		print_channels(s, chaninfo, 1/*allchans*/, 1/*verbose*/);
3987	} else
3988		print_regdomain(&regdomain, verbose);
3989}
3990
3991static void
3992list_mesh(int s)
3993{
3994	struct ieee80211req ireq;
3995	struct ieee80211req_mesh_route routes[128];
3996	struct ieee80211req_mesh_route *rt;
3997
3998	(void) memset(&ireq, 0, sizeof(ireq));
3999	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4000	ireq.i_type = IEEE80211_IOC_MESH_RTCMD;
4001	ireq.i_val = IEEE80211_MESH_RTCMD_LIST;
4002	ireq.i_data = &routes;
4003	ireq.i_len = sizeof(routes);
4004	if (ioctl(s, SIOCG80211, &ireq) < 0)
4005	 	err(1, "unable to get the Mesh routing table");
4006
4007	printf("%-17.17s %-17.17s %4s %4s %4s %6s %s\n"
4008		, "DEST"
4009		, "NEXT HOP"
4010		, "HOPS"
4011		, "METRIC"
4012		, "LIFETIME"
4013		, "MSEQ"
4014		, "FLAGS");
4015
4016	for (rt = &routes[0]; rt - &routes[0] < ireq.i_len / sizeof(*rt); rt++){
4017		printf("%s ",
4018		    ether_ntoa((const struct ether_addr *)rt->imr_dest));
4019		printf("%s %4u   %4u   %6u %6u    %c%c\n",
4020			ether_ntoa((const struct ether_addr *)rt->imr_nexthop),
4021			rt->imr_nhops, rt->imr_metric, rt->imr_lifetime,
4022			rt->imr_lastmseq,
4023			(rt->imr_flags & IEEE80211_MESHRT_FLAGS_VALID) ?
4024			    'V' : '!',
4025			(rt->imr_flags & IEEE80211_MESHRT_FLAGS_PROXY) ?
4026			    'P' : ' ');
4027	}
4028}
4029
4030static
4031DECL_CMD_FUNC(set80211list, arg, d)
4032{
4033#define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
4034
4035	LINE_INIT('\t');
4036
4037	if (iseq(arg, "sta"))
4038		list_stations(s);
4039	else if (iseq(arg, "scan") || iseq(arg, "ap"))
4040		list_scan(s);
4041	else if (iseq(arg, "chan") || iseq(arg, "freq"))
4042		list_channels(s, 1);
4043	else if (iseq(arg, "active"))
4044		list_channels(s, 0);
4045	else if (iseq(arg, "keys"))
4046		list_keys(s);
4047	else if (iseq(arg, "caps"))
4048		list_capabilities(s);
4049	else if (iseq(arg, "wme") || iseq(arg, "wmm"))
4050		list_wme(s);
4051	else if (iseq(arg, "mac"))
4052		list_mac(s);
4053	else if (iseq(arg, "txpow"))
4054		list_txpow(s);
4055	else if (iseq(arg, "roam"))
4056		list_roam(s);
4057	else if (iseq(arg, "txparam") || iseq(arg, "txparm"))
4058		list_txparams(s);
4059	else if (iseq(arg, "regdomain"))
4060		list_regdomain(s, 1);
4061	else if (iseq(arg, "countries"))
4062		list_countries();
4063	else if (iseq(arg, "mesh"))
4064		list_mesh(s);
4065	else
4066		errx(1, "Don't know how to list %s for %s", arg, name);
4067	LINE_BREAK();
4068#undef iseq
4069}
4070
4071static enum ieee80211_opmode
4072get80211opmode(int s)
4073{
4074	struct ifmediareq ifmr;
4075
4076	(void) memset(&ifmr, 0, sizeof(ifmr));
4077	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
4078
4079	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
4080		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
4081			if (ifmr.ifm_current & IFM_FLAG0)
4082				return IEEE80211_M_AHDEMO;
4083			else
4084				return IEEE80211_M_IBSS;
4085		}
4086		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
4087			return IEEE80211_M_HOSTAP;
4088		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
4089			return IEEE80211_M_MONITOR;
4090		if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
4091			return IEEE80211_M_MBSS;
4092	}
4093	return IEEE80211_M_STA;
4094}
4095
4096#if 0
4097static void
4098printcipher(int s, struct ieee80211req *ireq, int keylenop)
4099{
4100	switch (ireq->i_val) {
4101	case IEEE80211_CIPHER_WEP:
4102		ireq->i_type = keylenop;
4103		if (ioctl(s, SIOCG80211, ireq) != -1)
4104			printf("WEP-%s",
4105			    ireq->i_len <= 5 ? "40" :
4106			    ireq->i_len <= 13 ? "104" : "128");
4107		else
4108			printf("WEP");
4109		break;
4110	case IEEE80211_CIPHER_TKIP:
4111		printf("TKIP");
4112		break;
4113	case IEEE80211_CIPHER_AES_OCB:
4114		printf("AES-OCB");
4115		break;
4116	case IEEE80211_CIPHER_AES_CCM:
4117		printf("AES-CCM");
4118		break;
4119	case IEEE80211_CIPHER_CKIP:
4120		printf("CKIP");
4121		break;
4122	case IEEE80211_CIPHER_NONE:
4123		printf("NONE");
4124		break;
4125	default:
4126		printf("UNKNOWN (0x%x)", ireq->i_val);
4127		break;
4128	}
4129}
4130#endif
4131
4132static void
4133printkey(const struct ieee80211req_key *ik)
4134{
4135	static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
4136	int keylen = ik->ik_keylen;
4137	int printcontents;
4138
4139	printcontents = printkeys &&
4140		(memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
4141	if (printcontents)
4142		LINE_BREAK();
4143	switch (ik->ik_type) {
4144	case IEEE80211_CIPHER_WEP:
4145		/* compatibility */
4146		LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
4147		    keylen <= 5 ? "40-bit" :
4148		    keylen <= 13 ? "104-bit" : "128-bit");
4149		break;
4150	case IEEE80211_CIPHER_TKIP:
4151		if (keylen > 128/8)
4152			keylen -= 128/8;	/* ignore MIC for now */
4153		LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4154		break;
4155	case IEEE80211_CIPHER_AES_OCB:
4156		LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4157		break;
4158	case IEEE80211_CIPHER_AES_CCM:
4159		LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4160		break;
4161	case IEEE80211_CIPHER_CKIP:
4162		LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4163		break;
4164	case IEEE80211_CIPHER_NONE:
4165		LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4166		break;
4167	default:
4168		LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
4169			ik->ik_type, ik->ik_keyix+1, 8*keylen);
4170		break;
4171	}
4172	if (printcontents) {
4173		int i;
4174
4175		printf(" <");
4176		for (i = 0; i < keylen; i++)
4177			printf("%02x", ik->ik_keydata[i]);
4178		printf(">");
4179		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
4180		    (ik->ik_keyrsc != 0 || verbose))
4181			printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
4182		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
4183		    (ik->ik_keytsc != 0 || verbose))
4184			printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
4185		if (ik->ik_flags != 0 && verbose) {
4186			const char *sep = " ";
4187
4188			if (ik->ik_flags & IEEE80211_KEY_XMIT)
4189				printf("%stx", sep), sep = "+";
4190			if (ik->ik_flags & IEEE80211_KEY_RECV)
4191				printf("%srx", sep), sep = "+";
4192			if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
4193				printf("%sdef", sep), sep = "+";
4194		}
4195		LINE_BREAK();
4196	}
4197}
4198
4199static void
4200printrate(const char *tag, int v, int defrate, int defmcs)
4201{
4202	if ((v & IEEE80211_RATE_MCS) == 0) {
4203		if (v != defrate) {
4204			if (v & 1)
4205				LINE_CHECK("%s %d.5", tag, v/2);
4206			else
4207				LINE_CHECK("%s %d", tag, v/2);
4208		}
4209	} else {
4210		if (v != defmcs)
4211			LINE_CHECK("%s %d", tag, v &~ 0x80);
4212	}
4213}
4214
4215static int
4216getid(int s, int ix, void *data, size_t len, int *plen, int mesh)
4217{
4218	struct ieee80211req ireq;
4219
4220	(void) memset(&ireq, 0, sizeof(ireq));
4221	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4222	ireq.i_type = (!mesh) ? IEEE80211_IOC_SSID : IEEE80211_IOC_MESH_ID;
4223	ireq.i_val = ix;
4224	ireq.i_data = data;
4225	ireq.i_len = len;
4226	if (ioctl(s, SIOCG80211, &ireq) < 0)
4227		return -1;
4228	*plen = ireq.i_len;
4229	return 0;
4230}
4231
4232static void
4233ieee80211_status(int s)
4234{
4235	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
4236	enum ieee80211_opmode opmode = get80211opmode(s);
4237	int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode;
4238	uint8_t data[32];
4239	const struct ieee80211_channel *c;
4240	const struct ieee80211_roamparam *rp;
4241	const struct ieee80211_txparam *tp;
4242
4243	if (getid(s, -1, data, sizeof(data), &len, 0) < 0) {
4244		/* If we can't get the SSID, this isn't an 802.11 device. */
4245		return;
4246	}
4247
4248	/*
4249	 * Invalidate cached state so printing status for multiple
4250	 * if's doesn't reuse the first interfaces' cached state.
4251	 */
4252	gotcurchan = 0;
4253	gotroam = 0;
4254	gottxparams = 0;
4255	gothtconf = 0;
4256	gotregdomain = 0;
4257
4258	printf("\t");
4259	if (opmode == IEEE80211_M_MBSS) {
4260		printf("meshid ");
4261		getid(s, 0, data, sizeof(data), &len, 1);
4262		print_string(data, len);
4263	} else {
4264		if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
4265			num = 0;
4266		printf("ssid ");
4267		if (num > 1) {
4268			for (i = 0; i < num; i++) {
4269				if (getid(s, i, data, sizeof(data), &len, 0) >= 0 && len > 0) {
4270					printf(" %d:", i + 1);
4271					print_string(data, len);
4272				}
4273			}
4274		} else
4275			print_string(data, len);
4276	}
4277	c = getcurchan(s);
4278	if (c->ic_freq != IEEE80211_CHAN_ANY) {
4279		char buf[14];
4280		printf(" channel %d (%u MHz%s)", c->ic_ieee, c->ic_freq,
4281			get_chaninfo(c, 1, buf, sizeof(buf)));
4282	} else if (verbose)
4283		printf(" channel UNDEF");
4284
4285	if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 &&
4286	    (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
4287		printf(" bssid %s", ether_ntoa((struct ether_addr *)data));
4288
4289	if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) {
4290		printf("\n\tstationname ");
4291		print_string(data, len);
4292	}
4293
4294	spacer = ' ';		/* force first break */
4295	LINE_BREAK();
4296
4297	list_regdomain(s, 0);
4298
4299	wpa = 0;
4300	if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) {
4301		switch (val) {
4302		case IEEE80211_AUTH_NONE:
4303			LINE_CHECK("authmode NONE");
4304			break;
4305		case IEEE80211_AUTH_OPEN:
4306			LINE_CHECK("authmode OPEN");
4307			break;
4308		case IEEE80211_AUTH_SHARED:
4309			LINE_CHECK("authmode SHARED");
4310			break;
4311		case IEEE80211_AUTH_8021X:
4312			LINE_CHECK("authmode 802.1x");
4313			break;
4314		case IEEE80211_AUTH_WPA:
4315			if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0)
4316				wpa = 1;	/* default to WPA1 */
4317			switch (wpa) {
4318			case 2:
4319				LINE_CHECK("authmode WPA2/802.11i");
4320				break;
4321			case 3:
4322				LINE_CHECK("authmode WPA1+WPA2/802.11i");
4323				break;
4324			default:
4325				LINE_CHECK("authmode WPA");
4326				break;
4327			}
4328			break;
4329		case IEEE80211_AUTH_AUTO:
4330			LINE_CHECK("authmode AUTO");
4331			break;
4332		default:
4333			LINE_CHECK("authmode UNKNOWN (0x%x)", val);
4334			break;
4335		}
4336	}
4337
4338	if (wpa || verbose) {
4339		if (get80211val(s, IEEE80211_IOC_WPS, &val) != -1) {
4340			if (val)
4341				LINE_CHECK("wps");
4342			else if (verbose)
4343				LINE_CHECK("-wps");
4344		}
4345		if (get80211val(s, IEEE80211_IOC_TSN, &val) != -1) {
4346			if (val)
4347				LINE_CHECK("tsn");
4348			else if (verbose)
4349				LINE_CHECK("-tsn");
4350		}
4351		if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) {
4352			if (val)
4353				LINE_CHECK("countermeasures");
4354			else if (verbose)
4355				LINE_CHECK("-countermeasures");
4356		}
4357#if 0
4358		/* XXX not interesting with WPA done in user space */
4359		ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
4360		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4361		}
4362
4363		ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
4364		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4365			LINE_CHECK("mcastcipher ");
4366			printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
4367			spacer = ' ';
4368		}
4369
4370		ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
4371		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4372			LINE_CHECK("ucastcipher ");
4373			printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
4374		}
4375
4376		if (wpa & 2) {
4377			ireq.i_type = IEEE80211_IOC_RSNCAPS;
4378			if (ioctl(s, SIOCG80211, &ireq) != -1) {
4379				LINE_CHECK("RSN caps 0x%x", ireq.i_val);
4380				spacer = ' ';
4381			}
4382		}
4383
4384		ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
4385		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4386		}
4387#endif
4388	}
4389
4390	if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 &&
4391	    wepmode != IEEE80211_WEP_NOSUP) {
4392
4393		switch (wepmode) {
4394		case IEEE80211_WEP_OFF:
4395			LINE_CHECK("privacy OFF");
4396			break;
4397		case IEEE80211_WEP_ON:
4398			LINE_CHECK("privacy ON");
4399			break;
4400		case IEEE80211_WEP_MIXED:
4401			LINE_CHECK("privacy MIXED");
4402			break;
4403		default:
4404			LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
4405			break;
4406		}
4407
4408		/*
4409		 * If we get here then we've got WEP support so we need
4410		 * to print WEP status.
4411		 */
4412
4413		if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) {
4414			warn("WEP support, but no tx key!");
4415			goto end;
4416		}
4417		if (val != -1)
4418			LINE_CHECK("deftxkey %d", val+1);
4419		else if (wepmode != IEEE80211_WEP_OFF || verbose)
4420			LINE_CHECK("deftxkey UNDEF");
4421
4422		if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) {
4423			warn("WEP support, but no NUMWEPKEYS support!");
4424			goto end;
4425		}
4426
4427		for (i = 0; i < num; i++) {
4428			struct ieee80211req_key ik;
4429
4430			memset(&ik, 0, sizeof(ik));
4431			ik.ik_keyix = i;
4432			if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) {
4433				warn("WEP support, but can get keys!");
4434				goto end;
4435			}
4436			if (ik.ik_keylen != 0) {
4437				if (verbose)
4438					LINE_BREAK();
4439				printkey(&ik);
4440			}
4441		}
4442end:
4443		;
4444	}
4445
4446	if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 &&
4447	    val != IEEE80211_POWERSAVE_NOSUP ) {
4448		if (val != IEEE80211_POWERSAVE_OFF || verbose) {
4449			switch (val) {
4450			case IEEE80211_POWERSAVE_OFF:
4451				LINE_CHECK("powersavemode OFF");
4452				break;
4453			case IEEE80211_POWERSAVE_CAM:
4454				LINE_CHECK("powersavemode CAM");
4455				break;
4456			case IEEE80211_POWERSAVE_PSP:
4457				LINE_CHECK("powersavemode PSP");
4458				break;
4459			case IEEE80211_POWERSAVE_PSP_CAM:
4460				LINE_CHECK("powersavemode PSP-CAM");
4461				break;
4462			}
4463			if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1)
4464				LINE_CHECK("powersavesleep %d", val);
4465		}
4466	}
4467
4468	if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) {
4469		if (val & 1)
4470			LINE_CHECK("txpower %d.5", val/2);
4471		else
4472			LINE_CHECK("txpower %d", val/2);
4473	}
4474	if (verbose) {
4475		if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1)
4476			LINE_CHECK("txpowmax %.1f", val/2.);
4477	}
4478
4479	if (get80211val(s, IEEE80211_IOC_DOTD, &val) != -1) {
4480		if (val)
4481			LINE_CHECK("dotd");
4482		else if (verbose)
4483			LINE_CHECK("-dotd");
4484	}
4485
4486	if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) {
4487		if (val != IEEE80211_RTS_MAX || verbose)
4488			LINE_CHECK("rtsthreshold %d", val);
4489	}
4490
4491	if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) {
4492		if (val != IEEE80211_FRAG_MAX || verbose)
4493			LINE_CHECK("fragthreshold %d", val);
4494	}
4495	if (opmode == IEEE80211_M_STA || verbose) {
4496		if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) {
4497			if (val != IEEE80211_HWBMISS_MAX || verbose)
4498				LINE_CHECK("bmiss %d", val);
4499		}
4500	}
4501
4502	if (!verbose) {
4503		gettxparams(s);
4504		tp = &txparams.params[chan2mode(c)];
4505		printrate("ucastrate", tp->ucastrate,
4506		    IEEE80211_FIXED_RATE_NONE, IEEE80211_FIXED_RATE_NONE);
4507		printrate("mcastrate", tp->mcastrate, 2*1,
4508		    IEEE80211_RATE_MCS|0);
4509		printrate("mgmtrate", tp->mgmtrate, 2*1,
4510		    IEEE80211_RATE_MCS|0);
4511		if (tp->maxretry != 6)		/* XXX */
4512			LINE_CHECK("maxretry %d", tp->maxretry);
4513	} else {
4514		LINE_BREAK();
4515		list_txparams(s);
4516	}
4517
4518	bgscaninterval = -1;
4519	(void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval);
4520
4521	if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) {
4522		if (val != bgscaninterval || verbose)
4523			LINE_CHECK("scanvalid %u", val);
4524	}
4525
4526	bgscan = 0;
4527	if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) {
4528		if (bgscan)
4529			LINE_CHECK("bgscan");
4530		else if (verbose)
4531			LINE_CHECK("-bgscan");
4532	}
4533	if (bgscan || verbose) {
4534		if (bgscaninterval != -1)
4535			LINE_CHECK("bgscanintvl %u", bgscaninterval);
4536		if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1)
4537			LINE_CHECK("bgscanidle %u", val);
4538		if (!verbose) {
4539			getroam(s);
4540			rp = &roamparams.params[chan2mode(c)];
4541			if (rp->rssi & 1)
4542				LINE_CHECK("roam:rssi %u.5", rp->rssi/2);
4543			else
4544				LINE_CHECK("roam:rssi %u", rp->rssi/2);
4545			LINE_CHECK("roam:rate %u", rp->rate/2);
4546		} else {
4547			LINE_BREAK();
4548			list_roam(s);
4549			LINE_BREAK();
4550		}
4551	}
4552
4553	if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
4554		if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) {
4555			if (val)
4556				LINE_CHECK("pureg");
4557			else if (verbose)
4558				LINE_CHECK("-pureg");
4559		}
4560		if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) {
4561			switch (val) {
4562			case IEEE80211_PROTMODE_OFF:
4563				LINE_CHECK("protmode OFF");
4564				break;
4565			case IEEE80211_PROTMODE_CTS:
4566				LINE_CHECK("protmode CTS");
4567				break;
4568			case IEEE80211_PROTMODE_RTSCTS:
4569				LINE_CHECK("protmode RTSCTS");
4570				break;
4571			default:
4572				LINE_CHECK("protmode UNKNOWN (0x%x)", val);
4573				break;
4574			}
4575		}
4576	}
4577
4578	if (IEEE80211_IS_CHAN_HT(c) || verbose) {
4579		gethtconf(s);
4580		switch (htconf & 3) {
4581		case 0:
4582		case 2:
4583			LINE_CHECK("-ht");
4584			break;
4585		case 1:
4586			LINE_CHECK("ht20");
4587			break;
4588		case 3:
4589			if (verbose)
4590				LINE_CHECK("ht");
4591			break;
4592		}
4593		if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) {
4594			if (!val)
4595				LINE_CHECK("-htcompat");
4596			else if (verbose)
4597				LINE_CHECK("htcompat");
4598		}
4599		if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) {
4600			switch (val) {
4601			case 0:
4602				LINE_CHECK("-ampdu");
4603				break;
4604			case 1:
4605				LINE_CHECK("ampdutx -ampdurx");
4606				break;
4607			case 2:
4608				LINE_CHECK("-ampdutx ampdurx");
4609				break;
4610			case 3:
4611				if (verbose)
4612					LINE_CHECK("ampdu");
4613				break;
4614			}
4615		}
4616		if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) {
4617			switch (val) {
4618			case IEEE80211_HTCAP_MAXRXAMPDU_8K:
4619				LINE_CHECK("ampdulimit 8k");
4620				break;
4621			case IEEE80211_HTCAP_MAXRXAMPDU_16K:
4622				LINE_CHECK("ampdulimit 16k");
4623				break;
4624			case IEEE80211_HTCAP_MAXRXAMPDU_32K:
4625				LINE_CHECK("ampdulimit 32k");
4626				break;
4627			case IEEE80211_HTCAP_MAXRXAMPDU_64K:
4628				LINE_CHECK("ampdulimit 64k");
4629				break;
4630			}
4631		}
4632		if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) {
4633			switch (val) {
4634			case IEEE80211_HTCAP_MPDUDENSITY_NA:
4635				if (verbose)
4636					LINE_CHECK("ampdudensity NA");
4637				break;
4638			case IEEE80211_HTCAP_MPDUDENSITY_025:
4639				LINE_CHECK("ampdudensity .25");
4640				break;
4641			case IEEE80211_HTCAP_MPDUDENSITY_05:
4642				LINE_CHECK("ampdudensity .5");
4643				break;
4644			case IEEE80211_HTCAP_MPDUDENSITY_1:
4645				LINE_CHECK("ampdudensity 1");
4646				break;
4647			case IEEE80211_HTCAP_MPDUDENSITY_2:
4648				LINE_CHECK("ampdudensity 2");
4649				break;
4650			case IEEE80211_HTCAP_MPDUDENSITY_4:
4651				LINE_CHECK("ampdudensity 4");
4652				break;
4653			case IEEE80211_HTCAP_MPDUDENSITY_8:
4654				LINE_CHECK("ampdudensity 8");
4655				break;
4656			case IEEE80211_HTCAP_MPDUDENSITY_16:
4657				LINE_CHECK("ampdudensity 16");
4658				break;
4659			}
4660		}
4661		if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) {
4662			switch (val) {
4663			case 0:
4664				LINE_CHECK("-amsdu");
4665				break;
4666			case 1:
4667				LINE_CHECK("amsdutx -amsdurx");
4668				break;
4669			case 2:
4670				LINE_CHECK("-amsdutx amsdurx");
4671				break;
4672			case 3:
4673				if (verbose)
4674					LINE_CHECK("amsdu");
4675				break;
4676			}
4677		}
4678		/* XXX amsdu limit */
4679		if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) {
4680			if (val)
4681				LINE_CHECK("shortgi");
4682			else if (verbose)
4683				LINE_CHECK("-shortgi");
4684		}
4685		if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) {
4686			if (val == IEEE80211_PROTMODE_OFF)
4687				LINE_CHECK("htprotmode OFF");
4688			else if (val != IEEE80211_PROTMODE_RTSCTS)
4689				LINE_CHECK("htprotmode UNKNOWN (0x%x)", val);
4690			else if (verbose)
4691				LINE_CHECK("htprotmode RTSCTS");
4692		}
4693		if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) {
4694			if (val)
4695				LINE_CHECK("puren");
4696			else if (verbose)
4697				LINE_CHECK("-puren");
4698		}
4699		if (get80211val(s, IEEE80211_IOC_SMPS, &val) != -1) {
4700			if (val == IEEE80211_HTCAP_SMPS_DYNAMIC)
4701				LINE_CHECK("smpsdyn");
4702			else if (val == IEEE80211_HTCAP_SMPS_ENA)
4703				LINE_CHECK("smps");
4704			else if (verbose)
4705				LINE_CHECK("-smps");
4706		}
4707		if (get80211val(s, IEEE80211_IOC_RIFS, &val) != -1) {
4708			if (val)
4709				LINE_CHECK("rifs");
4710			else if (verbose)
4711				LINE_CHECK("-rifs");
4712		}
4713	}
4714
4715	if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) {
4716		if (wme)
4717			LINE_CHECK("wme");
4718		else if (verbose)
4719			LINE_CHECK("-wme");
4720	} else
4721		wme = 0;
4722
4723	if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) {
4724		if (val)
4725			LINE_CHECK("burst");
4726		else if (verbose)
4727			LINE_CHECK("-burst");
4728	}
4729
4730	if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) {
4731		if (val)
4732			LINE_CHECK("ff");
4733		else if (verbose)
4734			LINE_CHECK("-ff");
4735	}
4736	if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) {
4737		if (val)
4738			LINE_CHECK("dturbo");
4739		else if (verbose)
4740			LINE_CHECK("-dturbo");
4741	}
4742	if (get80211val(s, IEEE80211_IOC_DWDS, &val) != -1) {
4743		if (val)
4744			LINE_CHECK("dwds");
4745		else if (verbose)
4746			LINE_CHECK("-dwds");
4747	}
4748
4749	if (opmode == IEEE80211_M_HOSTAP) {
4750		if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) {
4751			if (val)
4752				LINE_CHECK("hidessid");
4753			else if (verbose)
4754				LINE_CHECK("-hidessid");
4755		}
4756		if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) {
4757			if (!val)
4758				LINE_CHECK("-apbridge");
4759			else if (verbose)
4760				LINE_CHECK("apbridge");
4761		}
4762		if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1)
4763			LINE_CHECK("dtimperiod %u", val);
4764
4765		if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) {
4766			if (!val)
4767				LINE_CHECK("-doth");
4768			else if (verbose)
4769				LINE_CHECK("doth");
4770		}
4771		if (get80211val(s, IEEE80211_IOC_DFS, &val) != -1) {
4772			if (!val)
4773				LINE_CHECK("-dfs");
4774			else if (verbose)
4775				LINE_CHECK("dfs");
4776		}
4777		if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) {
4778			if (!val)
4779				LINE_CHECK("-inact");
4780			else if (verbose)
4781				LINE_CHECK("inact");
4782		}
4783	} else {
4784		if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) {
4785			if (val != IEEE80211_ROAMING_AUTO || verbose) {
4786				switch (val) {
4787				case IEEE80211_ROAMING_DEVICE:
4788					LINE_CHECK("roaming DEVICE");
4789					break;
4790				case IEEE80211_ROAMING_AUTO:
4791					LINE_CHECK("roaming AUTO");
4792					break;
4793				case IEEE80211_ROAMING_MANUAL:
4794					LINE_CHECK("roaming MANUAL");
4795					break;
4796				default:
4797					LINE_CHECK("roaming UNKNOWN (0x%x)",
4798						val);
4799					break;
4800				}
4801			}
4802		}
4803	}
4804
4805	if (opmode == IEEE80211_M_AHDEMO) {
4806		if (get80211val(s, IEEE80211_IOC_TDMA_SLOT, &val) != -1)
4807			LINE_CHECK("tdmaslot %u", val);
4808		if (get80211val(s, IEEE80211_IOC_TDMA_SLOTCNT, &val) != -1)
4809			LINE_CHECK("tdmaslotcnt %u", val);
4810		if (get80211val(s, IEEE80211_IOC_TDMA_SLOTLEN, &val) != -1)
4811			LINE_CHECK("tdmaslotlen %u", val);
4812		if (get80211val(s, IEEE80211_IOC_TDMA_BINTERVAL, &val) != -1)
4813			LINE_CHECK("tdmabintval %u", val);
4814	} else if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) {
4815		/* XXX default define not visible */
4816		if (val != 100 || verbose)
4817			LINE_CHECK("bintval %u", val);
4818	}
4819
4820	if (wme && verbose) {
4821		LINE_BREAK();
4822		list_wme(s);
4823	}
4824
4825	if (opmode == IEEE80211_M_MBSS) {
4826		if (get80211val(s, IEEE80211_IOC_MESH_TTL, &val) != -1) {
4827			LINE_CHECK("meshttl %u", val);
4828		}
4829		if (get80211val(s, IEEE80211_IOC_MESH_AP, &val) != -1) {
4830			if (val)
4831				LINE_CHECK("meshpeering");
4832			else
4833				LINE_CHECK("-meshpeering");
4834		}
4835		if (get80211val(s, IEEE80211_IOC_MESH_FWRD, &val) != -1) {
4836			if (val)
4837				LINE_CHECK("meshforward");
4838			else
4839				LINE_CHECK("-meshforward");
4840		}
4841		if (get80211val(s, IEEE80211_IOC_MESH_GATE, &val) != -1) {
4842			if (val)
4843				LINE_CHECK("meshgate");
4844			else
4845				LINE_CHECK("-meshgate");
4846		}
4847		if (get80211len(s, IEEE80211_IOC_MESH_PR_METRIC, data, 12,
4848		    &len) != -1) {
4849			data[len] = '\0';
4850			LINE_CHECK("meshmetric %s", data);
4851		}
4852		if (get80211len(s, IEEE80211_IOC_MESH_PR_PATH, data, 12,
4853		    &len) != -1) {
4854			data[len] = '\0';
4855			LINE_CHECK("meshpath %s", data);
4856		}
4857		if (get80211val(s, IEEE80211_IOC_HWMP_ROOTMODE, &val) != -1) {
4858			switch (val) {
4859			case IEEE80211_HWMP_ROOTMODE_DISABLED:
4860				LINE_CHECK("hwmprootmode DISABLED");
4861				break;
4862			case IEEE80211_HWMP_ROOTMODE_NORMAL:
4863				LINE_CHECK("hwmprootmode NORMAL");
4864				break;
4865			case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
4866				LINE_CHECK("hwmprootmode PROACTIVE");
4867				break;
4868			case IEEE80211_HWMP_ROOTMODE_RANN:
4869				LINE_CHECK("hwmprootmode RANN");
4870				break;
4871			default:
4872				LINE_CHECK("hwmprootmode UNKNOWN(%d)", val);
4873				break;
4874			}
4875		}
4876		if (get80211val(s, IEEE80211_IOC_HWMP_MAXHOPS, &val) != -1) {
4877			LINE_CHECK("hwmpmaxhops %u", val);
4878		}
4879	}
4880
4881	LINE_BREAK();
4882}
4883
4884static int
4885get80211(int s, int type, void *data, int len)
4886{
4887	struct ieee80211req ireq;
4888
4889	(void) memset(&ireq, 0, sizeof(ireq));
4890	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4891	ireq.i_type = type;
4892	ireq.i_data = data;
4893	ireq.i_len = len;
4894	return ioctl(s, SIOCG80211, &ireq);
4895}
4896
4897static int
4898get80211len(int s, int type, void *data, int len, int *plen)
4899{
4900	struct ieee80211req ireq;
4901
4902	(void) memset(&ireq, 0, sizeof(ireq));
4903	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4904	ireq.i_type = type;
4905	ireq.i_len = len;
4906	assert(ireq.i_len == len);	/* NB: check for 16-bit truncation */
4907	ireq.i_data = data;
4908	if (ioctl(s, SIOCG80211, &ireq) < 0)
4909		return -1;
4910	*plen = ireq.i_len;
4911	return 0;
4912}
4913
4914static int
4915get80211val(int s, int type, int *val)
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	if (ioctl(s, SIOCG80211, &ireq) < 0)
4923		return -1;
4924	*val = ireq.i_val;
4925	return 0;
4926}
4927
4928static void
4929set80211(int s, int type, int val, int len, void *data)
4930{
4931	struct ieee80211req	ireq;
4932
4933	(void) memset(&ireq, 0, sizeof(ireq));
4934	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4935	ireq.i_type = type;
4936	ireq.i_val = val;
4937	ireq.i_len = len;
4938	assert(ireq.i_len == len);	/* NB: check for 16-bit truncation */
4939	ireq.i_data = data;
4940	if (ioctl(s, SIOCS80211, &ireq) < 0)
4941		err(1, "SIOCS80211");
4942}
4943
4944static const char *
4945get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
4946{
4947	int len;
4948	int hexstr;
4949	u_int8_t *p;
4950
4951	len = *lenp;
4952	p = buf;
4953	hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
4954	if (hexstr)
4955		val += 2;
4956	for (;;) {
4957		if (*val == '\0')
4958			break;
4959		if (sep != NULL && strchr(sep, *val) != NULL) {
4960			val++;
4961			break;
4962		}
4963		if (hexstr) {
4964			if (!isxdigit((u_char)val[0])) {
4965				warnx("bad hexadecimal digits");
4966				return NULL;
4967			}
4968			if (!isxdigit((u_char)val[1])) {
4969				warnx("odd count hexadecimal digits");
4970				return NULL;
4971			}
4972		}
4973		if (p >= buf + len) {
4974			if (hexstr)
4975				warnx("hexadecimal digits too long");
4976			else
4977				warnx("string too long");
4978			return NULL;
4979		}
4980		if (hexstr) {
4981#define	tohex(x)	(isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
4982			*p++ = (tohex((u_char)val[0]) << 4) |
4983			    tohex((u_char)val[1]);
4984#undef tohex
4985			val += 2;
4986		} else
4987			*p++ = *val++;
4988	}
4989	len = p - buf;
4990	/* The string "-" is treated as the empty string. */
4991	if (!hexstr && len == 1 && buf[0] == '-') {
4992		len = 0;
4993		memset(buf, 0, *lenp);
4994	} else if (len < *lenp)
4995		memset(p, 0, *lenp - len);
4996	*lenp = len;
4997	return val;
4998}
4999
5000static void
5001print_string(const u_int8_t *buf, int len)
5002{
5003	int i;
5004	int hasspc;
5005
5006	i = 0;
5007	hasspc = 0;
5008	for (; i < len; i++) {
5009		if (!isprint(buf[i]) && buf[i] != '\0')
5010			break;
5011		if (isspace(buf[i]))
5012			hasspc++;
5013	}
5014	if (i == len) {
5015		if (hasspc || len == 0 || buf[0] == '\0')
5016			printf("\"%.*s\"", len, buf);
5017		else
5018			printf("%.*s", len, buf);
5019	} else {
5020		printf("0x");
5021		for (i = 0; i < len; i++)
5022			printf("%02x", buf[i]);
5023	}
5024}
5025
5026/*
5027 * Virtual AP cloning support.
5028 */
5029static struct ieee80211_clone_params params = {
5030	.icp_opmode	= IEEE80211_M_STA,	/* default to station mode */
5031};
5032
5033static void
5034wlan_create(int s, struct ifreq *ifr)
5035{
5036	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
5037
5038	if (params.icp_parent[0] == '\0')
5039		errx(1, "must specify a parent device (wlandev) when creating "
5040		    "a wlan device");
5041	if (params.icp_opmode == IEEE80211_M_WDS &&
5042	    memcmp(params.icp_bssid, zerobssid, sizeof(zerobssid)) == 0)
5043		errx(1, "no bssid specified for WDS (use wlanbssid)");
5044	ifr->ifr_data = (caddr_t) &params;
5045	if (ioctl(s, SIOCIFCREATE2, ifr) < 0)
5046		err(1, "SIOCIFCREATE2");
5047}
5048
5049static
5050DECL_CMD_FUNC(set80211clone_wlandev, arg, d)
5051{
5052	strlcpy(params.icp_parent, arg, IFNAMSIZ);
5053}
5054
5055static
5056DECL_CMD_FUNC(set80211clone_wlanbssid, arg, d)
5057{
5058	const struct ether_addr *ea;
5059
5060	ea = ether_aton(arg);
5061	if (ea == NULL)
5062		errx(1, "%s: cannot parse bssid", arg);
5063	memcpy(params.icp_bssid, ea->octet, IEEE80211_ADDR_LEN);
5064}
5065
5066static
5067DECL_CMD_FUNC(set80211clone_wlanaddr, arg, d)
5068{
5069	const struct ether_addr *ea;
5070
5071	ea = ether_aton(arg);
5072	if (ea == NULL)
5073		errx(1, "%s: cannot parse address", arg);
5074	memcpy(params.icp_macaddr, ea->octet, IEEE80211_ADDR_LEN);
5075	params.icp_flags |= IEEE80211_CLONE_MACADDR;
5076}
5077
5078static
5079DECL_CMD_FUNC(set80211clone_wlanmode, arg, d)
5080{
5081#define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
5082	if (iseq(arg, "sta"))
5083		params.icp_opmode = IEEE80211_M_STA;
5084	else if (iseq(arg, "ahdemo") || iseq(arg, "adhoc-demo"))
5085		params.icp_opmode = IEEE80211_M_AHDEMO;
5086	else if (iseq(arg, "ibss") || iseq(arg, "adhoc"))
5087		params.icp_opmode = IEEE80211_M_IBSS;
5088	else if (iseq(arg, "ap") || iseq(arg, "host"))
5089		params.icp_opmode = IEEE80211_M_HOSTAP;
5090	else if (iseq(arg, "wds"))
5091		params.icp_opmode = IEEE80211_M_WDS;
5092	else if (iseq(arg, "monitor"))
5093		params.icp_opmode = IEEE80211_M_MONITOR;
5094	else if (iseq(arg, "tdma")) {
5095		params.icp_opmode = IEEE80211_M_AHDEMO;
5096		params.icp_flags |= IEEE80211_CLONE_TDMA;
5097	} else if (iseq(arg, "mesh") || iseq(arg, "mp")) /* mesh point */
5098		params.icp_opmode = IEEE80211_M_MBSS;
5099	else
5100		errx(1, "Don't know to create %s for %s", arg, name);
5101#undef iseq
5102}
5103
5104static void
5105set80211clone_beacons(const char *val, int d, int s, const struct afswtch *rafp)
5106{
5107	/* NB: inverted sense */
5108	if (d)
5109		params.icp_flags &= ~IEEE80211_CLONE_NOBEACONS;
5110	else
5111		params.icp_flags |= IEEE80211_CLONE_NOBEACONS;
5112}
5113
5114static void
5115set80211clone_bssid(const char *val, int d, int s, const struct afswtch *rafp)
5116{
5117	if (d)
5118		params.icp_flags |= IEEE80211_CLONE_BSSID;
5119	else
5120		params.icp_flags &= ~IEEE80211_CLONE_BSSID;
5121}
5122
5123static void
5124set80211clone_wdslegacy(const char *val, int d, int s, const struct afswtch *rafp)
5125{
5126	if (d)
5127		params.icp_flags |= IEEE80211_CLONE_WDSLEGACY;
5128	else
5129		params.icp_flags &= ~IEEE80211_CLONE_WDSLEGACY;
5130}
5131
5132static struct cmd ieee80211_cmds[] = {
5133	DEF_CMD_ARG("ssid",		set80211ssid),
5134	DEF_CMD_ARG("nwid",		set80211ssid),
5135	DEF_CMD_ARG("meshid",		set80211meshid),
5136	DEF_CMD_ARG("stationname",	set80211stationname),
5137	DEF_CMD_ARG("station",		set80211stationname),	/* BSD/OS */
5138	DEF_CMD_ARG("channel",		set80211channel),
5139	DEF_CMD_ARG("authmode",		set80211authmode),
5140	DEF_CMD_ARG("powersavemode",	set80211powersavemode),
5141	DEF_CMD("powersave",	1,	set80211powersave),
5142	DEF_CMD("-powersave",	0,	set80211powersave),
5143	DEF_CMD_ARG("powersavesleep", 	set80211powersavesleep),
5144	DEF_CMD_ARG("wepmode",		set80211wepmode),
5145	DEF_CMD("wep",		1,	set80211wep),
5146	DEF_CMD("-wep",		0,	set80211wep),
5147	DEF_CMD_ARG("deftxkey",		set80211weptxkey),
5148	DEF_CMD_ARG("weptxkey",		set80211weptxkey),
5149	DEF_CMD_ARG("wepkey",		set80211wepkey),
5150	DEF_CMD_ARG("nwkey",		set80211nwkey),		/* NetBSD */
5151	DEF_CMD("-nwkey",	0,	set80211wep),		/* NetBSD */
5152	DEF_CMD_ARG("rtsthreshold",	set80211rtsthreshold),
5153	DEF_CMD_ARG("protmode",		set80211protmode),
5154	DEF_CMD_ARG("txpower",		set80211txpower),
5155	DEF_CMD_ARG("roaming",		set80211roaming),
5156	DEF_CMD("wme",		1,	set80211wme),
5157	DEF_CMD("-wme",		0,	set80211wme),
5158	DEF_CMD("wmm",		1,	set80211wme),
5159	DEF_CMD("-wmm",		0,	set80211wme),
5160	DEF_CMD("hidessid",	1,	set80211hidessid),
5161	DEF_CMD("-hidessid",	0,	set80211hidessid),
5162	DEF_CMD("apbridge",	1,	set80211apbridge),
5163	DEF_CMD("-apbridge",	0,	set80211apbridge),
5164	DEF_CMD_ARG("chanlist",		set80211chanlist),
5165	DEF_CMD_ARG("bssid",		set80211bssid),
5166	DEF_CMD_ARG("ap",		set80211bssid),
5167	DEF_CMD("scan",	0,		set80211scan),
5168	DEF_CMD_ARG("list",		set80211list),
5169	DEF_CMD_ARG2("cwmin",		set80211cwmin),
5170	DEF_CMD_ARG2("cwmax",		set80211cwmax),
5171	DEF_CMD_ARG2("aifs",		set80211aifs),
5172	DEF_CMD_ARG2("txoplimit",	set80211txoplimit),
5173	DEF_CMD_ARG("acm",		set80211acm),
5174	DEF_CMD_ARG("-acm",		set80211noacm),
5175	DEF_CMD_ARG("ack",		set80211ackpolicy),
5176	DEF_CMD_ARG("-ack",		set80211noackpolicy),
5177	DEF_CMD_ARG2("bss:cwmin",	set80211bsscwmin),
5178	DEF_CMD_ARG2("bss:cwmax",	set80211bsscwmax),
5179	DEF_CMD_ARG2("bss:aifs",	set80211bssaifs),
5180	DEF_CMD_ARG2("bss:txoplimit",	set80211bsstxoplimit),
5181	DEF_CMD_ARG("dtimperiod",	set80211dtimperiod),
5182	DEF_CMD_ARG("bintval",		set80211bintval),
5183	DEF_CMD("mac:open",	IEEE80211_MACCMD_POLICY_OPEN,	set80211maccmd),
5184	DEF_CMD("mac:allow",	IEEE80211_MACCMD_POLICY_ALLOW,	set80211maccmd),
5185	DEF_CMD("mac:deny",	IEEE80211_MACCMD_POLICY_DENY,	set80211maccmd),
5186	DEF_CMD("mac:radius",	IEEE80211_MACCMD_POLICY_RADIUS,	set80211maccmd),
5187	DEF_CMD("mac:flush",	IEEE80211_MACCMD_FLUSH,		set80211maccmd),
5188	DEF_CMD("mac:detach",	IEEE80211_MACCMD_DETACH,	set80211maccmd),
5189	DEF_CMD_ARG("mac:add",		set80211addmac),
5190	DEF_CMD_ARG("mac:del",		set80211delmac),
5191	DEF_CMD_ARG("mac:kick",		set80211kickmac),
5192	DEF_CMD("pureg",	1,	set80211pureg),
5193	DEF_CMD("-pureg",	0,	set80211pureg),
5194	DEF_CMD("ff",		1,	set80211fastframes),
5195	DEF_CMD("-ff",		0,	set80211fastframes),
5196	DEF_CMD("dturbo",	1,	set80211dturbo),
5197	DEF_CMD("-dturbo",	0,	set80211dturbo),
5198	DEF_CMD("bgscan",	1,	set80211bgscan),
5199	DEF_CMD("-bgscan",	0,	set80211bgscan),
5200	DEF_CMD_ARG("bgscanidle",	set80211bgscanidle),
5201	DEF_CMD_ARG("bgscanintvl",	set80211bgscanintvl),
5202	DEF_CMD_ARG("scanvalid",	set80211scanvalid),
5203	DEF_CMD("quiet",        1,      set80211quiet),
5204	DEF_CMD("-quiet",       0,      set80211quiet),
5205	DEF_CMD_ARG("quiet_count",      set80211quietcount),
5206	DEF_CMD_ARG("quiet_period",     set80211quietperiod),
5207	DEF_CMD_ARG("quiet_dur",        set80211quietduration),
5208	DEF_CMD_ARG("quiet_offset",     set80211quietoffset),
5209	DEF_CMD_ARG("roam:rssi",	set80211roamrssi),
5210	DEF_CMD_ARG("roam:rate",	set80211roamrate),
5211	DEF_CMD_ARG("mcastrate",	set80211mcastrate),
5212	DEF_CMD_ARG("ucastrate",	set80211ucastrate),
5213	DEF_CMD_ARG("mgtrate",		set80211mgtrate),
5214	DEF_CMD_ARG("mgmtrate",		set80211mgtrate),
5215	DEF_CMD_ARG("maxretry",		set80211maxretry),
5216	DEF_CMD_ARG("fragthreshold",	set80211fragthreshold),
5217	DEF_CMD("burst",	1,	set80211burst),
5218	DEF_CMD("-burst",	0,	set80211burst),
5219	DEF_CMD_ARG("bmiss",		set80211bmissthreshold),
5220	DEF_CMD_ARG("bmissthreshold",	set80211bmissthreshold),
5221	DEF_CMD("shortgi",	1,	set80211shortgi),
5222	DEF_CMD("-shortgi",	0,	set80211shortgi),
5223	DEF_CMD("ampdurx",	2,	set80211ampdu),
5224	DEF_CMD("-ampdurx",	-2,	set80211ampdu),
5225	DEF_CMD("ampdutx",	1,	set80211ampdu),
5226	DEF_CMD("-ampdutx",	-1,	set80211ampdu),
5227	DEF_CMD("ampdu",	3,	set80211ampdu),		/* NB: tx+rx */
5228	DEF_CMD("-ampdu",	-3,	set80211ampdu),
5229	DEF_CMD_ARG("ampdulimit",	set80211ampdulimit),
5230	DEF_CMD_ARG("ampdudensity",	set80211ampdudensity),
5231	DEF_CMD("amsdurx",	2,	set80211amsdu),
5232	DEF_CMD("-amsdurx",	-2,	set80211amsdu),
5233	DEF_CMD("amsdutx",	1,	set80211amsdu),
5234	DEF_CMD("-amsdutx",	-1,	set80211amsdu),
5235	DEF_CMD("amsdu",	3,	set80211amsdu),		/* NB: tx+rx */
5236	DEF_CMD("-amsdu",	-3,	set80211amsdu),
5237	DEF_CMD_ARG("amsdulimit",	set80211amsdulimit),
5238	DEF_CMD("puren",	1,	set80211puren),
5239	DEF_CMD("-puren",	0,	set80211puren),
5240	DEF_CMD("doth",		1,	set80211doth),
5241	DEF_CMD("-doth",	0,	set80211doth),
5242	DEF_CMD("dfs",		1,	set80211dfs),
5243	DEF_CMD("-dfs",		0,	set80211dfs),
5244	DEF_CMD("htcompat",	1,	set80211htcompat),
5245	DEF_CMD("-htcompat",	0,	set80211htcompat),
5246	DEF_CMD("dwds",		1,	set80211dwds),
5247	DEF_CMD("-dwds",	0,	set80211dwds),
5248	DEF_CMD("inact",	1,	set80211inact),
5249	DEF_CMD("-inact",	0,	set80211inact),
5250	DEF_CMD("tsn",		1,	set80211tsn),
5251	DEF_CMD("-tsn",		0,	set80211tsn),
5252	DEF_CMD_ARG("regdomain",	set80211regdomain),
5253	DEF_CMD_ARG("country",		set80211country),
5254	DEF_CMD("indoor",	'I',	set80211location),
5255	DEF_CMD("-indoor",	'O',	set80211location),
5256	DEF_CMD("outdoor",	'O',	set80211location),
5257	DEF_CMD("-outdoor",	'I',	set80211location),
5258	DEF_CMD("anywhere",	' ',	set80211location),
5259	DEF_CMD("ecm",		1,	set80211ecm),
5260	DEF_CMD("-ecm",		0,	set80211ecm),
5261	DEF_CMD("dotd",		1,	set80211dotd),
5262	DEF_CMD("-dotd",	0,	set80211dotd),
5263	DEF_CMD_ARG("htprotmode",	set80211htprotmode),
5264	DEF_CMD("ht20",		1,	set80211htconf),
5265	DEF_CMD("-ht20",	0,	set80211htconf),
5266	DEF_CMD("ht40",		3,	set80211htconf),	/* NB: 20+40 */
5267	DEF_CMD("-ht40",	0,	set80211htconf),
5268	DEF_CMD("ht",		3,	set80211htconf),	/* NB: 20+40 */
5269	DEF_CMD("-ht",		0,	set80211htconf),
5270	DEF_CMD("rifs",		1,	set80211rifs),
5271	DEF_CMD("-rifs",	0,	set80211rifs),
5272	DEF_CMD("smps",		IEEE80211_HTCAP_SMPS_ENA,	set80211smps),
5273	DEF_CMD("smpsdyn",	IEEE80211_HTCAP_SMPS_DYNAMIC,	set80211smps),
5274	DEF_CMD("-smps",	IEEE80211_HTCAP_SMPS_OFF,	set80211smps),
5275	/* XXX for testing */
5276	DEF_CMD_ARG("chanswitch",	set80211chanswitch),
5277
5278	DEF_CMD_ARG("tdmaslot",		set80211tdmaslot),
5279	DEF_CMD_ARG("tdmaslotcnt",	set80211tdmaslotcnt),
5280	DEF_CMD_ARG("tdmaslotlen",	set80211tdmaslotlen),
5281	DEF_CMD_ARG("tdmabintval",	set80211tdmabintval),
5282
5283	DEF_CMD_ARG("meshttl",		set80211meshttl),
5284	DEF_CMD("meshforward",	1,	set80211meshforward),
5285	DEF_CMD("-meshforward",	0,	set80211meshforward),
5286	DEF_CMD("meshgate",	1,	set80211meshgate),
5287	DEF_CMD("-meshgate",	0,	set80211meshgate),
5288	DEF_CMD("meshpeering",	1,	set80211meshpeering),
5289	DEF_CMD("-meshpeering",	0,	set80211meshpeering),
5290	DEF_CMD_ARG("meshmetric",	set80211meshmetric),
5291	DEF_CMD_ARG("meshpath",		set80211meshpath),
5292	DEF_CMD("meshrt:flush",	IEEE80211_MESH_RTCMD_FLUSH,	set80211meshrtcmd),
5293	DEF_CMD_ARG("meshrt:add",	set80211addmeshrt),
5294	DEF_CMD_ARG("meshrt:del",	set80211delmeshrt),
5295	DEF_CMD_ARG("hwmprootmode",	set80211hwmprootmode),
5296	DEF_CMD_ARG("hwmpmaxhops",	set80211hwmpmaxhops),
5297
5298	/* vap cloning support */
5299	DEF_CLONE_CMD_ARG("wlanaddr",	set80211clone_wlanaddr),
5300	DEF_CLONE_CMD_ARG("wlanbssid",	set80211clone_wlanbssid),
5301	DEF_CLONE_CMD_ARG("wlandev",	set80211clone_wlandev),
5302	DEF_CLONE_CMD_ARG("wlanmode",	set80211clone_wlanmode),
5303	DEF_CLONE_CMD("beacons", 1,	set80211clone_beacons),
5304	DEF_CLONE_CMD("-beacons", 0,	set80211clone_beacons),
5305	DEF_CLONE_CMD("bssid",	1,	set80211clone_bssid),
5306	DEF_CLONE_CMD("-bssid",	0,	set80211clone_bssid),
5307	DEF_CLONE_CMD("wdslegacy", 1,	set80211clone_wdslegacy),
5308	DEF_CLONE_CMD("-wdslegacy", 0,	set80211clone_wdslegacy),
5309};
5310static struct afswtch af_ieee80211 = {
5311	.af_name	= "af_ieee80211",
5312	.af_af		= AF_UNSPEC,
5313	.af_other_status = ieee80211_status,
5314};
5315
5316static __constructor void
5317ieee80211_ctor(void)
5318{
5319#define	N(a)	(sizeof(a) / sizeof(a[0]))
5320	int i;
5321
5322	for (i = 0; i < N(ieee80211_cmds);  i++)
5323		cmd_register(&ieee80211_cmds[i]);
5324	af_register(&af_ieee80211);
5325	clone_setdefcallback("wlan", wlan_create);
5326#undef N
5327}
5328