ifieee80211.c revision 343506
1283625Sdim/*
2283625Sdim * Copyright 2001 The Aerospace Corporation.  All rights reserved.
3283625Sdim *
4283625Sdim * Redistribution and use in source and binary forms, with or without
5283625Sdim * modification, are permitted provided that the following conditions
6283625Sdim * are met:
7283625Sdim * 1. Redistributions of source code must retain the above copyright
8283625Sdim *    notice, this list of conditions and the following disclaimer.
9283625Sdim * 2. Redistributions in binary form must reproduce the above copyright
10283625Sdim *    notice, this list of conditions and the following disclaimer in the
11283625Sdim *    documentation and/or other materials provided with the distribution.
12283625Sdim * 3. The name of The Aerospace Corporation may not be used to endorse or
13283625Sdim *    promote products derived from this software.
14283625Sdim *
15283625Sdim * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION ``AS IS'' AND
16283625Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17283625Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18283625Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AEROSPACE CORPORATION BE LIABLE
19283625Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20283625Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21283625Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22283625Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23283625Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24283625Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25283625Sdim * SUCH DAMAGE.
26283625Sdim *
27283625Sdim * $FreeBSD: stable/10/sbin/ifconfig/ifieee80211.c 343506 2019-01-28 00:51:26Z avos $
28283625Sdim */
29283625Sdim
30283625Sdim/*-
31283625Sdim * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
32283625Sdim * All rights reserved.
33283625Sdim *
34283625Sdim * This code is derived from software contributed to The NetBSD Foundation
35283625Sdim * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
36283625Sdim * NASA Ames Research Center.
37283625Sdim *
38283625Sdim * Redistribution and use in source and binary forms, with or without
39283625Sdim * modification, are permitted provided that the following conditions
40283625Sdim * are met:
41283625Sdim * 1. Redistributions of source code must retain the above copyright
42283625Sdim *    notice, this list of conditions and the following disclaimer.
43283625Sdim * 2. Redistributions in binary form must reproduce the above copyright
44283625Sdim *    notice, this list of conditions and the following disclaimer in the
45283625Sdim *    documentation and/or other materials provided with the distribution.
46283625Sdim *
47283625Sdim * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
48283625Sdim * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
49283625Sdim * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
50283625Sdim * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
51283625Sdim * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
52283625Sdim * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
53283625Sdim * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
54283625Sdim * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
55283625Sdim * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
56283625Sdim * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
57283625Sdim * POSSIBILITY OF SUCH DAMAGE.
58283625Sdim */
59283625Sdim
60283625Sdim#include <sys/param.h>
61283625Sdim#include <sys/ioctl.h>
62283625Sdim#include <sys/socket.h>
63283625Sdim#include <sys/sysctl.h>
64283625Sdim#include <sys/time.h>
65283625Sdim
66283625Sdim#include <net/ethernet.h>
67283625Sdim#include <net/if.h>
68283625Sdim#include <net/if_dl.h>
69283625Sdim#include <net/if_types.h>
70283625Sdim#include <net/if_media.h>
71283625Sdim#include <net/route.h>
72283625Sdim
73283625Sdim#include <net80211/ieee80211_ioctl.h>
74283625Sdim#include <net80211/ieee80211_freebsd.h>
75283625Sdim#include <net80211/ieee80211_superg.h>
76283625Sdim#include <net80211/ieee80211_tdma.h>
77283625Sdim#include <net80211/ieee80211_mesh.h>
78283625Sdim
79283625Sdim#include <assert.h>
80283625Sdim#include <ctype.h>
81283625Sdim#include <err.h>
82283625Sdim#include <errno.h>
83283625Sdim#include <fcntl.h>
84283625Sdim#include <inttypes.h>
85283625Sdim#include <stdio.h>
86283625Sdim#include <stdlib.h>
87283625Sdim#include <string.h>
88283625Sdim#include <unistd.h>
89283625Sdim#include <stdarg.h>
90283625Sdim#include <stddef.h>		/* NB: for offsetof */
91283625Sdim
92283625Sdim#include "ifconfig.h"
93283625Sdim#include "regdomain.h"
94283625Sdim
95283625Sdim#ifndef IEEE80211_FIXED_RATE_NONE
96283625Sdim#define	IEEE80211_FIXED_RATE_NONE	0xff
97283625Sdim#endif
98283625Sdim
99283625Sdim/* XXX need these publicly defined or similar */
100283625Sdim#ifndef IEEE80211_NODE_AUTH
101283625Sdim#define	IEEE80211_NODE_AUTH	0x000001	/* authorized for data */
102296417Sdim#define	IEEE80211_NODE_QOS	0x000002	/* QoS enabled */
103283625Sdim#define	IEEE80211_NODE_ERP	0x000004	/* ERP enabled */
104283625Sdim#define	IEEE80211_NODE_PWR_MGT	0x000010	/* power save mode enabled */
105283625Sdim#define	IEEE80211_NODE_AREF	0x000020	/* authentication ref held */
106283625Sdim#define	IEEE80211_NODE_HT	0x000040	/* HT enabled */
107283625Sdim#define	IEEE80211_NODE_HTCOMPAT	0x000080	/* HT setup w/ vendor OUI's */
108283625Sdim#define	IEEE80211_NODE_WPS	0x000100	/* WPS association */
109283625Sdim#define	IEEE80211_NODE_TSN	0x000200	/* TSN association */
110283625Sdim#define	IEEE80211_NODE_AMPDU_RX	0x000400	/* AMPDU rx enabled */
111283625Sdim#define	IEEE80211_NODE_AMPDU_TX	0x000800	/* AMPDU tx enabled */
112283625Sdim#define	IEEE80211_NODE_MIMO_PS	0x001000	/* MIMO power save enabled */
113283625Sdim#define	IEEE80211_NODE_MIMO_RTS	0x002000	/* send RTS in MIMO PS */
114283625Sdim#define	IEEE80211_NODE_RIFS	0x004000	/* RIFS enabled */
115283625Sdim#define	IEEE80211_NODE_SGI20	0x008000	/* Short GI in HT20 enabled */
116283625Sdim#define	IEEE80211_NODE_SGI40	0x010000	/* Short GI in HT40 enabled */
117283625Sdim#define	IEEE80211_NODE_ASSOCID	0x020000	/* xmit requires associd */
118283625Sdim#define	IEEE80211_NODE_AMSDU_RX	0x040000	/* AMSDU rx enabled */
119283625Sdim#define	IEEE80211_NODE_AMSDU_TX	0x080000	/* AMSDU tx enabled */
120283625Sdim#endif
121283625Sdim
122283625Sdim#define	MAXCHAN	1536		/* max 1.5K channels */
123283625Sdim
124283625Sdim#define	MAXCOL	78
125283625Sdimstatic	int col;
126283625Sdimstatic	char spacer;
127283625Sdim
128283625Sdimstatic void LINE_INIT(char c);
129283625Sdimstatic void LINE_BREAK(void);
130283625Sdimstatic void LINE_CHECK(const char *fmt, ...);
131283625Sdim
132283625Sdimstatic const char *modename[IEEE80211_MODE_MAX] = {
133283625Sdim	[IEEE80211_MODE_AUTO]	  = "auto",
134283625Sdim	[IEEE80211_MODE_11A]	  = "11a",
135283625Sdim	[IEEE80211_MODE_11B]	  = "11b",
136283625Sdim	[IEEE80211_MODE_11G]	  = "11g",
137283625Sdim	[IEEE80211_MODE_FH]	  = "fh",
138283625Sdim	[IEEE80211_MODE_TURBO_A]  = "turboA",
139283625Sdim	[IEEE80211_MODE_TURBO_G]  = "turboG",
140283625Sdim	[IEEE80211_MODE_STURBO_A] = "sturbo",
141283625Sdim	[IEEE80211_MODE_11NA]	  = "11na",
142283625Sdim	[IEEE80211_MODE_11NG]	  = "11ng",
143283625Sdim	[IEEE80211_MODE_HALF]	  = "half",
144283625Sdim	[IEEE80211_MODE_QUARTER]  = "quarter"
145283625Sdim};
146283625Sdim
147283625Sdimstatic void set80211(int s, int type, int val, int len, void *data);
148283625Sdimstatic int get80211(int s, int type, void *data, int len);
149283625Sdimstatic int get80211len(int s, int type, void *data, int len, int *plen);
150283625Sdimstatic int get80211val(int s, int type, int *val);
151283625Sdimstatic const char *get_string(const char *val, const char *sep,
152283625Sdim    u_int8_t *buf, int *lenp);
153283625Sdimstatic void print_string(const u_int8_t *buf, int len);
154283625Sdimstatic void print_regdomain(const struct ieee80211_regdomain *, int);
155283625Sdimstatic void print_channels(int, const struct ieee80211req_chaninfo *,
156283625Sdim    int allchans, int verbose);
157283625Sdimstatic void regdomain_makechannels(struct ieee80211_regdomain_req *,
158283625Sdim    const struct ieee80211_devcaps_req *);
159283625Sdimstatic const char *mesh_linkstate_string(uint8_t state);
160283625Sdim
161283625Sdimstatic struct ieee80211req_chaninfo *chaninfo;
162283625Sdimstatic struct ieee80211_regdomain regdomain;
163283625Sdimstatic int gotregdomain = 0;
164283625Sdimstatic struct ieee80211_roamparams_req roamparams;
165283625Sdimstatic int gotroam = 0;
166283625Sdimstatic struct ieee80211_txparams_req txparams;
167283625Sdimstatic int gottxparams = 0;
168283625Sdimstatic struct ieee80211_channel curchan;
169283625Sdimstatic int gotcurchan = 0;
170283625Sdimstatic struct ifmediareq *ifmr;
171283625Sdimstatic int htconf = 0;
172283625Sdimstatic	int gothtconf = 0;
173283625Sdim
174283625Sdimstatic void
175283625Sdimgethtconf(int s)
176283625Sdim{
177283625Sdim	if (gothtconf)
178283625Sdim		return;
179296417Sdim	if (get80211val(s, IEEE80211_IOC_HTCONF, &htconf) < 0)
180283625Sdim		warn("unable to get HT configuration information");
181283625Sdim	gothtconf = 1;
182283625Sdim}
183283625Sdim
184283625Sdim/*
185283625Sdim * Collect channel info from the kernel.  We use this (mostly)
186283625Sdim * to handle mapping between frequency and IEEE channel number.
187283625Sdim */
188283625Sdimstatic void
189283625Sdimgetchaninfo(int s)
190283625Sdim{
191283625Sdim	if (chaninfo != NULL)
192283625Sdim		return;
193283625Sdim	chaninfo = malloc(IEEE80211_CHANINFO_SIZE(MAXCHAN));
194283625Sdim	if (chaninfo == NULL)
195283625Sdim		errx(1, "no space for channel list");
196283625Sdim	if (get80211(s, IEEE80211_IOC_CHANINFO, chaninfo,
197283625Sdim	    IEEE80211_CHANINFO_SIZE(MAXCHAN)) < 0)
198283625Sdim		err(1, "unable to get channel information");
199283625Sdim	ifmr = ifmedia_getstate(s);
200283625Sdim	gethtconf(s);
201283625Sdim}
202283625Sdim
203283625Sdimstatic struct regdata *
204283625Sdimgetregdata(void)
205283625Sdim{
206283625Sdim	static struct regdata *rdp = NULL;
207283625Sdim	if (rdp == NULL) {
208283625Sdim		rdp = lib80211_alloc_regdata();
209283625Sdim		if (rdp == NULL)
210283625Sdim			errx(-1, "missing or corrupted regdomain database");
211283625Sdim	}
212283625Sdim	return rdp;
213283625Sdim}
214283625Sdim
215283625Sdim/*
216283625Sdim * Given the channel at index i with attributes from,
217283625Sdim * check if there is a channel with attributes to in
218283625Sdim * the channel table.  With suitable attributes this
219283625Sdim * allows the caller to look for promotion; e.g. from
220283625Sdim * 11b > 11g.
221283625Sdim */
222283625Sdimstatic int
223283625Sdimcanpromote(int i, int from, int to)
224283625Sdim{
225283625Sdim	const struct ieee80211_channel *fc = &chaninfo->ic_chans[i];
226283625Sdim	u_int j;
227283625Sdim
228283625Sdim	if ((fc->ic_flags & from) != from)
229283625Sdim		return i;
230283625Sdim	/* NB: quick check exploiting ordering of chans w/ same frequency */
231283625Sdim	if (i+1 < chaninfo->ic_nchans &&
232283625Sdim	    chaninfo->ic_chans[i+1].ic_freq == fc->ic_freq &&
233283625Sdim	    (chaninfo->ic_chans[i+1].ic_flags & to) == to)
234283625Sdim		return i+1;
235283625Sdim	/* brute force search in case channel list is not ordered */
236283625Sdim	for (j = 0; j < chaninfo->ic_nchans; j++) {
237283625Sdim		const struct ieee80211_channel *tc = &chaninfo->ic_chans[j];
238283625Sdim		if (j != i &&
239283625Sdim		    tc->ic_freq == fc->ic_freq && (tc->ic_flags & to) == to)
240283625Sdim		return j;
241283625Sdim	}
242283625Sdim	return i;
243283625Sdim}
244283625Sdim
245283625Sdim/*
246283625Sdim * Handle channel promotion.  When a channel is specified with
247283625Sdim * only a frequency we want to promote it to the ``best'' channel
248283625Sdim * available.  The channel list has separate entries for 11b, 11g,
249283625Sdim * 11a, and 11n[ga] channels so specifying a frequency w/o any
250283625Sdim * attributes requires we upgrade, e.g. from 11b -> 11g.  This
251283625Sdim * gets complicated when the channel is specified on the same
252283625Sdim * command line with a media request that constrains the available
253283625Sdim * channe list (e.g. mode 11a); we want to honor that to avoid
254283625Sdim * confusing behaviour.
255283625Sdim */
256283625Sdimstatic int
257283625Sdimpromote(int i)
258283625Sdim{
259283625Sdim	/*
260283625Sdim	 * Query the current mode of the interface in case it's
261283625Sdim	 * constrained (e.g. to 11a).  We must do this carefully
262283625Sdim	 * as there may be a pending ifmedia request in which case
263283625Sdim	 * asking the kernel will give us the wrong answer.  This
264283625Sdim	 * is an unfortunate side-effect of the way ifconfig is
265283625Sdim	 * structure for modularity (yech).
266283625Sdim	 *
267283625Sdim	 * NB: ifmr is actually setup in getchaninfo (above); we
268283625Sdim	 *     assume it's called coincident with to this call so
269283625Sdim	 *     we have a ``current setting''; otherwise we must pass
270283625Sdim	 *     the socket descriptor down to here so we can make
271283625Sdim	 *     the ifmedia_getstate call ourselves.
272283625Sdim	 */
273283625Sdim	int chanmode = ifmr != NULL ? IFM_MODE(ifmr->ifm_current) : IFM_AUTO;
274283625Sdim
275283625Sdim	/* when ambiguous promote to ``best'' */
276283625Sdim	/* NB: we abitrarily pick HT40+ over HT40- */
277283625Sdim	if (chanmode != IFM_IEEE80211_11B)
278283625Sdim		i = canpromote(i, IEEE80211_CHAN_B, IEEE80211_CHAN_G);
279283625Sdim	if (chanmode != IFM_IEEE80211_11G && (htconf & 1)) {
280283625Sdim		i = canpromote(i, IEEE80211_CHAN_G,
281283625Sdim			IEEE80211_CHAN_G | IEEE80211_CHAN_HT20);
282283625Sdim		if (htconf & 2) {
283283625Sdim			i = canpromote(i, IEEE80211_CHAN_G,
284283625Sdim				IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D);
285286684Sdim			i = canpromote(i, IEEE80211_CHAN_G,
286286684Sdim				IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U);
287283625Sdim		}
288283625Sdim	}
289283625Sdim	if (chanmode != IFM_IEEE80211_11A && (htconf & 1)) {
290286684Sdim		i = canpromote(i, IEEE80211_CHAN_A,
291283625Sdim			IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
292283625Sdim		if (htconf & 2) {
293283625Sdim			i = canpromote(i, IEEE80211_CHAN_A,
294283625Sdim				IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
295286684Sdim			i = canpromote(i, IEEE80211_CHAN_A,
296286684Sdim				IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
297283625Sdim		}
298283625Sdim	}
299286684Sdim	return i;
300283625Sdim}
301283625Sdim
302283625Sdimstatic void
303283625Sdimmapfreq(struct ieee80211_channel *chan, int freq, int flags)
304283625Sdim{
305283625Sdim	u_int i;
306283625Sdim
307283625Sdim	for (i = 0; i < chaninfo->ic_nchans; i++) {
308283625Sdim		const struct ieee80211_channel *c = &chaninfo->ic_chans[i];
309283625Sdim
310283625Sdim		if (c->ic_freq == freq && (c->ic_flags & flags) == flags) {
311283625Sdim			if (flags == 0) {
312283625Sdim				/* when ambiguous promote to ``best'' */
313283625Sdim				c = &chaninfo->ic_chans[promote(i)];
314283625Sdim			}
315283625Sdim			*chan = *c;
316283625Sdim			return;
317283625Sdim		}
318283625Sdim	}
319283625Sdim	errx(1, "unknown/undefined frequency %u/0x%x", freq, flags);
320283625Sdim}
321283625Sdim
322283625Sdimstatic void
323283625Sdimmapchan(struct ieee80211_channel *chan, int ieee, int flags)
324283625Sdim{
325283625Sdim	u_int i;
326283625Sdim
327283625Sdim	for (i = 0; i < chaninfo->ic_nchans; i++) {
328283625Sdim		const struct ieee80211_channel *c = &chaninfo->ic_chans[i];
329283625Sdim
330283625Sdim		if (c->ic_ieee == ieee && (c->ic_flags & flags) == flags) {
331283625Sdim			if (flags == 0) {
332283625Sdim				/* when ambiguous promote to ``best'' */
333283625Sdim				c = &chaninfo->ic_chans[promote(i)];
334296417Sdim			}
335296417Sdim			*chan = *c;
336296417Sdim			return;
337283625Sdim		}
338283625Sdim	}
339283625Sdim	errx(1, "unknown/undefined channel number %d flags 0x%x", ieee, flags);
340283625Sdim}
341283625Sdim
342283625Sdimstatic const struct ieee80211_channel *
343283625Sdimgetcurchan(int s)
344283625Sdim{
345283625Sdim	if (gotcurchan)
346283625Sdim		return &curchan;
347283625Sdim	if (get80211(s, IEEE80211_IOC_CURCHAN, &curchan, sizeof(curchan)) < 0) {
348283625Sdim		int val;
349283625Sdim		/* fall back to legacy ioctl */
350283625Sdim		if (get80211val(s, IEEE80211_IOC_CHANNEL, &val) < 0)
351283625Sdim			err(-1, "cannot figure out current channel");
352283625Sdim		getchaninfo(s);
353283625Sdim		mapchan(&curchan, val, 0);
354283625Sdim	}
355283625Sdim	gotcurchan = 1;
356283625Sdim	return &curchan;
357283625Sdim}
358283625Sdim
359283625Sdimstatic enum ieee80211_phymode
360283625Sdimchan2mode(const struct ieee80211_channel *c)
361296417Sdim{
362296417Sdim	if (IEEE80211_IS_CHAN_HTA(c))
363296417Sdim		return IEEE80211_MODE_11NA;
364283625Sdim	if (IEEE80211_IS_CHAN_HTG(c))
365283625Sdim		return IEEE80211_MODE_11NG;
366283625Sdim	if (IEEE80211_IS_CHAN_108A(c))
367283625Sdim		return IEEE80211_MODE_TURBO_A;
368283625Sdim	if (IEEE80211_IS_CHAN_108G(c))
369283625Sdim		return IEEE80211_MODE_TURBO_G;
370283625Sdim	if (IEEE80211_IS_CHAN_ST(c))
371283625Sdim		return IEEE80211_MODE_STURBO_A;
372283625Sdim	if (IEEE80211_IS_CHAN_FHSS(c))
373283625Sdim		return IEEE80211_MODE_FH;
374283625Sdim	if (IEEE80211_IS_CHAN_HALF(c))
375296417Sdim		return IEEE80211_MODE_HALF;
376283625Sdim	if (IEEE80211_IS_CHAN_QUARTER(c))
377283625Sdim		return IEEE80211_MODE_QUARTER;
378283625Sdim	if (IEEE80211_IS_CHAN_A(c))
379283625Sdim		return IEEE80211_MODE_11A;
380283625Sdim	if (IEEE80211_IS_CHAN_ANYG(c))
381283625Sdim		return IEEE80211_MODE_11G;
382283625Sdim	if (IEEE80211_IS_CHAN_B(c))
383283625Sdim		return IEEE80211_MODE_11B;
384283625Sdim	return IEEE80211_MODE_AUTO;
385283625Sdim}
386283625Sdim
387283625Sdimstatic void
388283625Sdimgetroam(int s)
389296417Sdim{
390283625Sdim	if (gotroam)
391283625Sdim		return;
392283625Sdim	if (get80211(s, IEEE80211_IOC_ROAM,
393283625Sdim	    &roamparams, sizeof(roamparams)) < 0)
394296417Sdim		err(1, "unable to get roaming parameters");
395283625Sdim	gotroam = 1;
396283625Sdim}
397283625Sdim
398283625Sdimstatic void
399283625Sdimsetroam_cb(int s, void *arg)
400283625Sdim{
401283625Sdim	struct ieee80211_roamparams_req *roam = arg;
402283625Sdim	set80211(s, IEEE80211_IOC_ROAM, 0, sizeof(*roam), roam);
403283625Sdim}
404283625Sdim
405283625Sdimstatic void
406283625Sdimgettxparams(int s)
407283625Sdim{
408283625Sdim	if (gottxparams)
409283625Sdim		return;
410283625Sdim	if (get80211(s, IEEE80211_IOC_TXPARAMS,
411283625Sdim	    &txparams, sizeof(txparams)) < 0)
412283625Sdim		err(1, "unable to get transmit parameters");
413283625Sdim	gottxparams = 1;
414283625Sdim}
415283625Sdim
416283625Sdimstatic void
417283625Sdimsettxparams_cb(int s, void *arg)
418283625Sdim{
419283625Sdim	struct ieee80211_txparams_req *txp = arg;
420283625Sdim	set80211(s, IEEE80211_IOC_TXPARAMS, 0, sizeof(*txp), txp);
421283625Sdim}
422283625Sdim
423283625Sdimstatic void
424283625Sdimgetregdomain(int s)
425283625Sdim{
426283625Sdim	if (gotregdomain)
427283625Sdim		return;
428296417Sdim	if (get80211(s, IEEE80211_IOC_REGDOMAIN,
429283625Sdim	    &regdomain, sizeof(regdomain)) < 0)
430283625Sdim		err(1, "unable to get regulatory domain info");
431283625Sdim	gotregdomain = 1;
432283625Sdim}
433283625Sdim
434283625Sdimstatic void
435296417Sdimgetdevcaps(int s, struct ieee80211_devcaps_req *dc)
436283625Sdim{
437283625Sdim	if (get80211(s, IEEE80211_IOC_DEVCAPS, dc,
438283625Sdim	    IEEE80211_DEVCAPS_SPACE(dc)) < 0)
439283625Sdim		err(1, "unable to get device capabilities");
440283625Sdim}
441283625Sdim
442296417Sdimstatic void
443296417Sdimsetregdomain_cb(int s, void *arg)
444283625Sdim{
445283625Sdim	struct ieee80211_regdomain_req *req;
446283625Sdim	struct ieee80211_regdomain *rd = arg;
447283625Sdim	struct ieee80211_devcaps_req *dc;
448283625Sdim	struct regdata *rdp = getregdata();
449283625Sdim
450283625Sdim	if (rd->country != NO_COUNTRY) {
451283625Sdim		const struct country *cc;
452296417Sdim		/*
453283625Sdim		 * Check current country seting to make sure it's
454283625Sdim		 * compatible with the new regdomain.  If not, then
455283625Sdim		 * override it with any default country for this
456283625Sdim		 * SKU.  If we cannot arrange a match, then abort.
457296417Sdim		 */
458296417Sdim		cc = lib80211_country_findbycc(rdp, rd->country);
459283625Sdim		if (cc == NULL)
460283625Sdim			errx(1, "unknown ISO country code %d", rd->country);
461283625Sdim		if (cc->rd->sku != rd->regdomain) {
462283625Sdim			const struct regdomain *rp;
463283625Sdim			/*
464283625Sdim			 * Check if country is incompatible with regdomain.
465283625Sdim			 * To enable multiple regdomains for a country code
466283625Sdim			 * we permit a mismatch between the regdomain and
467283625Sdim			 * the country's associated regdomain when the
468283625Sdim			 * regdomain is setup w/o a default country.  For
469283625Sdim			 * example, US is bound to the FCC regdomain but
470283625Sdim			 * we allow US to be combined with FCC3 because FCC3
471283625Sdim			 * has not default country.  This allows bogus
472283625Sdim			 * combinations like FCC3+DK which are resolved when
473283625Sdim			 * constructing the channel list by deferring to the
474283625Sdim			 * regdomain to construct the channel list.
475283625Sdim			 */
476283625Sdim			rp = lib80211_regdomain_findbysku(rdp, rd->regdomain);
477283625Sdim			if (rp == NULL)
478283625Sdim				errx(1, "country %s (%s) is not usable with "
479283625Sdim				    "regdomain %d", cc->isoname, cc->name,
480283625Sdim				    rd->regdomain);
481283625Sdim			else if (rp->cc != NULL && rp->cc != cc)
482283625Sdim				errx(1, "country %s (%s) is not usable with "
483283625Sdim				   "regdomain %s", cc->isoname, cc->name,
484283625Sdim				   rp->name);
485283625Sdim		}
486283625Sdim	}
487283625Sdim	/*
488283625Sdim	 * Fetch the device capabilities and calculate the
489283625Sdim	 * full set of netbands for which we request a new
490283625Sdim	 * channel list be constructed.  Once that's done we
491283625Sdim	 * push the regdomain info + channel list to the kernel.
492283625Sdim	 */
493283625Sdim	dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
494283625Sdim	if (dc == NULL)
495283625Sdim		errx(1, "no space for device capabilities");
496296417Sdim	dc->dc_chaninfo.ic_nchans = MAXCHAN;
497283625Sdim	getdevcaps(s, dc);
498283625Sdim#if 0
499283625Sdim	if (verbose) {
500283625Sdim		printf("drivercaps: 0x%x\n", dc->dc_drivercaps);
501283625Sdim		printf("cryptocaps: 0x%x\n", dc->dc_cryptocaps);
502283625Sdim		printf("htcaps    : 0x%x\n", dc->dc_htcaps);
503283625Sdim		memcpy(chaninfo, &dc->dc_chaninfo,
504283625Sdim		    IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
505283625Sdim		print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, 1/*verbose*/);
506283625Sdim	}
507283625Sdim#endif
508283625Sdim	req = malloc(IEEE80211_REGDOMAIN_SIZE(dc->dc_chaninfo.ic_nchans));
509283625Sdim	if (req == NULL)
510283625Sdim		errx(1, "no space for regdomain request");
511283625Sdim	req->rd = *rd;
512283625Sdim	regdomain_makechannels(req, dc);
513283625Sdim	if (verbose) {
514283625Sdim		LINE_INIT(':');
515283625Sdim		print_regdomain(rd, 1/*verbose*/);
516283625Sdim		LINE_BREAK();
517283625Sdim		/* blech, reallocate channel list for new data */
518283625Sdim		if (chaninfo != NULL)
519283625Sdim			free(chaninfo);
520283625Sdim		chaninfo = malloc(IEEE80211_CHANINFO_SPACE(&req->chaninfo));
521283625Sdim		if (chaninfo == NULL)
522283625Sdim			errx(1, "no space for channel list");
523283625Sdim		memcpy(chaninfo, &req->chaninfo,
524283625Sdim		    IEEE80211_CHANINFO_SPACE(&req->chaninfo));
525283625Sdim		print_channels(s, &req->chaninfo, 1/*allchans*/, 1/*verbose*/);
526283625Sdim	}
527283625Sdim	if (req->chaninfo.ic_nchans == 0)
528283625Sdim		errx(1, "no channels calculated");
529283625Sdim	set80211(s, IEEE80211_IOC_REGDOMAIN, 0,
530283625Sdim	    IEEE80211_REGDOMAIN_SPACE(req), req);
531283625Sdim	free(req);
532283625Sdim	free(dc);
533283625Sdim}
534283625Sdim
535283625Sdimstatic int
536283625Sdimieee80211_mhz2ieee(int freq, int flags)
537283625Sdim{
538283625Sdim	struct ieee80211_channel chan;
539283625Sdim	mapfreq(&chan, freq, flags);
540283625Sdim	return chan.ic_ieee;
541283625Sdim}
542283625Sdim
543283625Sdimstatic int
544283625Sdimisanyarg(const char *arg)
545283625Sdim{
546283625Sdim	return (strncmp(arg, "-", 1) == 0 ||
547283625Sdim	    strncasecmp(arg, "any", 3) == 0 || strncasecmp(arg, "off", 3) == 0);
548283625Sdim}
549283625Sdim
550283625Sdimstatic void
551296417Sdimset80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
552283625Sdim{
553283625Sdim	int		ssid;
554283625Sdim	int		len;
555283625Sdim	u_int8_t	data[IEEE80211_NWID_LEN];
556283625Sdim
557283625Sdim	ssid = 0;
558283625Sdim	len = strlen(val);
559283625Sdim	if (len > 2 && isdigit((int)val[0]) && val[1] == ':') {
560283625Sdim		ssid = atoi(val)-1;
561283625Sdim		val += 2;
562283625Sdim	}
563283625Sdim
564283625Sdim	bzero(data, sizeof(data));
565283625Sdim	len = sizeof(data);
566283625Sdim	if (get_string(val, NULL, data, &len) == NULL)
567283625Sdim		exit(1);
568283625Sdim
569283625Sdim	set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
570283625Sdim}
571283625Sdim
572283625Sdimstatic void
573283625Sdimset80211meshid(const char *val, int d, int s, const struct afswtch *rafp)
574283625Sdim{
575283625Sdim	int		len;
576283625Sdim	u_int8_t	data[IEEE80211_NWID_LEN];
577283625Sdim
578283625Sdim	memset(data, 0, sizeof(data));
579283625Sdim	len = sizeof(data);
580283625Sdim	if (get_string(val, NULL, data, &len) == NULL)
581296417Sdim		exit(1);
582296417Sdim
583283625Sdim	set80211(s, IEEE80211_IOC_MESH_ID, 0, len, data);
584283625Sdim}
585283625Sdim
586283625Sdimstatic void
587283625Sdimset80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
588283625Sdim{
589283625Sdim	int			len;
590283625Sdim	u_int8_t		data[33];
591283625Sdim
592283625Sdim	bzero(data, sizeof(data));
593283625Sdim	len = sizeof(data);
594296417Sdim	get_string(val, NULL, data, &len);
595283625Sdim
596283625Sdim	set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
597283625Sdim}
598283625Sdim
599283625Sdim/*
600283625Sdim * Parse a channel specification for attributes/flags.
601283625Sdim * The syntax is:
602283625Sdim *	freq/xx		channel width (5,10,20,40,40+,40-)
603283625Sdim *	freq:mode	channel mode (a,b,g,h,n,t,s,d)
604283625Sdim *
605283625Sdim * These can be combined in either order; e.g. 2437:ng/40.
606284734Sdim * Modes are case insensitive.
607284734Sdim *
608283625Sdim * The result is not validated here; it's assumed to be
609283625Sdim * checked against the channel table fetched from the kernel.
610283625Sdim */
611283625Sdimstatic int
612283625Sdimgetchannelflags(const char *val, int freq)
613283625Sdim{
614283625Sdim#define	_CHAN_HT	0x80000000
615283625Sdim	const char *cp;
616283625Sdim	int flags;
617283625Sdim
618283625Sdim	flags = 0;
619283625Sdim
620283625Sdim	cp = strchr(val, ':');
621283625Sdim	if (cp != NULL) {
622283625Sdim		for (cp++; isalpha((int) *cp); cp++) {
623283625Sdim			/* accept mixed case */
624283625Sdim			int c = *cp;
625283625Sdim			if (isupper(c))
626283625Sdim				c = tolower(c);
627283625Sdim			switch (c) {
628283625Sdim			case 'a':		/* 802.11a */
629283625Sdim				flags |= IEEE80211_CHAN_A;
630283625Sdim				break;
631283625Sdim			case 'b':		/* 802.11b */
632283625Sdim				flags |= IEEE80211_CHAN_B;
633283625Sdim				break;
634283625Sdim			case 'g':		/* 802.11g */
635283625Sdim				flags |= IEEE80211_CHAN_G;
636283625Sdim				break;
637283625Sdim			case 'h':		/* ht = 802.11n */
638283625Sdim			case 'n':		/* 802.11n */
639283625Sdim				flags |= _CHAN_HT;	/* NB: private */
640283625Sdim				break;
641283625Sdim			case 'd':		/* dt = Atheros Dynamic Turbo */
642283625Sdim				flags |= IEEE80211_CHAN_TURBO;
643283625Sdim				break;
644283625Sdim			case 't':		/* ht, dt, st, t */
645283625Sdim				/* dt and unadorned t specify Dynamic Turbo */
646283625Sdim				if ((flags & (IEEE80211_CHAN_STURBO|_CHAN_HT)) == 0)
647283625Sdim					flags |= IEEE80211_CHAN_TURBO;
648283625Sdim				break;
649283625Sdim			case 's':		/* st = Atheros Static Turbo */
650283625Sdim				flags |= IEEE80211_CHAN_STURBO;
651283625Sdim				break;
652283625Sdim			default:
653283625Sdim				errx(-1, "%s: Invalid channel attribute %c\n",
654283625Sdim				    val, *cp);
655283625Sdim			}
656283625Sdim		}
657283625Sdim	}
658283625Sdim	cp = strchr(val, '/');
659283625Sdim	if (cp != NULL) {
660283625Sdim		char *ep;
661283625Sdim		u_long cw = strtoul(cp+1, &ep, 10);
662283625Sdim
663296417Sdim		switch (cw) {
664283625Sdim		case 5:
665283625Sdim			flags |= IEEE80211_CHAN_QUARTER;
666283625Sdim			break;
667283625Sdim		case 10:
668283625Sdim			flags |= IEEE80211_CHAN_HALF;
669283625Sdim			break;
670283625Sdim		case 20:
671283625Sdim			/* NB: this may be removed below */
672283625Sdim			flags |= IEEE80211_CHAN_HT20;
673283625Sdim			break;
674283625Sdim		case 40:
675283625Sdim			if (ep != NULL && *ep == '+')
676283625Sdim				flags |= IEEE80211_CHAN_HT40U;
677283625Sdim			else if (ep != NULL && *ep == '-')
678283625Sdim				flags |= IEEE80211_CHAN_HT40D;
679283625Sdim			break;
680283625Sdim		default:
681283625Sdim			errx(-1, "%s: Invalid channel width\n", val);
682283625Sdim		}
683283625Sdim	}
684283625Sdim	/*
685283625Sdim	 * Cleanup specifications.
686283625Sdim	 */
687283625Sdim	if ((flags & _CHAN_HT) == 0) {
688283625Sdim		/*
689283625Sdim		 * If user specified freq/20 or freq/40 quietly remove
690283625Sdim		 * HT cw attributes depending on channel use.  To give
691283625Sdim		 * an explicit 20/40 width for an HT channel you must
692283625Sdim		 * indicate it is an HT channel since all HT channels
693283625Sdim		 * are also usable for legacy operation; e.g. freq:n/40.
694283625Sdim		 */
695283625Sdim		flags &= ~IEEE80211_CHAN_HT;
696283625Sdim	} else {
697283625Sdim		/*
698283625Sdim		 * Remove private indicator that this is an HT channel
699283625Sdim		 * and if no explicit channel width has been given
700283625Sdim		 * provide the default settings.
701283625Sdim		 */
702283625Sdim		flags &= ~_CHAN_HT;
703283625Sdim		if ((flags & IEEE80211_CHAN_HT) == 0) {
704283625Sdim			struct ieee80211_channel chan;
705284734Sdim			/*
706296417Sdim			 * Consult the channel list to see if we can use
707283625Sdim			 * HT40+ or HT40- (if both the map routines choose).
708296417Sdim			 */
709283625Sdim			if (freq > 255)
710284734Sdim				mapfreq(&chan, freq, 0);
711283625Sdim			else
712283625Sdim				mapchan(&chan, freq, 0);
713283625Sdim			flags |= (chan.ic_flags & IEEE80211_CHAN_HT);
714283625Sdim		}
715283625Sdim	}
716283625Sdim	return flags;
717283625Sdim#undef _CHAN_HT
718283625Sdim}
719283625Sdim
720283625Sdimstatic void
721283625Sdimgetchannel(int s, struct ieee80211_channel *chan, const char *val)
722283625Sdim{
723283625Sdim	int v, flags;
724283625Sdim	char *eptr;
725283625Sdim
726283625Sdim	memset(chan, 0, sizeof(*chan));
727283625Sdim	if (isanyarg(val)) {
728283625Sdim		chan->ic_freq = IEEE80211_CHAN_ANY;
729283625Sdim		return;
730283625Sdim	}
731283625Sdim	getchaninfo(s);
732283625Sdim	errno = 0;
733283625Sdim	v = strtol(val, &eptr, 10);
734283625Sdim	if (val[0] == '\0' || val == eptr || errno == ERANGE ||
735283625Sdim	    /* channel may be suffixed with nothing, :flag, or /width */
736283625Sdim	    (eptr[0] != '\0' && eptr[0] != ':' && eptr[0] != '/'))
737283625Sdim		errx(1, "invalid channel specification%s",
738283625Sdim		    errno == ERANGE ? " (out of range)" : "");
739283625Sdim	flags = getchannelflags(val, v);
740283625Sdim	if (v > 255) {		/* treat as frequency */
741283625Sdim		mapfreq(chan, v, flags);
742283625Sdim	} else {
743283625Sdim		mapchan(chan, v, flags);
744283625Sdim	}
745283625Sdim}
746283625Sdim
747283625Sdimstatic void
748283625Sdimset80211channel(const char *val, int d, int s, const struct afswtch *rafp)
749283625Sdim{
750283625Sdim	struct ieee80211_channel chan;
751283625Sdim
752283625Sdim	getchannel(s, &chan, val);
753283625Sdim	set80211(s, IEEE80211_IOC_CURCHAN, 0, sizeof(chan), &chan);
754283625Sdim}
755283625Sdim
756283625Sdimstatic void
757283625Sdimset80211chanswitch(const char *val, int d, int s, const struct afswtch *rafp)
758283625Sdim{
759283625Sdim	struct ieee80211_chanswitch_req csr;
760283625Sdim
761283625Sdim	getchannel(s, &csr.csa_chan, val);
762283625Sdim	csr.csa_mode = 1;
763283625Sdim	csr.csa_count = 5;
764283625Sdim	set80211(s, IEEE80211_IOC_CHANSWITCH, 0, sizeof(csr), &csr);
765283625Sdim}
766283625Sdim
767283625Sdimstatic void
768283625Sdimset80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
769283625Sdim{
770283625Sdim	int	mode;
771283625Sdim
772283625Sdim	if (strcasecmp(val, "none") == 0) {
773283625Sdim		mode = IEEE80211_AUTH_NONE;
774283625Sdim	} else if (strcasecmp(val, "open") == 0) {
775283625Sdim		mode = IEEE80211_AUTH_OPEN;
776283625Sdim	} else if (strcasecmp(val, "shared") == 0) {
777283625Sdim		mode = IEEE80211_AUTH_SHARED;
778283625Sdim	} else if (strcasecmp(val, "8021x") == 0) {
779283625Sdim		mode = IEEE80211_AUTH_8021X;
780283625Sdim	} else if (strcasecmp(val, "wpa") == 0) {
781283625Sdim		mode = IEEE80211_AUTH_WPA;
782283625Sdim	} else {
783283625Sdim		errx(1, "unknown authmode");
784283625Sdim	}
785283625Sdim
786283625Sdim	set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
787283625Sdim}
788283625Sdim
789283625Sdimstatic void
790283625Sdimset80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
791283625Sdim{
792283625Sdim	int	mode;
793283625Sdim
794283625Sdim	if (strcasecmp(val, "off") == 0) {
795283625Sdim		mode = IEEE80211_POWERSAVE_OFF;
796283625Sdim	} else if (strcasecmp(val, "on") == 0) {
797283625Sdim		mode = IEEE80211_POWERSAVE_ON;
798283625Sdim	} else if (strcasecmp(val, "cam") == 0) {
799283625Sdim		mode = IEEE80211_POWERSAVE_CAM;
800283625Sdim	} else if (strcasecmp(val, "psp") == 0) {
801283625Sdim		mode = IEEE80211_POWERSAVE_PSP;
802283625Sdim	} else if (strcasecmp(val, "psp-cam") == 0) {
803283625Sdim		mode = IEEE80211_POWERSAVE_PSP_CAM;
804283625Sdim	} else {
805283625Sdim		errx(1, "unknown powersavemode");
806283625Sdim	}
807283625Sdim
808283625Sdim	set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
809283625Sdim}
810283625Sdim
811283625Sdimstatic void
812283625Sdimset80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
813283625Sdim{
814283625Sdim	if (d == 0)
815283625Sdim		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
816283625Sdim		    0, NULL);
817283625Sdim	else
818283625Sdim		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
819283625Sdim		    0, NULL);
820283625Sdim}
821283625Sdim
822283625Sdimstatic void
823283625Sdimset80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
824283625Sdim{
825283625Sdim	set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
826283625Sdim}
827283625Sdim
828283625Sdimstatic void
829283625Sdimset80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
830283625Sdim{
831283625Sdim	int	mode;
832283625Sdim
833283625Sdim	if (strcasecmp(val, "off") == 0) {
834283625Sdim		mode = IEEE80211_WEP_OFF;
835283625Sdim	} else if (strcasecmp(val, "on") == 0) {
836283625Sdim		mode = IEEE80211_WEP_ON;
837283625Sdim	} else if (strcasecmp(val, "mixed") == 0) {
838283625Sdim		mode = IEEE80211_WEP_MIXED;
839283625Sdim	} else {
840283625Sdim		errx(1, "unknown wep mode");
841283625Sdim	}
842283625Sdim
843283625Sdim	set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
844296417Sdim}
845283625Sdim
846283625Sdimstatic void
847283625Sdimset80211wep(const char *val, int d, int s, const struct afswtch *rafp)
848283625Sdim{
849283625Sdim	set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
850283625Sdim}
851283625Sdim
852283625Sdimstatic int
853283625Sdimisundefarg(const char *arg)
854283625Sdim{
855283625Sdim	return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0);
856283625Sdim}
857283625Sdim
858283625Sdimstatic void
859283625Sdimset80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
860283625Sdim{
861283625Sdim	if (isundefarg(val))
862283625Sdim		set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
863283625Sdim	else
864283625Sdim		set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
865283625Sdim}
866283625Sdim
867283625Sdimstatic void
868283625Sdimset80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
869283625Sdim{
870283625Sdim	int		key = 0;
871283625Sdim	int		len;
872283625Sdim	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
873283625Sdim
874283625Sdim	if (isdigit((int)val[0]) && val[1] == ':') {
875296417Sdim		key = atoi(val)-1;
876296417Sdim		val += 2;
877283625Sdim	}
878283625Sdim
879283625Sdim	bzero(data, sizeof(data));
880283625Sdim	len = sizeof(data);
881296417Sdim	get_string(val, NULL, data, &len);
882296417Sdim
883283625Sdim	set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
884283625Sdim}
885283625Sdim
886283625Sdim/*
887283625Sdim * This function is purely a NetBSD compatibility interface.  The NetBSD
888283625Sdim * interface is too inflexible, but it's there so we'll support it since
889283625Sdim * it's not all that hard.
890283625Sdim */
891283625Sdimstatic void
892283625Sdimset80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
893283625Sdim{
894283625Sdim	int		txkey;
895283625Sdim	int		i, len;
896283625Sdim	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
897283625Sdim
898283625Sdim	set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
899283625Sdim
900283625Sdim	if (isdigit((int)val[0]) && val[1] == ':') {
901283625Sdim		txkey = val[0]-'0'-1;
902283625Sdim		val += 2;
903283625Sdim
904283625Sdim		for (i = 0; i < 4; i++) {
905283625Sdim			bzero(data, sizeof(data));
906283625Sdim			len = sizeof(data);
907283625Sdim			val = get_string(val, ",", data, &len);
908283625Sdim			if (val == NULL)
909283625Sdim				exit(1);
910283625Sdim
911283625Sdim			set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
912283625Sdim		}
913283625Sdim	} else {
914283625Sdim		bzero(data, sizeof(data));
915283625Sdim		len = sizeof(data);
916283625Sdim		get_string(val, NULL, data, &len);
917283625Sdim		txkey = 0;
918283625Sdim
919283625Sdim		set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
920283625Sdim
921283625Sdim		bzero(data, sizeof(data));
922283625Sdim		for (i = 1; i < 4; i++)
923283625Sdim			set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
924283625Sdim	}
925283625Sdim
926283625Sdim	set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
927283625Sdim}
928283625Sdim
929283625Sdimstatic void
930283625Sdimset80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
931283625Sdim{
932283625Sdim	set80211(s, IEEE80211_IOC_RTSTHRESHOLD,
933283625Sdim		isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL);
934283625Sdim}
935283625Sdim
936283625Sdimstatic void
937283625Sdimset80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
938283625Sdim{
939283625Sdim	int	mode;
940283625Sdim
941283625Sdim	if (strcasecmp(val, "off") == 0) {
942283625Sdim		mode = IEEE80211_PROTMODE_OFF;
943283625Sdim	} else if (strcasecmp(val, "cts") == 0) {
944283625Sdim		mode = IEEE80211_PROTMODE_CTS;
945283625Sdim	} else if (strncasecmp(val, "rtscts", 3) == 0) {
946283625Sdim		mode = IEEE80211_PROTMODE_RTSCTS;
947283625Sdim	} else {
948283625Sdim		errx(1, "unknown protection mode");
949283625Sdim	}
950283625Sdim
951283625Sdim	set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
952283625Sdim}
953283625Sdim
954283625Sdimstatic void
955283625Sdimset80211htprotmode(const char *val, int d, int s, const struct afswtch *rafp)
956283625Sdim{
957283625Sdim	int	mode;
958283625Sdim
959283625Sdim	if (strcasecmp(val, "off") == 0) {
960283625Sdim		mode = IEEE80211_PROTMODE_OFF;
961283625Sdim	} else if (strncasecmp(val, "rts", 3) == 0) {
962283625Sdim		mode = IEEE80211_PROTMODE_RTSCTS;
963283625Sdim	} else {
964283625Sdim		errx(1, "unknown protection mode");
965283625Sdim	}
966283625Sdim
967283625Sdim	set80211(s, IEEE80211_IOC_HTPROTMODE, mode, 0, NULL);
968283625Sdim}
969283625Sdim
970283625Sdimstatic void
971283625Sdimset80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
972283625Sdim{
973283625Sdim	double v = atof(val);
974283625Sdim	int txpow;
975283625Sdim
976296417Sdim	txpow = (int) (2*v);
977283625Sdim	if (txpow != 2*v)
978283625Sdim		errx(-1, "invalid tx power (must be .5 dBm units)");
979283625Sdim	set80211(s, IEEE80211_IOC_TXPOWER, txpow, 0, NULL);
980283625Sdim}
981283625Sdim
982283625Sdim#define	IEEE80211_ROAMING_DEVICE	0
983283625Sdim#define	IEEE80211_ROAMING_AUTO		1
984283625Sdim#define	IEEE80211_ROAMING_MANUAL	2
985283625Sdim
986283625Sdimstatic void
987283625Sdimset80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
988283625Sdim{
989283625Sdim	int mode;
990296417Sdim
991283625Sdim	if (strcasecmp(val, "device") == 0) {
992283625Sdim		mode = IEEE80211_ROAMING_DEVICE;
993283625Sdim	} else if (strcasecmp(val, "auto") == 0) {
994283625Sdim		mode = IEEE80211_ROAMING_AUTO;
995283625Sdim	} else if (strcasecmp(val, "manual") == 0) {
996283625Sdim		mode = IEEE80211_ROAMING_MANUAL;
997283625Sdim	} else {
998283625Sdim		errx(1, "unknown roaming mode");
999283625Sdim	}
1000283625Sdim	set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
1001283625Sdim}
1002283625Sdim
1003283625Sdimstatic void
1004283625Sdimset80211wme(const char *val, int d, int s, const struct afswtch *rafp)
1005283625Sdim{
1006296417Sdim	set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
1007283625Sdim}
1008283625Sdim
1009283625Sdimstatic void
1010283625Sdimset80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
1011283625Sdim{
1012283625Sdim	set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
1013283625Sdim}
1014283625Sdim
1015283625Sdimstatic void
1016283625Sdimset80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
1017283625Sdim{
1018283625Sdim	set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
1019283625Sdim}
1020283625Sdim
1021283625Sdimstatic void
1022283625Sdimset80211fastframes(const char *val, int d, int s, const struct afswtch *rafp)
1023283625Sdim{
1024283625Sdim	set80211(s, IEEE80211_IOC_FF, d, 0, NULL);
1025283625Sdim}
1026283625Sdim
1027283625Sdimstatic void
1028283625Sdimset80211dturbo(const char *val, int d, int s, const struct afswtch *rafp)
1029283625Sdim{
1030283625Sdim	set80211(s, IEEE80211_IOC_TURBOP, d, 0, NULL);
1031283625Sdim}
1032283625Sdim
1033283625Sdimstatic void
1034283625Sdimset80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
1035283625Sdim{
1036283625Sdim	struct ieee80211req_chanlist chanlist;
1037283625Sdim	char *temp, *cp, *tp;
1038283625Sdim
1039283625Sdim	temp = malloc(strlen(val) + 1);
1040283625Sdim	if (temp == NULL)
1041283625Sdim		errx(1, "malloc failed");
1042283625Sdim	strcpy(temp, val);
1043283625Sdim	memset(&chanlist, 0, sizeof(chanlist));
1044283625Sdim	cp = temp;
1045283625Sdim	for (;;) {
1046283625Sdim		int first, last, f, c;
1047283625Sdim
1048283625Sdim		tp = strchr(cp, ',');
1049283625Sdim		if (tp != NULL)
1050283625Sdim			*tp++ = '\0';
1051283625Sdim		switch (sscanf(cp, "%u-%u", &first, &last)) {
1052283625Sdim		case 1:
1053283625Sdim			if (first > IEEE80211_CHAN_MAX)
1054283625Sdim				errx(-1, "channel %u out of range, max %u",
1055296417Sdim					first, IEEE80211_CHAN_MAX);
1056283625Sdim			setbit(chanlist.ic_channels, first);
1057283625Sdim			break;
1058283625Sdim		case 2:
1059283625Sdim			if (first > IEEE80211_CHAN_MAX)
1060283625Sdim				errx(-1, "channel %u out of range, max %u",
1061283625Sdim					first, IEEE80211_CHAN_MAX);
1062283625Sdim			if (last > IEEE80211_CHAN_MAX)
1063283625Sdim				errx(-1, "channel %u out of range, max %u",
1064283625Sdim					last, IEEE80211_CHAN_MAX);
1065283625Sdim			if (first > last)
1066283625Sdim				errx(-1, "void channel range, %u > %u",
1067283625Sdim					first, last);
1068283625Sdim			for (f = first; f <= last; f++)
1069283625Sdim				setbit(chanlist.ic_channels, f);
1070283625Sdim			break;
1071283625Sdim		}
1072283625Sdim		if (tp == NULL)
1073283625Sdim			break;
1074283625Sdim		c = *tp;
1075283625Sdim		while (isspace(c))
1076283625Sdim			tp++;
1077283625Sdim		if (!isdigit(c))
1078283625Sdim			break;
1079283625Sdim		cp = tp;
1080283625Sdim	}
1081283625Sdim	set80211(s, IEEE80211_IOC_CHANLIST, 0, sizeof(chanlist), &chanlist);
1082283625Sdim}
1083283625Sdim
1084283625Sdimstatic void
1085283625Sdimset80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
1086283625Sdim{
1087283625Sdim
1088283625Sdim	if (!isanyarg(val)) {
1089283625Sdim		char *temp;
1090283625Sdim		struct sockaddr_dl sdl;
1091283625Sdim
1092283625Sdim		temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1093283625Sdim		if (temp == NULL)
1094283625Sdim			errx(1, "malloc failed");
1095283625Sdim		temp[0] = ':';
1096283625Sdim		strcpy(temp + 1, val);
1097283625Sdim		sdl.sdl_len = sizeof(sdl);
1098283625Sdim		link_addr(temp, &sdl);
1099283625Sdim		free(temp);
1100283625Sdim		if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1101283625Sdim			errx(1, "malformed link-level address");
1102283625Sdim		set80211(s, IEEE80211_IOC_BSSID, 0,
1103283625Sdim			IEEE80211_ADDR_LEN, LLADDR(&sdl));
1104283625Sdim	} else {
1105283625Sdim		uint8_t zerobssid[IEEE80211_ADDR_LEN];
1106283625Sdim		memset(zerobssid, 0, sizeof(zerobssid));
1107283625Sdim		set80211(s, IEEE80211_IOC_BSSID, 0,
1108283625Sdim			IEEE80211_ADDR_LEN, zerobssid);
1109283625Sdim	}
1110283625Sdim}
1111283625Sdim
1112283625Sdimstatic int
1113283625Sdimgetac(const char *ac)
1114283625Sdim{
1115283625Sdim	if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0)
1116283625Sdim		return WME_AC_BE;
1117283625Sdim	if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0)
1118283625Sdim		return WME_AC_BK;
1119283625Sdim	if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0)
1120283625Sdim		return WME_AC_VI;
1121283625Sdim	if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0)
1122283625Sdim		return WME_AC_VO;
1123296417Sdim	errx(1, "unknown wme access class %s", ac);
1124296417Sdim}
1125283625Sdim
1126283625Sdimstatic
1127283625SdimDECL_CMD_FUNC2(set80211cwmin, ac, val)
1128283625Sdim{
1129283625Sdim	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
1130283625Sdim}
1131283625Sdim
1132283625Sdimstatic
1133283625SdimDECL_CMD_FUNC2(set80211cwmax, ac, val)
1134283625Sdim{
1135283625Sdim	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
1136283625Sdim}
1137283625Sdim
1138283625Sdimstatic
1139283625SdimDECL_CMD_FUNC2(set80211aifs, ac, val)
1140283625Sdim{
1141283625Sdim	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
1142283625Sdim}
1143283625Sdim
1144283625Sdimstatic
1145283625SdimDECL_CMD_FUNC2(set80211txoplimit, ac, val)
1146283625Sdim{
1147283625Sdim	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
1148283625Sdim}
1149283625Sdim
1150283625Sdimstatic
1151283625SdimDECL_CMD_FUNC(set80211acm, ac, d)
1152283625Sdim{
1153283625Sdim	set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL);
1154283625Sdim}
1155283625Sdimstatic
1156283625SdimDECL_CMD_FUNC(set80211noacm, ac, d)
1157283625Sdim{
1158283625Sdim	set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL);
1159283625Sdim}
1160283625Sdim
1161283625Sdimstatic
1162283625SdimDECL_CMD_FUNC(set80211ackpolicy, ac, d)
1163283625Sdim{
1164283625Sdim	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL);
1165283625Sdim}
1166283625Sdimstatic
1167283625SdimDECL_CMD_FUNC(set80211noackpolicy, ac, d)
1168283625Sdim{
1169283625Sdim	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL);
1170283625Sdim}
1171283625Sdim
1172283625Sdimstatic
1173283625SdimDECL_CMD_FUNC2(set80211bsscwmin, ac, val)
1174283625Sdim{
1175283625Sdim	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
1176283625Sdim		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1177283625Sdim}
1178283625Sdim
1179283625Sdimstatic
1180283625SdimDECL_CMD_FUNC2(set80211bsscwmax, ac, val)
1181283625Sdim{
1182283625Sdim	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
1183283625Sdim		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1184283625Sdim}
1185283625Sdim
1186283625Sdimstatic
1187283625SdimDECL_CMD_FUNC2(set80211bssaifs, ac, val)
1188283625Sdim{
1189283625Sdim	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
1190283625Sdim		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1191296417Sdim}
1192296417Sdim
1193283625Sdimstatic
1194283625SdimDECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
1195283625Sdim{
1196283625Sdim	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
1197283625Sdim		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1198283625Sdim}
1199283625Sdim
1200283625Sdimstatic
1201283625SdimDECL_CMD_FUNC(set80211dtimperiod, val, d)
1202283625Sdim{
1203283625Sdim	set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
1204283625Sdim}
1205283625Sdim
1206283625Sdimstatic
1207283625SdimDECL_CMD_FUNC(set80211bintval, val, d)
1208296417Sdim{
1209283625Sdim	set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
1210283625Sdim}
1211283625Sdim
1212296417Sdimstatic void
1213283625Sdimset80211macmac(int s, int op, const char *val)
1214283625Sdim{
1215283625Sdim	char *temp;
1216283625Sdim	struct sockaddr_dl sdl;
1217283625Sdim
1218283625Sdim	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1219283625Sdim	if (temp == NULL)
1220283625Sdim		errx(1, "malloc failed");
1221283625Sdim	temp[0] = ':';
1222283625Sdim	strcpy(temp + 1, val);
1223283625Sdim	sdl.sdl_len = sizeof(sdl);
1224283625Sdim	link_addr(temp, &sdl);
1225283625Sdim	free(temp);
1226283625Sdim	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1227283625Sdim		errx(1, "malformed link-level address");
1228283625Sdim	set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
1229283625Sdim}
1230283625Sdim
1231283625Sdimstatic
1232283625SdimDECL_CMD_FUNC(set80211addmac, val, d)
1233283625Sdim{
1234283625Sdim	set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
1235283625Sdim}
1236283625Sdim
1237283625Sdimstatic
1238283625SdimDECL_CMD_FUNC(set80211delmac, val, d)
1239283625Sdim{
1240283625Sdim	set80211macmac(s, IEEE80211_IOC_DELMAC, val);
1241283625Sdim}
1242283625Sdim
1243283625Sdimstatic
1244283625SdimDECL_CMD_FUNC(set80211kickmac, val, d)
1245283625Sdim{
1246283625Sdim	char *temp;
1247283625Sdim	struct sockaddr_dl sdl;
1248283625Sdim	struct ieee80211req_mlme mlme;
1249283625Sdim
1250283625Sdim	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1251283625Sdim	if (temp == NULL)
1252283625Sdim		errx(1, "malloc failed");
1253283625Sdim	temp[0] = ':';
1254283625Sdim	strcpy(temp + 1, val);
1255283625Sdim	sdl.sdl_len = sizeof(sdl);
1256283625Sdim	link_addr(temp, &sdl);
1257283625Sdim	free(temp);
1258283625Sdim	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1259283625Sdim		errx(1, "malformed link-level address");
1260283625Sdim	memset(&mlme, 0, sizeof(mlme));
1261283625Sdim	mlme.im_op = IEEE80211_MLME_DEAUTH;
1262283625Sdim	mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE;
1263283625Sdim	memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN);
1264283625Sdim	set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), &mlme);
1265283625Sdim}
1266283625Sdim
1267283625Sdimstatic
1268283625SdimDECL_CMD_FUNC(set80211maccmd, val, d)
1269283625Sdim{
1270283625Sdim	set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
1271283625Sdim}
1272283625Sdim
1273283625Sdimstatic void
1274283625Sdimset80211meshrtmac(int s, int req, const char *val)
1275283625Sdim{
1276283625Sdim	char *temp;
1277283625Sdim	struct sockaddr_dl sdl;
1278283625Sdim
1279283625Sdim	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1280283625Sdim	if (temp == NULL)
1281283625Sdim		errx(1, "malloc failed");
1282283625Sdim	temp[0] = ':';
1283283625Sdim	strcpy(temp + 1, val);
1284283625Sdim	sdl.sdl_len = sizeof(sdl);
1285283625Sdim	link_addr(temp, &sdl);
1286283625Sdim	free(temp);
1287283625Sdim	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1288283625Sdim		errx(1, "malformed link-level address");
1289283625Sdim	set80211(s, IEEE80211_IOC_MESH_RTCMD, req,
1290283625Sdim	    IEEE80211_ADDR_LEN, LLADDR(&sdl));
1291283625Sdim}
1292283625Sdim
1293283625Sdimstatic
1294283625SdimDECL_CMD_FUNC(set80211addmeshrt, val, d)
1295283625Sdim{
1296296417Sdim	set80211meshrtmac(s, IEEE80211_MESH_RTCMD_ADD, val);
1297283625Sdim}
1298283625Sdim
1299296417Sdimstatic
1300283625SdimDECL_CMD_FUNC(set80211delmeshrt, val, d)
1301283625Sdim{
1302283625Sdim	set80211meshrtmac(s, IEEE80211_MESH_RTCMD_DELETE, val);
1303283625Sdim}
1304283625Sdim
1305283625Sdimstatic
1306283625SdimDECL_CMD_FUNC(set80211meshrtcmd, val, d)
1307283625Sdim{
1308	set80211(s, IEEE80211_IOC_MESH_RTCMD, d, 0, NULL);
1309}
1310
1311static
1312DECL_CMD_FUNC(set80211hwmprootmode, val, d)
1313{
1314	int mode;
1315
1316	if (strcasecmp(val, "normal") == 0)
1317		mode = IEEE80211_HWMP_ROOTMODE_NORMAL;
1318	else if (strcasecmp(val, "proactive") == 0)
1319		mode = IEEE80211_HWMP_ROOTMODE_PROACTIVE;
1320	else if (strcasecmp(val, "rann") == 0)
1321		mode = IEEE80211_HWMP_ROOTMODE_RANN;
1322	else
1323		mode = IEEE80211_HWMP_ROOTMODE_DISABLED;
1324	set80211(s, IEEE80211_IOC_HWMP_ROOTMODE, mode, 0, NULL);
1325}
1326
1327static
1328DECL_CMD_FUNC(set80211hwmpmaxhops, val, d)
1329{
1330	set80211(s, IEEE80211_IOC_HWMP_MAXHOPS, atoi(val), 0, NULL);
1331}
1332
1333static void
1334set80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
1335{
1336	set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
1337}
1338
1339static void
1340set80211quiet(const char *val, int d, int s, const struct afswtch *rafp)
1341{
1342	set80211(s, IEEE80211_IOC_QUIET, d, 0, NULL);
1343}
1344
1345static
1346DECL_CMD_FUNC(set80211quietperiod, val, d)
1347{
1348	set80211(s, IEEE80211_IOC_QUIET_PERIOD, atoi(val), 0, NULL);
1349}
1350
1351static
1352DECL_CMD_FUNC(set80211quietcount, val, d)
1353{
1354	set80211(s, IEEE80211_IOC_QUIET_COUNT, atoi(val), 0, NULL);
1355}
1356
1357static
1358DECL_CMD_FUNC(set80211quietduration, val, d)
1359{
1360	set80211(s, IEEE80211_IOC_QUIET_DUR, atoi(val), 0, NULL);
1361}
1362
1363static
1364DECL_CMD_FUNC(set80211quietoffset, val, d)
1365{
1366	set80211(s, IEEE80211_IOC_QUIET_OFFSET, atoi(val), 0, NULL);
1367}
1368
1369static void
1370set80211bgscan(const char *val, int d, int s, const struct afswtch *rafp)
1371{
1372	set80211(s, IEEE80211_IOC_BGSCAN, d, 0, NULL);
1373}
1374
1375static
1376DECL_CMD_FUNC(set80211bgscanidle, val, d)
1377{
1378	set80211(s, IEEE80211_IOC_BGSCAN_IDLE, atoi(val), 0, NULL);
1379}
1380
1381static
1382DECL_CMD_FUNC(set80211bgscanintvl, val, d)
1383{
1384	set80211(s, IEEE80211_IOC_BGSCAN_INTERVAL, atoi(val), 0, NULL);
1385}
1386
1387static
1388DECL_CMD_FUNC(set80211scanvalid, val, d)
1389{
1390	set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL);
1391}
1392
1393/*
1394 * Parse an optional trailing specification of which netbands
1395 * to apply a parameter to.  This is basically the same syntax
1396 * as used for channels but you can concatenate to specify
1397 * multiple.  For example:
1398 *	14:abg		apply to 11a, 11b, and 11g
1399 *	6:ht		apply to 11na and 11ng
1400 * We don't make a big effort to catch silly things; this is
1401 * really a convenience mechanism.
1402 */
1403static int
1404getmodeflags(const char *val)
1405{
1406	const char *cp;
1407	int flags;
1408
1409	flags = 0;
1410
1411	cp = strchr(val, ':');
1412	if (cp != NULL) {
1413		for (cp++; isalpha((int) *cp); cp++) {
1414			/* accept mixed case */
1415			int c = *cp;
1416			if (isupper(c))
1417				c = tolower(c);
1418			switch (c) {
1419			case 'a':		/* 802.11a */
1420				flags |= IEEE80211_CHAN_A;
1421				break;
1422			case 'b':		/* 802.11b */
1423				flags |= IEEE80211_CHAN_B;
1424				break;
1425			case 'g':		/* 802.11g */
1426				flags |= IEEE80211_CHAN_G;
1427				break;
1428			case 'n':		/* 802.11n */
1429				flags |= IEEE80211_CHAN_HT;
1430				break;
1431			case 'd':		/* dt = Atheros Dynamic Turbo */
1432				flags |= IEEE80211_CHAN_TURBO;
1433				break;
1434			case 't':		/* ht, dt, st, t */
1435				/* dt and unadorned t specify Dynamic Turbo */
1436				if ((flags & (IEEE80211_CHAN_STURBO|IEEE80211_CHAN_HT)) == 0)
1437					flags |= IEEE80211_CHAN_TURBO;
1438				break;
1439			case 's':		/* st = Atheros Static Turbo */
1440				flags |= IEEE80211_CHAN_STURBO;
1441				break;
1442			case 'h':		/* 1/2-width channels */
1443				flags |= IEEE80211_CHAN_HALF;
1444				break;
1445			case 'q':		/* 1/4-width channels */
1446				flags |= IEEE80211_CHAN_QUARTER;
1447				break;
1448			default:
1449				errx(-1, "%s: Invalid mode attribute %c\n",
1450				    val, *cp);
1451			}
1452		}
1453	}
1454	return flags;
1455}
1456
1457#define	_APPLY(_flags, _base, _param, _v) do {				\
1458    if (_flags & IEEE80211_CHAN_HT) {					\
1459	    if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1460		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1461		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1462	    } else if (_flags & IEEE80211_CHAN_5GHZ)			\
1463		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1464	    else							\
1465		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1466    }									\
1467    if (_flags & IEEE80211_CHAN_TURBO) {				\
1468	    if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1469		    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;	\
1470		    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;	\
1471	    } else if (_flags & IEEE80211_CHAN_5GHZ)			\
1472		    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;	\
1473	    else							\
1474		    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;	\
1475    }									\
1476    if (_flags & IEEE80211_CHAN_STURBO)					\
1477	    _base.params[IEEE80211_MODE_STURBO_A]._param = _v;		\
1478    if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)		\
1479	    _base.params[IEEE80211_MODE_11A]._param = _v;		\
1480    if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)		\
1481	    _base.params[IEEE80211_MODE_11G]._param = _v;		\
1482    if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)		\
1483	    _base.params[IEEE80211_MODE_11B]._param = _v;		\
1484    if (_flags & IEEE80211_CHAN_HALF)					\
1485	    _base.params[IEEE80211_MODE_HALF]._param = _v;		\
1486    if (_flags & IEEE80211_CHAN_QUARTER)				\
1487	    _base.params[IEEE80211_MODE_QUARTER]._param = _v;		\
1488} while (0)
1489#define	_APPLY1(_flags, _base, _param, _v) do {				\
1490    if (_flags & IEEE80211_CHAN_HT) {					\
1491	    if (_flags & IEEE80211_CHAN_5GHZ)				\
1492		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1493	    else							\
1494		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1495    } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A)	\
1496	    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;		\
1497    else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G)	\
1498	    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;		\
1499    else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST)		\
1500	    _base.params[IEEE80211_MODE_STURBO_A]._param = _v;		\
1501    else if (_flags & IEEE80211_CHAN_HALF)				\
1502	    _base.params[IEEE80211_MODE_HALF]._param = _v;		\
1503    else if (_flags & IEEE80211_CHAN_QUARTER)				\
1504	    _base.params[IEEE80211_MODE_QUARTER]._param = _v;		\
1505    else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)		\
1506	    _base.params[IEEE80211_MODE_11A]._param = _v;		\
1507    else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)		\
1508	    _base.params[IEEE80211_MODE_11G]._param = _v;		\
1509    else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)		\
1510	    _base.params[IEEE80211_MODE_11B]._param = _v;		\
1511} while (0)
1512#define	_APPLY_RATE(_flags, _base, _param, _v) do {			\
1513    if (_flags & IEEE80211_CHAN_HT) {					\
1514	(_v) = (_v / 2) | IEEE80211_RATE_MCS;				\
1515    }									\
1516    _APPLY(_flags, _base, _param, _v);					\
1517} while (0)
1518#define	_APPLY_RATE1(_flags, _base, _param, _v) do {			\
1519    if (_flags & IEEE80211_CHAN_HT) {					\
1520	(_v) = (_v / 2) | IEEE80211_RATE_MCS;				\
1521    }									\
1522    _APPLY1(_flags, _base, _param, _v);					\
1523} while (0)
1524
1525static
1526DECL_CMD_FUNC(set80211roamrssi, val, d)
1527{
1528	double v = atof(val);
1529	int rssi, flags;
1530
1531	rssi = (int) (2*v);
1532	if (rssi != 2*v)
1533		errx(-1, "invalid rssi (must be .5 dBm units)");
1534	flags = getmodeflags(val);
1535	getroam(s);
1536	if (flags == 0) {		/* NB: no flags => current channel */
1537		flags = getcurchan(s)->ic_flags;
1538		_APPLY1(flags, roamparams, rssi, rssi);
1539	} else
1540		_APPLY(flags, roamparams, rssi, rssi);
1541	callback_register(setroam_cb, &roamparams);
1542}
1543
1544static int
1545getrate(const char *val, const char *tag)
1546{
1547	double v = atof(val);
1548	int rate;
1549
1550	rate = (int) (2*v);
1551	if (rate != 2*v)
1552		errx(-1, "invalid %s rate (must be .5 Mb/s units)", tag);
1553	return rate;		/* NB: returns 2x the specified value */
1554}
1555
1556static
1557DECL_CMD_FUNC(set80211roamrate, val, d)
1558{
1559	int rate, flags;
1560
1561	rate = getrate(val, "roam");
1562	flags = getmodeflags(val);
1563	getroam(s);
1564	if (flags == 0) {		/* NB: no flags => current channel */
1565		flags = getcurchan(s)->ic_flags;
1566		_APPLY_RATE1(flags, roamparams, rate, rate);
1567	} else
1568		_APPLY_RATE(flags, roamparams, rate, rate);
1569	callback_register(setroam_cb, &roamparams);
1570}
1571
1572static
1573DECL_CMD_FUNC(set80211mcastrate, val, d)
1574{
1575	int rate, flags;
1576
1577	rate = getrate(val, "mcast");
1578	flags = getmodeflags(val);
1579	gettxparams(s);
1580	if (flags == 0) {		/* NB: no flags => current channel */
1581		flags = getcurchan(s)->ic_flags;
1582		_APPLY_RATE1(flags, txparams, mcastrate, rate);
1583	} else
1584		_APPLY_RATE(flags, txparams, mcastrate, rate);
1585	callback_register(settxparams_cb, &txparams);
1586}
1587
1588static
1589DECL_CMD_FUNC(set80211mgtrate, val, d)
1590{
1591	int rate, flags;
1592
1593	rate = getrate(val, "mgmt");
1594	flags = getmodeflags(val);
1595	gettxparams(s);
1596	if (flags == 0) {		/* NB: no flags => current channel */
1597		flags = getcurchan(s)->ic_flags;
1598		_APPLY_RATE1(flags, txparams, mgmtrate, rate);
1599	} else
1600		_APPLY_RATE(flags, txparams, mgmtrate, rate);
1601	callback_register(settxparams_cb, &txparams);
1602}
1603
1604static
1605DECL_CMD_FUNC(set80211ucastrate, val, d)
1606{
1607	int flags;
1608
1609	gettxparams(s);
1610	flags = getmodeflags(val);
1611	if (isanyarg(val)) {
1612		if (flags == 0) {	/* NB: no flags => current channel */
1613			flags = getcurchan(s)->ic_flags;
1614			_APPLY1(flags, txparams, ucastrate,
1615			    IEEE80211_FIXED_RATE_NONE);
1616		} else
1617			_APPLY(flags, txparams, ucastrate,
1618			    IEEE80211_FIXED_RATE_NONE);
1619	} else {
1620		int rate = getrate(val, "ucast");
1621		if (flags == 0) {	/* NB: no flags => current channel */
1622			flags = getcurchan(s)->ic_flags;
1623			_APPLY_RATE1(flags, txparams, ucastrate, rate);
1624		} else
1625			_APPLY_RATE(flags, txparams, ucastrate, rate);
1626	}
1627	callback_register(settxparams_cb, &txparams);
1628}
1629
1630static
1631DECL_CMD_FUNC(set80211maxretry, val, d)
1632{
1633	int v = atoi(val), flags;
1634
1635	flags = getmodeflags(val);
1636	gettxparams(s);
1637	if (flags == 0) {		/* NB: no flags => current channel */
1638		flags = getcurchan(s)->ic_flags;
1639		_APPLY1(flags, txparams, maxretry, v);
1640	} else
1641		_APPLY(flags, txparams, maxretry, v);
1642	callback_register(settxparams_cb, &txparams);
1643}
1644#undef _APPLY_RATE
1645#undef _APPLY
1646
1647static
1648DECL_CMD_FUNC(set80211fragthreshold, val, d)
1649{
1650	set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
1651		isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
1652}
1653
1654static
1655DECL_CMD_FUNC(set80211bmissthreshold, val, d)
1656{
1657	set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
1658		isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
1659}
1660
1661static void
1662set80211burst(const char *val, int d, int s, const struct afswtch *rafp)
1663{
1664	set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
1665}
1666
1667static void
1668set80211doth(const char *val, int d, int s, const struct afswtch *rafp)
1669{
1670	set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL);
1671}
1672
1673static void
1674set80211dfs(const char *val, int d, int s, const struct afswtch *rafp)
1675{
1676	set80211(s, IEEE80211_IOC_DFS, d, 0, NULL);
1677}
1678
1679static void
1680set80211shortgi(const char *val, int d, int s, const struct afswtch *rafp)
1681{
1682	set80211(s, IEEE80211_IOC_SHORTGI,
1683		d ? (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) : 0,
1684		0, NULL);
1685}
1686
1687static void
1688set80211ampdu(const char *val, int d, int s, const struct afswtch *rafp)
1689{
1690	int ampdu;
1691
1692	if (get80211val(s, IEEE80211_IOC_AMPDU, &ampdu) < 0)
1693		errx(-1, "cannot get AMPDU setting");
1694	if (d < 0) {
1695		d = -d;
1696		ampdu &= ~d;
1697	} else
1698		ampdu |= d;
1699	set80211(s, IEEE80211_IOC_AMPDU, ampdu, 0, NULL);
1700}
1701
1702static
1703DECL_CMD_FUNC(set80211ampdulimit, val, d)
1704{
1705	int v;
1706
1707	switch (atoi(val)) {
1708	case 8:
1709	case 8*1024:
1710		v = IEEE80211_HTCAP_MAXRXAMPDU_8K;
1711		break;
1712	case 16:
1713	case 16*1024:
1714		v = IEEE80211_HTCAP_MAXRXAMPDU_16K;
1715		break;
1716	case 32:
1717	case 32*1024:
1718		v = IEEE80211_HTCAP_MAXRXAMPDU_32K;
1719		break;
1720	case 64:
1721	case 64*1024:
1722		v = IEEE80211_HTCAP_MAXRXAMPDU_64K;
1723		break;
1724	default:
1725		errx(-1, "invalid A-MPDU limit %s", val);
1726	}
1727	set80211(s, IEEE80211_IOC_AMPDU_LIMIT, v, 0, NULL);
1728}
1729
1730static
1731DECL_CMD_FUNC(set80211ampdudensity, val, d)
1732{
1733	int v;
1734
1735	if (isanyarg(val) || strcasecmp(val, "na") == 0)
1736		v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1737	else switch ((int)(atof(val)*4)) {
1738	case 0:
1739		v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1740		break;
1741	case 1:
1742		v = IEEE80211_HTCAP_MPDUDENSITY_025;
1743		break;
1744	case 2:
1745		v = IEEE80211_HTCAP_MPDUDENSITY_05;
1746		break;
1747	case 4:
1748		v = IEEE80211_HTCAP_MPDUDENSITY_1;
1749		break;
1750	case 8:
1751		v = IEEE80211_HTCAP_MPDUDENSITY_2;
1752		break;
1753	case 16:
1754		v = IEEE80211_HTCAP_MPDUDENSITY_4;
1755		break;
1756	case 32:
1757		v = IEEE80211_HTCAP_MPDUDENSITY_8;
1758		break;
1759	case 64:
1760		v = IEEE80211_HTCAP_MPDUDENSITY_16;
1761		break;
1762	default:
1763		errx(-1, "invalid A-MPDU density %s", val);
1764	}
1765	set80211(s, IEEE80211_IOC_AMPDU_DENSITY, v, 0, NULL);
1766}
1767
1768static void
1769set80211amsdu(const char *val, int d, int s, const struct afswtch *rafp)
1770{
1771	int amsdu;
1772
1773	if (get80211val(s, IEEE80211_IOC_AMSDU, &amsdu) < 0)
1774		err(-1, "cannot get AMSDU setting");
1775	if (d < 0) {
1776		d = -d;
1777		amsdu &= ~d;
1778	} else
1779		amsdu |= d;
1780	set80211(s, IEEE80211_IOC_AMSDU, amsdu, 0, NULL);
1781}
1782
1783static
1784DECL_CMD_FUNC(set80211amsdulimit, val, d)
1785{
1786	set80211(s, IEEE80211_IOC_AMSDU_LIMIT, atoi(val), 0, NULL);
1787}
1788
1789static void
1790set80211puren(const char *val, int d, int s, const struct afswtch *rafp)
1791{
1792	set80211(s, IEEE80211_IOC_PUREN, d, 0, NULL);
1793}
1794
1795static void
1796set80211htcompat(const char *val, int d, int s, const struct afswtch *rafp)
1797{
1798	set80211(s, IEEE80211_IOC_HTCOMPAT, d, 0, NULL);
1799}
1800
1801static void
1802set80211htconf(const char *val, int d, int s, const struct afswtch *rafp)
1803{
1804	set80211(s, IEEE80211_IOC_HTCONF, d, 0, NULL);
1805	htconf = d;
1806}
1807
1808static void
1809set80211dwds(const char *val, int d, int s, const struct afswtch *rafp)
1810{
1811	set80211(s, IEEE80211_IOC_DWDS, d, 0, NULL);
1812}
1813
1814static void
1815set80211inact(const char *val, int d, int s, const struct afswtch *rafp)
1816{
1817	set80211(s, IEEE80211_IOC_INACTIVITY, d, 0, NULL);
1818}
1819
1820static void
1821set80211tsn(const char *val, int d, int s, const struct afswtch *rafp)
1822{
1823	set80211(s, IEEE80211_IOC_TSN, d, 0, NULL);
1824}
1825
1826static void
1827set80211dotd(const char *val, int d, int s, const struct afswtch *rafp)
1828{
1829	set80211(s, IEEE80211_IOC_DOTD, d, 0, NULL);
1830}
1831
1832static void
1833set80211smps(const char *val, int d, int s, const struct afswtch *rafp)
1834{
1835	set80211(s, IEEE80211_IOC_SMPS, d, 0, NULL);
1836}
1837
1838static void
1839set80211rifs(const char *val, int d, int s, const struct afswtch *rafp)
1840{
1841	set80211(s, IEEE80211_IOC_RIFS, d, 0, NULL);
1842}
1843
1844static
1845DECL_CMD_FUNC(set80211tdmaslot, val, d)
1846{
1847	set80211(s, IEEE80211_IOC_TDMA_SLOT, atoi(val), 0, NULL);
1848}
1849
1850static
1851DECL_CMD_FUNC(set80211tdmaslotcnt, val, d)
1852{
1853	set80211(s, IEEE80211_IOC_TDMA_SLOTCNT, atoi(val), 0, NULL);
1854}
1855
1856static
1857DECL_CMD_FUNC(set80211tdmaslotlen, val, d)
1858{
1859	set80211(s, IEEE80211_IOC_TDMA_SLOTLEN, atoi(val), 0, NULL);
1860}
1861
1862static
1863DECL_CMD_FUNC(set80211tdmabintval, val, d)
1864{
1865	set80211(s, IEEE80211_IOC_TDMA_BINTERVAL, atoi(val), 0, NULL);
1866}
1867
1868static
1869DECL_CMD_FUNC(set80211meshttl, val, d)
1870{
1871	set80211(s, IEEE80211_IOC_MESH_TTL, atoi(val), 0, NULL);
1872}
1873
1874static
1875DECL_CMD_FUNC(set80211meshforward, val, d)
1876{
1877	set80211(s, IEEE80211_IOC_MESH_FWRD, d, 0, NULL);
1878}
1879
1880static
1881DECL_CMD_FUNC(set80211meshgate, val, d)
1882{
1883	set80211(s, IEEE80211_IOC_MESH_GATE, d, 0, NULL);
1884}
1885
1886static
1887DECL_CMD_FUNC(set80211meshpeering, val, d)
1888{
1889	set80211(s, IEEE80211_IOC_MESH_AP, d, 0, NULL);
1890}
1891
1892static
1893DECL_CMD_FUNC(set80211meshmetric, val, d)
1894{
1895	char v[12];
1896
1897	memcpy(v, val, sizeof(v));
1898	set80211(s, IEEE80211_IOC_MESH_PR_METRIC, 0, 0, v);
1899}
1900
1901static
1902DECL_CMD_FUNC(set80211meshpath, val, d)
1903{
1904	char v[12];
1905
1906	memcpy(v, val, sizeof(v));
1907	set80211(s, IEEE80211_IOC_MESH_PR_PATH, 0, 0, v);
1908}
1909
1910static int
1911regdomain_sort(const void *a, const void *b)
1912{
1913#define	CHAN_ALL \
1914	(IEEE80211_CHAN_ALLTURBO|IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)
1915	const struct ieee80211_channel *ca = a;
1916	const struct ieee80211_channel *cb = b;
1917
1918	return ca->ic_freq == cb->ic_freq ?
1919	    (ca->ic_flags & CHAN_ALL) - (cb->ic_flags & CHAN_ALL) :
1920	    ca->ic_freq - cb->ic_freq;
1921#undef CHAN_ALL
1922}
1923
1924static const struct ieee80211_channel *
1925chanlookup(const struct ieee80211_channel chans[], int nchans,
1926	int freq, int flags)
1927{
1928	int i;
1929
1930	flags &= IEEE80211_CHAN_ALLTURBO;
1931	for (i = 0; i < nchans; i++) {
1932		const struct ieee80211_channel *c = &chans[i];
1933		if (c->ic_freq == freq &&
1934		    (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
1935			return c;
1936	}
1937	return NULL;
1938}
1939
1940static int
1941chanfind(const struct ieee80211_channel chans[], int nchans, int flags)
1942{
1943	int i;
1944
1945	for (i = 0; i < nchans; i++) {
1946		const struct ieee80211_channel *c = &chans[i];
1947		if ((c->ic_flags & flags) == flags)
1948			return 1;
1949	}
1950	return 0;
1951}
1952
1953/*
1954 * Check channel compatibility.
1955 */
1956static int
1957checkchan(const struct ieee80211req_chaninfo *avail, int freq, int flags)
1958{
1959	flags &= ~REQ_FLAGS;
1960	/*
1961	 * Check if exact channel is in the calibration table;
1962	 * everything below is to deal with channels that we
1963	 * want to include but that are not explicitly listed.
1964	 */
1965	if (flags & IEEE80211_CHAN_HT40) {
1966		/* NB: we use an HT40 channel center that matches HT20 */
1967		flags = (flags &~ IEEE80211_CHAN_HT40) | IEEE80211_CHAN_HT20;
1968	}
1969	if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, flags) != NULL)
1970		return 1;
1971	if (flags & IEEE80211_CHAN_GSM) {
1972		/*
1973		 * XXX GSM frequency mapping is handled in the kernel
1974		 * so we cannot find them in the calibration table;
1975		 * just accept the channel and the kernel will reject
1976		 * the channel list if it's wrong.
1977		 */
1978		return 1;
1979	}
1980	/*
1981	 * If this is a 1/2 or 1/4 width channel allow it if a full
1982	 * width channel is present for this frequency, and the device
1983	 * supports fractional channels on this band.  This is a hack
1984	 * that avoids bloating the calibration table; it may be better
1985	 * by per-band attributes though (we are effectively calculating
1986	 * this attribute by scanning the channel list ourself).
1987	 */
1988	if ((flags & (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) == 0)
1989		return 0;
1990	if (chanlookup(avail->ic_chans, avail->ic_nchans, freq,
1991	    flags &~ (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) == NULL)
1992		return 0;
1993	if (flags & IEEE80211_CHAN_HALF) {
1994		return chanfind(avail->ic_chans, avail->ic_nchans,
1995		    IEEE80211_CHAN_HALF |
1996		       (flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
1997	} else {
1998		return chanfind(avail->ic_chans, avail->ic_nchans,
1999		    IEEE80211_CHAN_QUARTER |
2000			(flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
2001	}
2002}
2003
2004static void
2005regdomain_addchans(struct ieee80211req_chaninfo *ci,
2006	const netband_head *bands,
2007	const struct ieee80211_regdomain *reg,
2008	uint32_t chanFlags,
2009	const struct ieee80211req_chaninfo *avail)
2010{
2011	const struct netband *nb;
2012	const struct freqband *b;
2013	struct ieee80211_channel *c, *prev;
2014	int freq, hi_adj, lo_adj, channelSep;
2015	uint32_t flags;
2016
2017	hi_adj = (chanFlags & IEEE80211_CHAN_HT40U) ? -20 : 0;
2018	lo_adj = (chanFlags & IEEE80211_CHAN_HT40D) ? 20 : 0;
2019	channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40;
2020	LIST_FOREACH(nb, bands, next) {
2021		b = nb->band;
2022		if (verbose) {
2023			printf("%s:", __func__);
2024			printb(" chanFlags", chanFlags, IEEE80211_CHAN_BITS);
2025			printb(" bandFlags", nb->flags | b->flags,
2026			    IEEE80211_CHAN_BITS);
2027			putchar('\n');
2028		}
2029		prev = NULL;
2030		for (freq = b->freqStart + lo_adj;
2031		     freq <= b->freqEnd + hi_adj; freq += b->chanSep) {
2032			/*
2033			 * Construct flags for the new channel.  We take
2034			 * the attributes from the band descriptions except
2035			 * for HT40 which is enabled generically (i.e. +/-
2036			 * extension channel) in the band description and
2037			 * then constrained according by channel separation.
2038			 */
2039			flags = nb->flags | b->flags;
2040			if (flags & IEEE80211_CHAN_HT) {
2041				/*
2042				 * HT channels are generated specially; we're
2043				 * called to add HT20, HT40+, and HT40- chan's
2044				 * so we need to expand only band specs for
2045				 * the HT channel type being added.
2046				 */
2047				if ((chanFlags & IEEE80211_CHAN_HT20) &&
2048				    (flags & IEEE80211_CHAN_HT20) == 0) {
2049					if (verbose)
2050						printf("%u: skip, not an "
2051						    "HT20 channel\n", freq);
2052					continue;
2053				}
2054				if ((chanFlags & IEEE80211_CHAN_HT40) &&
2055				    (flags & IEEE80211_CHAN_HT40) == 0) {
2056					if (verbose)
2057						printf("%u: skip, not an "
2058						    "HT40 channel\n", freq);
2059					continue;
2060				}
2061				/* NB: HT attribute comes from caller */
2062				flags &= ~IEEE80211_CHAN_HT;
2063				flags |= chanFlags & IEEE80211_CHAN_HT;
2064			}
2065			/*
2066			 * Check if device can operate on this frequency.
2067			 */
2068			if (!checkchan(avail, freq, flags)) {
2069				if (verbose) {
2070					printf("%u: skip, ", freq);
2071					printb("flags", flags,
2072					    IEEE80211_CHAN_BITS);
2073					printf(" not available\n");
2074				}
2075				continue;
2076			}
2077			if ((flags & REQ_ECM) && !reg->ecm) {
2078				if (verbose)
2079					printf("%u: skip, ECM channel\n", freq);
2080				continue;
2081			}
2082			if ((flags & REQ_INDOOR) && reg->location == 'O') {
2083				if (verbose)
2084					printf("%u: skip, indoor channel\n",
2085					    freq);
2086				continue;
2087			}
2088			if ((flags & REQ_OUTDOOR) && reg->location == 'I') {
2089				if (verbose)
2090					printf("%u: skip, outdoor channel\n",
2091					    freq);
2092				continue;
2093			}
2094			if ((flags & IEEE80211_CHAN_HT40) &&
2095			    prev != NULL && (freq - prev->ic_freq) < channelSep) {
2096				if (verbose)
2097					printf("%u: skip, only %u channel "
2098					    "separation, need %d\n", freq,
2099					    freq - prev->ic_freq, channelSep);
2100				continue;
2101			}
2102			if (ci->ic_nchans == IEEE80211_CHAN_MAX) {
2103				if (verbose)
2104					printf("%u: skip, channel table full\n",
2105					    freq);
2106				break;
2107			}
2108			c = &ci->ic_chans[ci->ic_nchans++];
2109			memset(c, 0, sizeof(*c));
2110			c->ic_freq = freq;
2111			c->ic_flags = flags;
2112			if (c->ic_flags & IEEE80211_CHAN_DFS)
2113				c->ic_maxregpower = nb->maxPowerDFS;
2114			else
2115				c->ic_maxregpower = nb->maxPower;
2116			if (verbose) {
2117				printf("[%3d] add freq %u ",
2118				    ci->ic_nchans-1, c->ic_freq);
2119				printb("flags", c->ic_flags, IEEE80211_CHAN_BITS);
2120				printf(" power %u\n", c->ic_maxregpower);
2121			}
2122			/* NB: kernel fills in other fields */
2123			prev = c;
2124		}
2125	}
2126}
2127
2128static void
2129regdomain_makechannels(
2130	struct ieee80211_regdomain_req *req,
2131	const struct ieee80211_devcaps_req *dc)
2132{
2133	struct regdata *rdp = getregdata();
2134	const struct country *cc;
2135	const struct ieee80211_regdomain *reg = &req->rd;
2136	struct ieee80211req_chaninfo *ci = &req->chaninfo;
2137	const struct regdomain *rd;
2138
2139	/*
2140	 * Locate construction table for new channel list.  We treat
2141	 * the regdomain/SKU as definitive so a country can be in
2142	 * multiple with different properties (e.g. US in FCC+FCC3).
2143	 * If no regdomain is specified then we fallback on the country
2144	 * code to find the associated regdomain since countries always
2145	 * belong to at least one regdomain.
2146	 */
2147	if (reg->regdomain == 0) {
2148		cc = lib80211_country_findbycc(rdp, reg->country);
2149		if (cc == NULL)
2150			errx(1, "internal error, country %d not found",
2151			    reg->country);
2152		rd = cc->rd;
2153	} else
2154		rd = lib80211_regdomain_findbysku(rdp, reg->regdomain);
2155	if (rd == NULL)
2156		errx(1, "internal error, regdomain %d not found",
2157			    reg->regdomain);
2158	if (rd->sku != SKU_DEBUG) {
2159		/*
2160		 * regdomain_addchans incrememnts the channel count for
2161		 * each channel it adds so initialize ic_nchans to zero.
2162		 * Note that we know we have enough space to hold all possible
2163		 * channels because the devcaps list size was used to
2164		 * allocate our request.
2165		 */
2166		ci->ic_nchans = 0;
2167		if (!LIST_EMPTY(&rd->bands_11b))
2168			regdomain_addchans(ci, &rd->bands_11b, reg,
2169			    IEEE80211_CHAN_B, &dc->dc_chaninfo);
2170		if (!LIST_EMPTY(&rd->bands_11g))
2171			regdomain_addchans(ci, &rd->bands_11g, reg,
2172			    IEEE80211_CHAN_G, &dc->dc_chaninfo);
2173		if (!LIST_EMPTY(&rd->bands_11a))
2174			regdomain_addchans(ci, &rd->bands_11a, reg,
2175			    IEEE80211_CHAN_A, &dc->dc_chaninfo);
2176		if (!LIST_EMPTY(&rd->bands_11na) && dc->dc_htcaps != 0) {
2177			regdomain_addchans(ci, &rd->bands_11na, reg,
2178			    IEEE80211_CHAN_A | IEEE80211_CHAN_HT20,
2179			    &dc->dc_chaninfo);
2180			if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
2181				regdomain_addchans(ci, &rd->bands_11na, reg,
2182				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U,
2183				    &dc->dc_chaninfo);
2184				regdomain_addchans(ci, &rd->bands_11na, reg,
2185				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D,
2186				    &dc->dc_chaninfo);
2187			}
2188		}
2189		if (!LIST_EMPTY(&rd->bands_11ng) && dc->dc_htcaps != 0) {
2190			regdomain_addchans(ci, &rd->bands_11ng, reg,
2191			    IEEE80211_CHAN_G | IEEE80211_CHAN_HT20,
2192			    &dc->dc_chaninfo);
2193			if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
2194				regdomain_addchans(ci, &rd->bands_11ng, reg,
2195				    IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U,
2196				    &dc->dc_chaninfo);
2197				regdomain_addchans(ci, &rd->bands_11ng, reg,
2198				    IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D,
2199				    &dc->dc_chaninfo);
2200			}
2201		}
2202		qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]),
2203		    regdomain_sort);
2204	} else
2205		memcpy(ci, &dc->dc_chaninfo,
2206		    IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
2207}
2208
2209static void
2210list_countries(void)
2211{
2212	struct regdata *rdp = getregdata();
2213	const struct country *cp;
2214	const struct regdomain *dp;
2215	int i;
2216
2217	i = 0;
2218	printf("\nCountry codes:\n");
2219	LIST_FOREACH(cp, &rdp->countries, next) {
2220		printf("%2s %-15.15s%s", cp->isoname,
2221		    cp->name, ((i+1)%4) == 0 ? "\n" : " ");
2222		i++;
2223	}
2224	i = 0;
2225	printf("\nRegulatory domains:\n");
2226	LIST_FOREACH(dp, &rdp->domains, next) {
2227		printf("%-15.15s%s", dp->name, ((i+1)%4) == 0 ? "\n" : " ");
2228		i++;
2229	}
2230	printf("\n");
2231}
2232
2233static void
2234defaultcountry(const struct regdomain *rd)
2235{
2236	struct regdata *rdp = getregdata();
2237	const struct country *cc;
2238
2239	cc = lib80211_country_findbycc(rdp, rd->cc->code);
2240	if (cc == NULL)
2241		errx(1, "internal error, ISO country code %d not "
2242		    "defined for regdomain %s", rd->cc->code, rd->name);
2243	regdomain.country = cc->code;
2244	regdomain.isocc[0] = cc->isoname[0];
2245	regdomain.isocc[1] = cc->isoname[1];
2246}
2247
2248static
2249DECL_CMD_FUNC(set80211regdomain, val, d)
2250{
2251	struct regdata *rdp = getregdata();
2252	const struct regdomain *rd;
2253
2254	rd = lib80211_regdomain_findbyname(rdp, val);
2255	if (rd == NULL) {
2256		char *eptr;
2257		long sku = strtol(val, &eptr, 0);
2258
2259		if (eptr != val)
2260			rd = lib80211_regdomain_findbysku(rdp, sku);
2261		if (eptr == val || rd == NULL)
2262			errx(1, "unknown regdomain %s", val);
2263	}
2264	getregdomain(s);
2265	regdomain.regdomain = rd->sku;
2266	if (regdomain.country == 0 && rd->cc != NULL) {
2267		/*
2268		 * No country code setup and there's a default
2269		 * one for this regdomain fill it in.
2270		 */
2271		defaultcountry(rd);
2272	}
2273	callback_register(setregdomain_cb, &regdomain);
2274}
2275
2276static
2277DECL_CMD_FUNC(set80211country, val, d)
2278{
2279	struct regdata *rdp = getregdata();
2280	const struct country *cc;
2281
2282	cc = lib80211_country_findbyname(rdp, val);
2283	if (cc == NULL) {
2284		char *eptr;
2285		long code = strtol(val, &eptr, 0);
2286
2287		if (eptr != val)
2288			cc = lib80211_country_findbycc(rdp, code);
2289		if (eptr == val || cc == NULL)
2290			errx(1, "unknown ISO country code %s", val);
2291	}
2292	getregdomain(s);
2293	regdomain.regdomain = cc->rd->sku;
2294	regdomain.country = cc->code;
2295	regdomain.isocc[0] = cc->isoname[0];
2296	regdomain.isocc[1] = cc->isoname[1];
2297	callback_register(setregdomain_cb, &regdomain);
2298}
2299
2300static void
2301set80211location(const char *val, int d, int s, const struct afswtch *rafp)
2302{
2303	getregdomain(s);
2304	regdomain.location = d;
2305	callback_register(setregdomain_cb, &regdomain);
2306}
2307
2308static void
2309set80211ecm(const char *val, int d, int s, const struct afswtch *rafp)
2310{
2311	getregdomain(s);
2312	regdomain.ecm = d;
2313	callback_register(setregdomain_cb, &regdomain);
2314}
2315
2316static void
2317LINE_INIT(char c)
2318{
2319	spacer = c;
2320	if (c == '\t')
2321		col = 8;
2322	else
2323		col = 1;
2324}
2325
2326static void
2327LINE_BREAK(void)
2328{
2329	if (spacer != '\t') {
2330		printf("\n");
2331		spacer = '\t';
2332	}
2333	col = 8;		/* 8-col tab */
2334}
2335
2336static void
2337LINE_CHECK(const char *fmt, ...)
2338{
2339	char buf[80];
2340	va_list ap;
2341	int n;
2342
2343	va_start(ap, fmt);
2344	n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
2345	va_end(ap);
2346	col += 1+n;
2347	if (col > MAXCOL) {
2348		LINE_BREAK();
2349		col += n;
2350	}
2351	buf[0] = spacer;
2352	printf("%s", buf);
2353	spacer = ' ';
2354}
2355
2356static int
2357getmaxrate(const uint8_t rates[15], uint8_t nrates)
2358{
2359	int i, maxrate = -1;
2360
2361	for (i = 0; i < nrates; i++) {
2362		int rate = rates[i] & IEEE80211_RATE_VAL;
2363		if (rate > maxrate)
2364			maxrate = rate;
2365	}
2366	return maxrate / 2;
2367}
2368
2369static const char *
2370getcaps(int capinfo)
2371{
2372	static char capstring[32];
2373	char *cp = capstring;
2374
2375	if (capinfo & IEEE80211_CAPINFO_ESS)
2376		*cp++ = 'E';
2377	if (capinfo & IEEE80211_CAPINFO_IBSS)
2378		*cp++ = 'I';
2379	if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
2380		*cp++ = 'c';
2381	if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
2382		*cp++ = 'C';
2383	if (capinfo & IEEE80211_CAPINFO_PRIVACY)
2384		*cp++ = 'P';
2385	if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
2386		*cp++ = 'S';
2387	if (capinfo & IEEE80211_CAPINFO_PBCC)
2388		*cp++ = 'B';
2389	if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
2390		*cp++ = 'A';
2391	if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
2392		*cp++ = 's';
2393	if (capinfo & IEEE80211_CAPINFO_RSN)
2394		*cp++ = 'R';
2395	if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
2396		*cp++ = 'D';
2397	*cp = '\0';
2398	return capstring;
2399}
2400
2401static const char *
2402getflags(int flags)
2403{
2404	static char flagstring[32];
2405	char *cp = flagstring;
2406
2407	if (flags & IEEE80211_NODE_AUTH)
2408		*cp++ = 'A';
2409	if (flags & IEEE80211_NODE_QOS)
2410		*cp++ = 'Q';
2411	if (flags & IEEE80211_NODE_ERP)
2412		*cp++ = 'E';
2413	if (flags & IEEE80211_NODE_PWR_MGT)
2414		*cp++ = 'P';
2415	if (flags & IEEE80211_NODE_HT) {
2416		*cp++ = 'H';
2417		if (flags & IEEE80211_NODE_HTCOMPAT)
2418			*cp++ = '+';
2419	}
2420	if (flags & IEEE80211_NODE_WPS)
2421		*cp++ = 'W';
2422	if (flags & IEEE80211_NODE_TSN)
2423		*cp++ = 'N';
2424	if (flags & IEEE80211_NODE_AMPDU_TX)
2425		*cp++ = 'T';
2426	if (flags & IEEE80211_NODE_AMPDU_RX)
2427		*cp++ = 'R';
2428	if (flags & IEEE80211_NODE_MIMO_PS) {
2429		*cp++ = 'M';
2430		if (flags & IEEE80211_NODE_MIMO_RTS)
2431			*cp++ = '+';
2432	}
2433	if (flags & IEEE80211_NODE_RIFS)
2434		*cp++ = 'I';
2435	if (flags & IEEE80211_NODE_SGI40) {
2436		*cp++ = 'S';
2437		if (flags & IEEE80211_NODE_SGI20)
2438			*cp++ = '+';
2439	} else if (flags & IEEE80211_NODE_SGI20)
2440		*cp++ = 's';
2441	if (flags & IEEE80211_NODE_AMSDU_TX)
2442		*cp++ = 't';
2443	if (flags & IEEE80211_NODE_AMSDU_RX)
2444		*cp++ = 'r';
2445	*cp = '\0';
2446	return flagstring;
2447}
2448
2449static void
2450printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
2451{
2452	printf("%s", tag);
2453	if (verbose) {
2454		maxlen -= strlen(tag)+2;
2455		if (2*ielen > maxlen)
2456			maxlen--;
2457		printf("<");
2458		for (; ielen > 0; ie++, ielen--) {
2459			if (maxlen-- <= 0)
2460				break;
2461			printf("%02x", *ie);
2462		}
2463		if (ielen != 0)
2464			printf("-");
2465		printf(">");
2466	}
2467}
2468
2469#define LE_READ_2(p)					\
2470	((u_int16_t)					\
2471	 ((((const u_int8_t *)(p))[0]      ) |		\
2472	  (((const u_int8_t *)(p))[1] <<  8)))
2473#define LE_READ_4(p)					\
2474	((u_int32_t)					\
2475	 ((((const u_int8_t *)(p))[0]      ) |		\
2476	  (((const u_int8_t *)(p))[1] <<  8) |		\
2477	  (((const u_int8_t *)(p))[2] << 16) |		\
2478	  (((const u_int8_t *)(p))[3] << 24)))
2479
2480/*
2481 * NB: The decoding routines assume a properly formatted ie
2482 *     which should be safe as the kernel only retains them
2483 *     if they parse ok.
2484 */
2485
2486static void
2487printwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2488{
2489#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
2490	static const char *acnames[] = { "BE", "BK", "VO", "VI" };
2491	const struct ieee80211_wme_param *wme =
2492	    (const struct ieee80211_wme_param *) ie;
2493	int i;
2494
2495	printf("%s", tag);
2496	if (!verbose)
2497		return;
2498	printf("<qosinfo 0x%x", wme->param_qosInfo);
2499	ie += offsetof(struct ieee80211_wme_param, params_acParams);
2500	for (i = 0; i < WME_NUM_AC; i++) {
2501		const struct ieee80211_wme_acparams *ac =
2502		    &wme->params_acParams[i];
2503
2504		printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]"
2505			, acnames[i]
2506			, MS(ac->acp_aci_aifsn, WME_PARAM_ACM) ? "acm " : ""
2507			, MS(ac->acp_aci_aifsn, WME_PARAM_AIFSN)
2508			, MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMIN)
2509			, MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMAX)
2510			, LE_READ_2(&ac->acp_txop)
2511		);
2512	}
2513	printf(">");
2514#undef MS
2515}
2516
2517static void
2518printwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2519{
2520	printf("%s", tag);
2521	if (verbose) {
2522		const struct ieee80211_wme_info *wme =
2523		    (const struct ieee80211_wme_info *) ie;
2524		printf("<version 0x%x info 0x%x>",
2525		    wme->wme_version, wme->wme_info);
2526	}
2527}
2528
2529static void
2530printhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2531{
2532	printf("%s", tag);
2533	if (verbose) {
2534		const struct ieee80211_ie_htcap *htcap =
2535		    (const struct ieee80211_ie_htcap *) ie;
2536		const char *sep;
2537		int i, j;
2538
2539		printf("<cap 0x%x param 0x%x",
2540		    LE_READ_2(&htcap->hc_cap), htcap->hc_param);
2541		printf(" mcsset[");
2542		sep = "";
2543		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2544			if (isset(htcap->hc_mcsset, i)) {
2545				for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2546					if (isclr(htcap->hc_mcsset, j))
2547						break;
2548				j--;
2549				if (i == j)
2550					printf("%s%u", sep, i);
2551				else
2552					printf("%s%u-%u", sep, i, j);
2553				i += j-i;
2554				sep = ",";
2555			}
2556		printf("] extcap 0x%x txbf 0x%x antenna 0x%x>",
2557		    LE_READ_2(&htcap->hc_extcap),
2558		    LE_READ_4(&htcap->hc_txbf),
2559		    htcap->hc_antenna);
2560	}
2561}
2562
2563static void
2564printhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2565{
2566	printf("%s", tag);
2567	if (verbose) {
2568		const struct ieee80211_ie_htinfo *htinfo =
2569		    (const struct ieee80211_ie_htinfo *) ie;
2570		const char *sep;
2571		int i, j;
2572
2573		printf("<ctl %u, %x,%x,%x,%x", htinfo->hi_ctrlchannel,
2574		    htinfo->hi_byte1, htinfo->hi_byte2, htinfo->hi_byte3,
2575		    LE_READ_2(&htinfo->hi_byte45));
2576		printf(" basicmcs[");
2577		sep = "";
2578		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2579			if (isset(htinfo->hi_basicmcsset, i)) {
2580				for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2581					if (isclr(htinfo->hi_basicmcsset, j))
2582						break;
2583				j--;
2584				if (i == j)
2585					printf("%s%u", sep, i);
2586				else
2587					printf("%s%u-%u", sep, i, j);
2588				i += j-i;
2589				sep = ",";
2590			}
2591		printf("]>");
2592	}
2593}
2594
2595static void
2596printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2597{
2598
2599	printf("%s", tag);
2600	if (verbose) {
2601		const struct ieee80211_ath_ie *ath =
2602			(const struct ieee80211_ath_ie *)ie;
2603
2604		printf("<");
2605		if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME)
2606			printf("DTURBO,");
2607		if (ath->ath_capability & ATHEROS_CAP_COMPRESSION)
2608			printf("COMP,");
2609		if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME)
2610			printf("FF,");
2611		if (ath->ath_capability & ATHEROS_CAP_XR)
2612			printf("XR,");
2613		if (ath->ath_capability & ATHEROS_CAP_AR)
2614			printf("AR,");
2615		if (ath->ath_capability & ATHEROS_CAP_BURST)
2616			printf("BURST,");
2617		if (ath->ath_capability & ATHEROS_CAP_WME)
2618			printf("WME,");
2619		if (ath->ath_capability & ATHEROS_CAP_BOOST)
2620			printf("BOOST,");
2621		printf("0x%x>", LE_READ_2(ath->ath_defkeyix));
2622	}
2623}
2624
2625
2626static void
2627printmeshconf(const char *tag, const uint8_t *ie, size_t ielen, int maxlen)
2628{
2629#define MATCHOUI(field, oui, string)					\
2630do {									\
2631	if (memcmp(field, oui, 4) == 0)					\
2632		printf("%s", string);					\
2633} while (0)
2634
2635	printf("%s", tag);
2636	if (verbose) {
2637		const struct ieee80211_meshconf_ie *mconf =
2638			(const struct ieee80211_meshconf_ie *)ie;
2639		printf("<PATH:");
2640		if (mconf->conf_pselid == IEEE80211_MESHCONF_PATH_HWMP)
2641			printf("HWMP");
2642		else
2643			printf("UNKNOWN");
2644		printf(" LINK:");
2645		if (mconf->conf_pmetid == IEEE80211_MESHCONF_METRIC_AIRTIME)
2646			printf("AIRTIME");
2647		else
2648			printf("UNKNOWN");
2649		printf(" CONGESTION:");
2650		if (mconf->conf_ccid == IEEE80211_MESHCONF_CC_DISABLED)
2651			printf("DISABLED");
2652		else
2653			printf("UNKNOWN");
2654		printf(" SYNC:");
2655		if (mconf->conf_syncid == IEEE80211_MESHCONF_SYNC_NEIGHOFF)
2656			printf("NEIGHOFF");
2657		else
2658			printf("UNKNOWN");
2659		printf(" AUTH:");
2660		if (mconf->conf_authid == IEEE80211_MESHCONF_AUTH_DISABLED)
2661			printf("DISABLED");
2662		else
2663			printf("UNKNOWN");
2664		printf(" FORM:0x%x CAPS:0x%x>", mconf->conf_form,
2665		    mconf->conf_cap);
2666	}
2667#undef MATCHOUI
2668}
2669
2670static const char *
2671wpa_cipher(const u_int8_t *sel)
2672{
2673#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
2674	u_int32_t w = LE_READ_4(sel);
2675
2676	switch (w) {
2677	case WPA_SEL(WPA_CSE_NULL):
2678		return "NONE";
2679	case WPA_SEL(WPA_CSE_WEP40):
2680		return "WEP40";
2681	case WPA_SEL(WPA_CSE_WEP104):
2682		return "WEP104";
2683	case WPA_SEL(WPA_CSE_TKIP):
2684		return "TKIP";
2685	case WPA_SEL(WPA_CSE_CCMP):
2686		return "AES-CCMP";
2687	}
2688	return "?";		/* NB: so 1<< is discarded */
2689#undef WPA_SEL
2690}
2691
2692static const char *
2693wpa_keymgmt(const u_int8_t *sel)
2694{
2695#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
2696	u_int32_t w = LE_READ_4(sel);
2697
2698	switch (w) {
2699	case WPA_SEL(WPA_ASE_8021X_UNSPEC):
2700		return "8021X-UNSPEC";
2701	case WPA_SEL(WPA_ASE_8021X_PSK):
2702		return "8021X-PSK";
2703	case WPA_SEL(WPA_ASE_NONE):
2704		return "NONE";
2705	}
2706	return "?";
2707#undef WPA_SEL
2708}
2709
2710static void
2711printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2712{
2713	u_int8_t len = ie[1];
2714
2715	printf("%s", tag);
2716	if (verbose) {
2717		const char *sep;
2718		int n;
2719
2720		ie += 6, len -= 4;		/* NB: len is payload only */
2721
2722		printf("<v%u", LE_READ_2(ie));
2723		ie += 2, len -= 2;
2724
2725		printf(" mc:%s", wpa_cipher(ie));
2726		ie += 4, len -= 4;
2727
2728		/* unicast ciphers */
2729		n = LE_READ_2(ie);
2730		ie += 2, len -= 2;
2731		sep = " uc:";
2732		for (; n > 0; n--) {
2733			printf("%s%s", sep, wpa_cipher(ie));
2734			ie += 4, len -= 4;
2735			sep = "+";
2736		}
2737
2738		/* key management algorithms */
2739		n = LE_READ_2(ie);
2740		ie += 2, len -= 2;
2741		sep = " km:";
2742		for (; n > 0; n--) {
2743			printf("%s%s", sep, wpa_keymgmt(ie));
2744			ie += 4, len -= 4;
2745			sep = "+";
2746		}
2747
2748		if (len > 2)		/* optional capabilities */
2749			printf(", caps 0x%x", LE_READ_2(ie));
2750		printf(">");
2751	}
2752}
2753
2754static const char *
2755rsn_cipher(const u_int8_t *sel)
2756{
2757#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
2758	u_int32_t w = LE_READ_4(sel);
2759
2760	switch (w) {
2761	case RSN_SEL(RSN_CSE_NULL):
2762		return "NONE";
2763	case RSN_SEL(RSN_CSE_WEP40):
2764		return "WEP40";
2765	case RSN_SEL(RSN_CSE_WEP104):
2766		return "WEP104";
2767	case RSN_SEL(RSN_CSE_TKIP):
2768		return "TKIP";
2769	case RSN_SEL(RSN_CSE_CCMP):
2770		return "AES-CCMP";
2771	case RSN_SEL(RSN_CSE_WRAP):
2772		return "AES-OCB";
2773	}
2774	return "?";
2775#undef WPA_SEL
2776}
2777
2778static const char *
2779rsn_keymgmt(const u_int8_t *sel)
2780{
2781#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
2782	u_int32_t w = LE_READ_4(sel);
2783
2784	switch (w) {
2785	case RSN_SEL(RSN_ASE_8021X_UNSPEC):
2786		return "8021X-UNSPEC";
2787	case RSN_SEL(RSN_ASE_8021X_PSK):
2788		return "8021X-PSK";
2789	case RSN_SEL(RSN_ASE_NONE):
2790		return "NONE";
2791	}
2792	return "?";
2793#undef RSN_SEL
2794}
2795
2796static void
2797printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2798{
2799	printf("%s", tag);
2800	if (verbose) {
2801		const char *sep;
2802		int n;
2803
2804		ie += 2, ielen -= 2;
2805
2806		printf("<v%u", LE_READ_2(ie));
2807		ie += 2, ielen -= 2;
2808
2809		printf(" mc:%s", rsn_cipher(ie));
2810		ie += 4, ielen -= 4;
2811
2812		/* unicast ciphers */
2813		n = LE_READ_2(ie);
2814		ie += 2, ielen -= 2;
2815		sep = " uc:";
2816		for (; n > 0; n--) {
2817			printf("%s%s", sep, rsn_cipher(ie));
2818			ie += 4, ielen -= 4;
2819			sep = "+";
2820		}
2821
2822		/* key management algorithms */
2823		n = LE_READ_2(ie);
2824		ie += 2, ielen -= 2;
2825		sep = " km:";
2826		for (; n > 0; n--) {
2827			printf("%s%s", sep, rsn_keymgmt(ie));
2828			ie += 4, ielen -= 4;
2829			sep = "+";
2830		}
2831
2832		if (ielen > 2)		/* optional capabilities */
2833			printf(", caps 0x%x", LE_READ_2(ie));
2834		/* XXXPMKID */
2835		printf(">");
2836	}
2837}
2838
2839/* XXX move to a public include file */
2840#define IEEE80211_WPS_DEV_PASS_ID	0x1012
2841#define IEEE80211_WPS_SELECTED_REG	0x1041
2842#define IEEE80211_WPS_SETUP_STATE	0x1044
2843#define IEEE80211_WPS_UUID_E		0x1047
2844#define IEEE80211_WPS_VERSION		0x104a
2845
2846#define BE_READ_2(p)					\
2847	((u_int16_t)					\
2848	 ((((const u_int8_t *)(p))[1]      ) |		\
2849	  (((const u_int8_t *)(p))[0] <<  8)))
2850
2851static void
2852printwpsie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2853{
2854	u_int8_t len = ie[1];
2855
2856	printf("%s", tag);
2857	if (verbose) {
2858		static const char *dev_pass_id[] = {
2859			"D",	/* Default (PIN) */
2860			"U",	/* User-specified */
2861			"M",	/* Machine-specified */
2862			"K",	/* Rekey */
2863			"P",	/* PushButton */
2864			"R"	/* Registrar-specified */
2865		};
2866		int n;
2867
2868		ie +=6, len -= 4;		/* NB: len is payload only */
2869
2870		/* WPS IE in Beacon and Probe Resp frames have different fields */
2871		printf("<");
2872		while (len) {
2873			uint16_t tlv_type = BE_READ_2(ie);
2874			uint16_t tlv_len  = BE_READ_2(ie + 2);
2875
2876			ie += 4, len -= 4;
2877
2878			switch (tlv_type) {
2879			case IEEE80211_WPS_VERSION:
2880				printf("v:%d.%d", *ie >> 4, *ie & 0xf);
2881				break;
2882			case IEEE80211_WPS_SETUP_STATE:
2883				/* Only 1 and 2 are valid */
2884				if (*ie == 0 || *ie >= 3)
2885					printf(" state:B");
2886				else
2887					printf(" st:%s", *ie == 1 ? "N" : "C");
2888				break;
2889			case IEEE80211_WPS_SELECTED_REG:
2890				printf(" sel:%s", *ie ? "T" : "F");
2891				break;
2892			case IEEE80211_WPS_DEV_PASS_ID:
2893				n = LE_READ_2(ie);
2894				if (n < nitems(dev_pass_id))
2895					printf(" dpi:%s", dev_pass_id[n]);
2896				break;
2897			case IEEE80211_WPS_UUID_E:
2898				printf(" uuid-e:");
2899				for (n = 0; n < (tlv_len - 1); n++)
2900					printf("%02x-", ie[n]);
2901				printf("%02x", ie[n]);
2902				break;
2903			}
2904			ie += tlv_len, len -= tlv_len;
2905		}
2906		printf(">");
2907	}
2908}
2909
2910static void
2911printtdmaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2912{
2913	printf("%s", tag);
2914	if (verbose && ielen >= sizeof(struct ieee80211_tdma_param)) {
2915		const struct ieee80211_tdma_param *tdma =
2916		   (const struct ieee80211_tdma_param *) ie;
2917
2918		/* XXX tstamp */
2919		printf("<v%u slot:%u slotcnt:%u slotlen:%u bintval:%u inuse:0x%x>",
2920		    tdma->tdma_version, tdma->tdma_slot, tdma->tdma_slotcnt,
2921		    LE_READ_2(&tdma->tdma_slotlen), tdma->tdma_bintval,
2922		    tdma->tdma_inuse[0]);
2923	}
2924}
2925
2926/*
2927 * Copy the ssid string contents into buf, truncating to fit.  If the
2928 * ssid is entirely printable then just copy intact.  Otherwise convert
2929 * to hexadecimal.  If the result is truncated then replace the last
2930 * three characters with "...".
2931 */
2932static int
2933copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
2934{
2935	const u_int8_t *p;
2936	size_t maxlen;
2937	u_int i;
2938
2939	if (essid_len > bufsize)
2940		maxlen = bufsize;
2941	else
2942		maxlen = essid_len;
2943	/* determine printable or not */
2944	for (i = 0, p = essid; i < maxlen; i++, p++) {
2945		if (*p < ' ' || *p > 0x7e)
2946			break;
2947	}
2948	if (i != maxlen) {		/* not printable, print as hex */
2949		if (bufsize < 3)
2950			return 0;
2951		strlcpy(buf, "0x", bufsize);
2952		bufsize -= 2;
2953		p = essid;
2954		for (i = 0; i < maxlen && bufsize >= 2; i++) {
2955			sprintf(&buf[2+2*i], "%02x", p[i]);
2956			bufsize -= 2;
2957		}
2958		if (i != essid_len)
2959			memcpy(&buf[2+2*i-3], "...", 3);
2960	} else {			/* printable, truncate as needed */
2961		memcpy(buf, essid, maxlen);
2962		if (maxlen != essid_len)
2963			memcpy(&buf[maxlen-3], "...", 3);
2964	}
2965	return maxlen;
2966}
2967
2968static void
2969printssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2970{
2971	char ssid[2*IEEE80211_NWID_LEN+1];
2972
2973	printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid);
2974}
2975
2976static void
2977printrates(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2978{
2979	const char *sep;
2980	int i;
2981
2982	printf("%s", tag);
2983	sep = "<";
2984	for (i = 2; i < ielen; i++) {
2985		printf("%s%s%d", sep,
2986		    ie[i] & IEEE80211_RATE_BASIC ? "B" : "",
2987		    ie[i] & IEEE80211_RATE_VAL);
2988		sep = ",";
2989	}
2990	printf(">");
2991}
2992
2993static void
2994printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2995{
2996	const struct ieee80211_country_ie *cie =
2997	   (const struct ieee80211_country_ie *) ie;
2998	int i, nbands, schan, nchan;
2999
3000	printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]);
3001	nbands = (cie->len - 3) / sizeof(cie->band[0]);
3002	for (i = 0; i < nbands; i++) {
3003		schan = cie->band[i].schan;
3004		nchan = cie->band[i].nchan;
3005		if (nchan != 1)
3006			printf(" %u-%u,%u", schan, schan + nchan-1,
3007			    cie->band[i].maxtxpwr);
3008		else
3009			printf(" %u,%u", schan, cie->band[i].maxtxpwr);
3010	}
3011	printf(">");
3012}
3013
3014/* unaligned little endian access */
3015#define LE_READ_4(p)					\
3016	((u_int32_t)					\
3017	 ((((const u_int8_t *)(p))[0]      ) |		\
3018	  (((const u_int8_t *)(p))[1] <<  8) |		\
3019	  (((const u_int8_t *)(p))[2] << 16) |		\
3020	  (((const u_int8_t *)(p))[3] << 24)))
3021
3022static __inline int
3023iswpaoui(const u_int8_t *frm)
3024{
3025	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
3026}
3027
3028static __inline int
3029iswmeinfo(const u_int8_t *frm)
3030{
3031	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
3032		frm[6] == WME_INFO_OUI_SUBTYPE;
3033}
3034
3035static __inline int
3036iswmeparam(const u_int8_t *frm)
3037{
3038	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
3039		frm[6] == WME_PARAM_OUI_SUBTYPE;
3040}
3041
3042static __inline int
3043isatherosoui(const u_int8_t *frm)
3044{
3045	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
3046}
3047
3048static __inline int
3049istdmaoui(const uint8_t *frm)
3050{
3051	return frm[1] > 3 && LE_READ_4(frm+2) == ((TDMA_OUI_TYPE<<24)|TDMA_OUI);
3052}
3053
3054static __inline int
3055iswpsoui(const uint8_t *frm)
3056{
3057	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPS_OUI_TYPE<<24)|WPA_OUI);
3058}
3059
3060static const char *
3061iename(int elemid)
3062{
3063	switch (elemid) {
3064	case IEEE80211_ELEMID_FHPARMS:	return " FHPARMS";
3065	case IEEE80211_ELEMID_CFPARMS:	return " CFPARMS";
3066	case IEEE80211_ELEMID_TIM:	return " TIM";
3067	case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS";
3068	case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE";
3069	case IEEE80211_ELEMID_PWRCNSTR:	return " PWRCNSTR";
3070	case IEEE80211_ELEMID_PWRCAP:	return " PWRCAP";
3071	case IEEE80211_ELEMID_TPCREQ:	return " TPCREQ";
3072	case IEEE80211_ELEMID_TPCREP:	return " TPCREP";
3073	case IEEE80211_ELEMID_SUPPCHAN:	return " SUPPCHAN";
3074	case IEEE80211_ELEMID_CSA:	return " CSA";
3075	case IEEE80211_ELEMID_MEASREQ:	return " MEASREQ";
3076	case IEEE80211_ELEMID_MEASREP:	return " MEASREP";
3077	case IEEE80211_ELEMID_QUIET:	return " QUIET";
3078	case IEEE80211_ELEMID_IBSSDFS:	return " IBSSDFS";
3079	case IEEE80211_ELEMID_TPC:	return " TPC";
3080	case IEEE80211_ELEMID_CCKM:	return " CCKM";
3081	}
3082	return " ???";
3083}
3084
3085static void
3086printies(const u_int8_t *vp, int ielen, int maxcols)
3087{
3088	while (ielen > 0) {
3089		switch (vp[0]) {
3090		case IEEE80211_ELEMID_SSID:
3091			if (verbose)
3092				printssid(" SSID", vp, 2+vp[1], maxcols);
3093			break;
3094		case IEEE80211_ELEMID_RATES:
3095		case IEEE80211_ELEMID_XRATES:
3096			if (verbose)
3097				printrates(vp[0] == IEEE80211_ELEMID_RATES ?
3098				    " RATES" : " XRATES", vp, 2+vp[1], maxcols);
3099			break;
3100		case IEEE80211_ELEMID_DSPARMS:
3101			if (verbose)
3102				printf(" DSPARMS<%u>", vp[2]);
3103			break;
3104		case IEEE80211_ELEMID_COUNTRY:
3105			if (verbose)
3106				printcountry(" COUNTRY", vp, 2+vp[1], maxcols);
3107			break;
3108		case IEEE80211_ELEMID_ERP:
3109			if (verbose)
3110				printf(" ERP<0x%x>", vp[2]);
3111			break;
3112		case IEEE80211_ELEMID_VENDOR:
3113			if (iswpaoui(vp))
3114				printwpaie(" WPA", vp, 2+vp[1], maxcols);
3115			else if (iswmeinfo(vp))
3116				printwmeinfo(" WME", vp, 2+vp[1], maxcols);
3117			else if (iswmeparam(vp))
3118				printwmeparam(" WME", vp, 2+vp[1], maxcols);
3119			else if (isatherosoui(vp))
3120				printathie(" ATH", vp, 2+vp[1], maxcols);
3121			else if (iswpsoui(vp))
3122				printwpsie(" WPS", vp, 2+vp[1], maxcols);
3123			else if (istdmaoui(vp))
3124				printtdmaie(" TDMA", vp, 2+vp[1], maxcols);
3125			else if (verbose)
3126				printie(" VEN", vp, 2+vp[1], maxcols);
3127			break;
3128		case IEEE80211_ELEMID_RSN:
3129			printrsnie(" RSN", vp, 2+vp[1], maxcols);
3130			break;
3131		case IEEE80211_ELEMID_HTCAP:
3132			printhtcap(" HTCAP", vp, 2+vp[1], maxcols);
3133			break;
3134		case IEEE80211_ELEMID_HTINFO:
3135			if (verbose)
3136				printhtinfo(" HTINFO", vp, 2+vp[1], maxcols);
3137			break;
3138		case IEEE80211_ELEMID_MESHID:
3139			if (verbose)
3140				printssid(" MESHID", vp, 2+vp[1], maxcols);
3141			break;
3142		case IEEE80211_ELEMID_MESHCONF:
3143			printmeshconf(" MESHCONF", vp, 2+vp[1], maxcols);
3144			break;
3145		default:
3146			if (verbose)
3147				printie(iename(vp[0]), vp, 2+vp[1], maxcols);
3148			break;
3149		}
3150		ielen -= 2+vp[1];
3151		vp += 2+vp[1];
3152	}
3153}
3154
3155static void
3156printmimo(const struct ieee80211_mimo_info *mi)
3157{
3158	/* NB: don't muddy display unless there's something to show */
3159	if (mi->rssi[0] != 0 || mi->rssi[1] != 0 || mi->rssi[2] != 0) {
3160		/* XXX ignore EVM for now */
3161		printf(" (rssi %d:%d:%d nf %d:%d:%d)",
3162		    mi->rssi[0], mi->rssi[1], mi->rssi[2],
3163		    mi->noise[0], mi->noise[1], mi->noise[2]);
3164	}
3165}
3166
3167static void
3168list_scan(int s)
3169{
3170	uint8_t buf[24*1024];
3171	char ssid[IEEE80211_NWID_LEN+1];
3172	const uint8_t *cp;
3173	int len, ssidmax, idlen;
3174
3175	if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0)
3176		errx(1, "unable to get scan results");
3177	if (len < sizeof(struct ieee80211req_scan_result))
3178		return;
3179
3180	getchaninfo(s);
3181
3182	ssidmax = verbose ? IEEE80211_NWID_LEN - 1 : 14;
3183	printf("%-*.*s  %-17.17s  %4s %4s  %-7s  %3s %4s\n"
3184		, ssidmax, ssidmax, "SSID/MESH ID"
3185		, "BSSID"
3186		, "CHAN"
3187		, "RATE"
3188		, " S:N"
3189		, "INT"
3190		, "CAPS"
3191	);
3192	cp = buf;
3193	do {
3194		const struct ieee80211req_scan_result *sr;
3195		const uint8_t *vp, *idp;
3196
3197		sr = (const struct ieee80211req_scan_result *) cp;
3198		vp = cp + sr->isr_ie_off;
3199		if (sr->isr_meshid_len) {
3200			idp = vp + sr->isr_ssid_len;
3201			idlen = sr->isr_meshid_len;
3202		} else {
3203			idp = vp;
3204			idlen = sr->isr_ssid_len;
3205		}
3206		printf("%-*.*s  %s  %3d  %3dM %3d:%-3d  %3d %-4.4s"
3207			, ssidmax
3208			  , copy_essid(ssid, ssidmax, idp, idlen)
3209			  , ssid
3210			, ether_ntoa((const struct ether_addr *) sr->isr_bssid)
3211			, ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
3212			, getmaxrate(sr->isr_rates, sr->isr_nrates)
3213			, (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise
3214			, sr->isr_intval
3215			, getcaps(sr->isr_capinfo)
3216		);
3217		printies(vp + sr->isr_ssid_len + sr->isr_meshid_len,
3218		    sr->isr_ie_len, 24);
3219		printf("\n");
3220		cp += sr->isr_len, len -= sr->isr_len;
3221	} while (len >= sizeof(struct ieee80211req_scan_result));
3222}
3223
3224static void
3225scan_and_wait(int s)
3226{
3227	struct ieee80211_scan_req sr;
3228	struct ieee80211req ireq;
3229	int sroute;
3230
3231	sroute = socket(PF_ROUTE, SOCK_RAW, 0);
3232	if (sroute < 0) {
3233		perror("socket(PF_ROUTE,SOCK_RAW)");
3234		return;
3235	}
3236	(void) memset(&ireq, 0, sizeof(ireq));
3237	(void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
3238	ireq.i_type = IEEE80211_IOC_SCAN_REQ;
3239
3240	memset(&sr, 0, sizeof(sr));
3241	sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
3242		    | IEEE80211_IOC_SCAN_BGSCAN
3243		    | IEEE80211_IOC_SCAN_NOPICK
3244		    | IEEE80211_IOC_SCAN_ONCE;
3245	sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
3246	sr.sr_nssid = 0;
3247
3248	ireq.i_data = &sr;
3249	ireq.i_len = sizeof(sr);
3250	/*
3251	 * NB: only root can trigger a scan so ignore errors. Also ignore
3252	 * possible errors from net80211, even if no new scan could be
3253	 * started there might still be a valid scan cache.
3254	 */
3255	if (ioctl(s, SIOCS80211, &ireq) == 0) {
3256		char buf[2048];
3257		struct if_announcemsghdr *ifan;
3258		struct rt_msghdr *rtm;
3259
3260		do {
3261			if (read(sroute, buf, sizeof(buf)) < 0) {
3262				perror("read(PF_ROUTE)");
3263				break;
3264			}
3265			rtm = (struct rt_msghdr *) buf;
3266			if (rtm->rtm_version != RTM_VERSION)
3267				break;
3268			ifan = (struct if_announcemsghdr *) rtm;
3269		} while (rtm->rtm_type != RTM_IEEE80211 ||
3270		    ifan->ifan_what != RTM_IEEE80211_SCAN);
3271	}
3272	close(sroute);
3273}
3274
3275static
3276DECL_CMD_FUNC(set80211scan, val, d)
3277{
3278	scan_and_wait(s);
3279	list_scan(s);
3280}
3281
3282static enum ieee80211_opmode get80211opmode(int s);
3283
3284static int
3285gettxseq(const struct ieee80211req_sta_info *si)
3286{
3287	int i, txseq;
3288
3289	if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3290		return si->isi_txseqs[0];
3291	/* XXX not right but usually what folks want */
3292	txseq = 0;
3293	for (i = 0; i < IEEE80211_TID_SIZE; i++)
3294		if (si->isi_txseqs[i] > txseq)
3295			txseq = si->isi_txseqs[i];
3296	return txseq;
3297}
3298
3299static int
3300getrxseq(const struct ieee80211req_sta_info *si)
3301{
3302	int i, rxseq;
3303
3304	if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3305		return si->isi_rxseqs[0];
3306	/* XXX not right but usually what folks want */
3307	rxseq = 0;
3308	for (i = 0; i < IEEE80211_TID_SIZE; i++)
3309		if (si->isi_rxseqs[i] > rxseq)
3310			rxseq = si->isi_rxseqs[i];
3311	return rxseq;
3312}
3313
3314static void
3315list_stations(int s)
3316{
3317	union {
3318		struct ieee80211req_sta_req req;
3319		uint8_t buf[24*1024];
3320	} u;
3321	enum ieee80211_opmode opmode = get80211opmode(s);
3322	const uint8_t *cp;
3323	int len;
3324
3325	/* broadcast address =>'s get all stations */
3326	(void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
3327	if (opmode == IEEE80211_M_STA) {
3328		/*
3329		 * Get information about the associated AP.
3330		 */
3331		(void) get80211(s, IEEE80211_IOC_BSSID,
3332		    u.req.is_u.macaddr, IEEE80211_ADDR_LEN);
3333	}
3334	if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0)
3335		errx(1, "unable to get station information");
3336	if (len < sizeof(struct ieee80211req_sta_info))
3337		return;
3338
3339	getchaninfo(s);
3340
3341	if (opmode == IEEE80211_M_MBSS)
3342		printf("%-17.17s %4s %5s %5s %7s %4s %4s %4s %6s %6s\n"
3343			, "ADDR"
3344			, "CHAN"
3345			, "LOCAL"
3346			, "PEER"
3347			, "STATE"
3348			, "RATE"
3349			, "RSSI"
3350			, "IDLE"
3351			, "TXSEQ"
3352			, "RXSEQ"
3353		);
3354	else
3355		printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %-7s\n"
3356			, "ADDR"
3357			, "AID"
3358			, "CHAN"
3359			, "RATE"
3360			, "RSSI"
3361			, "IDLE"
3362			, "TXSEQ"
3363			, "RXSEQ"
3364			, "CAPS"
3365			, "FLAG"
3366		);
3367	cp = (const uint8_t *) u.req.info;
3368	do {
3369		const struct ieee80211req_sta_info *si;
3370
3371		si = (const struct ieee80211req_sta_info *) cp;
3372		if (si->isi_len < sizeof(*si))
3373			break;
3374		if (opmode == IEEE80211_M_MBSS)
3375			printf("%s %4d %5x %5x %7.7s %3dM %4.1f %4d %6d %6d"
3376				, ether_ntoa((const struct ether_addr*)
3377				    si->isi_macaddr)
3378				, ieee80211_mhz2ieee(si->isi_freq,
3379				    si->isi_flags)
3380				, si->isi_localid
3381				, si->isi_peerid
3382				, mesh_linkstate_string(si->isi_peerstate)
3383				, si->isi_txmbps/2
3384				, si->isi_rssi/2.
3385				, si->isi_inact
3386				, gettxseq(si)
3387				, getrxseq(si)
3388			);
3389		else
3390			printf("%s %4u %4d %3dM %4.1f %4d %6d %6d %-4.4s %-7.7s"
3391				, ether_ntoa((const struct ether_addr*)
3392				    si->isi_macaddr)
3393				, IEEE80211_AID(si->isi_associd)
3394				, ieee80211_mhz2ieee(si->isi_freq,
3395				    si->isi_flags)
3396				, si->isi_txmbps/2
3397				, si->isi_rssi/2.
3398				, si->isi_inact
3399				, gettxseq(si)
3400				, getrxseq(si)
3401				, getcaps(si->isi_capinfo)
3402				, getflags(si->isi_state)
3403			);
3404		printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
3405		printmimo(&si->isi_mimo);
3406		printf("\n");
3407		cp += si->isi_len, len -= si->isi_len;
3408	} while (len >= sizeof(struct ieee80211req_sta_info));
3409}
3410
3411static const char *
3412mesh_linkstate_string(uint8_t state)
3413{
3414	static const char *state_names[] = {
3415	    [0] = "IDLE",
3416	    [1] = "OPEN-TX",
3417	    [2] = "OPEN-RX",
3418	    [3] = "CONF-RX",
3419	    [4] = "ESTAB",
3420	    [5] = "HOLDING",
3421	};
3422
3423	if (state >= nitems(state_names)) {
3424		static char buf[10];
3425		snprintf(buf, sizeof(buf), "#%u", state);
3426		return buf;
3427	} else
3428		return state_names[state];
3429}
3430
3431static const char *
3432get_chaninfo(const struct ieee80211_channel *c, int precise,
3433	char buf[], size_t bsize)
3434{
3435	buf[0] = '\0';
3436	if (IEEE80211_IS_CHAN_FHSS(c))
3437		strlcat(buf, " FHSS", bsize);
3438	if (IEEE80211_IS_CHAN_A(c))
3439		strlcat(buf, " 11a", bsize);
3440	else if (IEEE80211_IS_CHAN_ANYG(c))
3441		strlcat(buf, " 11g", bsize);
3442	else if (IEEE80211_IS_CHAN_B(c))
3443		strlcat(buf, " 11b", bsize);
3444	if (IEEE80211_IS_CHAN_HALF(c))
3445		strlcat(buf, "/10MHz", bsize);
3446	if (IEEE80211_IS_CHAN_QUARTER(c))
3447		strlcat(buf, "/5MHz", bsize);
3448	if (IEEE80211_IS_CHAN_TURBO(c))
3449		strlcat(buf, " Turbo", bsize);
3450	if (precise) {
3451		if (IEEE80211_IS_CHAN_HT20(c))
3452			strlcat(buf, " ht/20", bsize);
3453		else if (IEEE80211_IS_CHAN_HT40D(c))
3454			strlcat(buf, " ht/40-", bsize);
3455		else if (IEEE80211_IS_CHAN_HT40U(c))
3456			strlcat(buf, " ht/40+", bsize);
3457	} else {
3458		if (IEEE80211_IS_CHAN_HT(c))
3459			strlcat(buf, " ht", bsize);
3460	}
3461	return buf;
3462}
3463
3464static void
3465print_chaninfo(const struct ieee80211_channel *c, int verb)
3466{
3467	char buf[14];
3468
3469	if (verb)
3470		printf("Channel %3u : %u%c%c%c%c%c MHz%-14.14s",
3471		    ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
3472		    IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
3473		    IEEE80211_IS_CHAN_DFS(c) ? 'D' : ' ',
3474		    IEEE80211_IS_CHAN_RADAR(c) ? 'R' : ' ',
3475		    IEEE80211_IS_CHAN_CWINT(c) ? 'I' : ' ',
3476		    IEEE80211_IS_CHAN_CACDONE(c) ? 'C' : ' ',
3477		    get_chaninfo(c, verb, buf, sizeof(buf)));
3478	else
3479	printf("Channel %3u : %u%c MHz%-14.14s",
3480	    ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
3481	    IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
3482	    get_chaninfo(c, verb, buf, sizeof(buf)));
3483
3484}
3485
3486static int
3487chanpref(const struct ieee80211_channel *c)
3488{
3489	if (IEEE80211_IS_CHAN_HT40(c))
3490		return 40;
3491	if (IEEE80211_IS_CHAN_HT20(c))
3492		return 30;
3493	if (IEEE80211_IS_CHAN_HALF(c))
3494		return 10;
3495	if (IEEE80211_IS_CHAN_QUARTER(c))
3496		return 5;
3497	if (IEEE80211_IS_CHAN_TURBO(c))
3498		return 25;
3499	if (IEEE80211_IS_CHAN_A(c))
3500		return 20;
3501	if (IEEE80211_IS_CHAN_G(c))
3502		return 20;
3503	if (IEEE80211_IS_CHAN_B(c))
3504		return 15;
3505	if (IEEE80211_IS_CHAN_PUREG(c))
3506		return 15;
3507	return 0;
3508}
3509
3510static void
3511print_channels(int s, const struct ieee80211req_chaninfo *chans,
3512	int allchans, int verb)
3513{
3514	struct ieee80211req_chaninfo *achans;
3515	uint8_t reported[IEEE80211_CHAN_BYTES];
3516	const struct ieee80211_channel *c;
3517	int i, half;
3518
3519	achans = malloc(IEEE80211_CHANINFO_SPACE(chans));
3520	if (achans == NULL)
3521		errx(1, "no space for active channel list");
3522	achans->ic_nchans = 0;
3523	memset(reported, 0, sizeof(reported));
3524	if (!allchans) {
3525		struct ieee80211req_chanlist active;
3526
3527		if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0)
3528			errx(1, "unable to get active channel list");
3529		for (i = 0; i < chans->ic_nchans; i++) {
3530			c = &chans->ic_chans[i];
3531			if (!isset(active.ic_channels, c->ic_ieee))
3532				continue;
3533			/*
3534			 * Suppress compatible duplicates unless
3535			 * verbose.  The kernel gives us it's
3536			 * complete channel list which has separate
3537			 * entries for 11g/11b and 11a/turbo.
3538			 */
3539			if (isset(reported, c->ic_ieee) && !verb) {
3540				/* XXX we assume duplicates are adjacent */
3541				achans->ic_chans[achans->ic_nchans-1] = *c;
3542			} else {
3543				achans->ic_chans[achans->ic_nchans++] = *c;
3544				setbit(reported, c->ic_ieee);
3545			}
3546		}
3547	} else {
3548		for (i = 0; i < chans->ic_nchans; i++) {
3549			c = &chans->ic_chans[i];
3550			/* suppress duplicates as above */
3551			if (isset(reported, c->ic_ieee) && !verb) {
3552				/* XXX we assume duplicates are adjacent */
3553				struct ieee80211_channel *a =
3554				    &achans->ic_chans[achans->ic_nchans-1];
3555				if (chanpref(c) > chanpref(a))
3556					*a = *c;
3557			} else {
3558				achans->ic_chans[achans->ic_nchans++] = *c;
3559				setbit(reported, c->ic_ieee);
3560			}
3561		}
3562	}
3563	half = achans->ic_nchans / 2;
3564	if (achans->ic_nchans % 2)
3565		half++;
3566
3567	for (i = 0; i < achans->ic_nchans / 2; i++) {
3568		print_chaninfo(&achans->ic_chans[i], verb);
3569		print_chaninfo(&achans->ic_chans[half+i], verb);
3570		printf("\n");
3571	}
3572	if (achans->ic_nchans % 2) {
3573		print_chaninfo(&achans->ic_chans[i], verb);
3574		printf("\n");
3575	}
3576	free(achans);
3577}
3578
3579static void
3580list_channels(int s, int allchans)
3581{
3582	getchaninfo(s);
3583	print_channels(s, chaninfo, allchans, verbose);
3584}
3585
3586static void
3587print_txpow(const struct ieee80211_channel *c)
3588{
3589	printf("Channel %3u : %u MHz %3.1f reg %2d  ",
3590	    c->ic_ieee, c->ic_freq,
3591	    c->ic_maxpower/2., c->ic_maxregpower);
3592}
3593
3594static void
3595print_txpow_verbose(const struct ieee80211_channel *c)
3596{
3597	print_chaninfo(c, 1);
3598	printf("min %4.1f dBm  max %3.1f dBm  reg %2d dBm",
3599	    c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
3600	/* indicate where regulatory cap limits power use */
3601	if (c->ic_maxpower > 2*c->ic_maxregpower)
3602		printf(" <");
3603}
3604
3605static void
3606list_txpow(int s)
3607{
3608	struct ieee80211req_chaninfo *achans;
3609	uint8_t reported[IEEE80211_CHAN_BYTES];
3610	struct ieee80211_channel *c, *prev;
3611	int i, half;
3612
3613	getchaninfo(s);
3614	achans = malloc(IEEE80211_CHANINFO_SPACE(chaninfo));
3615	if (achans == NULL)
3616		errx(1, "no space for active channel list");
3617	achans->ic_nchans = 0;
3618	memset(reported, 0, sizeof(reported));
3619	for (i = 0; i < chaninfo->ic_nchans; i++) {
3620		c = &chaninfo->ic_chans[i];
3621		/* suppress duplicates as above */
3622		if (isset(reported, c->ic_ieee) && !verbose) {
3623			/* XXX we assume duplicates are adjacent */
3624			assert(achans->ic_nchans > 0);
3625			prev = &achans->ic_chans[achans->ic_nchans-1];
3626			/* display highest power on channel */
3627			if (c->ic_maxpower > prev->ic_maxpower)
3628				*prev = *c;
3629		} else {
3630			achans->ic_chans[achans->ic_nchans++] = *c;
3631			setbit(reported, c->ic_ieee);
3632		}
3633	}
3634	if (!verbose) {
3635		half = achans->ic_nchans / 2;
3636		if (achans->ic_nchans % 2)
3637			half++;
3638
3639		for (i = 0; i < achans->ic_nchans / 2; i++) {
3640			print_txpow(&achans->ic_chans[i]);
3641			print_txpow(&achans->ic_chans[half+i]);
3642			printf("\n");
3643		}
3644		if (achans->ic_nchans % 2) {
3645			print_txpow(&achans->ic_chans[i]);
3646			printf("\n");
3647		}
3648	} else {
3649		for (i = 0; i < achans->ic_nchans; i++) {
3650			print_txpow_verbose(&achans->ic_chans[i]);
3651			printf("\n");
3652		}
3653	}
3654	free(achans);
3655}
3656
3657static void
3658list_keys(int s)
3659{
3660}
3661
3662#define	IEEE80211_C_BITS \
3663	"\20\1STA\002803ENCAP\7FF\10TURBOP\11IBSS\12PMGT" \
3664	"\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \
3665	"\21MONITOR\22DFS\23MBSS\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
3666	"\37TXFRAG\40TDMA"
3667
3668static void
3669list_capabilities(int s)
3670{
3671	struct ieee80211_devcaps_req *dc;
3672
3673	if (verbose)
3674		dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
3675	else
3676		dc = malloc(IEEE80211_DEVCAPS_SIZE(1));
3677	if (dc == NULL)
3678		errx(1, "no space for device capabilities");
3679	dc->dc_chaninfo.ic_nchans = verbose ? MAXCHAN : 1;
3680	getdevcaps(s, dc);
3681	printb("drivercaps", dc->dc_drivercaps, IEEE80211_C_BITS);
3682	if (dc->dc_cryptocaps != 0 || verbose) {
3683		putchar('\n');
3684		printb("cryptocaps", dc->dc_cryptocaps, IEEE80211_CRYPTO_BITS);
3685	}
3686	if (dc->dc_htcaps != 0 || verbose) {
3687		putchar('\n');
3688		printb("htcaps", dc->dc_htcaps, IEEE80211_HTCAP_BITS);
3689	}
3690	putchar('\n');
3691	if (verbose) {
3692		chaninfo = &dc->dc_chaninfo;	/* XXX */
3693		print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, verbose);
3694	}
3695	free(dc);
3696}
3697
3698static int
3699get80211wme(int s, int param, int ac, int *val)
3700{
3701	struct ieee80211req ireq;
3702
3703	(void) memset(&ireq, 0, sizeof(ireq));
3704	(void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
3705	ireq.i_type = param;
3706	ireq.i_len = ac;
3707	if (ioctl(s, SIOCG80211, &ireq) < 0) {
3708		warn("cannot get WME parameter %d, ac %d%s",
3709		    param, ac & IEEE80211_WMEPARAM_VAL,
3710		    ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : "");
3711		return -1;
3712	}
3713	*val = ireq.i_val;
3714	return 0;
3715}
3716
3717static void
3718list_wme_aci(int s, const char *tag, int ac)
3719{
3720	int val;
3721
3722	printf("\t%s", tag);
3723
3724	/* show WME BSS parameters */
3725	if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1)
3726		printf(" cwmin %2u", val);
3727	if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1)
3728		printf(" cwmax %2u", val);
3729	if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1)
3730		printf(" aifs %2u", val);
3731	if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1)
3732		printf(" txopLimit %3u", val);
3733	if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) {
3734		if (val)
3735			printf(" acm");
3736		else if (verbose)
3737			printf(" -acm");
3738	}
3739	/* !BSS only */
3740	if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
3741		if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) {
3742			if (!val)
3743				printf(" -ack");
3744			else if (verbose)
3745				printf(" ack");
3746		}
3747	}
3748	printf("\n");
3749}
3750
3751static void
3752list_wme(int s)
3753{
3754	static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
3755	int ac;
3756
3757	if (verbose) {
3758		/* display both BSS and local settings */
3759		for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
3760	again:
3761			if (ac & IEEE80211_WMEPARAM_BSS)
3762				list_wme_aci(s, "     ", ac);
3763			else
3764				list_wme_aci(s, acnames[ac], ac);
3765			if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
3766				ac |= IEEE80211_WMEPARAM_BSS;
3767				goto again;
3768			} else
3769				ac &= ~IEEE80211_WMEPARAM_BSS;
3770		}
3771	} else {
3772		/* display only channel settings */
3773		for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++)
3774			list_wme_aci(s, acnames[ac], ac);
3775	}
3776}
3777
3778static void
3779list_roam(int s)
3780{
3781	const struct ieee80211_roamparam *rp;
3782	int mode;
3783
3784	getroam(s);
3785	for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
3786		rp = &roamparams.params[mode];
3787		if (rp->rssi == 0 && rp->rate == 0)
3788			continue;
3789		if (mode == IEEE80211_MODE_11NA || mode == IEEE80211_MODE_11NG) {
3790			if (rp->rssi & 1)
3791				LINE_CHECK("roam:%-7.7s rssi %2u.5dBm  MCS %2u    ",
3792				    modename[mode], rp->rssi/2,
3793				    rp->rate &~ IEEE80211_RATE_MCS);
3794			else
3795				LINE_CHECK("roam:%-7.7s rssi %4udBm  MCS %2u    ",
3796				    modename[mode], rp->rssi/2,
3797				    rp->rate &~ IEEE80211_RATE_MCS);
3798		} else {
3799			if (rp->rssi & 1)
3800				LINE_CHECK("roam:%-7.7s rssi %2u.5dBm rate %2u Mb/s",
3801				    modename[mode], rp->rssi/2, rp->rate/2);
3802			else
3803				LINE_CHECK("roam:%-7.7s rssi %4udBm rate %2u Mb/s",
3804				    modename[mode], rp->rssi/2, rp->rate/2);
3805		}
3806	}
3807}
3808
3809static void
3810list_txparams(int s)
3811{
3812	const struct ieee80211_txparam *tp;
3813	int mode;
3814
3815	gettxparams(s);
3816	for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
3817		tp = &txparams.params[mode];
3818		if (tp->mgmtrate == 0 && tp->mcastrate == 0)
3819			continue;
3820		if (mode == IEEE80211_MODE_11NA || mode == IEEE80211_MODE_11NG) {
3821			if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
3822				LINE_CHECK("%-7.7s ucast NONE    mgmt %2u MCS  "
3823				    "mcast %2u MCS  maxretry %u",
3824				    modename[mode],
3825				    tp->mgmtrate &~ IEEE80211_RATE_MCS,
3826				    tp->mcastrate &~ IEEE80211_RATE_MCS,
3827				    tp->maxretry);
3828			else
3829				LINE_CHECK("%-7.7s ucast %2u MCS  mgmt %2u MCS  "
3830				    "mcast %2u MCS  maxretry %u",
3831				    modename[mode],
3832				    tp->ucastrate &~ IEEE80211_RATE_MCS,
3833				    tp->mgmtrate &~ IEEE80211_RATE_MCS,
3834				    tp->mcastrate &~ IEEE80211_RATE_MCS,
3835				    tp->maxretry);
3836		} else {
3837			if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
3838				LINE_CHECK("%-7.7s ucast NONE    mgmt %2u Mb/s "
3839				    "mcast %2u Mb/s maxretry %u",
3840				    modename[mode],
3841				    tp->mgmtrate/2,
3842				    tp->mcastrate/2, tp->maxretry);
3843			else
3844				LINE_CHECK("%-7.7s ucast %2u Mb/s mgmt %2u Mb/s "
3845				    "mcast %2u Mb/s maxretry %u",
3846				    modename[mode],
3847				    tp->ucastrate/2, tp->mgmtrate/2,
3848				    tp->mcastrate/2, tp->maxretry);
3849		}
3850	}
3851}
3852
3853static void
3854printpolicy(int policy)
3855{
3856	switch (policy) {
3857	case IEEE80211_MACCMD_POLICY_OPEN:
3858		printf("policy: open\n");
3859		break;
3860	case IEEE80211_MACCMD_POLICY_ALLOW:
3861		printf("policy: allow\n");
3862		break;
3863	case IEEE80211_MACCMD_POLICY_DENY:
3864		printf("policy: deny\n");
3865		break;
3866	case IEEE80211_MACCMD_POLICY_RADIUS:
3867		printf("policy: radius\n");
3868		break;
3869	default:
3870		printf("policy: unknown (%u)\n", policy);
3871		break;
3872	}
3873}
3874
3875static void
3876list_mac(int s)
3877{
3878	struct ieee80211req ireq;
3879	struct ieee80211req_maclist *acllist;
3880	int i, nacls, policy, len;
3881	uint8_t *data;
3882	char c;
3883
3884	(void) memset(&ireq, 0, sizeof(ireq));
3885	(void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
3886	ireq.i_type = IEEE80211_IOC_MACCMD;
3887	ireq.i_val = IEEE80211_MACCMD_POLICY;
3888	if (ioctl(s, SIOCG80211, &ireq) < 0) {
3889		if (errno == EINVAL) {
3890			printf("No acl policy loaded\n");
3891			return;
3892		}
3893		err(1, "unable to get mac policy");
3894	}
3895	policy = ireq.i_val;
3896	if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
3897		c = '*';
3898	} else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
3899		c = '+';
3900	} else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
3901		c = '-';
3902	} else if (policy == IEEE80211_MACCMD_POLICY_RADIUS) {
3903		c = 'r';		/* NB: should never have entries */
3904	} else {
3905		printf("policy: unknown (%u)\n", policy);
3906		c = '?';
3907	}
3908	if (verbose || c == '?')
3909		printpolicy(policy);
3910
3911	ireq.i_val = IEEE80211_MACCMD_LIST;
3912	ireq.i_len = 0;
3913	if (ioctl(s, SIOCG80211, &ireq) < 0)
3914		err(1, "unable to get mac acl list size");
3915	if (ireq.i_len == 0) {		/* NB: no acls */
3916		if (!(verbose || c == '?'))
3917			printpolicy(policy);
3918		return;
3919	}
3920	len = ireq.i_len;
3921
3922	data = malloc(len);
3923	if (data == NULL)
3924		err(1, "out of memory for acl list");
3925
3926	ireq.i_data = data;
3927	if (ioctl(s, SIOCG80211, &ireq) < 0)
3928		err(1, "unable to get mac acl list");
3929	nacls = len / sizeof(*acllist);
3930	acllist = (struct ieee80211req_maclist *) data;
3931	for (i = 0; i < nacls; i++)
3932		printf("%c%s\n", c, ether_ntoa(
3933			(const struct ether_addr *) acllist[i].ml_macaddr));
3934	free(data);
3935}
3936
3937static void
3938print_regdomain(const struct ieee80211_regdomain *reg, int verb)
3939{
3940	if ((reg->regdomain != 0 &&
3941	    reg->regdomain != reg->country) || verb) {
3942		const struct regdomain *rd =
3943		    lib80211_regdomain_findbysku(getregdata(), reg->regdomain);
3944		if (rd == NULL)
3945			LINE_CHECK("regdomain %d", reg->regdomain);
3946		else
3947			LINE_CHECK("regdomain %s", rd->name);
3948	}
3949	if (reg->country != 0 || verb) {
3950		const struct country *cc =
3951		    lib80211_country_findbycc(getregdata(), reg->country);
3952		if (cc == NULL)
3953			LINE_CHECK("country %d", reg->country);
3954		else
3955			LINE_CHECK("country %s", cc->isoname);
3956	}
3957	if (reg->location == 'I')
3958		LINE_CHECK("indoor");
3959	else if (reg->location == 'O')
3960		LINE_CHECK("outdoor");
3961	else if (verb)
3962		LINE_CHECK("anywhere");
3963	if (reg->ecm)
3964		LINE_CHECK("ecm");
3965	else if (verb)
3966		LINE_CHECK("-ecm");
3967}
3968
3969static void
3970list_regdomain(int s, int channelsalso)
3971{
3972	getregdomain(s);
3973	if (channelsalso) {
3974		getchaninfo(s);
3975		spacer = ':';
3976		print_regdomain(&regdomain, 1);
3977		LINE_BREAK();
3978		print_channels(s, chaninfo, 1/*allchans*/, 1/*verbose*/);
3979	} else
3980		print_regdomain(&regdomain, verbose);
3981}
3982
3983static void
3984list_mesh(int s)
3985{
3986	struct ieee80211req ireq;
3987	struct ieee80211req_mesh_route routes[128];
3988	struct ieee80211req_mesh_route *rt;
3989
3990	(void) memset(&ireq, 0, sizeof(ireq));
3991	(void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
3992	ireq.i_type = IEEE80211_IOC_MESH_RTCMD;
3993	ireq.i_val = IEEE80211_MESH_RTCMD_LIST;
3994	ireq.i_data = &routes;
3995	ireq.i_len = sizeof(routes);
3996	if (ioctl(s, SIOCG80211, &ireq) < 0)
3997	 	err(1, "unable to get the Mesh routing table");
3998
3999	printf("%-17.17s %-17.17s %4s %4s %4s %6s %s\n"
4000		, "DEST"
4001		, "NEXT HOP"
4002		, "HOPS"
4003		, "METRIC"
4004		, "LIFETIME"
4005		, "MSEQ"
4006		, "FLAGS");
4007
4008	for (rt = &routes[0]; rt - &routes[0] < ireq.i_len / sizeof(*rt); rt++){
4009		printf("%s ",
4010		    ether_ntoa((const struct ether_addr *)rt->imr_dest));
4011		printf("%s %4u   %4u   %6u %6u    %c%c\n",
4012			ether_ntoa((const struct ether_addr *)rt->imr_nexthop),
4013			rt->imr_nhops, rt->imr_metric, rt->imr_lifetime,
4014			rt->imr_lastmseq,
4015			(rt->imr_flags & IEEE80211_MESHRT_FLAGS_DISCOVER) ?
4016			    'D' :
4017			(rt->imr_flags & IEEE80211_MESHRT_FLAGS_VALID) ?
4018			    'V' : '!',
4019			(rt->imr_flags & IEEE80211_MESHRT_FLAGS_PROXY) ?
4020			    'P' :
4021			(rt->imr_flags & IEEE80211_MESHRT_FLAGS_GATE) ?
4022			    'G' :' ');
4023	}
4024}
4025
4026static
4027DECL_CMD_FUNC(set80211list, arg, d)
4028{
4029#define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
4030
4031	LINE_INIT('\t');
4032
4033	if (iseq(arg, "sta"))
4034		list_stations(s);
4035	else if (iseq(arg, "scan") || iseq(arg, "ap"))
4036		list_scan(s);
4037	else if (iseq(arg, "chan") || iseq(arg, "freq"))
4038		list_channels(s, 1);
4039	else if (iseq(arg, "active"))
4040		list_channels(s, 0);
4041	else if (iseq(arg, "keys"))
4042		list_keys(s);
4043	else if (iseq(arg, "caps"))
4044		list_capabilities(s);
4045	else if (iseq(arg, "wme") || iseq(arg, "wmm"))
4046		list_wme(s);
4047	else if (iseq(arg, "mac"))
4048		list_mac(s);
4049	else if (iseq(arg, "txpow"))
4050		list_txpow(s);
4051	else if (iseq(arg, "roam"))
4052		list_roam(s);
4053	else if (iseq(arg, "txparam") || iseq(arg, "txparm"))
4054		list_txparams(s);
4055	else if (iseq(arg, "regdomain"))
4056		list_regdomain(s, 1);
4057	else if (iseq(arg, "countries"))
4058		list_countries();
4059	else if (iseq(arg, "mesh"))
4060		list_mesh(s);
4061	else
4062		errx(1, "Don't know how to list %s for %s", arg, name);
4063	LINE_BREAK();
4064#undef iseq
4065}
4066
4067static enum ieee80211_opmode
4068get80211opmode(int s)
4069{
4070	struct ifmediareq ifmr;
4071
4072	(void) memset(&ifmr, 0, sizeof(ifmr));
4073	(void) strlcpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
4074
4075	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
4076		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
4077			if (ifmr.ifm_current & IFM_FLAG0)
4078				return IEEE80211_M_AHDEMO;
4079			else
4080				return IEEE80211_M_IBSS;
4081		}
4082		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
4083			return IEEE80211_M_HOSTAP;
4084		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
4085			return IEEE80211_M_MONITOR;
4086		if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
4087			return IEEE80211_M_MBSS;
4088	}
4089	return IEEE80211_M_STA;
4090}
4091
4092#if 0
4093static void
4094printcipher(int s, struct ieee80211req *ireq, int keylenop)
4095{
4096	switch (ireq->i_val) {
4097	case IEEE80211_CIPHER_WEP:
4098		ireq->i_type = keylenop;
4099		if (ioctl(s, SIOCG80211, ireq) != -1)
4100			printf("WEP-%s",
4101			    ireq->i_len <= 5 ? "40" :
4102			    ireq->i_len <= 13 ? "104" : "128");
4103		else
4104			printf("WEP");
4105		break;
4106	case IEEE80211_CIPHER_TKIP:
4107		printf("TKIP");
4108		break;
4109	case IEEE80211_CIPHER_AES_OCB:
4110		printf("AES-OCB");
4111		break;
4112	case IEEE80211_CIPHER_AES_CCM:
4113		printf("AES-CCM");
4114		break;
4115	case IEEE80211_CIPHER_CKIP:
4116		printf("CKIP");
4117		break;
4118	case IEEE80211_CIPHER_NONE:
4119		printf("NONE");
4120		break;
4121	default:
4122		printf("UNKNOWN (0x%x)", ireq->i_val);
4123		break;
4124	}
4125}
4126#endif
4127
4128static void
4129printkey(const struct ieee80211req_key *ik)
4130{
4131	static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
4132	u_int keylen = ik->ik_keylen;
4133	int printcontents;
4134
4135	printcontents = printkeys &&
4136		(memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
4137	if (printcontents)
4138		LINE_BREAK();
4139	switch (ik->ik_type) {
4140	case IEEE80211_CIPHER_WEP:
4141		/* compatibility */
4142		LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
4143		    keylen <= 5 ? "40-bit" :
4144		    keylen <= 13 ? "104-bit" : "128-bit");
4145		break;
4146	case IEEE80211_CIPHER_TKIP:
4147		if (keylen > 128/8)
4148			keylen -= 128/8;	/* ignore MIC for now */
4149		LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4150		break;
4151	case IEEE80211_CIPHER_AES_OCB:
4152		LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4153		break;
4154	case IEEE80211_CIPHER_AES_CCM:
4155		LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4156		break;
4157	case IEEE80211_CIPHER_CKIP:
4158		LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4159		break;
4160	case IEEE80211_CIPHER_NONE:
4161		LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4162		break;
4163	default:
4164		LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
4165			ik->ik_type, ik->ik_keyix+1, 8*keylen);
4166		break;
4167	}
4168	if (printcontents) {
4169		u_int i;
4170
4171		printf(" <");
4172		for (i = 0; i < keylen; i++)
4173			printf("%02x", ik->ik_keydata[i]);
4174		printf(">");
4175		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
4176		    (ik->ik_keyrsc != 0 || verbose))
4177			printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
4178		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
4179		    (ik->ik_keytsc != 0 || verbose))
4180			printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
4181		if (ik->ik_flags != 0 && verbose) {
4182			const char *sep = " ";
4183
4184			if (ik->ik_flags & IEEE80211_KEY_XMIT)
4185				printf("%stx", sep), sep = "+";
4186			if (ik->ik_flags & IEEE80211_KEY_RECV)
4187				printf("%srx", sep), sep = "+";
4188			if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
4189				printf("%sdef", sep), sep = "+";
4190		}
4191		LINE_BREAK();
4192	}
4193}
4194
4195static void
4196printrate(const char *tag, int v, int defrate, int defmcs)
4197{
4198	if ((v & IEEE80211_RATE_MCS) == 0) {
4199		if (v != defrate) {
4200			if (v & 1)
4201				LINE_CHECK("%s %d.5", tag, v/2);
4202			else
4203				LINE_CHECK("%s %d", tag, v/2);
4204		}
4205	} else {
4206		if (v != defmcs)
4207			LINE_CHECK("%s %d", tag, v &~ 0x80);
4208	}
4209}
4210
4211static int
4212getid(int s, int ix, void *data, size_t len, int *plen, int mesh)
4213{
4214	struct ieee80211req ireq;
4215
4216	(void) memset(&ireq, 0, sizeof(ireq));
4217	(void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
4218	ireq.i_type = (!mesh) ? IEEE80211_IOC_SSID : IEEE80211_IOC_MESH_ID;
4219	ireq.i_val = ix;
4220	ireq.i_data = data;
4221	ireq.i_len = len;
4222	if (ioctl(s, SIOCG80211, &ireq) < 0)
4223		return -1;
4224	*plen = ireq.i_len;
4225	return 0;
4226}
4227
4228static void
4229ieee80211_status(int s)
4230{
4231	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
4232	enum ieee80211_opmode opmode = get80211opmode(s);
4233	int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode;
4234	uint8_t data[32];
4235	const struct ieee80211_channel *c;
4236	const struct ieee80211_roamparam *rp;
4237	const struct ieee80211_txparam *tp;
4238
4239	if (getid(s, -1, data, sizeof(data), &len, 0) < 0) {
4240		/* If we can't get the SSID, this isn't an 802.11 device. */
4241		return;
4242	}
4243
4244	/*
4245	 * Invalidate cached state so printing status for multiple
4246	 * if's doesn't reuse the first interfaces' cached state.
4247	 */
4248	gotcurchan = 0;
4249	gotroam = 0;
4250	gottxparams = 0;
4251	gothtconf = 0;
4252	gotregdomain = 0;
4253
4254	printf("\t");
4255	if (opmode == IEEE80211_M_MBSS) {
4256		printf("meshid ");
4257		getid(s, 0, data, sizeof(data), &len, 1);
4258		print_string(data, len);
4259	} else {
4260		if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
4261			num = 0;
4262		printf("ssid ");
4263		if (num > 1) {
4264			for (i = 0; i < num; i++) {
4265				if (getid(s, i, data, sizeof(data), &len, 0) >= 0 && len > 0) {
4266					printf(" %d:", i + 1);
4267					print_string(data, len);
4268				}
4269			}
4270		} else
4271			print_string(data, len);
4272	}
4273	c = getcurchan(s);
4274	if (c->ic_freq != IEEE80211_CHAN_ANY) {
4275		char buf[14];
4276		printf(" channel %d (%u MHz%s)", c->ic_ieee, c->ic_freq,
4277			get_chaninfo(c, 1, buf, sizeof(buf)));
4278	} else if (verbose)
4279		printf(" channel UNDEF");
4280
4281	if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 &&
4282	    (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
4283		printf(" bssid %s", ether_ntoa((struct ether_addr *)data));
4284
4285	if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) {
4286		printf("\n\tstationname ");
4287		print_string(data, len);
4288	}
4289
4290	spacer = ' ';		/* force first break */
4291	LINE_BREAK();
4292
4293	list_regdomain(s, 0);
4294
4295	wpa = 0;
4296	if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) {
4297		switch (val) {
4298		case IEEE80211_AUTH_NONE:
4299			LINE_CHECK("authmode NONE");
4300			break;
4301		case IEEE80211_AUTH_OPEN:
4302			LINE_CHECK("authmode OPEN");
4303			break;
4304		case IEEE80211_AUTH_SHARED:
4305			LINE_CHECK("authmode SHARED");
4306			break;
4307		case IEEE80211_AUTH_8021X:
4308			LINE_CHECK("authmode 802.1x");
4309			break;
4310		case IEEE80211_AUTH_WPA:
4311			if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0)
4312				wpa = 1;	/* default to WPA1 */
4313			switch (wpa) {
4314			case 2:
4315				LINE_CHECK("authmode WPA2/802.11i");
4316				break;
4317			case 3:
4318				LINE_CHECK("authmode WPA1+WPA2/802.11i");
4319				break;
4320			default:
4321				LINE_CHECK("authmode WPA");
4322				break;
4323			}
4324			break;
4325		case IEEE80211_AUTH_AUTO:
4326			LINE_CHECK("authmode AUTO");
4327			break;
4328		default:
4329			LINE_CHECK("authmode UNKNOWN (0x%x)", val);
4330			break;
4331		}
4332	}
4333
4334	if (wpa || verbose) {
4335		if (get80211val(s, IEEE80211_IOC_WPS, &val) != -1) {
4336			if (val)
4337				LINE_CHECK("wps");
4338			else if (verbose)
4339				LINE_CHECK("-wps");
4340		}
4341		if (get80211val(s, IEEE80211_IOC_TSN, &val) != -1) {
4342			if (val)
4343				LINE_CHECK("tsn");
4344			else if (verbose)
4345				LINE_CHECK("-tsn");
4346		}
4347		if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) {
4348			if (val)
4349				LINE_CHECK("countermeasures");
4350			else if (verbose)
4351				LINE_CHECK("-countermeasures");
4352		}
4353#if 0
4354		/* XXX not interesting with WPA done in user space */
4355		ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
4356		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4357		}
4358
4359		ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
4360		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4361			LINE_CHECK("mcastcipher ");
4362			printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
4363			spacer = ' ';
4364		}
4365
4366		ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
4367		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4368			LINE_CHECK("ucastcipher ");
4369			printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
4370		}
4371
4372		if (wpa & 2) {
4373			ireq.i_type = IEEE80211_IOC_RSNCAPS;
4374			if (ioctl(s, SIOCG80211, &ireq) != -1) {
4375				LINE_CHECK("RSN caps 0x%x", ireq.i_val);
4376				spacer = ' ';
4377			}
4378		}
4379
4380		ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
4381		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4382		}
4383#endif
4384	}
4385
4386	if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 &&
4387	    wepmode != IEEE80211_WEP_NOSUP) {
4388
4389		switch (wepmode) {
4390		case IEEE80211_WEP_OFF:
4391			LINE_CHECK("privacy OFF");
4392			break;
4393		case IEEE80211_WEP_ON:
4394			LINE_CHECK("privacy ON");
4395			break;
4396		case IEEE80211_WEP_MIXED:
4397			LINE_CHECK("privacy MIXED");
4398			break;
4399		default:
4400			LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
4401			break;
4402		}
4403
4404		/*
4405		 * If we get here then we've got WEP support so we need
4406		 * to print WEP status.
4407		 */
4408
4409		if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) {
4410			warn("WEP support, but no tx key!");
4411			goto end;
4412		}
4413		if (val != -1)
4414			LINE_CHECK("deftxkey %d", val+1);
4415		else if (wepmode != IEEE80211_WEP_OFF || verbose)
4416			LINE_CHECK("deftxkey UNDEF");
4417
4418		if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) {
4419			warn("WEP support, but no NUMWEPKEYS support!");
4420			goto end;
4421		}
4422
4423		for (i = 0; i < num; i++) {
4424			struct ieee80211req_key ik;
4425
4426			memset(&ik, 0, sizeof(ik));
4427			ik.ik_keyix = i;
4428			if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) {
4429				warn("WEP support, but can get keys!");
4430				goto end;
4431			}
4432			if (ik.ik_keylen != 0) {
4433				if (verbose)
4434					LINE_BREAK();
4435				printkey(&ik);
4436			}
4437		}
4438end:
4439		;
4440	}
4441
4442	if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 &&
4443	    val != IEEE80211_POWERSAVE_NOSUP ) {
4444		if (val != IEEE80211_POWERSAVE_OFF || verbose) {
4445			switch (val) {
4446			case IEEE80211_POWERSAVE_OFF:
4447				LINE_CHECK("powersavemode OFF");
4448				break;
4449			case IEEE80211_POWERSAVE_CAM:
4450				LINE_CHECK("powersavemode CAM");
4451				break;
4452			case IEEE80211_POWERSAVE_PSP:
4453				LINE_CHECK("powersavemode PSP");
4454				break;
4455			case IEEE80211_POWERSAVE_PSP_CAM:
4456				LINE_CHECK("powersavemode PSP-CAM");
4457				break;
4458			}
4459			if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1)
4460				LINE_CHECK("powersavesleep %d", val);
4461		}
4462	}
4463
4464	if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) {
4465		if (val & 1)
4466			LINE_CHECK("txpower %d.5", val/2);
4467		else
4468			LINE_CHECK("txpower %d", val/2);
4469	}
4470	if (verbose) {
4471		if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1)
4472			LINE_CHECK("txpowmax %.1f", val/2.);
4473	}
4474
4475	if (get80211val(s, IEEE80211_IOC_DOTD, &val) != -1) {
4476		if (val)
4477			LINE_CHECK("dotd");
4478		else if (verbose)
4479			LINE_CHECK("-dotd");
4480	}
4481
4482	if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) {
4483		if (val != IEEE80211_RTS_MAX || verbose)
4484			LINE_CHECK("rtsthreshold %d", val);
4485	}
4486
4487	if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) {
4488		if (val != IEEE80211_FRAG_MAX || verbose)
4489			LINE_CHECK("fragthreshold %d", val);
4490	}
4491	if (opmode == IEEE80211_M_STA || verbose) {
4492		if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) {
4493			if (val != IEEE80211_HWBMISS_MAX || verbose)
4494				LINE_CHECK("bmiss %d", val);
4495		}
4496	}
4497
4498	if (!verbose) {
4499		gettxparams(s);
4500		tp = &txparams.params[chan2mode(c)];
4501		printrate("ucastrate", tp->ucastrate,
4502		    IEEE80211_FIXED_RATE_NONE, IEEE80211_FIXED_RATE_NONE);
4503		printrate("mcastrate", tp->mcastrate, 2*1,
4504		    IEEE80211_RATE_MCS|0);
4505		printrate("mgmtrate", tp->mgmtrate, 2*1,
4506		    IEEE80211_RATE_MCS|0);
4507		if (tp->maxretry != 6)		/* XXX */
4508			LINE_CHECK("maxretry %d", tp->maxretry);
4509	} else {
4510		LINE_BREAK();
4511		list_txparams(s);
4512	}
4513
4514	bgscaninterval = -1;
4515	(void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval);
4516
4517	if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) {
4518		if (val != bgscaninterval || verbose)
4519			LINE_CHECK("scanvalid %u", val);
4520	}
4521
4522	bgscan = 0;
4523	if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) {
4524		if (bgscan)
4525			LINE_CHECK("bgscan");
4526		else if (verbose)
4527			LINE_CHECK("-bgscan");
4528	}
4529	if (bgscan || verbose) {
4530		if (bgscaninterval != -1)
4531			LINE_CHECK("bgscanintvl %u", bgscaninterval);
4532		if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1)
4533			LINE_CHECK("bgscanidle %u", val);
4534		if (!verbose) {
4535			getroam(s);
4536			rp = &roamparams.params[chan2mode(c)];
4537			if (rp->rssi & 1)
4538				LINE_CHECK("roam:rssi %u.5", rp->rssi/2);
4539			else
4540				LINE_CHECK("roam:rssi %u", rp->rssi/2);
4541			LINE_CHECK("roam:rate %u", rp->rate/2);
4542		} else {
4543			LINE_BREAK();
4544			list_roam(s);
4545			LINE_BREAK();
4546		}
4547	}
4548
4549	if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
4550		if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) {
4551			if (val)
4552				LINE_CHECK("pureg");
4553			else if (verbose)
4554				LINE_CHECK("-pureg");
4555		}
4556		if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) {
4557			switch (val) {
4558			case IEEE80211_PROTMODE_OFF:
4559				LINE_CHECK("protmode OFF");
4560				break;
4561			case IEEE80211_PROTMODE_CTS:
4562				LINE_CHECK("protmode CTS");
4563				break;
4564			case IEEE80211_PROTMODE_RTSCTS:
4565				LINE_CHECK("protmode RTSCTS");
4566				break;
4567			default:
4568				LINE_CHECK("protmode UNKNOWN (0x%x)", val);
4569				break;
4570			}
4571		}
4572	}
4573
4574	if (IEEE80211_IS_CHAN_HT(c) || verbose) {
4575		gethtconf(s);
4576		switch (htconf & 3) {
4577		case 0:
4578		case 2:
4579			LINE_CHECK("-ht");
4580			break;
4581		case 1:
4582			LINE_CHECK("ht20");
4583			break;
4584		case 3:
4585			if (verbose)
4586				LINE_CHECK("ht");
4587			break;
4588		}
4589		if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) {
4590			if (!val)
4591				LINE_CHECK("-htcompat");
4592			else if (verbose)
4593				LINE_CHECK("htcompat");
4594		}
4595		if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) {
4596			switch (val) {
4597			case 0:
4598				LINE_CHECK("-ampdu");
4599				break;
4600			case 1:
4601				LINE_CHECK("ampdutx -ampdurx");
4602				break;
4603			case 2:
4604				LINE_CHECK("-ampdutx ampdurx");
4605				break;
4606			case 3:
4607				if (verbose)
4608					LINE_CHECK("ampdu");
4609				break;
4610			}
4611		}
4612		if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) {
4613			switch (val) {
4614			case IEEE80211_HTCAP_MAXRXAMPDU_8K:
4615				LINE_CHECK("ampdulimit 8k");
4616				break;
4617			case IEEE80211_HTCAP_MAXRXAMPDU_16K:
4618				LINE_CHECK("ampdulimit 16k");
4619				break;
4620			case IEEE80211_HTCAP_MAXRXAMPDU_32K:
4621				LINE_CHECK("ampdulimit 32k");
4622				break;
4623			case IEEE80211_HTCAP_MAXRXAMPDU_64K:
4624				LINE_CHECK("ampdulimit 64k");
4625				break;
4626			}
4627		}
4628		if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) {
4629			switch (val) {
4630			case IEEE80211_HTCAP_MPDUDENSITY_NA:
4631				if (verbose)
4632					LINE_CHECK("ampdudensity NA");
4633				break;
4634			case IEEE80211_HTCAP_MPDUDENSITY_025:
4635				LINE_CHECK("ampdudensity .25");
4636				break;
4637			case IEEE80211_HTCAP_MPDUDENSITY_05:
4638				LINE_CHECK("ampdudensity .5");
4639				break;
4640			case IEEE80211_HTCAP_MPDUDENSITY_1:
4641				LINE_CHECK("ampdudensity 1");
4642				break;
4643			case IEEE80211_HTCAP_MPDUDENSITY_2:
4644				LINE_CHECK("ampdudensity 2");
4645				break;
4646			case IEEE80211_HTCAP_MPDUDENSITY_4:
4647				LINE_CHECK("ampdudensity 4");
4648				break;
4649			case IEEE80211_HTCAP_MPDUDENSITY_8:
4650				LINE_CHECK("ampdudensity 8");
4651				break;
4652			case IEEE80211_HTCAP_MPDUDENSITY_16:
4653				LINE_CHECK("ampdudensity 16");
4654				break;
4655			}
4656		}
4657		if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) {
4658			switch (val) {
4659			case 0:
4660				LINE_CHECK("-amsdu");
4661				break;
4662			case 1:
4663				LINE_CHECK("amsdutx -amsdurx");
4664				break;
4665			case 2:
4666				LINE_CHECK("-amsdutx amsdurx");
4667				break;
4668			case 3:
4669				if (verbose)
4670					LINE_CHECK("amsdu");
4671				break;
4672			}
4673		}
4674		/* XXX amsdu limit */
4675		if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) {
4676			if (val)
4677				LINE_CHECK("shortgi");
4678			else if (verbose)
4679				LINE_CHECK("-shortgi");
4680		}
4681		if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) {
4682			if (val == IEEE80211_PROTMODE_OFF)
4683				LINE_CHECK("htprotmode OFF");
4684			else if (val != IEEE80211_PROTMODE_RTSCTS)
4685				LINE_CHECK("htprotmode UNKNOWN (0x%x)", val);
4686			else if (verbose)
4687				LINE_CHECK("htprotmode RTSCTS");
4688		}
4689		if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) {
4690			if (val)
4691				LINE_CHECK("puren");
4692			else if (verbose)
4693				LINE_CHECK("-puren");
4694		}
4695		if (get80211val(s, IEEE80211_IOC_SMPS, &val) != -1) {
4696			if (val == IEEE80211_HTCAP_SMPS_DYNAMIC)
4697				LINE_CHECK("smpsdyn");
4698			else if (val == IEEE80211_HTCAP_SMPS_ENA)
4699				LINE_CHECK("smps");
4700			else if (verbose)
4701				LINE_CHECK("-smps");
4702		}
4703		if (get80211val(s, IEEE80211_IOC_RIFS, &val) != -1) {
4704			if (val)
4705				LINE_CHECK("rifs");
4706			else if (verbose)
4707				LINE_CHECK("-rifs");
4708		}
4709	}
4710
4711	if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) {
4712		if (wme)
4713			LINE_CHECK("wme");
4714		else if (verbose)
4715			LINE_CHECK("-wme");
4716	} else
4717		wme = 0;
4718
4719	if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) {
4720		if (val)
4721			LINE_CHECK("burst");
4722		else if (verbose)
4723			LINE_CHECK("-burst");
4724	}
4725
4726	if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) {
4727		if (val)
4728			LINE_CHECK("ff");
4729		else if (verbose)
4730			LINE_CHECK("-ff");
4731	}
4732	if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) {
4733		if (val)
4734			LINE_CHECK("dturbo");
4735		else if (verbose)
4736			LINE_CHECK("-dturbo");
4737	}
4738	if (get80211val(s, IEEE80211_IOC_DWDS, &val) != -1) {
4739		if (val)
4740			LINE_CHECK("dwds");
4741		else if (verbose)
4742			LINE_CHECK("-dwds");
4743	}
4744
4745	if (opmode == IEEE80211_M_HOSTAP) {
4746		if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) {
4747			if (val)
4748				LINE_CHECK("hidessid");
4749			else if (verbose)
4750				LINE_CHECK("-hidessid");
4751		}
4752		if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) {
4753			if (!val)
4754				LINE_CHECK("-apbridge");
4755			else if (verbose)
4756				LINE_CHECK("apbridge");
4757		}
4758		if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1)
4759			LINE_CHECK("dtimperiod %u", val);
4760
4761		if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) {
4762			if (!val)
4763				LINE_CHECK("-doth");
4764			else if (verbose)
4765				LINE_CHECK("doth");
4766		}
4767		if (get80211val(s, IEEE80211_IOC_DFS, &val) != -1) {
4768			if (!val)
4769				LINE_CHECK("-dfs");
4770			else if (verbose)
4771				LINE_CHECK("dfs");
4772		}
4773		if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) {
4774			if (!val)
4775				LINE_CHECK("-inact");
4776			else if (verbose)
4777				LINE_CHECK("inact");
4778		}
4779	} else {
4780		if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) {
4781			if (val != IEEE80211_ROAMING_AUTO || verbose) {
4782				switch (val) {
4783				case IEEE80211_ROAMING_DEVICE:
4784					LINE_CHECK("roaming DEVICE");
4785					break;
4786				case IEEE80211_ROAMING_AUTO:
4787					LINE_CHECK("roaming AUTO");
4788					break;
4789				case IEEE80211_ROAMING_MANUAL:
4790					LINE_CHECK("roaming MANUAL");
4791					break;
4792				default:
4793					LINE_CHECK("roaming UNKNOWN (0x%x)",
4794						val);
4795					break;
4796				}
4797			}
4798		}
4799	}
4800
4801	if (opmode == IEEE80211_M_AHDEMO) {
4802		if (get80211val(s, IEEE80211_IOC_TDMA_SLOT, &val) != -1)
4803			LINE_CHECK("tdmaslot %u", val);
4804		if (get80211val(s, IEEE80211_IOC_TDMA_SLOTCNT, &val) != -1)
4805			LINE_CHECK("tdmaslotcnt %u", val);
4806		if (get80211val(s, IEEE80211_IOC_TDMA_SLOTLEN, &val) != -1)
4807			LINE_CHECK("tdmaslotlen %u", val);
4808		if (get80211val(s, IEEE80211_IOC_TDMA_BINTERVAL, &val) != -1)
4809			LINE_CHECK("tdmabintval %u", val);
4810	} else if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) {
4811		/* XXX default define not visible */
4812		if (val != 100 || verbose)
4813			LINE_CHECK("bintval %u", val);
4814	}
4815
4816	if (wme && verbose) {
4817		LINE_BREAK();
4818		list_wme(s);
4819	}
4820
4821	if (opmode == IEEE80211_M_MBSS) {
4822		if (get80211val(s, IEEE80211_IOC_MESH_TTL, &val) != -1) {
4823			LINE_CHECK("meshttl %u", val);
4824		}
4825		if (get80211val(s, IEEE80211_IOC_MESH_AP, &val) != -1) {
4826			if (val)
4827				LINE_CHECK("meshpeering");
4828			else
4829				LINE_CHECK("-meshpeering");
4830		}
4831		if (get80211val(s, IEEE80211_IOC_MESH_FWRD, &val) != -1) {
4832			if (val)
4833				LINE_CHECK("meshforward");
4834			else
4835				LINE_CHECK("-meshforward");
4836		}
4837		if (get80211val(s, IEEE80211_IOC_MESH_GATE, &val) != -1) {
4838			if (val)
4839				LINE_CHECK("meshgate");
4840			else
4841				LINE_CHECK("-meshgate");
4842		}
4843		if (get80211len(s, IEEE80211_IOC_MESH_PR_METRIC, data, 12,
4844		    &len) != -1) {
4845			data[len] = '\0';
4846			LINE_CHECK("meshmetric %s", data);
4847		}
4848		if (get80211len(s, IEEE80211_IOC_MESH_PR_PATH, data, 12,
4849		    &len) != -1) {
4850			data[len] = '\0';
4851			LINE_CHECK("meshpath %s", data);
4852		}
4853		if (get80211val(s, IEEE80211_IOC_HWMP_ROOTMODE, &val) != -1) {
4854			switch (val) {
4855			case IEEE80211_HWMP_ROOTMODE_DISABLED:
4856				LINE_CHECK("hwmprootmode DISABLED");
4857				break;
4858			case IEEE80211_HWMP_ROOTMODE_NORMAL:
4859				LINE_CHECK("hwmprootmode NORMAL");
4860				break;
4861			case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
4862				LINE_CHECK("hwmprootmode PROACTIVE");
4863				break;
4864			case IEEE80211_HWMP_ROOTMODE_RANN:
4865				LINE_CHECK("hwmprootmode RANN");
4866				break;
4867			default:
4868				LINE_CHECK("hwmprootmode UNKNOWN(%d)", val);
4869				break;
4870			}
4871		}
4872		if (get80211val(s, IEEE80211_IOC_HWMP_MAXHOPS, &val) != -1) {
4873			LINE_CHECK("hwmpmaxhops %u", val);
4874		}
4875	}
4876
4877	LINE_BREAK();
4878}
4879
4880static int
4881get80211(int s, int type, void *data, int len)
4882{
4883	struct ieee80211req ireq;
4884
4885	(void) memset(&ireq, 0, sizeof(ireq));
4886	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4887	ireq.i_type = type;
4888	ireq.i_data = data;
4889	ireq.i_len = len;
4890	return ioctl(s, SIOCG80211, &ireq);
4891}
4892
4893static int
4894get80211len(int s, int type, void *data, int len, int *plen)
4895{
4896	struct ieee80211req ireq;
4897
4898	(void) memset(&ireq, 0, sizeof(ireq));
4899	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4900	ireq.i_type = type;
4901	ireq.i_len = len;
4902	assert(ireq.i_len == len);	/* NB: check for 16-bit truncation */
4903	ireq.i_data = data;
4904	if (ioctl(s, SIOCG80211, &ireq) < 0)
4905		return -1;
4906	*plen = ireq.i_len;
4907	return 0;
4908}
4909
4910static int
4911get80211val(int s, int type, int *val)
4912{
4913	struct ieee80211req ireq;
4914
4915	(void) memset(&ireq, 0, sizeof(ireq));
4916	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4917	ireq.i_type = type;
4918	if (ioctl(s, SIOCG80211, &ireq) < 0)
4919		return -1;
4920	*val = ireq.i_val;
4921	return 0;
4922}
4923
4924static void
4925set80211(int s, int type, int val, int len, void *data)
4926{
4927	struct ieee80211req	ireq;
4928
4929	(void) memset(&ireq, 0, sizeof(ireq));
4930	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4931	ireq.i_type = type;
4932	ireq.i_val = val;
4933	ireq.i_len = len;
4934	assert(ireq.i_len == len);	/* NB: check for 16-bit truncation */
4935	ireq.i_data = data;
4936	if (ioctl(s, SIOCS80211, &ireq) < 0)
4937		err(1, "SIOCS80211");
4938}
4939
4940static const char *
4941get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
4942{
4943	int len;
4944	int hexstr;
4945	u_int8_t *p;
4946
4947	len = *lenp;
4948	p = buf;
4949	hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
4950	if (hexstr)
4951		val += 2;
4952	for (;;) {
4953		if (*val == '\0')
4954			break;
4955		if (sep != NULL && strchr(sep, *val) != NULL) {
4956			val++;
4957			break;
4958		}
4959		if (hexstr) {
4960			if (!isxdigit((u_char)val[0])) {
4961				warnx("bad hexadecimal digits");
4962				return NULL;
4963			}
4964			if (!isxdigit((u_char)val[1])) {
4965				warnx("odd count hexadecimal digits");
4966				return NULL;
4967			}
4968		}
4969		if (p >= buf + len) {
4970			if (hexstr)
4971				warnx("hexadecimal digits too long");
4972			else
4973				warnx("string too long");
4974			return NULL;
4975		}
4976		if (hexstr) {
4977#define	tohex(x)	(isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
4978			*p++ = (tohex((u_char)val[0]) << 4) |
4979			    tohex((u_char)val[1]);
4980#undef tohex
4981			val += 2;
4982		} else
4983			*p++ = *val++;
4984	}
4985	len = p - buf;
4986	/* The string "-" is treated as the empty string. */
4987	if (!hexstr && len == 1 && buf[0] == '-') {
4988		len = 0;
4989		memset(buf, 0, *lenp);
4990	} else if (len < *lenp)
4991		memset(p, 0, *lenp - len);
4992	*lenp = len;
4993	return val;
4994}
4995
4996static void
4997print_string(const u_int8_t *buf, int len)
4998{
4999	int i;
5000	int hasspc;
5001
5002	i = 0;
5003	hasspc = 0;
5004	for (; i < len; i++) {
5005		if (!isprint(buf[i]) && buf[i] != '\0')
5006			break;
5007		if (isspace(buf[i]))
5008			hasspc++;
5009	}
5010	if (i == len) {
5011		if (hasspc || len == 0 || buf[0] == '\0')
5012			printf("\"%.*s\"", len, buf);
5013		else
5014			printf("%.*s", len, buf);
5015	} else {
5016		printf("0x");
5017		for (i = 0; i < len; i++)
5018			printf("%02x", buf[i]);
5019	}
5020}
5021
5022/*
5023 * Virtual AP cloning support.
5024 */
5025static struct ieee80211_clone_params params = {
5026	.icp_opmode	= IEEE80211_M_STA,	/* default to station mode */
5027};
5028
5029static void
5030wlan_create(int s, struct ifreq *ifr)
5031{
5032	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
5033
5034	if (params.icp_parent[0] == '\0')
5035		errx(1, "must specify a parent device (wlandev) when creating "
5036		    "a wlan device");
5037	if (params.icp_opmode == IEEE80211_M_WDS &&
5038	    memcmp(params.icp_bssid, zerobssid, sizeof(zerobssid)) == 0)
5039		errx(1, "no bssid specified for WDS (use wlanbssid)");
5040	ifr->ifr_data = (caddr_t) &params;
5041	if (ioctl(s, SIOCIFCREATE2, ifr) < 0)
5042		err(1, "SIOCIFCREATE2");
5043}
5044
5045static
5046DECL_CMD_FUNC(set80211clone_wlandev, arg, d)
5047{
5048	strlcpy(params.icp_parent, arg, IFNAMSIZ);
5049}
5050
5051static
5052DECL_CMD_FUNC(set80211clone_wlanbssid, arg, d)
5053{
5054	const struct ether_addr *ea;
5055
5056	ea = ether_aton(arg);
5057	if (ea == NULL)
5058		errx(1, "%s: cannot parse bssid", arg);
5059	memcpy(params.icp_bssid, ea->octet, IEEE80211_ADDR_LEN);
5060}
5061
5062static
5063DECL_CMD_FUNC(set80211clone_wlanaddr, arg, d)
5064{
5065	const struct ether_addr *ea;
5066
5067	ea = ether_aton(arg);
5068	if (ea == NULL)
5069		errx(1, "%s: cannot parse address", arg);
5070	memcpy(params.icp_macaddr, ea->octet, IEEE80211_ADDR_LEN);
5071	params.icp_flags |= IEEE80211_CLONE_MACADDR;
5072}
5073
5074static
5075DECL_CMD_FUNC(set80211clone_wlanmode, arg, d)
5076{
5077#define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
5078	if (iseq(arg, "sta"))
5079		params.icp_opmode = IEEE80211_M_STA;
5080	else if (iseq(arg, "ahdemo") || iseq(arg, "adhoc-demo"))
5081		params.icp_opmode = IEEE80211_M_AHDEMO;
5082	else if (iseq(arg, "ibss") || iseq(arg, "adhoc"))
5083		params.icp_opmode = IEEE80211_M_IBSS;
5084	else if (iseq(arg, "ap") || iseq(arg, "host"))
5085		params.icp_opmode = IEEE80211_M_HOSTAP;
5086	else if (iseq(arg, "wds"))
5087		params.icp_opmode = IEEE80211_M_WDS;
5088	else if (iseq(arg, "monitor"))
5089		params.icp_opmode = IEEE80211_M_MONITOR;
5090	else if (iseq(arg, "tdma")) {
5091		params.icp_opmode = IEEE80211_M_AHDEMO;
5092		params.icp_flags |= IEEE80211_CLONE_TDMA;
5093	} else if (iseq(arg, "mesh") || iseq(arg, "mp")) /* mesh point */
5094		params.icp_opmode = IEEE80211_M_MBSS;
5095	else
5096		errx(1, "Don't know to create %s for %s", arg, name);
5097#undef iseq
5098}
5099
5100static void
5101set80211clone_beacons(const char *val, int d, int s, const struct afswtch *rafp)
5102{
5103	/* NB: inverted sense */
5104	if (d)
5105		params.icp_flags &= ~IEEE80211_CLONE_NOBEACONS;
5106	else
5107		params.icp_flags |= IEEE80211_CLONE_NOBEACONS;
5108}
5109
5110static void
5111set80211clone_bssid(const char *val, int d, int s, const struct afswtch *rafp)
5112{
5113	if (d)
5114		params.icp_flags |= IEEE80211_CLONE_BSSID;
5115	else
5116		params.icp_flags &= ~IEEE80211_CLONE_BSSID;
5117}
5118
5119static void
5120set80211clone_wdslegacy(const char *val, int d, int s, const struct afswtch *rafp)
5121{
5122	if (d)
5123		params.icp_flags |= IEEE80211_CLONE_WDSLEGACY;
5124	else
5125		params.icp_flags &= ~IEEE80211_CLONE_WDSLEGACY;
5126}
5127
5128static struct cmd ieee80211_cmds[] = {
5129	DEF_CMD_ARG("ssid",		set80211ssid),
5130	DEF_CMD_ARG("nwid",		set80211ssid),
5131	DEF_CMD_ARG("meshid",		set80211meshid),
5132	DEF_CMD_ARG("stationname",	set80211stationname),
5133	DEF_CMD_ARG("station",		set80211stationname),	/* BSD/OS */
5134	DEF_CMD_ARG("channel",		set80211channel),
5135	DEF_CMD_ARG("authmode",		set80211authmode),
5136	DEF_CMD_ARG("powersavemode",	set80211powersavemode),
5137	DEF_CMD("powersave",	1,	set80211powersave),
5138	DEF_CMD("-powersave",	0,	set80211powersave),
5139	DEF_CMD_ARG("powersavesleep", 	set80211powersavesleep),
5140	DEF_CMD_ARG("wepmode",		set80211wepmode),
5141	DEF_CMD("wep",		1,	set80211wep),
5142	DEF_CMD("-wep",		0,	set80211wep),
5143	DEF_CMD_ARG("deftxkey",		set80211weptxkey),
5144	DEF_CMD_ARG("weptxkey",		set80211weptxkey),
5145	DEF_CMD_ARG("wepkey",		set80211wepkey),
5146	DEF_CMD_ARG("nwkey",		set80211nwkey),		/* NetBSD */
5147	DEF_CMD("-nwkey",	0,	set80211wep),		/* NetBSD */
5148	DEF_CMD_ARG("rtsthreshold",	set80211rtsthreshold),
5149	DEF_CMD_ARG("protmode",		set80211protmode),
5150	DEF_CMD_ARG("txpower",		set80211txpower),
5151	DEF_CMD_ARG("roaming",		set80211roaming),
5152	DEF_CMD("wme",		1,	set80211wme),
5153	DEF_CMD("-wme",		0,	set80211wme),
5154	DEF_CMD("wmm",		1,	set80211wme),
5155	DEF_CMD("-wmm",		0,	set80211wme),
5156	DEF_CMD("hidessid",	1,	set80211hidessid),
5157	DEF_CMD("-hidessid",	0,	set80211hidessid),
5158	DEF_CMD("apbridge",	1,	set80211apbridge),
5159	DEF_CMD("-apbridge",	0,	set80211apbridge),
5160	DEF_CMD_ARG("chanlist",		set80211chanlist),
5161	DEF_CMD_ARG("bssid",		set80211bssid),
5162	DEF_CMD_ARG("ap",		set80211bssid),
5163	DEF_CMD("scan",	0,		set80211scan),
5164	DEF_CMD_ARG("list",		set80211list),
5165	DEF_CMD_ARG2("cwmin",		set80211cwmin),
5166	DEF_CMD_ARG2("cwmax",		set80211cwmax),
5167	DEF_CMD_ARG2("aifs",		set80211aifs),
5168	DEF_CMD_ARG2("txoplimit",	set80211txoplimit),
5169	DEF_CMD_ARG("acm",		set80211acm),
5170	DEF_CMD_ARG("-acm",		set80211noacm),
5171	DEF_CMD_ARG("ack",		set80211ackpolicy),
5172	DEF_CMD_ARG("-ack",		set80211noackpolicy),
5173	DEF_CMD_ARG2("bss:cwmin",	set80211bsscwmin),
5174	DEF_CMD_ARG2("bss:cwmax",	set80211bsscwmax),
5175	DEF_CMD_ARG2("bss:aifs",	set80211bssaifs),
5176	DEF_CMD_ARG2("bss:txoplimit",	set80211bsstxoplimit),
5177	DEF_CMD_ARG("dtimperiod",	set80211dtimperiod),
5178	DEF_CMD_ARG("bintval",		set80211bintval),
5179	DEF_CMD("mac:open",	IEEE80211_MACCMD_POLICY_OPEN,	set80211maccmd),
5180	DEF_CMD("mac:allow",	IEEE80211_MACCMD_POLICY_ALLOW,	set80211maccmd),
5181	DEF_CMD("mac:deny",	IEEE80211_MACCMD_POLICY_DENY,	set80211maccmd),
5182	DEF_CMD("mac:radius",	IEEE80211_MACCMD_POLICY_RADIUS,	set80211maccmd),
5183	DEF_CMD("mac:flush",	IEEE80211_MACCMD_FLUSH,		set80211maccmd),
5184	DEF_CMD("mac:detach",	IEEE80211_MACCMD_DETACH,	set80211maccmd),
5185	DEF_CMD_ARG("mac:add",		set80211addmac),
5186	DEF_CMD_ARG("mac:del",		set80211delmac),
5187	DEF_CMD_ARG("mac:kick",		set80211kickmac),
5188	DEF_CMD("pureg",	1,	set80211pureg),
5189	DEF_CMD("-pureg",	0,	set80211pureg),
5190	DEF_CMD("ff",		1,	set80211fastframes),
5191	DEF_CMD("-ff",		0,	set80211fastframes),
5192	DEF_CMD("dturbo",	1,	set80211dturbo),
5193	DEF_CMD("-dturbo",	0,	set80211dturbo),
5194	DEF_CMD("bgscan",	1,	set80211bgscan),
5195	DEF_CMD("-bgscan",	0,	set80211bgscan),
5196	DEF_CMD_ARG("bgscanidle",	set80211bgscanidle),
5197	DEF_CMD_ARG("bgscanintvl",	set80211bgscanintvl),
5198	DEF_CMD_ARG("scanvalid",	set80211scanvalid),
5199	DEF_CMD("quiet",        1,      set80211quiet),
5200	DEF_CMD("-quiet",       0,      set80211quiet),
5201	DEF_CMD_ARG("quiet_count",      set80211quietcount),
5202	DEF_CMD_ARG("quiet_period",     set80211quietperiod),
5203	DEF_CMD_ARG("quiet_dur",        set80211quietduration),
5204	DEF_CMD_ARG("quiet_offset",     set80211quietoffset),
5205	DEF_CMD_ARG("roam:rssi",	set80211roamrssi),
5206	DEF_CMD_ARG("roam:rate",	set80211roamrate),
5207	DEF_CMD_ARG("mcastrate",	set80211mcastrate),
5208	DEF_CMD_ARG("ucastrate",	set80211ucastrate),
5209	DEF_CMD_ARG("mgtrate",		set80211mgtrate),
5210	DEF_CMD_ARG("mgmtrate",		set80211mgtrate),
5211	DEF_CMD_ARG("maxretry",		set80211maxretry),
5212	DEF_CMD_ARG("fragthreshold",	set80211fragthreshold),
5213	DEF_CMD("burst",	1,	set80211burst),
5214	DEF_CMD("-burst",	0,	set80211burst),
5215	DEF_CMD_ARG("bmiss",		set80211bmissthreshold),
5216	DEF_CMD_ARG("bmissthreshold",	set80211bmissthreshold),
5217	DEF_CMD("shortgi",	1,	set80211shortgi),
5218	DEF_CMD("-shortgi",	0,	set80211shortgi),
5219	DEF_CMD("ampdurx",	2,	set80211ampdu),
5220	DEF_CMD("-ampdurx",	-2,	set80211ampdu),
5221	DEF_CMD("ampdutx",	1,	set80211ampdu),
5222	DEF_CMD("-ampdutx",	-1,	set80211ampdu),
5223	DEF_CMD("ampdu",	3,	set80211ampdu),		/* NB: tx+rx */
5224	DEF_CMD("-ampdu",	-3,	set80211ampdu),
5225	DEF_CMD_ARG("ampdulimit",	set80211ampdulimit),
5226	DEF_CMD_ARG("ampdudensity",	set80211ampdudensity),
5227	DEF_CMD("amsdurx",	2,	set80211amsdu),
5228	DEF_CMD("-amsdurx",	-2,	set80211amsdu),
5229	DEF_CMD("amsdutx",	1,	set80211amsdu),
5230	DEF_CMD("-amsdutx",	-1,	set80211amsdu),
5231	DEF_CMD("amsdu",	3,	set80211amsdu),		/* NB: tx+rx */
5232	DEF_CMD("-amsdu",	-3,	set80211amsdu),
5233	DEF_CMD_ARG("amsdulimit",	set80211amsdulimit),
5234	DEF_CMD("puren",	1,	set80211puren),
5235	DEF_CMD("-puren",	0,	set80211puren),
5236	DEF_CMD("doth",		1,	set80211doth),
5237	DEF_CMD("-doth",	0,	set80211doth),
5238	DEF_CMD("dfs",		1,	set80211dfs),
5239	DEF_CMD("-dfs",		0,	set80211dfs),
5240	DEF_CMD("htcompat",	1,	set80211htcompat),
5241	DEF_CMD("-htcompat",	0,	set80211htcompat),
5242	DEF_CMD("dwds",		1,	set80211dwds),
5243	DEF_CMD("-dwds",	0,	set80211dwds),
5244	DEF_CMD("inact",	1,	set80211inact),
5245	DEF_CMD("-inact",	0,	set80211inact),
5246	DEF_CMD("tsn",		1,	set80211tsn),
5247	DEF_CMD("-tsn",		0,	set80211tsn),
5248	DEF_CMD_ARG("regdomain",	set80211regdomain),
5249	DEF_CMD_ARG("country",		set80211country),
5250	DEF_CMD("indoor",	'I',	set80211location),
5251	DEF_CMD("-indoor",	'O',	set80211location),
5252	DEF_CMD("outdoor",	'O',	set80211location),
5253	DEF_CMD("-outdoor",	'I',	set80211location),
5254	DEF_CMD("anywhere",	' ',	set80211location),
5255	DEF_CMD("ecm",		1,	set80211ecm),
5256	DEF_CMD("-ecm",		0,	set80211ecm),
5257	DEF_CMD("dotd",		1,	set80211dotd),
5258	DEF_CMD("-dotd",	0,	set80211dotd),
5259	DEF_CMD_ARG("htprotmode",	set80211htprotmode),
5260	DEF_CMD("ht20",		1,	set80211htconf),
5261	DEF_CMD("-ht20",	0,	set80211htconf),
5262	DEF_CMD("ht40",		3,	set80211htconf),	/* NB: 20+40 */
5263	DEF_CMD("-ht40",	0,	set80211htconf),
5264	DEF_CMD("ht",		3,	set80211htconf),	/* NB: 20+40 */
5265	DEF_CMD("-ht",		0,	set80211htconf),
5266	DEF_CMD("rifs",		1,	set80211rifs),
5267	DEF_CMD("-rifs",	0,	set80211rifs),
5268	DEF_CMD("smps",		IEEE80211_HTCAP_SMPS_ENA,	set80211smps),
5269	DEF_CMD("smpsdyn",	IEEE80211_HTCAP_SMPS_DYNAMIC,	set80211smps),
5270	DEF_CMD("-smps",	IEEE80211_HTCAP_SMPS_OFF,	set80211smps),
5271	/* XXX for testing */
5272	DEF_CMD_ARG("chanswitch",	set80211chanswitch),
5273
5274	DEF_CMD_ARG("tdmaslot",		set80211tdmaslot),
5275	DEF_CMD_ARG("tdmaslotcnt",	set80211tdmaslotcnt),
5276	DEF_CMD_ARG("tdmaslotlen",	set80211tdmaslotlen),
5277	DEF_CMD_ARG("tdmabintval",	set80211tdmabintval),
5278
5279	DEF_CMD_ARG("meshttl",		set80211meshttl),
5280	DEF_CMD("meshforward",	1,	set80211meshforward),
5281	DEF_CMD("-meshforward",	0,	set80211meshforward),
5282	DEF_CMD("meshgate",	1,	set80211meshgate),
5283	DEF_CMD("-meshgate",	0,	set80211meshgate),
5284	DEF_CMD("meshpeering",	1,	set80211meshpeering),
5285	DEF_CMD("-meshpeering",	0,	set80211meshpeering),
5286	DEF_CMD_ARG("meshmetric",	set80211meshmetric),
5287	DEF_CMD_ARG("meshpath",		set80211meshpath),
5288	DEF_CMD("meshrt:flush",	IEEE80211_MESH_RTCMD_FLUSH,	set80211meshrtcmd),
5289	DEF_CMD_ARG("meshrt:add",	set80211addmeshrt),
5290	DEF_CMD_ARG("meshrt:del",	set80211delmeshrt),
5291	DEF_CMD_ARG("hwmprootmode",	set80211hwmprootmode),
5292	DEF_CMD_ARG("hwmpmaxhops",	set80211hwmpmaxhops),
5293
5294	/* vap cloning support */
5295	DEF_CLONE_CMD_ARG("wlanaddr",	set80211clone_wlanaddr),
5296	DEF_CLONE_CMD_ARG("wlanbssid",	set80211clone_wlanbssid),
5297	DEF_CLONE_CMD_ARG("wlandev",	set80211clone_wlandev),
5298	DEF_CLONE_CMD_ARG("wlanmode",	set80211clone_wlanmode),
5299	DEF_CLONE_CMD("beacons", 1,	set80211clone_beacons),
5300	DEF_CLONE_CMD("-beacons", 0,	set80211clone_beacons),
5301	DEF_CLONE_CMD("bssid",	1,	set80211clone_bssid),
5302	DEF_CLONE_CMD("-bssid",	0,	set80211clone_bssid),
5303	DEF_CLONE_CMD("wdslegacy", 1,	set80211clone_wdslegacy),
5304	DEF_CLONE_CMD("-wdslegacy", 0,	set80211clone_wdslegacy),
5305};
5306static struct afswtch af_ieee80211 = {
5307	.af_name	= "af_ieee80211",
5308	.af_af		= AF_UNSPEC,
5309	.af_other_status = ieee80211_status,
5310};
5311
5312static __constructor void
5313ieee80211_ctor(void)
5314{
5315	int i;
5316
5317	for (i = 0; i < nitems(ieee80211_cmds);  i++)
5318		cmd_register(&ieee80211_cmds[i]);
5319	af_register(&af_ieee80211);
5320	clone_setdefcallback("wlan", wlan_create);
5321}
5322