ifieee80211.c revision 234895
1193323Sed/*
2193323Sed * Copyright 2001 The Aerospace Corporation.  All rights reserved.
3193323Sed *
4193323Sed * Redistribution and use in source and binary forms, with or without
5193323Sed * modification, are permitted provided that the following conditions
6193323Sed * are met:
7193323Sed * 1. Redistributions of source code must retain the above copyright
8193323Sed *    notice, this list of conditions and the following disclaimer.
9193323Sed * 2. Redistributions in binary form must reproduce the above copyright
10193323Sed *    notice, this list of conditions and the following disclaimer in the
11193323Sed *    documentation and/or other materials provided with the distribution.
12193323Sed * 3. The name of The Aerospace Corporation may not be used to endorse or
13193323Sed *    promote products derived from this software.
14193323Sed *
15193323Sed * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION ``AS IS'' AND
16193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18193323Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AEROSPACE CORPORATION BE LIABLE
19193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22193323Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23193323Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24193323Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25193323Sed * SUCH DAMAGE.
26193323Sed *
27193323Sed * $FreeBSD: head/sbin/ifconfig/ifieee80211.c 234895 2012-05-01 16:17:17Z monthadar $
28193323Sed */
29249423Sdim
30249423Sdim/*-
31249423Sdim * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
32193323Sed * All rights reserved.
33193323Sed *
34193323Sed * This code is derived from software contributed to The NetBSD Foundation
35249423Sdim * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
36249423Sdim * NASA Ames Research Center.
37249423Sdim *
38249423Sdim * Redistribution and use in source and binary forms, with or without
39249423Sdim * modification, are permitted provided that the following conditions
40249423Sdim * are met:
41249423Sdim * 1. Redistributions of source code must retain the above copyright
42249423Sdim *    notice, this list of conditions and the following disclaimer.
43193323Sed * 2. Redistributions in binary form must reproduce the above copyright
44224145Sdim *    notice, this list of conditions and the following disclaimer in the
45193323Sed *    documentation and/or other materials provided with the distribution.
46198090Srdivacky *
47249423Sdim * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
48249423Sdim * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
49193323Sed * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
50226633Sdim * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
51193323Sed * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
52193323Sed * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
53224145Sdim * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
54224145Sdim * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
55224145Sdim * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
56224145Sdim * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
57224145Sdim * POSSIBILITY OF SUCH DAMAGE.
58193323Sed */
59234353Sdim
60234353Sdim#include <sys/param.h>
61234353Sdim#include <sys/ioctl.h>
62234353Sdim#include <sys/socket.h>
63234353Sdim#include <sys/sysctl.h>
64234353Sdim#include <sys/time.h>
65223017Sdim
66193323Sed#include <net/ethernet.h>
67198090Srdivacky#include <net/if.h>
68193323Sed#include <net/if_dl.h>
69193323Sed#include <net/if_types.h>
70195098Sed#include <net/if_media.h>
71243830Sdim#include <net/route.h>
72243830Sdim
73224145Sdim#include <net80211/ieee80211_ioctl.h>
74221345Sdim#include <net80211/ieee80211_freebsd.h>
75193323Sed#include <net80211/ieee80211_superg.h>
76193323Sed#include <net80211/ieee80211_tdma.h>
77193323Sed#include <net80211/ieee80211_mesh.h>
78198090Srdivacky
79234353Sdim#include <assert.h>
80224145Sdim#include <ctype.h>
81218893Sdim#include <err.h>
82218893Sdim#include <errno.h>
83193323Sed#include <fcntl.h>
84198090Srdivacky#include <inttypes.h>
85193323Sed#include <stdio.h>
86198090Srdivacky#include <stdlib.h>
87198090Srdivacky#include <string.h>
88198090Srdivacky#include <unistd.h>
89198090Srdivacky#include <stdarg.h>
90198090Srdivacky#include <stddef.h>		/* NB: for offsetof */
91198090Srdivacky
92198090Srdivacky#include "ifconfig.h"
93198090Srdivacky#include "regdomain.h"
94198090Srdivacky
95198090Srdivacky#ifndef IEEE80211_FIXED_RATE_NONE
96198090Srdivacky#define	IEEE80211_FIXED_RATE_NONE	0xff
97193323Sed#endif
98193323Sed
99224145Sdim/* XXX need these publicly defined or similar */
100224145Sdim#ifndef IEEE80211_NODE_AUTH
101224145Sdim#define	IEEE80211_NODE_AUTH	0x000001	/* authorized for data */
102224145Sdim#define	IEEE80211_NODE_QOS	0x000002	/* QoS enabled */
103221345Sdim#define	IEEE80211_NODE_ERP	0x000004	/* ERP enabled */
104193323Sed#define	IEEE80211_NODE_PWR_MGT	0x000010	/* power save mode enabled */
105224145Sdim#define	IEEE80211_NODE_AREF	0x000020	/* authentication ref held */
106224145Sdim#define	IEEE80211_NODE_HT	0x000040	/* HT enabled */
107224145Sdim#define	IEEE80211_NODE_HTCOMPAT	0x000080	/* HT setup w/ vendor OUI's */
108226633Sdim#define	IEEE80211_NODE_WPS	0x000100	/* WPS association */
109226633Sdim#define	IEEE80211_NODE_TSN	0x000200	/* TSN association */
110224145Sdim#define	IEEE80211_NODE_AMPDU_RX	0x000400	/* AMPDU rx enabled */
111224145Sdim#define	IEEE80211_NODE_AMPDU_TX	0x000800	/* AMPDU tx enabled */
112226633Sdim#define	IEEE80211_NODE_MIMO_PS	0x001000	/* MIMO power save enabled */
113226633Sdim#define	IEEE80211_NODE_MIMO_RTS	0x002000	/* send RTS in MIMO PS */
114223017Sdim#define	IEEE80211_NODE_RIFS	0x004000	/* RIFS enabled */
115195098Sed#define	IEEE80211_NODE_SGI20	0x008000	/* Short GI in HT20 enabled */
116193323Sed#define	IEEE80211_NODE_SGI40	0x010000	/* Short GI in HT40 enabled */
117193323Sed#define	IEEE80211_NODE_ASSOCID	0x020000	/* xmit requires associd */
118193323Sed#define	IEEE80211_NODE_AMSDU_RX	0x040000	/* AMSDU rx enabled */
119193323Sed#define	IEEE80211_NODE_AMSDU_TX	0x080000	/* AMSDU tx enabled */
120218893Sdim#endif
121223017Sdim
122218893Sdim#define	MAXCHAN	1536		/* max 1.5K channels */
123218893Sdim
124218893Sdim#define	MAXCOL	78
125218893Sdimstatic	int col;
126218893Sdimstatic	char spacer;
127218893Sdim
128223017Sdimstatic void LINE_INIT(char c);
129193323Sedstatic void LINE_BREAK(void);
130193323Sedstatic void LINE_CHECK(const char *fmt, ...);
131193323Sed
132193323Sedstatic const char *modename[IEEE80211_MODE_MAX] = {
133193323Sed	[IEEE80211_MODE_AUTO]	  = "auto",
134221345Sdim	[IEEE80211_MODE_11A]	  = "11a",
135221345Sdim	[IEEE80211_MODE_11B]	  = "11b",
136221345Sdim	[IEEE80211_MODE_11G]	  = "11g",
137221345Sdim	[IEEE80211_MODE_FH]	  = "fh",
138221345Sdim	[IEEE80211_MODE_TURBO_A]  = "turboA",
139221345Sdim	[IEEE80211_MODE_TURBO_G]  = "turboG",
140221345Sdim	[IEEE80211_MODE_STURBO_A] = "sturbo",
141221345Sdim	[IEEE80211_MODE_11NA]	  = "11na",
142221345Sdim	[IEEE80211_MODE_11NG]	  = "11ng",
143221345Sdim	[IEEE80211_MODE_HALF]	  = "half",
144221345Sdim	[IEEE80211_MODE_QUARTER]  = "quarter"
145221345Sdim};
146221345Sdim
147221345Sdimstatic void set80211(int s, int type, int val, int len, void *data);
148221345Sdimstatic int get80211(int s, int type, void *data, int len);
149221345Sdimstatic int get80211len(int s, int type, void *data, int len, int *plen);
150221345Sdimstatic int get80211val(int s, int type, int *val);
151221345Sdimstatic const char *get_string(const char *val, const char *sep,
152221345Sdim    u_int8_t *buf, int *lenp);
153221345Sdimstatic void print_string(const u_int8_t *buf, int len);
154221345Sdimstatic void print_regdomain(const struct ieee80211_regdomain *, int);
155221345Sdimstatic void print_channels(int, const struct ieee80211req_chaninfo *,
156221345Sdim    int allchans, int verbose);
157221345Sdimstatic void regdomain_makechannels(struct ieee80211_regdomain_req *,
158221345Sdim    const struct ieee80211_devcaps_req *);
159221345Sdimstatic const char *mesh_linkstate_string(uint8_t state);
160221345Sdim
161221345Sdimstatic struct ieee80211req_chaninfo *chaninfo;
162221345Sdimstatic struct ieee80211_regdomain regdomain;
163221345Sdimstatic int gotregdomain = 0;
164221345Sdimstatic struct ieee80211_roamparams_req roamparams;
165221345Sdimstatic int gotroam = 0;
166221345Sdimstatic struct ieee80211_txparams_req txparams;
167221345Sdimstatic int gottxparams = 0;
168234353Sdimstatic struct ieee80211_channel curchan;
169234353Sdimstatic int gotcurchan = 0;
170234353Sdimstatic struct ifmediareq *ifmr;
171234353Sdimstatic int htconf = 0;
172234353Sdimstatic	int gothtconf = 0;
173221345Sdim
174221345Sdimstatic void
175221345Sdimgethtconf(int s)
176221345Sdim{
177221345Sdim	if (gothtconf)
178221345Sdim		return;
179221345Sdim	if (get80211val(s, IEEE80211_IOC_HTCONF, &htconf) < 0)
180221345Sdim		warn("unable to get HT configuration information");
181221345Sdim	gothtconf = 1;
182221345Sdim}
183221345Sdim
184221345Sdim/*
185221345Sdim * Collect channel info from the kernel.  We use this (mostly)
186226633Sdim * to handle mapping between frequency and IEEE channel number.
187226633Sdim */
188226633Sdimstatic void
189226633Sdimgetchaninfo(int s)
190226633Sdim{
191226633Sdim	if (chaninfo != NULL)
192226633Sdim		return;
193226633Sdim	chaninfo = malloc(IEEE80211_CHANINFO_SIZE(MAXCHAN));
194226633Sdim	if (chaninfo == NULL)
195226633Sdim		errx(1, "no space for channel list");
196226633Sdim	if (get80211(s, IEEE80211_IOC_CHANINFO, chaninfo,
197226633Sdim	    IEEE80211_CHANINFO_SIZE(MAXCHAN)) < 0)
198226633Sdim		err(1, "unable to get channel information");
199226633Sdim	ifmr = ifmedia_getstate(s);
200226633Sdim	gethtconf(s);
201226633Sdim}
202226633Sdim
203226633Sdimstatic struct regdata *
204226633Sdimgetregdata(void)
205226633Sdim{
206226633Sdim	static struct regdata *rdp = NULL;
207226633Sdim	if (rdp == NULL) {
208226633Sdim		rdp = lib80211_alloc_regdata();
209226633Sdim		if (rdp == NULL)
210226633Sdim			errx(-1, "missing or corrupted regdomain database");
211226633Sdim	}
212226633Sdim	return rdp;
213226633Sdim}
214226633Sdim
215226633Sdim/*
216224145Sdim * Given the channel at index i with attributes from,
217224145Sdim * check if there is a channel with attributes to in
218224145Sdim * the channel table.  With suitable attributes this
219223017Sdim * allows the caller to look for promotion; e.g. from
220224145Sdim * 11b > 11g.
221224145Sdim */
222224145Sdimstatic int
223224145Sdimcanpromote(int i, int from, int to)
224224145Sdim{
225224145Sdim	const struct ieee80211_channel *fc = &chaninfo->ic_chans[i];
226224145Sdim	int j;
227223017Sdim
228224145Sdim	if ((fc->ic_flags & from) != from)
229223017Sdim		return i;
230223017Sdim	/* NB: quick check exploiting ordering of chans w/ same frequency */
231207618Srdivacky	if (i+1 < chaninfo->ic_nchans &&
232224145Sdim	    chaninfo->ic_chans[i+1].ic_freq == fc->ic_freq &&
233224145Sdim	    (chaninfo->ic_chans[i+1].ic_flags & to) == to)
234224145Sdim		return i+1;
235224145Sdim	/* brute force search in case channel list is not ordered */
236224145Sdim	for (j = 0; j < chaninfo->ic_nchans; j++) {
237224145Sdim		const struct ieee80211_channel *tc = &chaninfo->ic_chans[j];
238224145Sdim		if (j != i &&
239224145Sdim		    tc->ic_freq == fc->ic_freq && (tc->ic_flags & to) == to)
240223017Sdim		return j;
241224145Sdim	}
242224145Sdim	return i;
243224145Sdim}
244223017Sdim
245224145Sdim/*
246224145Sdim * Handle channel promotion.  When a channel is specified with
247224145Sdim * only a frequency we want to promote it to the ``best'' channel
248223017Sdim * available.  The channel list has separate entries for 11b, 11g,
249224145Sdim * 11a, and 11n[ga] channels so specifying a frequency w/o any
250224145Sdim * attributes requires we upgrade, e.g. from 11b -> 11g.  This
251224145Sdim * gets complicated when the channel is specified on the same
252223017Sdim * command line with a media request that constrains the available
253224145Sdim * channe list (e.g. mode 11a); we want to honor that to avoid
254224145Sdim * confusing behaviour.
255224145Sdim */
256224145Sdimstatic int
257224145Sdimpromote(int i)
258223017Sdim{
259224145Sdim	/*
260224145Sdim	 * Query the current mode of the interface in case it's
261224145Sdim	 * constrained (e.g. to 11a).  We must do this carefully
262224145Sdim	 * as there may be a pending ifmedia request in which case
263224145Sdim	 * asking the kernel will give us the wrong answer.  This
264224145Sdim	 * is an unfortunate side-effect of the way ifconfig is
265224145Sdim	 * structure for modularity (yech).
266224145Sdim	 *
267224145Sdim	 * NB: ifmr is actually setup in getchaninfo (above); we
268224145Sdim	 *     assume it's called coincident with to this call so
269224145Sdim	 *     we have a ``current setting''; otherwise we must pass
270224145Sdim	 *     the socket descriptor down to here so we can make
271224145Sdim	 *     the ifmedia_getstate call ourselves.
272224145Sdim	 */
273224145Sdim	int chanmode = ifmr != NULL ? IFM_MODE(ifmr->ifm_current) : IFM_AUTO;
274224145Sdim
275224145Sdim	/* when ambiguous promote to ``best'' */
276224145Sdim	/* NB: we abitrarily pick HT40+ over HT40- */
277224145Sdim	if (chanmode != IFM_IEEE80211_11B)
278224145Sdim		i = canpromote(i, IEEE80211_CHAN_B, IEEE80211_CHAN_G);
279224145Sdim	if (chanmode != IFM_IEEE80211_11G && (htconf & 1)) {
280224145Sdim		i = canpromote(i, IEEE80211_CHAN_G,
281224145Sdim			IEEE80211_CHAN_G | IEEE80211_CHAN_HT20);
282224145Sdim		if (htconf & 2) {
283224145Sdim			i = canpromote(i, IEEE80211_CHAN_G,
284224145Sdim				IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D);
285224145Sdim			i = canpromote(i, IEEE80211_CHAN_G,
286224145Sdim				IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U);
287224145Sdim		}
288224145Sdim	}
289224145Sdim	if (chanmode != IFM_IEEE80211_11A && (htconf & 1)) {
290224145Sdim		i = canpromote(i, IEEE80211_CHAN_A,
291224145Sdim			IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
292224145Sdim		if (htconf & 2) {
293224145Sdim			i = canpromote(i, IEEE80211_CHAN_A,
294224145Sdim				IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
295224145Sdim			i = canpromote(i, IEEE80211_CHAN_A,
296224145Sdim				IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
297224145Sdim		}
298224145Sdim	}
299224145Sdim	return i;
300224145Sdim}
301224145Sdim
302224145Sdimstatic void
303224145Sdimmapfreq(struct ieee80211_channel *chan, int freq, int flags)
304224145Sdim{
305224145Sdim	int i;
306224145Sdim
307224145Sdim	for (i = 0; i < chaninfo->ic_nchans; i++) {
308224145Sdim		const struct ieee80211_channel *c = &chaninfo->ic_chans[i];
309224145Sdim
310224145Sdim		if (c->ic_freq == freq && (c->ic_flags & flags) == flags) {
311224145Sdim			if (flags == 0) {
312224145Sdim				/* when ambiguous promote to ``best'' */
313224145Sdim				c = &chaninfo->ic_chans[promote(i)];
314224145Sdim			}
315224145Sdim			*chan = *c;
316224145Sdim			return;
317224145Sdim		}
318224145Sdim	}
319224145Sdim	errx(1, "unknown/undefined frequency %u/0x%x", freq, flags);
320224145Sdim}
321223017Sdim
322223017Sdimstatic void
323224145Sdimmapchan(struct ieee80211_channel *chan, int ieee, int flags)
324224145Sdim{
325224145Sdim	int i;
326224145Sdim
327224145Sdim	for (i = 0; i < chaninfo->ic_nchans; i++) {
328223017Sdim		const struct ieee80211_channel *c = &chaninfo->ic_chans[i];
329224145Sdim
330224145Sdim		if (c->ic_ieee == ieee && (c->ic_flags & flags) == flags) {
331224145Sdim			if (flags == 0) {
332224145Sdim				/* when ambiguous promote to ``best'' */
333224145Sdim				c = &chaninfo->ic_chans[promote(i)];
334224145Sdim			}
335224145Sdim			*chan = *c;
336224145Sdim			return;
337224145Sdim		}
338224145Sdim	}
339224145Sdim	errx(1, "unknown/undefined channel number %d flags 0x%x", ieee, flags);
340226633Sdim}
341226633Sdim
342224145Sdimstatic const struct ieee80211_channel *
343224145Sdimgetcurchan(int s)
344224145Sdim{
345226633Sdim	if (gotcurchan)
346226633Sdim		return &curchan;
347226633Sdim	if (get80211(s, IEEE80211_IOC_CURCHAN, &curchan, sizeof(curchan)) < 0) {
348224145Sdim		int val;
349193323Sed		/* fall back to legacy ioctl */
350193323Sed		if (get80211val(s, IEEE80211_IOC_CHANNEL, &val) < 0)
351224145Sdim			err(-1, "cannot figure out current channel");
352224145Sdim		getchaninfo(s);
353224145Sdim		mapchan(&curchan, val, 0);
354224145Sdim	}
355224145Sdim	gotcurchan = 1;
356224145Sdim	return &curchan;
357224145Sdim}
358224145Sdim
359224145Sdimstatic enum ieee80211_phymode
360224145Sdimchan2mode(const struct ieee80211_channel *c)
361224145Sdim{
362224145Sdim	if (IEEE80211_IS_CHAN_HTA(c))
363224145Sdim		return IEEE80211_MODE_11NA;
364224145Sdim	if (IEEE80211_IS_CHAN_HTG(c))
365193323Sed		return IEEE80211_MODE_11NG;
366224145Sdim	if (IEEE80211_IS_CHAN_108A(c))
367226633Sdim		return IEEE80211_MODE_TURBO_A;
368226633Sdim	if (IEEE80211_IS_CHAN_108G(c))
369224145Sdim		return IEEE80211_MODE_TURBO_G;
370224145Sdim	if (IEEE80211_IS_CHAN_ST(c))
371224145Sdim		return IEEE80211_MODE_STURBO_A;
372226633Sdim	if (IEEE80211_IS_CHAN_FHSS(c))
373226633Sdim		return IEEE80211_MODE_FH;
374226633Sdim	if (IEEE80211_IS_CHAN_HALF(c))
375224145Sdim		return IEEE80211_MODE_HALF;
376224145Sdim	if (IEEE80211_IS_CHAN_QUARTER(c))
377224145Sdim		return IEEE80211_MODE_QUARTER;
378224145Sdim	if (IEEE80211_IS_CHAN_A(c))
379224145Sdim		return IEEE80211_MODE_11A;
380224145Sdim	if (IEEE80211_IS_CHAN_ANYG(c))
381224145Sdim		return IEEE80211_MODE_11G;
382224145Sdim	if (IEEE80211_IS_CHAN_B(c))
383224145Sdim		return IEEE80211_MODE_11B;
384224145Sdim	return IEEE80211_MODE_AUTO;
385224145Sdim}
386224145Sdim
387224145Sdimstatic void
388224145Sdimgetroam(int s)
389224145Sdim{
390224145Sdim	if (gotroam)
391193323Sed		return;
392193323Sed	if (get80211(s, IEEE80211_IOC_ROAM,
393226633Sdim	    &roamparams, sizeof(roamparams)) < 0)
394193323Sed		err(1, "unable to get roaming parameters");
395224145Sdim	gotroam = 1;
396224145Sdim}
397224145Sdim
398224145Sdimstatic void
399193323Sedsetroam_cb(int s, void *arg)
400224145Sdim{
401224145Sdim	struct ieee80211_roamparams_req *roam = arg;
402224145Sdim	set80211(s, IEEE80211_IOC_ROAM, 0, sizeof(*roam), roam);
403224145Sdim}
404193323Sed
405224145Sdimstatic void
406224145Sdimgettxparams(int s)
407224145Sdim{
408193323Sed	if (gottxparams)
409224145Sdim		return;
410224145Sdim	if (get80211(s, IEEE80211_IOC_TXPARAMS,
411224145Sdim	    &txparams, sizeof(txparams)) < 0)
412193323Sed		err(1, "unable to get transmit parameters");
413224145Sdim	gottxparams = 1;
414224145Sdim}
415224145Sdim
416224145Sdimstatic void
417243830Sdimsettxparams_cb(int s, void *arg)
418224145Sdim{
419224145Sdim	struct ieee80211_txparams_req *txp = arg;
420224145Sdim	set80211(s, IEEE80211_IOC_TXPARAMS, 0, sizeof(*txp), txp);
421243830Sdim}
422224145Sdim
423224145Sdimstatic void
424224145Sdimgetregdomain(int s)
425224145Sdim{
426224145Sdim	if (gotregdomain)
427224145Sdim		return;
428224145Sdim	if (get80211(s, IEEE80211_IOC_REGDOMAIN,
429224145Sdim	    &regdomain, sizeof(regdomain)) < 0)
430224145Sdim		err(1, "unable to get regulatory domain info");
431224145Sdim	gotregdomain = 1;
432226633Sdim}
433224145Sdim
434243830Sdimstatic void
435224145Sdimgetdevcaps(int s, struct ieee80211_devcaps_req *dc)
436226633Sdim{
437193323Sed	if (get80211(s, IEEE80211_IOC_DEVCAPS, dc,
438193323Sed	    IEEE80211_DEVCAPS_SPACE(dc)) < 0)
439224145Sdim		err(1, "unable to get device capabilities");
440224145Sdim}
441224145Sdim
442224145Sdimstatic void
443224145Sdimsetregdomain_cb(int s, void *arg)
444224145Sdim{
445224145Sdim	struct ieee80211_regdomain_req *req;
446224145Sdim	struct ieee80211_regdomain *rd = arg;
447224145Sdim	struct ieee80211_devcaps_req *dc;
448224145Sdim	struct regdata *rdp = getregdata();
449224145Sdim
450224145Sdim	if (rd->country != NO_COUNTRY) {
451224145Sdim		const struct country *cc;
452224145Sdim		/*
453224145Sdim		 * Check current country seting to make sure it's
454224145Sdim		 * compatible with the new regdomain.  If not, then
455224145Sdim		 * override it with any default country for this
456224145Sdim		 * SKU.  If we cannot arrange a match, then abort.
457224145Sdim		 */
458224145Sdim		cc = lib80211_country_findbycc(rdp, rd->country);
459224145Sdim		if (cc == NULL)
460224145Sdim			errx(1, "unknown ISO country code %d", rd->country);
461224145Sdim		if (cc->rd->sku != rd->regdomain) {
462224145Sdim			const struct regdomain *rp;
463224145Sdim			/*
464224145Sdim			 * Check if country is incompatible with regdomain.
465224145Sdim			 * To enable multiple regdomains for a country code
466224145Sdim			 * we permit a mismatch between the regdomain and
467193323Sed			 * the country's associated regdomain when the
468193323Sed			 * regdomain is setup w/o a default country.  For
469193323Sed			 * example, US is bound to the FCC regdomain but
470193323Sed			 * we allow US to be combined with FCC3 because FCC3
471193323Sed			 * has not default country.  This allows bogus
472193323Sed			 * combinations like FCC3+DK which are resolved when
473193323Sed			 * constructing the channel list by deferring to the
474193323Sed			 * regdomain to construct the channel list.
475193323Sed			 */
476193323Sed			rp = lib80211_regdomain_findbysku(rdp, rd->regdomain);
477218893Sdim			if (rp == NULL)
478193323Sed				errx(1, "country %s (%s) is not usable with "
479205218Srdivacky				    "regdomain %d", cc->isoname, cc->name,
480193323Sed				    rd->regdomain);
481193323Sed			else if (rp->cc != NULL && rp->cc != cc)
482193323Sed				errx(1, "country %s (%s) is not usable with "
483193323Sed				   "regdomain %s", cc->isoname, cc->name,
484193323Sed				   rp->name);
485193323Sed		}
486193323Sed	}
487193323Sed	/*
488193323Sed	 * Fetch the device capabilities and calculate the
489193323Sed	 * full set of netbands for which we request a new
490193323Sed	 * channel list be constructed.  Once that's done we
491193323Sed	 * push the regdomain info + channel list to the kernel.
492193323Sed	 */
493193323Sed	dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
494193323Sed	if (dc == NULL)
495193323Sed		errx(1, "no space for device capabilities");
496193323Sed	dc->dc_chaninfo.ic_nchans = MAXCHAN;
497193323Sed	getdevcaps(s, dc);
498193323Sed#if 0
499193323Sed	if (verbose) {
500193323Sed		printf("drivercaps: 0x%x\n", dc->dc_drivercaps);
501193323Sed		printf("cryptocaps: 0x%x\n", dc->dc_cryptocaps);
502204642Srdivacky		printf("htcaps    : 0x%x\n", dc->dc_htcaps);
503204642Srdivacky		memcpy(chaninfo, &dc->dc_chaninfo,
504204642Srdivacky		    IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
505204642Srdivacky		print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, 1/*verbose*/);
506204642Srdivacky	}
507204642Srdivacky#endif
508204642Srdivacky	req = malloc(IEEE80211_REGDOMAIN_SIZE(dc->dc_chaninfo.ic_nchans));
509204642Srdivacky	if (req == NULL)
510204642Srdivacky		errx(1, "no space for regdomain request");
511204642Srdivacky	req->rd = *rd;
512204642Srdivacky	regdomain_makechannels(req, dc);
513204642Srdivacky	if (verbose) {
514193323Sed		LINE_INIT(':');
515193323Sed		print_regdomain(rd, 1/*verbose*/);
516193323Sed		LINE_BREAK();
517193323Sed		/* blech, reallocate channel list for new data */
518193323Sed		if (chaninfo != NULL)
519204642Srdivacky			free(chaninfo);
520193323Sed		chaninfo = malloc(IEEE80211_CHANINFO_SPACE(&req->chaninfo));
521193323Sed		if (chaninfo == NULL)
522193323Sed			errx(1, "no space for channel list");
523193323Sed		memcpy(chaninfo, &req->chaninfo,
524193323Sed		    IEEE80211_CHANINFO_SPACE(&req->chaninfo));
525193323Sed		print_channels(s, &req->chaninfo, 1/*allchans*/, 1/*verbose*/);
526193323Sed	}
527193323Sed	if (req->chaninfo.ic_nchans == 0)
528201360Srdivacky		errx(1, "no channels calculated");
529193323Sed	set80211(s, IEEE80211_IOC_REGDOMAIN, 0,
530193323Sed	    IEEE80211_REGDOMAIN_SPACE(req), req);
531193323Sed	free(req);
532193323Sed	free(dc);
533193323Sed}
534198090Srdivacky
535263508Sdimstatic int
536263508Sdimieee80211_mhz2ieee(int freq, int flags)
537193323Sed{
538193323Sed	struct ieee80211_channel chan;
539249423Sdim	mapfreq(&chan, freq, flags);
540249423Sdim	return chan.ic_ieee;
541249423Sdim}
542249423Sdim
543249423Sdimstatic int
544249423Sdimisanyarg(const char *arg)
545249423Sdim{
546249423Sdim	return (strncmp(arg, "-", 1) == 0 ||
547249423Sdim	    strncasecmp(arg, "any", 3) == 0 || strncasecmp(arg, "off", 3) == 0);
548249423Sdim}
549249423Sdim
550249423Sdimstatic void
551249423Sdimset80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
552249423Sdim{
553249423Sdim	int		ssid;
554249423Sdim	int		len;
555249423Sdim	u_int8_t	data[IEEE80211_NWID_LEN];
556249423Sdim
557249423Sdim	ssid = 0;
558249423Sdim	len = strlen(val);
559249423Sdim	if (len > 2 && isdigit((int)val[0]) && val[1] == ':') {
560249423Sdim		ssid = atoi(val)-1;
561249423Sdim		val += 2;
562249423Sdim	}
563249423Sdim
564249423Sdim	bzero(data, sizeof(data));
565249423Sdim	len = sizeof(data);
566249423Sdim	if (get_string(val, NULL, data, &len) == NULL)
567249423Sdim		exit(1);
568249423Sdim
569249423Sdim	set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
570249423Sdim}
571249423Sdim
572249423Sdimstatic void
573249423Sdimset80211meshid(const char *val, int d, int s, const struct afswtch *rafp)
574249423Sdim{
575249423Sdim	int		len;
576249423Sdim	u_int8_t	data[IEEE80211_NWID_LEN];
577249423Sdim
578195098Sed	memset(data, 0, sizeof(data));
579193323Sed	len = sizeof(data);
580202375Srdivacky	if (get_string(val, NULL, data, &len) == NULL)
581198090Srdivacky		exit(1);
582193323Sed
583221345Sdim	set80211(s, IEEE80211_IOC_MESH_ID, 0, len, data);
584221345Sdim}
585221345Sdim
586221345Sdimstatic void
587221345Sdimset80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
588221345Sdim{
589221345Sdim	int			len;
590193323Sed	u_int8_t		data[33];
591193323Sed
592243830Sdim	bzero(data, sizeof(data));
593243830Sdim	len = sizeof(data);
594243830Sdim	get_string(val, NULL, data, &len);
595243830Sdim
596193323Sed	set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
597198090Srdivacky}
598198090Srdivacky
599198090Srdivacky/*
600198090Srdivacky * Parse a channel specification for attributes/flags.
601193323Sed * The syntax is:
602243830Sdim *	freq/xx		channel width (5,10,20,40,40+,40-)
603193323Sed *	freq:mode	channel mode (a,b,g,h,n,t,s,d)
604193323Sed *
605198090Srdivacky * These can be combined in either order; e.g. 2437:ng/40.
606195098Sed * Modes are case insensitive.
607195098Sed *
608198892Srdivacky * The result is not validated here; it's assumed to be
609195098Sed * checked against the channel table fetched from the kernel.
610195098Sed */
611195098Sedstatic int
612195098Sedgetchannelflags(const char *val, int freq)
613195098Sed{
614193323Sed#define	_CHAN_HT	0x80000000
615193323Sed	const char *cp;
616205407Srdivacky	int flags;
617205407Srdivacky
618205407Srdivacky	flags = 0;
619205407Srdivacky
620193323Sed	cp = strchr(val, ':');
621193323Sed	if (cp != NULL) {
622224145Sdim		for (cp++; isalpha((int) *cp); cp++) {
623224145Sdim			/* accept mixed case */
624224145Sdim			int c = *cp;
625224145Sdim			if (isupper(c))
626223017Sdim				c = tolower(c);
627223017Sdim			switch (c) {
628223017Sdim			case 'a':		/* 802.11a */
629223017Sdim				flags |= IEEE80211_CHAN_A;
630223017Sdim				break;
631226633Sdim			case 'b':		/* 802.11b */
632226633Sdim				flags |= IEEE80211_CHAN_B;
633226633Sdim				break;
634207618Srdivacky			case 'g':		/* 802.11g */
635226633Sdim				flags |= IEEE80211_CHAN_G;
636223017Sdim				break;
637226633Sdim			case 'h':		/* ht = 802.11n */
638226633Sdim			case 'n':		/* 802.11n */
639226633Sdim				flags |= _CHAN_HT;	/* NB: private */
640243830Sdim				break;
641226633Sdim			case 'd':		/* dt = Atheros Dynamic Turbo */
642226633Sdim				flags |= IEEE80211_CHAN_TURBO;
643226633Sdim				break;
644226633Sdim			case 't':		/* ht, dt, st, t */
645226633Sdim				/* dt and unadorned t specify Dynamic Turbo */
646243830Sdim				if ((flags & (IEEE80211_CHAN_STURBO|_CHAN_HT)) == 0)
647226633Sdim					flags |= IEEE80211_CHAN_TURBO;
648226633Sdim				break;
649226633Sdim			case 's':		/* st = Atheros Static Turbo */
650226633Sdim				flags |= IEEE80211_CHAN_STURBO;
651226633Sdim				break;
652223017Sdim			default:
653207618Srdivacky				errx(-1, "%s: Invalid channel attribute %c\n",
654226633Sdim				    val, *cp);
655223017Sdim			}
656223017Sdim		}
657226633Sdim	}
658226633Sdim	cp = strchr(val, '/');
659226633Sdim	if (cp != NULL) {
660226633Sdim		char *ep;
661226633Sdim		u_long cw = strtoul(cp+1, &ep, 10);
662226633Sdim
663223017Sdim		switch (cw) {
664223017Sdim		case 5:
665223017Sdim			flags |= IEEE80211_CHAN_QUARTER;
666207618Srdivacky			break;
667224145Sdim		case 10:
668224145Sdim			flags |= IEEE80211_CHAN_HALF;
669224145Sdim			break;
670223017Sdim		case 20:
671223017Sdim			/* NB: this may be removed below */
672207618Srdivacky			flags |= IEEE80211_CHAN_HT20;
673223017Sdim			break;
674224145Sdim		case 40:
675223017Sdim			if (ep != NULL && *ep == '+')
676207618Srdivacky				flags |= IEEE80211_CHAN_HT40U;
677224145Sdim			else if (ep != NULL && *ep == '-')
678224145Sdim				flags |= IEEE80211_CHAN_HT40D;
679207618Srdivacky			break;
680207618Srdivacky		default:
681223017Sdim			errx(-1, "%s: Invalid channel width\n", val);
682226633Sdim		}
683226633Sdim	}
684226633Sdim	/*
685226633Sdim	 * Cleanup specifications.
686226633Sdim	 */
687226633Sdim	if ((flags & _CHAN_HT) == 0) {
688226633Sdim		/*
689226633Sdim		 * If user specified freq/20 or freq/40 quietly remove
690226633Sdim		 * HT cw attributes depending on channel use.  To give
691226633Sdim		 * an explicit 20/40 width for an HT channel you must
692226633Sdim		 * indicate it is an HT channel since all HT channels
693226633Sdim		 * are also usable for legacy operation; e.g. freq:n/40.
694226633Sdim		 */
695226633Sdim		flags &= ~IEEE80211_CHAN_HT;
696226633Sdim	} else {
697223017Sdim		/*
698223017Sdim		 * Remove private indicator that this is an HT channel
699223017Sdim		 * and if no explicit channel width has been given
700223017Sdim		 * provide the default settings.
701223017Sdim		 */
702223017Sdim		flags &= ~_CHAN_HT;
703224145Sdim		if ((flags & IEEE80211_CHAN_HT) == 0) {
704223017Sdim			struct ieee80211_channel chan;
705226633Sdim			/*
706223017Sdim			 * Consult the channel list to see if we can use
707207618Srdivacky			 * HT40+ or HT40- (if both the map routines choose).
708224145Sdim			 */
709224145Sdim			if (freq > 255)
710224145Sdim				mapfreq(&chan, freq, 0);
711223017Sdim			else
712224145Sdim				mapchan(&chan, freq, 0);
713207618Srdivacky			flags |= (chan.ic_flags & IEEE80211_CHAN_HT);
714224145Sdim		}
715223017Sdim	}
716223017Sdim	return flags;
717223017Sdim#undef _CHAN_HT
718224145Sdim}
719207618Srdivacky
720224145Sdimstatic void
721226633Sdimgetchannel(int s, struct ieee80211_channel *chan, const char *val)
722207618Srdivacky{
723223017Sdim	int v, flags;
724226633Sdim	char *eptr;
725224145Sdim
726223017Sdim	memset(chan, 0, sizeof(*chan));
727226633Sdim	if (isanyarg(val)) {
728224145Sdim		chan->ic_freq = IEEE80211_CHAN_ANY;
729224145Sdim		return;
730223017Sdim	}
731223017Sdim	getchaninfo(s);
732223017Sdim	errno = 0;
733223017Sdim	v = strtol(val, &eptr, 10);
734223017Sdim	if (val[0] == '\0' || val == eptr || errno == ERANGE ||
735223017Sdim	    /* channel may be suffixed with nothing, :flag, or /width */
736224145Sdim	    (eptr[0] != '\0' && eptr[0] != ':' && eptr[0] != '/'))
737224145Sdim		errx(1, "invalid channel specification%s",
738223017Sdim		    errno == ERANGE ? " (out of range)" : "");
739223017Sdim	flags = getchannelflags(val, v);
740207618Srdivacky	if (v > 255) {		/* treat as frequency */
741224145Sdim		mapfreq(chan, v, flags);
742223017Sdim	} else {
743223017Sdim		mapchan(chan, v, flags);
744226633Sdim	}
745226633Sdim}
746223017Sdim
747226633Sdimstatic void
748226633Sdimset80211channel(const char *val, int d, int s, const struct afswtch *rafp)
749223017Sdim{
750223017Sdim	struct ieee80211_channel chan;
751226633Sdim
752224145Sdim	getchannel(s, &chan, val);
753234353Sdim	set80211(s, IEEE80211_IOC_CURCHAN, 0, sizeof(chan), &chan);
754226633Sdim}
755224145Sdim
756223017Sdimstatic void
757223017Sdimset80211chanswitch(const char *val, int d, int s, const struct afswtch *rafp)
758223017Sdim{
759226633Sdim	struct ieee80211_chanswitch_req csr;
760226633Sdim
761226633Sdim	getchannel(s, &csr.csa_chan, val);
762226633Sdim	csr.csa_mode = 1;
763226633Sdim	csr.csa_count = 5;
764226633Sdim	set80211(s, IEEE80211_IOC_CHANSWITCH, 0, sizeof(csr), &csr);
765226633Sdim}
766226633Sdim
767226633Sdimstatic void
768226633Sdimset80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
769226633Sdim{
770226633Sdim	int	mode;
771226633Sdim
772226633Sdim	if (strcasecmp(val, "none") == 0) {
773226633Sdim		mode = IEEE80211_AUTH_NONE;
774226633Sdim	} else if (strcasecmp(val, "open") == 0) {
775226633Sdim		mode = IEEE80211_AUTH_OPEN;
776226633Sdim	} else if (strcasecmp(val, "shared") == 0) {
777226633Sdim		mode = IEEE80211_AUTH_SHARED;
778226633Sdim	} else if (strcasecmp(val, "8021x") == 0) {
779226633Sdim		mode = IEEE80211_AUTH_8021X;
780223017Sdim	} else if (strcasecmp(val, "wpa") == 0) {
781223017Sdim		mode = IEEE80211_AUTH_WPA;
782223017Sdim	} else {
783207618Srdivacky		errx(1, "unknown authmode");
784223017Sdim	}
785223017Sdim
786223017Sdim	set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
787226633Sdim}
788226633Sdim
789223017Sdimstatic void
790223017Sdimset80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
791223017Sdim{
792223017Sdim	int	mode;
793223017Sdim
794223017Sdim	if (strcasecmp(val, "off") == 0) {
795223017Sdim		mode = IEEE80211_POWERSAVE_OFF;
796223017Sdim	} else if (strcasecmp(val, "on") == 0) {
797223017Sdim		mode = IEEE80211_POWERSAVE_ON;
798223017Sdim	} else if (strcasecmp(val, "cam") == 0) {
799223017Sdim		mode = IEEE80211_POWERSAVE_CAM;
800223017Sdim	} else if (strcasecmp(val, "psp") == 0) {
801223017Sdim		mode = IEEE80211_POWERSAVE_PSP;
802226633Sdim	} else if (strcasecmp(val, "psp-cam") == 0) {
803223017Sdim		mode = IEEE80211_POWERSAVE_PSP_CAM;
804223017Sdim	} else {
805223017Sdim		errx(1, "unknown powersavemode");
806223017Sdim	}
807223017Sdim
808223017Sdim	set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
809226633Sdim}
810226633Sdim
811226633Sdimstatic void
812226633Sdimset80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
813223017Sdim{
814226633Sdim	if (d == 0)
815223017Sdim		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
816223017Sdim		    0, NULL);
817223017Sdim	else
818226633Sdim		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
819223017Sdim		    0, NULL);
820224145Sdim}
821224145Sdim
822224145Sdimstatic void
823224145Sdimset80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
824224145Sdim{
825223017Sdim	set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
826207618Srdivacky}
827207618Srdivacky
828207618Srdivackystatic void
829226633Sdimset80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
830226633Sdim{
831226633Sdim	int	mode;
832226633Sdim
833226633Sdim	if (strcasecmp(val, "off") == 0) {
834226633Sdim		mode = IEEE80211_WEP_OFF;
835226633Sdim	} else if (strcasecmp(val, "on") == 0) {
836226633Sdim		mode = IEEE80211_WEP_ON;
837223017Sdim	} else if (strcasecmp(val, "mixed") == 0) {
838226633Sdim		mode = IEEE80211_WEP_MIXED;
839226633Sdim	} else {
840226633Sdim		errx(1, "unknown wep mode");
841226633Sdim	}
842223017Sdim
843226633Sdim	set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
844226633Sdim}
845226633Sdim
846226633Sdimstatic void
847226633Sdimset80211wep(const char *val, int d, int s, const struct afswtch *rafp)
848226633Sdim{
849226633Sdim	set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
850226633Sdim}
851226633Sdim
852226633Sdimstatic int
853226633Sdimisundefarg(const char *arg)
854223017Sdim{
855234353Sdim	return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0);
856234353Sdim}
857234353Sdim
858234353Sdimstatic void
859234353Sdimset80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
860226633Sdim{
861234353Sdim	if (isundefarg(val))
862226633Sdim		set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
863226633Sdim	else
864226633Sdim		set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
865226633Sdim}
866223017Sdim
867223017Sdimstatic void
868226633Sdimset80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
869226633Sdim{
870226633Sdim	int		key = 0;
871226633Sdim	int		len;
872226633Sdim	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
873224145Sdim
874224145Sdim	if (isdigit((int)val[0]) && val[1] == ':') {
875224145Sdim		key = atoi(val)-1;
876224145Sdim		val += 2;
877224145Sdim	}
878224145Sdim
879224145Sdim	bzero(data, sizeof(data));
880224145Sdim	len = sizeof(data);
881224145Sdim	get_string(val, NULL, data, &len);
882224145Sdim
883224145Sdim	set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
884224145Sdim}
885224145Sdim
886224145Sdim/*
887224145Sdim * This function is purely a NetBSD compatibility interface.  The NetBSD
888224145Sdim * interface is too inflexible, but it's there so we'll support it since
889224145Sdim * it's not all that hard.
890224145Sdim */
891224145Sdimstatic void
892224145Sdimset80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
893224145Sdim{
894223017Sdim	int		txkey;
895223017Sdim	int		i, len;
896234353Sdim	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
897224145Sdim
898224145Sdim	set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
899226633Sdim
900226633Sdim	if (isdigit((int)val[0]) && val[1] == ':') {
901223017Sdim		txkey = val[0]-'0'-1;
902223017Sdim		val += 2;
903223017Sdim
904226633Sdim		for (i = 0; i < 4; i++) {
905226633Sdim			bzero(data, sizeof(data));
906226633Sdim			len = sizeof(data);
907226633Sdim			val = get_string(val, ",", data, &len);
908223017Sdim			if (val == NULL)
909223017Sdim				exit(1);
910223017Sdim
911226633Sdim			set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
912226633Sdim		}
913223017Sdim	} else {
914223017Sdim		bzero(data, sizeof(data));
915223017Sdim		len = sizeof(data);
916223017Sdim		get_string(val, NULL, data, &len);
917223017Sdim		txkey = 0;
918223017Sdim
919226633Sdim		set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
920226633Sdim
921226633Sdim		bzero(data, sizeof(data));
922223017Sdim		for (i = 1; i < 4; i++)
923223017Sdim			set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
924226633Sdim	}
925226633Sdim
926226633Sdim	set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
927223017Sdim}
928226633Sdim
929226633Sdimstatic void
930223017Sdimset80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
931224145Sdim{
932224145Sdim	set80211(s, IEEE80211_IOC_RTSTHRESHOLD,
933224145Sdim		isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL);
934224145Sdim}
935224145Sdim
936223017Sdimstatic void
937223017Sdimset80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
938223017Sdim{
939223017Sdim	int	mode;
940224145Sdim
941224145Sdim	if (strcasecmp(val, "off") == 0) {
942226633Sdim		mode = IEEE80211_PROTMODE_OFF;
943223017Sdim	} else if (strcasecmp(val, "cts") == 0) {
944226633Sdim		mode = IEEE80211_PROTMODE_CTS;
945226633Sdim	} else if (strncasecmp(val, "rtscts", 3) == 0) {
946226633Sdim		mode = IEEE80211_PROTMODE_RTSCTS;
947223017Sdim	} else {
948223017Sdim		errx(1, "unknown protection mode");
949223017Sdim	}
950226633Sdim
951226633Sdim	set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
952226633Sdim}
953223017Sdim
954223017Sdimstatic void
955226633Sdimset80211htprotmode(const char *val, int d, int s, const struct afswtch *rafp)
956224145Sdim{
957226633Sdim	int	mode;
958224145Sdim
959224145Sdim	if (strcasecmp(val, "off") == 0) {
960223017Sdim		mode = IEEE80211_PROTMODE_OFF;
961223017Sdim	} else if (strncasecmp(val, "rts", 3) == 0) {
962223017Sdim		mode = IEEE80211_PROTMODE_RTSCTS;
963226633Sdim	} else {
964234353Sdim		errx(1, "unknown protection mode");
965223017Sdim	}
966223017Sdim
967226633Sdim	set80211(s, IEEE80211_IOC_HTPROTMODE, mode, 0, NULL);
968223017Sdim}
969223017Sdim
970223017Sdimstatic void
971224145Sdimset80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
972224145Sdim{
973223017Sdim	double v = atof(val);
974223017Sdim	int txpow;
975224145Sdim
976223017Sdim	txpow = (int) (2*v);
977223017Sdim	if (txpow != 2*v)
978223017Sdim		errx(-1, "invalid tx power (must be .5 dBm units)");
979223017Sdim	set80211(s, IEEE80211_IOC_TXPOWER, txpow, 0, NULL);
980223017Sdim}
981223017Sdim
982223017Sdim#define	IEEE80211_ROAMING_DEVICE	0
983223017Sdim#define	IEEE80211_ROAMING_AUTO		1
984223017Sdim#define	IEEE80211_ROAMING_MANUAL	2
985223017Sdim
986223017Sdimstatic void
987224145Sdimset80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
988224145Sdim{
989224145Sdim	int mode;
990224145Sdim
991224145Sdim	if (strcasecmp(val, "device") == 0) {
992226633Sdim		mode = IEEE80211_ROAMING_DEVICE;
993224145Sdim	} else if (strcasecmp(val, "auto") == 0) {
994224145Sdim		mode = IEEE80211_ROAMING_AUTO;
995226633Sdim	} else if (strcasecmp(val, "manual") == 0) {
996224145Sdim		mode = IEEE80211_ROAMING_MANUAL;
997224145Sdim	} else {
998226633Sdim		errx(1, "unknown roaming mode");
999224145Sdim	}
1000224145Sdim	set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
1001224145Sdim}
1002223017Sdim
1003223017Sdimstatic void
1004223017Sdimset80211wme(const char *val, int d, int s, const struct afswtch *rafp)
1005224145Sdim{
1006223017Sdim	set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
1007223017Sdim}
1008223017Sdim
1009223017Sdimstatic void
1010223017Sdimset80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
1011223017Sdim{
1012224145Sdim	set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
1013223017Sdim}
1014223017Sdim
1015223017Sdimstatic void
1016224145Sdimset80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
1017223017Sdim{
1018223017Sdim	set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
1019223017Sdim}
1020223017Sdim
1021223017Sdimstatic void
1022223017Sdimset80211fastframes(const char *val, int d, int s, const struct afswtch *rafp)
1023223017Sdim{
1024223017Sdim	set80211(s, IEEE80211_IOC_FF, d, 0, NULL);
1025223017Sdim}
1026223017Sdim
1027223017Sdimstatic void
1028223017Sdimset80211dturbo(const char *val, int d, int s, const struct afswtch *rafp)
1029224145Sdim{
1030223017Sdim	set80211(s, IEEE80211_IOC_TURBOP, d, 0, NULL);
1031224145Sdim}
1032223017Sdim
1033223017Sdimstatic void
1034223017Sdimset80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
1035223017Sdim{
1036223017Sdim	struct ieee80211req_chanlist chanlist;
1037223017Sdim	char *temp, *cp, *tp;
1038223017Sdim
1039223017Sdim	temp = malloc(strlen(val) + 1);
1040223017Sdim	if (temp == NULL)
1041223017Sdim		errx(1, "malloc failed");
1042223017Sdim	strcpy(temp, val);
1043223017Sdim	memset(&chanlist, 0, sizeof(chanlist));
1044223017Sdim	cp = temp;
1045223017Sdim	for (;;) {
1046223017Sdim		int first, last, f, c;
1047223017Sdim
1048223017Sdim		tp = strchr(cp, ',');
1049223017Sdim		if (tp != NULL)
1050223017Sdim			*tp++ = '\0';
1051223017Sdim		switch (sscanf(cp, "%u-%u", &first, &last)) {
1052223017Sdim		case 1:
1053223017Sdim			if (first > IEEE80211_CHAN_MAX)
1054223017Sdim				errx(-1, "channel %u out of range, max %u",
1055223017Sdim					first, IEEE80211_CHAN_MAX);
1056223017Sdim			setbit(chanlist.ic_channels, first);
1057223017Sdim			break;
1058223017Sdim		case 2:
1059224145Sdim			if (first > IEEE80211_CHAN_MAX)
1060223017Sdim				errx(-1, "channel %u out of range, max %u",
1061224145Sdim					first, IEEE80211_CHAN_MAX);
1062224145Sdim			if (last > IEEE80211_CHAN_MAX)
1063224145Sdim				errx(-1, "channel %u out of range, max %u",
1064223017Sdim					last, IEEE80211_CHAN_MAX);
1065226633Sdim			if (first > last)
1066223017Sdim				errx(-1, "void channel range, %u > %u",
1067223017Sdim					first, last);
1068223017Sdim			for (f = first; f <= last; f++)
1069234353Sdim				setbit(chanlist.ic_channels, f);
1070223017Sdim			break;
1071223017Sdim		}
1072224145Sdim		if (tp == NULL)
1073226633Sdim			break;
1074224145Sdim		c = *tp;
1075223017Sdim		while (isspace(c))
1076226633Sdim			tp++;
1077226633Sdim		if (!isdigit(c))
1078223017Sdim			break;
1079224145Sdim		cp = tp;
1080223017Sdim	}
1081223017Sdim	set80211(s, IEEE80211_IOC_CHANLIST, 0, sizeof(chanlist), &chanlist);
1082224145Sdim}
1083224145Sdim
1084224145Sdimstatic void
1085224145Sdimset80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
1086223017Sdim{
1087226633Sdim
1088226633Sdim	if (!isanyarg(val)) {
1089224145Sdim		char *temp;
1090224145Sdim		struct sockaddr_dl sdl;
1091226633Sdim
1092224145Sdim		temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1093226633Sdim		if (temp == NULL)
1094226633Sdim			errx(1, "malloc failed");
1095226633Sdim		temp[0] = ':';
1096226633Sdim		strcpy(temp + 1, val);
1097194178Sed		sdl.sdl_len = sizeof(sdl);
1098224145Sdim		link_addr(temp, &sdl);
1099224145Sdim		free(temp);
1100224145Sdim		if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1101193323Sed			errx(1, "malformed link-level address");
1102224145Sdim		set80211(s, IEEE80211_IOC_BSSID, 0,
1103224145Sdim			IEEE80211_ADDR_LEN, LLADDR(&sdl));
1104224145Sdim	} else {
1105224145Sdim		uint8_t zerobssid[IEEE80211_ADDR_LEN];
1106224145Sdim		memset(zerobssid, 0, sizeof(zerobssid));
1107224145Sdim		set80211(s, IEEE80211_IOC_BSSID, 0,
1108224145Sdim			IEEE80211_ADDR_LEN, zerobssid);
1109224145Sdim	}
1110224145Sdim}
1111224145Sdim
1112226633Sdimstatic int
1113224145Sdimgetac(const char *ac)
1114224145Sdim{
1115193323Sed	if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0)
1116224145Sdim		return WME_AC_BE;
1117226633Sdim	if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0)
1118193323Sed		return WME_AC_BK;
1119226633Sdim	if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0)
1120221345Sdim		return WME_AC_VI;
1121226633Sdim	if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0)
1122226633Sdim		return WME_AC_VO;
1123224145Sdim	errx(1, "unknown wme access class %s", ac);
1124224145Sdim}
1125193323Sed
1126226633Sdimstatic
1127226633SdimDECL_CMD_FUNC2(set80211cwmin, ac, val)
1128224145Sdim{
1129224145Sdim	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
1130224145Sdim}
1131224145Sdim
1132224145Sdimstatic
1133224145SdimDECL_CMD_FUNC2(set80211cwmax, ac, val)
1134193323Sed{
1135193323Sed	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
1136226633Sdim}
1137226633Sdim
1138226633Sdimstatic
1139207618SrdivackyDECL_CMD_FUNC2(set80211aifs, ac, val)
1140226633Sdim{
1141226633Sdim	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
1142226633Sdim}
1143226633Sdim
1144234353Sdimstatic
1145226633SdimDECL_CMD_FUNC2(set80211txoplimit, ac, val)
1146234353Sdim{
1147234353Sdim	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
1148234353Sdim}
1149226633Sdim
1150226633Sdimstatic
1151226633SdimDECL_CMD_FUNC(set80211acm, ac, d)
1152226633Sdim{
1153226633Sdim	set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL);
1154226633Sdim}
1155226633Sdimstatic
1156226633SdimDECL_CMD_FUNC(set80211noacm, ac, d)
1157226633Sdim{
1158226633Sdim	set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL);
1159226633Sdim}
1160226633Sdim
1161226633Sdimstatic
1162226633SdimDECL_CMD_FUNC(set80211ackpolicy, ac, d)
1163226633Sdim{
1164224145Sdim	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL);
1165207618Srdivacky}
1166226633Sdimstatic
1167226633SdimDECL_CMD_FUNC(set80211noackpolicy, ac, d)
1168226633Sdim{
1169226633Sdim	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL);
1170226633Sdim}
1171226633Sdim
1172226633Sdimstatic
1173234353SdimDECL_CMD_FUNC2(set80211bsscwmin, ac, val)
1174226633Sdim{
1175226633Sdim	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
1176226633Sdim		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1177226633Sdim}
1178226633Sdim
1179226633Sdimstatic
1180226633SdimDECL_CMD_FUNC2(set80211bsscwmax, ac, val)
1181226633Sdim{
1182226633Sdim	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
1183226633Sdim		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1184234353Sdim}
1185234353Sdim
1186226633Sdimstatic
1187224145SdimDECL_CMD_FUNC2(set80211bssaifs, ac, val)
1188221345Sdim{
1189224145Sdim	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
1190224145Sdim		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1191224145Sdim}
1192234353Sdim
1193234353Sdimstatic
1194234353SdimDECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
1195234353Sdim{
1196234353Sdim	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
1197234353Sdim		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1198234353Sdim}
1199234353Sdim
1200234353Sdimstatic
1201234353SdimDECL_CMD_FUNC(set80211dtimperiod, val, d)
1202224145Sdim{
1203224145Sdim	set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
1204224145Sdim}
1205224145Sdim
1206224145Sdimstatic
1207207618SrdivackyDECL_CMD_FUNC(set80211bintval, val, d)
1208224145Sdim{
1209224145Sdim	set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
1210207618Srdivacky}
1211224145Sdim
1212224145Sdimstatic void
1213224145Sdimset80211macmac(int s, int op, const char *val)
1214224145Sdim{
1215224145Sdim	char *temp;
1216234353Sdim	struct sockaddr_dl sdl;
1217234353Sdim
1218226633Sdim	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1219226633Sdim	if (temp == NULL)
1220224145Sdim		errx(1, "malloc failed");
1221207618Srdivacky	temp[0] = ':';
1222207618Srdivacky	strcpy(temp + 1, val);
1223226633Sdim	sdl.sdl_len = sizeof(sdl);
1224226633Sdim	link_addr(temp, &sdl);
1225226633Sdim	free(temp);
1226226633Sdim	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1227226633Sdim		errx(1, "malformed link-level address");
1228226633Sdim	set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
1229226633Sdim}
1230226633Sdim
1231226633Sdimstatic
1232226633SdimDECL_CMD_FUNC(set80211addmac, val, d)
1233226633Sdim{
1234226633Sdim	set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
1235226633Sdim}
1236226633Sdim
1237226633Sdimstatic
1238226633SdimDECL_CMD_FUNC(set80211delmac, val, d)
1239226633Sdim{
1240226633Sdim	set80211macmac(s, IEEE80211_IOC_DELMAC, val);
1241226633Sdim}
1242226633Sdim
1243226633Sdimstatic
1244226633SdimDECL_CMD_FUNC(set80211kickmac, val, d)
1245226633Sdim{
1246226633Sdim	char *temp;
1247226633Sdim	struct sockaddr_dl sdl;
1248226633Sdim	struct ieee80211req_mlme mlme;
1249226633Sdim
1250226633Sdim	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1251226633Sdim	if (temp == NULL)
1252226633Sdim		errx(1, "malloc failed");
1253226633Sdim	temp[0] = ':';
1254226633Sdim	strcpy(temp + 1, val);
1255226633Sdim	sdl.sdl_len = sizeof(sdl);
1256226633Sdim	link_addr(temp, &sdl);
1257226633Sdim	free(temp);
1258226633Sdim	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1259226633Sdim		errx(1, "malformed link-level address");
1260239462Sdim	memset(&mlme, 0, sizeof(mlme));
1261239462Sdim	mlme.im_op = IEEE80211_MLME_DEAUTH;
1262226633Sdim	mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE;
1263226633Sdim	memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN);
1264226633Sdim	set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), &mlme);
1265226633Sdim}
1266226633Sdim
1267239462Sdimstatic
1268226633SdimDECL_CMD_FUNC(set80211maccmd, val, d)
1269226633Sdim{
1270226633Sdim	set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
1271226633Sdim}
1272239462Sdim
1273239462Sdimstatic void
1274239462Sdimset80211meshrtmac(int s, int req, const char *val)
1275239462Sdim{
1276239462Sdim	char *temp;
1277239462Sdim	struct sockaddr_dl sdl;
1278226633Sdim
1279239462Sdim	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1280226633Sdim	if (temp == NULL)
1281226633Sdim		errx(1, "malloc failed");
1282226633Sdim	temp[0] = ':';
1283226633Sdim	strcpy(temp + 1, val);
1284226633Sdim	sdl.sdl_len = sizeof(sdl);
1285226633Sdim	link_addr(temp, &sdl);
1286226633Sdim	free(temp);
1287226633Sdim	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1288226633Sdim		errx(1, "malformed link-level address");
1289226633Sdim	set80211(s, IEEE80211_IOC_MESH_RTCMD, req,
1290226633Sdim	    IEEE80211_ADDR_LEN, LLADDR(&sdl));
1291226633Sdim}
1292226633Sdim
1293226633Sdimstatic
1294226633SdimDECL_CMD_FUNC(set80211addmeshrt, val, d)
1295226633Sdim{
1296226633Sdim	set80211meshrtmac(s, IEEE80211_MESH_RTCMD_ADD, val);
1297226633Sdim}
1298226633Sdim
1299226633Sdimstatic
1300226633SdimDECL_CMD_FUNC(set80211delmeshrt, val, d)
1301226633Sdim{
1302226633Sdim	set80211meshrtmac(s, IEEE80211_MESH_RTCMD_DELETE, val);
1303226633Sdim}
1304243830Sdim
1305243830Sdimstatic
1306243830SdimDECL_CMD_FUNC(set80211meshrtcmd, val, d)
1307243830Sdim{
1308243830Sdim	set80211(s, IEEE80211_IOC_MESH_RTCMD, d, 0, NULL);
1309226633Sdim}
1310243830Sdim
1311226633Sdimstatic
1312226633SdimDECL_CMD_FUNC(set80211hwmprootmode, val, d)
1313226633Sdim{
1314239462Sdim	int mode;
1315239462Sdim
1316239462Sdim	if (strcasecmp(val, "normal") == 0)
1317239462Sdim		mode = IEEE80211_HWMP_ROOTMODE_NORMAL;
1318239462Sdim	else if (strcasecmp(val, "proactive") == 0)
1319239462Sdim		mode = IEEE80211_HWMP_ROOTMODE_PROACTIVE;
1320239462Sdim	else if (strcasecmp(val, "rann") == 0)
1321239462Sdim		mode = IEEE80211_HWMP_ROOTMODE_RANN;
1322239462Sdim	else
1323239462Sdim		mode = IEEE80211_HWMP_ROOTMODE_DISABLED;
1324239462Sdim	set80211(s, IEEE80211_IOC_HWMP_ROOTMODE, mode, 0, NULL);
1325239462Sdim}
1326239462Sdim
1327239462Sdimstatic
1328239462SdimDECL_CMD_FUNC(set80211hwmpmaxhops, val, d)
1329239462Sdim{
1330239462Sdim	set80211(s, IEEE80211_IOC_HWMP_MAXHOPS, atoi(val), 0, NULL);
1331239462Sdim}
1332239462Sdim
1333239462Sdimstatic void
1334239462Sdimset80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
1335239462Sdim{
1336239462Sdim	set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
1337239462Sdim}
1338239462Sdim
1339239462Sdimstatic void
1340239462Sdimset80211quiet(const char *val, int d, int s, const struct afswtch *rafp)
1341239462Sdim{
1342239462Sdim	set80211(s, IEEE80211_IOC_QUIET, d, 0, NULL);
1343239462Sdim}
1344239462Sdim
1345239462Sdimstatic
1346239462SdimDECL_CMD_FUNC(set80211quietperiod, val, d)
1347239462Sdim{
1348239462Sdim	set80211(s, IEEE80211_IOC_QUIET_PERIOD, atoi(val), 0, NULL);
1349239462Sdim}
1350239462Sdim
1351239462Sdimstatic
1352239462SdimDECL_CMD_FUNC(set80211quietcount, val, d)
1353239462Sdim{
1354239462Sdim	set80211(s, IEEE80211_IOC_QUIET_COUNT, atoi(val), 0, NULL);
1355239462Sdim}
1356226633Sdim
1357226633Sdimstatic
1358226633SdimDECL_CMD_FUNC(set80211quietduration, val, d)
1359226633Sdim{
1360226633Sdim	set80211(s, IEEE80211_IOC_QUIET_DUR, atoi(val), 0, NULL);
1361226633Sdim}
1362226633Sdim
1363226633Sdimstatic
1364226633SdimDECL_CMD_FUNC(set80211quietoffset, val, d)
1365226633Sdim{
1366226633Sdim	set80211(s, IEEE80211_IOC_QUIET_OFFSET, atoi(val), 0, NULL);
1367226633Sdim}
1368226633Sdim
1369226633Sdimstatic void
1370226633Sdimset80211bgscan(const char *val, int d, int s, const struct afswtch *rafp)
1371226633Sdim{
1372226633Sdim	set80211(s, IEEE80211_IOC_BGSCAN, d, 0, NULL);
1373226633Sdim}
1374226633Sdim
1375226633Sdimstatic
1376234353SdimDECL_CMD_FUNC(set80211bgscanidle, val, d)
1377234353Sdim{
1378234353Sdim	set80211(s, IEEE80211_IOC_BGSCAN_IDLE, atoi(val), 0, NULL);
1379234353Sdim}
1380239462Sdim
1381239462Sdimstatic
1382226633SdimDECL_CMD_FUNC(set80211bgscanintvl, val, d)
1383226633Sdim{
1384226633Sdim	set80211(s, IEEE80211_IOC_BGSCAN_INTERVAL, atoi(val), 0, NULL);
1385226633Sdim}
1386226633Sdim
1387226633Sdimstatic
1388226633SdimDECL_CMD_FUNC(set80211scanvalid, val, d)
1389243830Sdim{
1390226633Sdim	set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL);
1391226633Sdim}
1392226633Sdim
1393226633Sdim/*
1394226633Sdim * Parse an optional trailing specification of which netbands
1395226633Sdim * to apply a parameter to.  This is basically the same syntax
1396226633Sdim * as used for channels but you can concatenate to specify
1397226633Sdim * multiple.  For example:
1398226633Sdim *	14:abg		apply to 11a, 11b, and 11g
1399226633Sdim *	6:ht		apply to 11na and 11ng
1400226633Sdim * We don't make a big effort to catch silly things; this is
1401226633Sdim * really a convenience mechanism.
1402226633Sdim */
1403226633Sdimstatic int
1404226633Sdimgetmodeflags(const char *val)
1405226633Sdim{
1406234353Sdim	const char *cp;
1407234353Sdim	int flags;
1408234353Sdim
1409234353Sdim	flags = 0;
1410226633Sdim
1411226633Sdim	cp = strchr(val, ':');
1412226633Sdim	if (cp != NULL) {
1413226633Sdim		for (cp++; isalpha((int) *cp); cp++) {
1414226633Sdim			/* accept mixed case */
1415226633Sdim			int c = *cp;
1416226633Sdim			if (isupper(c))
1417226633Sdim				c = tolower(c);
1418226633Sdim			switch (c) {
1419226633Sdim			case 'a':		/* 802.11a */
1420226633Sdim				flags |= IEEE80211_CHAN_A;
1421226633Sdim				break;
1422226633Sdim			case 'b':		/* 802.11b */
1423226633Sdim				flags |= IEEE80211_CHAN_B;
1424226633Sdim				break;
1425226633Sdim			case 'g':		/* 802.11g */
1426226633Sdim				flags |= IEEE80211_CHAN_G;
1427226633Sdim				break;
1428226633Sdim			case 'n':		/* 802.11n */
1429226633Sdim				flags |= IEEE80211_CHAN_HT;
1430239462Sdim				break;
1431239462Sdim			case 'd':		/* dt = Atheros Dynamic Turbo */
1432239462Sdim				flags |= IEEE80211_CHAN_TURBO;
1433239462Sdim				break;
1434239462Sdim			case 't':		/* ht, dt, st, t */
1435239462Sdim				/* dt and unadorned t specify Dynamic Turbo */
1436239462Sdim				if ((flags & (IEEE80211_CHAN_STURBO|IEEE80211_CHAN_HT)) == 0)
1437239462Sdim					flags |= IEEE80211_CHAN_TURBO;
1438239462Sdim				break;
1439239462Sdim			case 's':		/* st = Atheros Static Turbo */
1440239462Sdim				flags |= IEEE80211_CHAN_STURBO;
1441239462Sdim				break;
1442239462Sdim			case 'h':		/* 1/2-width channels */
1443226633Sdim				flags |= IEEE80211_CHAN_HALF;
1444226633Sdim				break;
1445226633Sdim			case 'q':		/* 1/4-width channels */
1446226633Sdim				flags |= IEEE80211_CHAN_QUARTER;
1447226633Sdim				break;
1448226633Sdim			default:
1449226633Sdim				errx(-1, "%s: Invalid mode attribute %c\n",
1450226633Sdim				    val, *cp);
1451226633Sdim			}
1452226633Sdim		}
1453226633Sdim	}
1454226633Sdim	return flags;
1455226633Sdim}
1456226633Sdim
1457226633Sdim#define	IEEE80211_CHAN_HTA	(IEEE80211_CHAN_HT|IEEE80211_CHAN_5GHZ)
1458226633Sdim#define	IEEE80211_CHAN_HTG	(IEEE80211_CHAN_HT|IEEE80211_CHAN_2GHZ)
1459239462Sdim
1460226633Sdim#define	_APPLY(_flags, _base, _param, _v) do {				\
1461226633Sdim    if (_flags & IEEE80211_CHAN_HT) {					\
1462226633Sdim	    if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1463226633Sdim		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1464226633Sdim		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1465226633Sdim	    } else if (_flags & IEEE80211_CHAN_5GHZ)			\
1466226633Sdim		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1467226633Sdim	    else							\
1468234353Sdim		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1469234353Sdim    }									\
1470234353Sdim    if (_flags & IEEE80211_CHAN_TURBO) {				\
1471234353Sdim	    if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1472234353Sdim		    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;	\
1473234353Sdim		    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;	\
1474234353Sdim	    } else if (_flags & IEEE80211_CHAN_5GHZ)			\
1475234353Sdim		    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;	\
1476234353Sdim	    else							\
1477234353Sdim		    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;	\
1478234353Sdim    }									\
1479234353Sdim    if (_flags & IEEE80211_CHAN_STURBO)					\
1480234353Sdim	    _base.params[IEEE80211_MODE_STURBO_A]._param = _v;		\
1481234353Sdim    if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)		\
1482234353Sdim	    _base.params[IEEE80211_MODE_11A]._param = _v;		\
1483263508Sdim    if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)		\
1484263508Sdim	    _base.params[IEEE80211_MODE_11G]._param = _v;		\
1485263508Sdim    if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)		\
1486263508Sdim	    _base.params[IEEE80211_MODE_11B]._param = _v;		\
1487263508Sdim    if (_flags & IEEE80211_CHAN_HALF)					\
1488263508Sdim	    _base.params[IEEE80211_MODE_HALF]._param = _v;		\
1489234353Sdim    if (_flags & IEEE80211_CHAN_QUARTER)				\
1490263508Sdim	    _base.params[IEEE80211_MODE_QUARTER]._param = _v;		\
1491234353Sdim} while (0)
1492234353Sdim#define	_APPLY1(_flags, _base, _param, _v) do {				\
1493234353Sdim    if (_flags & IEEE80211_CHAN_HT) {					\
1494234353Sdim	    if (_flags & IEEE80211_CHAN_5GHZ)				\
1495234353Sdim		    _base.params[IEEE80211_MODE_11NA]._param = _v;	\
1496234353Sdim	    else							\
1497234353Sdim		    _base.params[IEEE80211_MODE_11NG]._param = _v;	\
1498234353Sdim    } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A)	\
1499234353Sdim	    _base.params[IEEE80211_MODE_TURBO_A]._param = _v;		\
1500234353Sdim    else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G)	\
1501234353Sdim	    _base.params[IEEE80211_MODE_TURBO_G]._param = _v;		\
1502263508Sdim    else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST)		\
1503234353Sdim	    _base.params[IEEE80211_MODE_STURBO_A]._param = _v;		\
1504234353Sdim    else if (_flags & IEEE80211_CHAN_HALF)				\
1505234353Sdim	    _base.params[IEEE80211_MODE_HALF]._param = _v;		\
1506234353Sdim    else if (_flags & IEEE80211_CHAN_QUARTER)				\
1507234353Sdim	    _base.params[IEEE80211_MODE_QUARTER]._param = _v;		\
1508234353Sdim    else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)		\
1509234353Sdim	    _base.params[IEEE80211_MODE_11A]._param = _v;		\
1510234353Sdim    else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)		\
1511234353Sdim	    _base.params[IEEE80211_MODE_11G]._param = _v;		\
1512234353Sdim    else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)		\
1513234353Sdim	    _base.params[IEEE80211_MODE_11B]._param = _v;		\
1514234353Sdim} while (0)
1515234353Sdim#define	_APPLY_RATE(_flags, _base, _param, _v) do {			\
1516263508Sdim    if (_flags & IEEE80211_CHAN_HT) {					\
1517263508Sdim	(_v) = (_v / 2) | IEEE80211_RATE_MCS;				\
1518263508Sdim    }									\
1519263508Sdim    _APPLY(_flags, _base, _param, _v);					\
1520234353Sdim} while (0)
1521234353Sdim#define	_APPLY_RATE1(_flags, _base, _param, _v) do {			\
1522234353Sdim    if (_flags & IEEE80211_CHAN_HT) {					\
1523234353Sdim	(_v) = (_v / 2) | IEEE80211_RATE_MCS;				\
1524234353Sdim    }									\
1525234353Sdim    _APPLY1(_flags, _base, _param, _v);					\
1526234353Sdim} while (0)
1527234353Sdim
1528234353Sdimstatic
1529234353SdimDECL_CMD_FUNC(set80211roamrssi, val, d)
1530234353Sdim{
1531234353Sdim	double v = atof(val);
1532234353Sdim	int rssi, flags;
1533234353Sdim
1534234353Sdim	rssi = (int) (2*v);
1535234353Sdim	if (rssi != 2*v)
1536234353Sdim		errx(-1, "invalid rssi (must be .5 dBm units)");
1537234353Sdim	flags = getmodeflags(val);
1538234353Sdim	getroam(s);
1539234353Sdim	if (flags == 0) {		/* NB: no flags => current channel */
1540234353Sdim		flags = getcurchan(s)->ic_flags;
1541234353Sdim		_APPLY1(flags, roamparams, rssi, rssi);
1542234353Sdim	} else
1543234353Sdim		_APPLY(flags, roamparams, rssi, rssi);
1544234353Sdim	callback_register(setroam_cb, &roamparams);
1545234353Sdim}
1546234353Sdim
1547234353Sdimstatic int
1548234353Sdimgetrate(const char *val, const char *tag)
1549234353Sdim{
1550234353Sdim	double v = atof(val);
1551224145Sdim	int rate;
1552224145Sdim
1553224145Sdim	rate = (int) (2*v);
1554224145Sdim	if (rate != 2*v)
1555224145Sdim		errx(-1, "invalid %s rate (must be .5 Mb/s units)", tag);
1556226633Sdim	return rate;		/* NB: returns 2x the specified value */
1557224145Sdim}
1558224145Sdim
1559224145Sdimstatic
1560224145SdimDECL_CMD_FUNC(set80211roamrate, val, d)
1561224145Sdim{
1562193323Sed	int rate, flags;
1563263508Sdim
1564263508Sdim	rate = getrate(val, "roam");
1565234353Sdim	flags = getmodeflags(val);
1566226633Sdim	getroam(s);
1567234353Sdim	if (flags == 0) {		/* NB: no flags => current channel */
1568234353Sdim		flags = getcurchan(s)->ic_flags;
1569234353Sdim		_APPLY_RATE1(flags, roamparams, rate, rate);
1570224145Sdim	} else
1571224145Sdim		_APPLY_RATE(flags, roamparams, rate, rate);
1572263508Sdim	callback_register(setroam_cb, &roamparams);
1573263508Sdim}
1574263508Sdim
1575263508Sdimstatic
1576224145SdimDECL_CMD_FUNC(set80211mcastrate, val, d)
1577224145Sdim{
1578224145Sdim	int rate, flags;
1579224145Sdim
1580224145Sdim	rate = getrate(val, "mcast");
1581221345Sdim	flags = getmodeflags(val);
1582234353Sdim	gettxparams(s);
1583234353Sdim	if (flags == 0) {		/* NB: no flags => current channel */
1584234353Sdim		flags = getcurchan(s)->ic_flags;
1585226633Sdim		_APPLY_RATE1(flags, txparams, mcastrate, rate);
1586224145Sdim	} else
1587234353Sdim		_APPLY_RATE(flags, txparams, mcastrate, rate);
1588226633Sdim	callback_register(settxparams_cb, &txparams);
1589224145Sdim}
1590226633Sdim
1591224145Sdimstatic
1592226633SdimDECL_CMD_FUNC(set80211mgtrate, val, d)
1593224145Sdim{
1594224145Sdim	int rate, flags;
1595224145Sdim
1596224145Sdim	rate = getrate(val, "mgmt");
1597226633Sdim	flags = getmodeflags(val);
1598226633Sdim	gettxparams(s);
1599234353Sdim	if (flags == 0) {		/* NB: no flags => current channel */
1600224145Sdim		flags = getcurchan(s)->ic_flags;
1601234353Sdim		_APPLY_RATE1(flags, txparams, mgmtrate, rate);
1602263508Sdim	} else
1603263508Sdim		_APPLY_RATE(flags, txparams, mgmtrate, rate);
1604263508Sdim	callback_register(settxparams_cb, &txparams);
1605263508Sdim}
1606263508Sdim
1607263508Sdimstatic
1608263508SdimDECL_CMD_FUNC(set80211ucastrate, val, d)
1609263508Sdim{
1610263508Sdim	int flags;
1611263508Sdim
1612263508Sdim	gettxparams(s);
1613263508Sdim	flags = getmodeflags(val);
1614263508Sdim	if (isanyarg(val)) {
1615263508Sdim		if (flags == 0) {	/* NB: no flags => current channel */
1616263508Sdim			flags = getcurchan(s)->ic_flags;
1617263508Sdim			_APPLY1(flags, txparams, ucastrate,
1618263508Sdim			    IEEE80211_FIXED_RATE_NONE);
1619263508Sdim		} else
1620263508Sdim			_APPLY(flags, txparams, ucastrate,
1621263508Sdim			    IEEE80211_FIXED_RATE_NONE);
1622263508Sdim	} else {
1623263508Sdim		int rate = getrate(val, "ucast");
1624263508Sdim		if (flags == 0) {	/* NB: no flags => current channel */
1625263508Sdim			flags = getcurchan(s)->ic_flags;
1626263508Sdim			_APPLY_RATE1(flags, txparams, ucastrate, rate);
1627263508Sdim		} else
1628263508Sdim			_APPLY_RATE(flags, txparams, ucastrate, rate);
1629263508Sdim	}
1630263508Sdim	callback_register(settxparams_cb, &txparams);
1631263508Sdim}
1632263508Sdim
1633263508Sdimstatic
1634263508SdimDECL_CMD_FUNC(set80211maxretry, val, d)
1635226633Sdim{
1636226633Sdim	int v = atoi(val), flags;
1637224145Sdim
1638224145Sdim	flags = getmodeflags(val);
1639224145Sdim	gettxparams(s);
1640224145Sdim	if (flags == 0) {		/* NB: no flags => current channel */
1641224145Sdim		flags = getcurchan(s)->ic_flags;
1642224145Sdim		_APPLY1(flags, txparams, maxretry, v);
1643224145Sdim	} else
1644224145Sdim		_APPLY(flags, txparams, maxretry, v);
1645224145Sdim	callback_register(settxparams_cb, &txparams);
1646224145Sdim}
1647224145Sdim#undef _APPLY_RATE
1648224145Sdim#undef _APPLY
1649193323Sed#undef IEEE80211_CHAN_HTA
1650193323Sed#undef IEEE80211_CHAN_HTG
1651224145Sdim
1652224145Sdimstatic
1653224145SdimDECL_CMD_FUNC(set80211fragthreshold, val, d)
1654224145Sdim{
1655193323Sed	set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
1656193323Sed		isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
1657193323Sed}
1658195098Sed
1659193323Sedstatic
1660193323SedDECL_CMD_FUNC(set80211bmissthreshold, val, d)
1661193323Sed{
1662199481Srdivacky	set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
1663199481Srdivacky		isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
1664199481Srdivacky}
1665226633Sdim
1666193323Sedstatic void
1667193323Sedset80211burst(const char *val, int d, int s, const struct afswtch *rafp)
1668193323Sed{
1669195098Sed	set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
1670195098Sed}
1671193323Sed
1672206083Srdivackystatic void
1673198090Srdivackyset80211doth(const char *val, int d, int s, const struct afswtch *rafp)
1674206083Srdivacky{
1675206083Srdivacky	set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL);
1676206083Srdivacky}
1677206083Srdivacky
1678206083Srdivackystatic void
1679198090Srdivackyset80211dfs(const char *val, int d, int s, const struct afswtch *rafp)
1680195098Sed{
1681206083Srdivacky	set80211(s, IEEE80211_IOC_DFS, d, 0, NULL);
1682205218Srdivacky}
1683205218Srdivacky
1684205218Srdivackystatic void
1685206083Srdivackyset80211shortgi(const char *val, int d, int s, const struct afswtch *rafp)
1686226633Sdim{
1687226633Sdim	set80211(s, IEEE80211_IOC_SHORTGI,
1688226633Sdim		d ? (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) : 0,
1689226633Sdim		0, NULL);
1690234353Sdim}
1691234353Sdim
1692234353Sdimstatic void
1693234353Sdimset80211ampdu(const char *val, int d, int s, const struct afswtch *rafp)
1694234353Sdim{
1695234353Sdim	int ampdu;
1696206083Srdivacky
1697193323Sed	if (get80211val(s, IEEE80211_IOC_AMPDU, &ampdu) < 0)
1698193323Sed		errx(-1, "cannot get AMPDU setting");
1699193323Sed	if (d < 0) {
1700193323Sed		d = -d;
1701193323Sed		ampdu &= ~d;
1702210299Sed	} else
1703210299Sed		ampdu |= d;
1704210299Sed	set80211(s, IEEE80211_IOC_AMPDU, ampdu, 0, NULL);
1705193323Sed}
1706193323Sed
1707193323Sedstatic
1708193323SedDECL_CMD_FUNC(set80211ampdulimit, val, d)
1709193323Sed{
1710193323Sed	int v;
1711193323Sed
1712193323Sed	switch (atoi(val)) {
1713193323Sed	case 8:
1714206083Srdivacky	case 8*1024:
1715193323Sed		v = IEEE80211_HTCAP_MAXRXAMPDU_8K;
1716193323Sed		break;
1717193323Sed	case 16:
1718206083Srdivacky	case 16*1024:
1719193323Sed		v = IEEE80211_HTCAP_MAXRXAMPDU_16K;
1720193323Sed		break;
1721193323Sed	case 32:
1722206083Srdivacky	case 32*1024:
1723206083Srdivacky		v = IEEE80211_HTCAP_MAXRXAMPDU_32K;
1724206083Srdivacky		break;
1725206083Srdivacky	case 64:
1726206083Srdivacky	case 64*1024:
1727206083Srdivacky		v = IEEE80211_HTCAP_MAXRXAMPDU_64K;
1728206083Srdivacky		break;
1729206083Srdivacky	default:
1730206083Srdivacky		errx(-1, "invalid A-MPDU limit %s", val);
1731206083Srdivacky	}
1732193323Sed	set80211(s, IEEE80211_IOC_AMPDU_LIMIT, v, 0, NULL);
1733206083Srdivacky}
1734206083Srdivacky
1735195098Sedstatic
1736206083SrdivackyDECL_CMD_FUNC(set80211ampdudensity, val, d)
1737195098Sed{
1738193323Sed	int v;
1739193323Sed
1740193323Sed	if (isanyarg(val) || strcasecmp(val, "na") == 0)
1741224145Sdim		v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1742224145Sdim	else switch ((int)(atof(val)*4)) {
1743224145Sdim	case 0:
1744224145Sdim		v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1745224145Sdim		break;
1746224145Sdim	case 1:
1747224145Sdim		v = IEEE80211_HTCAP_MPDUDENSITY_025;
1748224145Sdim		break;
1749224145Sdim	case 2:
1750224145Sdim		v = IEEE80211_HTCAP_MPDUDENSITY_05;
1751224145Sdim		break;
1752224145Sdim	case 4:
1753224145Sdim		v = IEEE80211_HTCAP_MPDUDENSITY_1;
1754224145Sdim		break;
1755193323Sed	case 8:
1756193323Sed		v = IEEE80211_HTCAP_MPDUDENSITY_2;
1757224145Sdim		break;
1758224145Sdim	case 16:
1759224145Sdim		v = IEEE80211_HTCAP_MPDUDENSITY_4;
1760243830Sdim		break;
1761243830Sdim	case 32:
1762193323Sed		v = IEEE80211_HTCAP_MPDUDENSITY_8;
1763224145Sdim		break;
1764224145Sdim	case 64:
1765206124Srdivacky		v = IEEE80211_HTCAP_MPDUDENSITY_16;
1766224145Sdim		break;
1767224145Sdim	default:
1768224145Sdim		errx(-1, "invalid A-MPDU density %s", val);
1769193323Sed	}
1770224145Sdim	set80211(s, IEEE80211_IOC_AMPDU_DENSITY, v, 0, NULL);
1771221345Sdim}
1772224145Sdim
1773224145Sdimstatic void
1774226633Sdimset80211amsdu(const char *val, int d, int s, const struct afswtch *rafp)
1775226633Sdim{
1776226633Sdim	int amsdu;
1777193323Sed
1778224145Sdim	if (get80211val(s, IEEE80211_IOC_AMSDU, &amsdu) < 0)
1779224145Sdim		err(-1, "cannot get AMSDU setting");
1780224145Sdim	if (d < 0) {
1781224145Sdim		d = -d;
1782224145Sdim		amsdu &= ~d;
1783224145Sdim	} else
1784234353Sdim		amsdu |= d;
1785234353Sdim	set80211(s, IEEE80211_IOC_AMSDU, amsdu, 0, NULL);
1786193323Sed}
1787224145Sdim
1788224145Sdimstatic
1789224145SdimDECL_CMD_FUNC(set80211amsdulimit, val, d)
1790224145Sdim{
1791224145Sdim	set80211(s, IEEE80211_IOC_AMSDU_LIMIT, atoi(val), 0, NULL);
1792224145Sdim}
1793224145Sdim
1794224145Sdimstatic void
1795221345Sdimset80211puren(const char *val, int d, int s, const struct afswtch *rafp)
1796224145Sdim{
1797234353Sdim	set80211(s, IEEE80211_IOC_PUREN, d, 0, NULL);
1798221345Sdim}
1799224145Sdim
1800224145Sdimstatic void
1801234353Sdimset80211htcompat(const char *val, int d, int s, const struct afswtch *rafp)
1802234353Sdim{
1803234353Sdim	set80211(s, IEEE80211_IOC_HTCOMPAT, d, 0, NULL);
1804234353Sdim}
1805234353Sdim
1806234353Sdimstatic void
1807234353Sdimset80211htconf(const char *val, int d, int s, const struct afswtch *rafp)
1808234353Sdim{
1809234353Sdim	set80211(s, IEEE80211_IOC_HTCONF, d, 0, NULL);
1810234353Sdim	htconf = d;
1811234353Sdim}
1812234353Sdim
1813234353Sdimstatic void
1814234353Sdimset80211dwds(const char *val, int d, int s, const struct afswtch *rafp)
1815234353Sdim{
1816234353Sdim	set80211(s, IEEE80211_IOC_DWDS, d, 0, NULL);
1817206124Srdivacky}
1818224145Sdim
1819224145Sdimstatic void
1820224145Sdimset80211inact(const char *val, int d, int s, const struct afswtch *rafp)
1821224145Sdim{
1822206124Srdivacky	set80211(s, IEEE80211_IOC_INACTIVITY, d, 0, NULL);
1823224145Sdim}
1824224145Sdim
1825224145Sdimstatic void
1826224145Sdimset80211tsn(const char *val, int d, int s, const struct afswtch *rafp)
1827224145Sdim{
1828243830Sdim	set80211(s, IEEE80211_IOC_TSN, d, 0, NULL);
1829193323Sed}
1830224145Sdim
1831193323Sedstatic void
1832224145Sdimset80211dotd(const char *val, int d, int s, const struct afswtch *rafp)
1833224145Sdim{
1834224145Sdim	set80211(s, IEEE80211_IOC_DOTD, d, 0, NULL);
1835193323Sed}
1836224145Sdim
1837243830Sdimstatic void
1838224145Sdimset80211smps(const char *val, int d, int s, const struct afswtch *rafp)
1839226633Sdim{
1840226633Sdim	set80211(s, IEEE80211_IOC_SMPS, d, 0, NULL);
1841226633Sdim}
1842226633Sdim
1843226633Sdimstatic void
1844226633Sdimset80211rifs(const char *val, int d, int s, const struct afswtch *rafp)
1845234353Sdim{
1846226633Sdim	set80211(s, IEEE80211_IOC_RIFS, d, 0, NULL);
1847226633Sdim}
1848226633Sdim
1849226633Sdimstatic
1850226633SdimDECL_CMD_FUNC(set80211tdmaslot, val, d)
1851226633Sdim{
1852226633Sdim	set80211(s, IEEE80211_IOC_TDMA_SLOT, atoi(val), 0, NULL);
1853226633Sdim}
1854226633Sdim
1855226633Sdimstatic
1856226633SdimDECL_CMD_FUNC(set80211tdmaslotcnt, val, d)
1857226633Sdim{
1858226633Sdim	set80211(s, IEEE80211_IOC_TDMA_SLOTCNT, atoi(val), 0, NULL);
1859224145Sdim}
1860193323Sed
1861static
1862DECL_CMD_FUNC(set80211tdmaslotlen, val, d)
1863{
1864	set80211(s, IEEE80211_IOC_TDMA_SLOTLEN, atoi(val), 0, NULL);
1865}
1866
1867static
1868DECL_CMD_FUNC(set80211tdmabintval, val, d)
1869{
1870	set80211(s, IEEE80211_IOC_TDMA_BINTERVAL, atoi(val), 0, NULL);
1871}
1872
1873static
1874DECL_CMD_FUNC(set80211meshttl, val, d)
1875{
1876	set80211(s, IEEE80211_IOC_MESH_TTL, atoi(val), 0, NULL);
1877}
1878
1879static
1880DECL_CMD_FUNC(set80211meshforward, val, d)
1881{
1882	set80211(s, IEEE80211_IOC_MESH_FWRD, d, 0, NULL);
1883}
1884
1885static
1886DECL_CMD_FUNC(set80211meshgate, val, d)
1887{
1888	set80211(s, IEEE80211_IOC_MESH_GATE, d, 0, NULL);
1889}
1890
1891static
1892DECL_CMD_FUNC(set80211meshpeering, val, d)
1893{
1894	set80211(s, IEEE80211_IOC_MESH_AP, d, 0, NULL);
1895}
1896
1897static
1898DECL_CMD_FUNC(set80211meshmetric, val, d)
1899{
1900	char v[12];
1901
1902	memcpy(v, val, sizeof(v));
1903	set80211(s, IEEE80211_IOC_MESH_PR_METRIC, 0, 0, v);
1904}
1905
1906static
1907DECL_CMD_FUNC(set80211meshpath, val, d)
1908{
1909	char v[12];
1910
1911	memcpy(v, val, sizeof(v));
1912	set80211(s, IEEE80211_IOC_MESH_PR_PATH, 0, 0, v);
1913}
1914
1915static int
1916regdomain_sort(const void *a, const void *b)
1917{
1918#define	CHAN_ALL \
1919	(IEEE80211_CHAN_ALLTURBO|IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)
1920	const struct ieee80211_channel *ca = a;
1921	const struct ieee80211_channel *cb = b;
1922
1923	return ca->ic_freq == cb->ic_freq ?
1924	    (ca->ic_flags & CHAN_ALL) - (cb->ic_flags & CHAN_ALL) :
1925	    ca->ic_freq - cb->ic_freq;
1926#undef CHAN_ALL
1927}
1928
1929static const struct ieee80211_channel *
1930chanlookup(const struct ieee80211_channel chans[], int nchans,
1931	int freq, int flags)
1932{
1933	int i;
1934
1935	flags &= IEEE80211_CHAN_ALLTURBO;
1936	for (i = 0; i < nchans; i++) {
1937		const struct ieee80211_channel *c = &chans[i];
1938		if (c->ic_freq == freq &&
1939		    (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
1940			return c;
1941	}
1942	return NULL;
1943}
1944
1945static int
1946chanfind(const struct ieee80211_channel chans[], int nchans, int flags)
1947{
1948	int i;
1949
1950	for (i = 0; i < nchans; i++) {
1951		const struct ieee80211_channel *c = &chans[i];
1952		if ((c->ic_flags & flags) == flags)
1953			return 1;
1954	}
1955	return 0;
1956}
1957
1958/*
1959 * Check channel compatibility.
1960 */
1961static int
1962checkchan(const struct ieee80211req_chaninfo *avail, int freq, int flags)
1963{
1964	flags &= ~REQ_FLAGS;
1965	/*
1966	 * Check if exact channel is in the calibration table;
1967	 * everything below is to deal with channels that we
1968	 * want to include but that are not explicitly listed.
1969	 */
1970	if (flags & IEEE80211_CHAN_HT40) {
1971		/* NB: we use an HT40 channel center that matches HT20 */
1972		flags = (flags &~ IEEE80211_CHAN_HT40) | IEEE80211_CHAN_HT20;
1973	}
1974	if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, flags) != NULL)
1975		return 1;
1976	if (flags & IEEE80211_CHAN_GSM) {
1977		/*
1978		 * XXX GSM frequency mapping is handled in the kernel
1979		 * so we cannot find them in the calibration table;
1980		 * just accept the channel and the kernel will reject
1981		 * the channel list if it's wrong.
1982		 */
1983		return 1;
1984	}
1985	/*
1986	 * If this is a 1/2 or 1/4 width channel allow it if a full
1987	 * width channel is present for this frequency, and the device
1988	 * supports fractional channels on this band.  This is a hack
1989	 * that avoids bloating the calibration table; it may be better
1990	 * by per-band attributes though (we are effectively calculating
1991	 * this attribute by scanning the channel list ourself).
1992	 */
1993	if ((flags & (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) == 0)
1994		return 0;
1995	if (chanlookup(avail->ic_chans, avail->ic_nchans, freq,
1996	    flags &~ (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) == NULL)
1997		return 0;
1998	if (flags & IEEE80211_CHAN_HALF) {
1999		return chanfind(avail->ic_chans, avail->ic_nchans,
2000		    IEEE80211_CHAN_HALF |
2001		       (flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
2002	} else {
2003		return chanfind(avail->ic_chans, avail->ic_nchans,
2004		    IEEE80211_CHAN_QUARTER |
2005			(flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
2006	}
2007}
2008
2009static void
2010regdomain_addchans(struct ieee80211req_chaninfo *ci,
2011	const netband_head *bands,
2012	const struct ieee80211_regdomain *reg,
2013	uint32_t chanFlags,
2014	const struct ieee80211req_chaninfo *avail)
2015{
2016	const struct netband *nb;
2017	const struct freqband *b;
2018	struct ieee80211_channel *c, *prev;
2019	int freq, hi_adj, lo_adj, channelSep;
2020	uint32_t flags;
2021
2022	hi_adj = (chanFlags & IEEE80211_CHAN_HT40U) ? -20 : 0;
2023	lo_adj = (chanFlags & IEEE80211_CHAN_HT40D) ? 20 : 0;
2024	channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40;
2025	LIST_FOREACH(nb, bands, next) {
2026		b = nb->band;
2027		if (verbose) {
2028			printf("%s:", __func__);
2029			printb(" chanFlags", chanFlags, IEEE80211_CHAN_BITS);
2030			printb(" bandFlags", nb->flags | b->flags,
2031			    IEEE80211_CHAN_BITS);
2032			putchar('\n');
2033		}
2034		prev = NULL;
2035		for (freq = b->freqStart + lo_adj;
2036		     freq <= b->freqEnd + hi_adj; freq += b->chanSep) {
2037			/*
2038			 * Construct flags for the new channel.  We take
2039			 * the attributes from the band descriptions except
2040			 * for HT40 which is enabled generically (i.e. +/-
2041			 * extension channel) in the band description and
2042			 * then constrained according by channel separation.
2043			 */
2044			flags = nb->flags | b->flags;
2045			if (flags & IEEE80211_CHAN_HT) {
2046				/*
2047				 * HT channels are generated specially; we're
2048				 * called to add HT20, HT40+, and HT40- chan's
2049				 * so we need to expand only band specs for
2050				 * the HT channel type being added.
2051				 */
2052				if ((chanFlags & IEEE80211_CHAN_HT20) &&
2053				    (flags & IEEE80211_CHAN_HT20) == 0) {
2054					if (verbose)
2055						printf("%u: skip, not an "
2056						    "HT20 channel\n", freq);
2057					continue;
2058				}
2059				if ((chanFlags & IEEE80211_CHAN_HT40) &&
2060				    (flags & IEEE80211_CHAN_HT40) == 0) {
2061					if (verbose)
2062						printf("%u: skip, not an "
2063						    "HT40 channel\n", freq);
2064					continue;
2065				}
2066				/* NB: HT attribute comes from caller */
2067				flags &= ~IEEE80211_CHAN_HT;
2068				flags |= chanFlags & IEEE80211_CHAN_HT;
2069			}
2070			/*
2071			 * Check if device can operate on this frequency.
2072			 */
2073			if (!checkchan(avail, freq, flags)) {
2074				if (verbose) {
2075					printf("%u: skip, ", freq);
2076					printb("flags", flags,
2077					    IEEE80211_CHAN_BITS);
2078					printf(" not available\n");
2079				}
2080				continue;
2081			}
2082			if ((flags & REQ_ECM) && !reg->ecm) {
2083				if (verbose)
2084					printf("%u: skip, ECM channel\n", freq);
2085				continue;
2086			}
2087			if ((flags & REQ_INDOOR) && reg->location == 'O') {
2088				if (verbose)
2089					printf("%u: skip, indoor channel\n",
2090					    freq);
2091				continue;
2092			}
2093			if ((flags & REQ_OUTDOOR) && reg->location == 'I') {
2094				if (verbose)
2095					printf("%u: skip, outdoor channel\n",
2096					    freq);
2097				continue;
2098			}
2099			if ((flags & IEEE80211_CHAN_HT40) &&
2100			    prev != NULL && (freq - prev->ic_freq) < channelSep) {
2101				if (verbose)
2102					printf("%u: skip, only %u channel "
2103					    "separation, need %d\n", freq,
2104					    freq - prev->ic_freq, channelSep);
2105				continue;
2106			}
2107			if (ci->ic_nchans == IEEE80211_CHAN_MAX) {
2108				if (verbose)
2109					printf("%u: skip, channel table full\n",
2110					    freq);
2111				break;
2112			}
2113			c = &ci->ic_chans[ci->ic_nchans++];
2114			memset(c, 0, sizeof(*c));
2115			c->ic_freq = freq;
2116			c->ic_flags = flags;
2117			if (c->ic_flags & IEEE80211_CHAN_DFS)
2118				c->ic_maxregpower = nb->maxPowerDFS;
2119			else
2120				c->ic_maxregpower = nb->maxPower;
2121			if (verbose) {
2122				printf("[%3d] add freq %u ",
2123				    ci->ic_nchans-1, c->ic_freq);
2124				printb("flags", c->ic_flags, IEEE80211_CHAN_BITS);
2125				printf(" power %u\n", c->ic_maxregpower);
2126			}
2127			/* NB: kernel fills in other fields */
2128			prev = c;
2129		}
2130	}
2131}
2132
2133static void
2134regdomain_makechannels(
2135	struct ieee80211_regdomain_req *req,
2136	const struct ieee80211_devcaps_req *dc)
2137{
2138	struct regdata *rdp = getregdata();
2139	const struct country *cc;
2140	const struct ieee80211_regdomain *reg = &req->rd;
2141	struct ieee80211req_chaninfo *ci = &req->chaninfo;
2142	const struct regdomain *rd;
2143
2144	/*
2145	 * Locate construction table for new channel list.  We treat
2146	 * the regdomain/SKU as definitive so a country can be in
2147	 * multiple with different properties (e.g. US in FCC+FCC3).
2148	 * If no regdomain is specified then we fallback on the country
2149	 * code to find the associated regdomain since countries always
2150	 * belong to at least one regdomain.
2151	 */
2152	if (reg->regdomain == 0) {
2153		cc = lib80211_country_findbycc(rdp, reg->country);
2154		if (cc == NULL)
2155			errx(1, "internal error, country %d not found",
2156			    reg->country);
2157		rd = cc->rd;
2158	} else
2159		rd = lib80211_regdomain_findbysku(rdp, reg->regdomain);
2160	if (rd == NULL)
2161		errx(1, "internal error, regdomain %d not found",
2162			    reg->regdomain);
2163	if (rd->sku != SKU_DEBUG) {
2164		/*
2165		 * regdomain_addchans incrememnts the channel count for
2166		 * each channel it adds so initialize ic_nchans to zero.
2167		 * Note that we know we have enough space to hold all possible
2168		 * channels because the devcaps list size was used to
2169		 * allocate our request.
2170		 */
2171		ci->ic_nchans = 0;
2172		if (!LIST_EMPTY(&rd->bands_11b))
2173			regdomain_addchans(ci, &rd->bands_11b, reg,
2174			    IEEE80211_CHAN_B, &dc->dc_chaninfo);
2175		if (!LIST_EMPTY(&rd->bands_11g))
2176			regdomain_addchans(ci, &rd->bands_11g, reg,
2177			    IEEE80211_CHAN_G, &dc->dc_chaninfo);
2178		if (!LIST_EMPTY(&rd->bands_11a))
2179			regdomain_addchans(ci, &rd->bands_11a, reg,
2180			    IEEE80211_CHAN_A, &dc->dc_chaninfo);
2181		if (!LIST_EMPTY(&rd->bands_11na) && dc->dc_htcaps != 0) {
2182			regdomain_addchans(ci, &rd->bands_11na, reg,
2183			    IEEE80211_CHAN_A | IEEE80211_CHAN_HT20,
2184			    &dc->dc_chaninfo);
2185			if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
2186				regdomain_addchans(ci, &rd->bands_11na, reg,
2187				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U,
2188				    &dc->dc_chaninfo);
2189				regdomain_addchans(ci, &rd->bands_11na, reg,
2190				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D,
2191				    &dc->dc_chaninfo);
2192			}
2193		}
2194		if (!LIST_EMPTY(&rd->bands_11ng) && dc->dc_htcaps != 0) {
2195			regdomain_addchans(ci, &rd->bands_11ng, reg,
2196			    IEEE80211_CHAN_G | IEEE80211_CHAN_HT20,
2197			    &dc->dc_chaninfo);
2198			if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
2199				regdomain_addchans(ci, &rd->bands_11ng, reg,
2200				    IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U,
2201				    &dc->dc_chaninfo);
2202				regdomain_addchans(ci, &rd->bands_11ng, reg,
2203				    IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D,
2204				    &dc->dc_chaninfo);
2205			}
2206		}
2207		qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]),
2208		    regdomain_sort);
2209	} else
2210		memcpy(ci, &dc->dc_chaninfo,
2211		    IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
2212}
2213
2214static void
2215list_countries(void)
2216{
2217	struct regdata *rdp = getregdata();
2218	const struct country *cp;
2219	const struct regdomain *dp;
2220	int i;
2221
2222	i = 0;
2223	printf("\nCountry codes:\n");
2224	LIST_FOREACH(cp, &rdp->countries, next) {
2225		printf("%2s %-15.15s%s", cp->isoname,
2226		    cp->name, ((i+1)%4) == 0 ? "\n" : " ");
2227		i++;
2228	}
2229	i = 0;
2230	printf("\nRegulatory domains:\n");
2231	LIST_FOREACH(dp, &rdp->domains, next) {
2232		printf("%-15.15s%s", dp->name, ((i+1)%4) == 0 ? "\n" : " ");
2233		i++;
2234	}
2235	printf("\n");
2236}
2237
2238static void
2239defaultcountry(const struct regdomain *rd)
2240{
2241	struct regdata *rdp = getregdata();
2242	const struct country *cc;
2243
2244	cc = lib80211_country_findbycc(rdp, rd->cc->code);
2245	if (cc == NULL)
2246		errx(1, "internal error, ISO country code %d not "
2247		    "defined for regdomain %s", rd->cc->code, rd->name);
2248	regdomain.country = cc->code;
2249	regdomain.isocc[0] = cc->isoname[0];
2250	regdomain.isocc[1] = cc->isoname[1];
2251}
2252
2253static
2254DECL_CMD_FUNC(set80211regdomain, val, d)
2255{
2256	struct regdata *rdp = getregdata();
2257	const struct regdomain *rd;
2258
2259	rd = lib80211_regdomain_findbyname(rdp, val);
2260	if (rd == NULL) {
2261		char *eptr;
2262		long sku = strtol(val, &eptr, 0);
2263
2264		if (eptr != val)
2265			rd = lib80211_regdomain_findbysku(rdp, sku);
2266		if (eptr == val || rd == NULL)
2267			errx(1, "unknown regdomain %s", val);
2268	}
2269	getregdomain(s);
2270	regdomain.regdomain = rd->sku;
2271	if (regdomain.country == 0 && rd->cc != NULL) {
2272		/*
2273		 * No country code setup and there's a default
2274		 * one for this regdomain fill it in.
2275		 */
2276		defaultcountry(rd);
2277	}
2278	callback_register(setregdomain_cb, &regdomain);
2279}
2280
2281static
2282DECL_CMD_FUNC(set80211country, val, d)
2283{
2284	struct regdata *rdp = getregdata();
2285	const struct country *cc;
2286
2287	cc = lib80211_country_findbyname(rdp, val);
2288	if (cc == NULL) {
2289		char *eptr;
2290		long code = strtol(val, &eptr, 0);
2291
2292		if (eptr != val)
2293			cc = lib80211_country_findbycc(rdp, code);
2294		if (eptr == val || cc == NULL)
2295			errx(1, "unknown ISO country code %s", val);
2296	}
2297	getregdomain(s);
2298	regdomain.regdomain = cc->rd->sku;
2299	regdomain.country = cc->code;
2300	regdomain.isocc[0] = cc->isoname[0];
2301	regdomain.isocc[1] = cc->isoname[1];
2302	callback_register(setregdomain_cb, &regdomain);
2303}
2304
2305static void
2306set80211location(const char *val, int d, int s, const struct afswtch *rafp)
2307{
2308	getregdomain(s);
2309	regdomain.location = d;
2310	callback_register(setregdomain_cb, &regdomain);
2311}
2312
2313static void
2314set80211ecm(const char *val, int d, int s, const struct afswtch *rafp)
2315{
2316	getregdomain(s);
2317	regdomain.ecm = d;
2318	callback_register(setregdomain_cb, &regdomain);
2319}
2320
2321static void
2322LINE_INIT(char c)
2323{
2324	spacer = c;
2325	if (c == '\t')
2326		col = 8;
2327	else
2328		col = 1;
2329}
2330
2331static void
2332LINE_BREAK(void)
2333{
2334	if (spacer != '\t') {
2335		printf("\n");
2336		spacer = '\t';
2337	}
2338	col = 8;		/* 8-col tab */
2339}
2340
2341static void
2342LINE_CHECK(const char *fmt, ...)
2343{
2344	char buf[80];
2345	va_list ap;
2346	int n;
2347
2348	va_start(ap, fmt);
2349	n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
2350	va_end(ap);
2351	col += 1+n;
2352	if (col > MAXCOL) {
2353		LINE_BREAK();
2354		col += n;
2355	}
2356	buf[0] = spacer;
2357	printf("%s", buf);
2358	spacer = ' ';
2359}
2360
2361static int
2362getmaxrate(const uint8_t rates[15], uint8_t nrates)
2363{
2364	int i, maxrate = -1;
2365
2366	for (i = 0; i < nrates; i++) {
2367		int rate = rates[i] & IEEE80211_RATE_VAL;
2368		if (rate > maxrate)
2369			maxrate = rate;
2370	}
2371	return maxrate / 2;
2372}
2373
2374static const char *
2375getcaps(int capinfo)
2376{
2377	static char capstring[32];
2378	char *cp = capstring;
2379
2380	if (capinfo & IEEE80211_CAPINFO_ESS)
2381		*cp++ = 'E';
2382	if (capinfo & IEEE80211_CAPINFO_IBSS)
2383		*cp++ = 'I';
2384	if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
2385		*cp++ = 'c';
2386	if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
2387		*cp++ = 'C';
2388	if (capinfo & IEEE80211_CAPINFO_PRIVACY)
2389		*cp++ = 'P';
2390	if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
2391		*cp++ = 'S';
2392	if (capinfo & IEEE80211_CAPINFO_PBCC)
2393		*cp++ = 'B';
2394	if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
2395		*cp++ = 'A';
2396	if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
2397		*cp++ = 's';
2398	if (capinfo & IEEE80211_CAPINFO_RSN)
2399		*cp++ = 'R';
2400	if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
2401		*cp++ = 'D';
2402	*cp = '\0';
2403	return capstring;
2404}
2405
2406static const char *
2407getflags(int flags)
2408{
2409	static char flagstring[32];
2410	char *cp = flagstring;
2411
2412	if (flags & IEEE80211_NODE_AUTH)
2413		*cp++ = 'A';
2414	if (flags & IEEE80211_NODE_QOS)
2415		*cp++ = 'Q';
2416	if (flags & IEEE80211_NODE_ERP)
2417		*cp++ = 'E';
2418	if (flags & IEEE80211_NODE_PWR_MGT)
2419		*cp++ = 'P';
2420	if (flags & IEEE80211_NODE_HT) {
2421		*cp++ = 'H';
2422		if (flags & IEEE80211_NODE_HTCOMPAT)
2423			*cp++ = '+';
2424	}
2425	if (flags & IEEE80211_NODE_WPS)
2426		*cp++ = 'W';
2427	if (flags & IEEE80211_NODE_TSN)
2428		*cp++ = 'N';
2429	if (flags & IEEE80211_NODE_AMPDU_TX)
2430		*cp++ = 'T';
2431	if (flags & IEEE80211_NODE_AMPDU_RX)
2432		*cp++ = 'R';
2433	if (flags & IEEE80211_NODE_MIMO_PS) {
2434		*cp++ = 'M';
2435		if (flags & IEEE80211_NODE_MIMO_RTS)
2436			*cp++ = '+';
2437	}
2438	if (flags & IEEE80211_NODE_RIFS)
2439		*cp++ = 'I';
2440	if (flags & IEEE80211_NODE_SGI40) {
2441		*cp++ = 'S';
2442		if (flags & IEEE80211_NODE_SGI20)
2443			*cp++ = '+';
2444	} else if (flags & IEEE80211_NODE_SGI20)
2445		*cp++ = 's';
2446	if (flags & IEEE80211_NODE_AMSDU_TX)
2447		*cp++ = 't';
2448	if (flags & IEEE80211_NODE_AMSDU_RX)
2449		*cp++ = 'r';
2450	*cp = '\0';
2451	return flagstring;
2452}
2453
2454static void
2455printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
2456{
2457	printf("%s", tag);
2458	if (verbose) {
2459		maxlen -= strlen(tag)+2;
2460		if (2*ielen > maxlen)
2461			maxlen--;
2462		printf("<");
2463		for (; ielen > 0; ie++, ielen--) {
2464			if (maxlen-- <= 0)
2465				break;
2466			printf("%02x", *ie);
2467		}
2468		if (ielen != 0)
2469			printf("-");
2470		printf(">");
2471	}
2472}
2473
2474#define LE_READ_2(p)					\
2475	((u_int16_t)					\
2476	 ((((const u_int8_t *)(p))[0]      ) |		\
2477	  (((const u_int8_t *)(p))[1] <<  8)))
2478#define LE_READ_4(p)					\
2479	((u_int32_t)					\
2480	 ((((const u_int8_t *)(p))[0]      ) |		\
2481	  (((const u_int8_t *)(p))[1] <<  8) |		\
2482	  (((const u_int8_t *)(p))[2] << 16) |		\
2483	  (((const u_int8_t *)(p))[3] << 24)))
2484
2485/*
2486 * NB: The decoding routines assume a properly formatted ie
2487 *     which should be safe as the kernel only retains them
2488 *     if they parse ok.
2489 */
2490
2491static void
2492printwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2493{
2494#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
2495	static const char *acnames[] = { "BE", "BK", "VO", "VI" };
2496	const struct ieee80211_wme_param *wme =
2497	    (const struct ieee80211_wme_param *) ie;
2498	int i;
2499
2500	printf("%s", tag);
2501	if (!verbose)
2502		return;
2503	printf("<qosinfo 0x%x", wme->param_qosInfo);
2504	ie += offsetof(struct ieee80211_wme_param, params_acParams);
2505	for (i = 0; i < WME_NUM_AC; i++) {
2506		const struct ieee80211_wme_acparams *ac =
2507		    &wme->params_acParams[i];
2508
2509		printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]"
2510			, acnames[i]
2511			, MS(ac->acp_aci_aifsn, WME_PARAM_ACM) ? "acm " : ""
2512			, MS(ac->acp_aci_aifsn, WME_PARAM_AIFSN)
2513			, MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMIN)
2514			, MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMAX)
2515			, LE_READ_2(&ac->acp_txop)
2516		);
2517	}
2518	printf(">");
2519#undef MS
2520}
2521
2522static void
2523printwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2524{
2525	printf("%s", tag);
2526	if (verbose) {
2527		const struct ieee80211_wme_info *wme =
2528		    (const struct ieee80211_wme_info *) ie;
2529		printf("<version 0x%x info 0x%x>",
2530		    wme->wme_version, wme->wme_info);
2531	}
2532}
2533
2534static void
2535printhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2536{
2537	printf("%s", tag);
2538	if (verbose) {
2539		const struct ieee80211_ie_htcap *htcap =
2540		    (const struct ieee80211_ie_htcap *) ie;
2541		const char *sep;
2542		int i, j;
2543
2544		printf("<cap 0x%x param 0x%x",
2545		    LE_READ_2(&htcap->hc_cap), htcap->hc_param);
2546		printf(" mcsset[");
2547		sep = "";
2548		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2549			if (isset(htcap->hc_mcsset, i)) {
2550				for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2551					if (isclr(htcap->hc_mcsset, j))
2552						break;
2553				j--;
2554				if (i == j)
2555					printf("%s%u", sep, i);
2556				else
2557					printf("%s%u-%u", sep, i, j);
2558				i += j-i;
2559				sep = ",";
2560			}
2561		printf("] extcap 0x%x txbf 0x%x antenna 0x%x>",
2562		    LE_READ_2(&htcap->hc_extcap),
2563		    LE_READ_4(&htcap->hc_txbf),
2564		    htcap->hc_antenna);
2565	}
2566}
2567
2568static void
2569printhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2570{
2571	printf("%s", tag);
2572	if (verbose) {
2573		const struct ieee80211_ie_htinfo *htinfo =
2574		    (const struct ieee80211_ie_htinfo *) ie;
2575		const char *sep;
2576		int i, j;
2577
2578		printf("<ctl %u, %x,%x,%x,%x", htinfo->hi_ctrlchannel,
2579		    htinfo->hi_byte1, htinfo->hi_byte2, htinfo->hi_byte3,
2580		    LE_READ_2(&htinfo->hi_byte45));
2581		printf(" basicmcs[");
2582		sep = "";
2583		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2584			if (isset(htinfo->hi_basicmcsset, i)) {
2585				for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2586					if (isclr(htinfo->hi_basicmcsset, j))
2587						break;
2588				j--;
2589				if (i == j)
2590					printf("%s%u", sep, i);
2591				else
2592					printf("%s%u-%u", sep, i, j);
2593				i += j-i;
2594				sep = ",";
2595			}
2596		printf("]>");
2597	}
2598}
2599
2600static void
2601printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2602{
2603
2604	printf("%s", tag);
2605	if (verbose) {
2606		const struct ieee80211_ath_ie *ath =
2607			(const struct ieee80211_ath_ie *)ie;
2608
2609		printf("<");
2610		if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME)
2611			printf("DTURBO,");
2612		if (ath->ath_capability & ATHEROS_CAP_COMPRESSION)
2613			printf("COMP,");
2614		if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME)
2615			printf("FF,");
2616		if (ath->ath_capability & ATHEROS_CAP_XR)
2617			printf("XR,");
2618		if (ath->ath_capability & ATHEROS_CAP_AR)
2619			printf("AR,");
2620		if (ath->ath_capability & ATHEROS_CAP_BURST)
2621			printf("BURST,");
2622		if (ath->ath_capability & ATHEROS_CAP_WME)
2623			printf("WME,");
2624		if (ath->ath_capability & ATHEROS_CAP_BOOST)
2625			printf("BOOST,");
2626		printf("0x%x>", LE_READ_2(ath->ath_defkeyix));
2627	}
2628}
2629
2630
2631static void
2632printmeshconf(const char *tag, const uint8_t *ie, size_t ielen, int maxlen)
2633{
2634#define MATCHOUI(field, oui, string)					\
2635do {									\
2636	if (memcmp(field, oui, 4) == 0)					\
2637		printf("%s", string);					\
2638} while (0)
2639
2640	printf("%s", tag);
2641	if (verbose) {
2642		const struct ieee80211_meshconf_ie *mconf =
2643			(const struct ieee80211_meshconf_ie *)ie;
2644		printf("<PATH:");
2645		if (mconf->conf_pselid == IEEE80211_MESHCONF_PATH_HWMP)
2646			printf("HWMP");
2647		else
2648			printf("UNKNOWN");
2649		printf(" LINK:");
2650		if (mconf->conf_pmetid == IEEE80211_MESHCONF_METRIC_AIRTIME)
2651			printf("AIRTIME");
2652		else
2653			printf("UNKNOWN");
2654		printf(" CONGESTION:");
2655		if (mconf->conf_ccid == IEEE80211_MESHCONF_CC_DISABLED)
2656			printf("DISABLED");
2657		else
2658			printf("UNKNOWN");
2659		printf(" SYNC:");
2660		if (mconf->conf_syncid == IEEE80211_MESHCONF_SYNC_NEIGHOFF)
2661			printf("NEIGHOFF");
2662		else
2663			printf("UNKNOWN");
2664		printf(" AUTH:");
2665		if (mconf->conf_authid == IEEE80211_MESHCONF_AUTH_DISABLED)
2666			printf("DISABLED");
2667		else
2668			printf("UNKNOWN");
2669		printf(" FORM:0x%x CAPS:0x%x>", mconf->conf_form,
2670		    mconf->conf_cap);
2671	}
2672#undef MATCHOUI
2673}
2674
2675static const char *
2676wpa_cipher(const u_int8_t *sel)
2677{
2678#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
2679	u_int32_t w = LE_READ_4(sel);
2680
2681	switch (w) {
2682	case WPA_SEL(WPA_CSE_NULL):
2683		return "NONE";
2684	case WPA_SEL(WPA_CSE_WEP40):
2685		return "WEP40";
2686	case WPA_SEL(WPA_CSE_WEP104):
2687		return "WEP104";
2688	case WPA_SEL(WPA_CSE_TKIP):
2689		return "TKIP";
2690	case WPA_SEL(WPA_CSE_CCMP):
2691		return "AES-CCMP";
2692	}
2693	return "?";		/* NB: so 1<< is discarded */
2694#undef WPA_SEL
2695}
2696
2697static const char *
2698wpa_keymgmt(const u_int8_t *sel)
2699{
2700#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
2701	u_int32_t w = LE_READ_4(sel);
2702
2703	switch (w) {
2704	case WPA_SEL(WPA_ASE_8021X_UNSPEC):
2705		return "8021X-UNSPEC";
2706	case WPA_SEL(WPA_ASE_8021X_PSK):
2707		return "8021X-PSK";
2708	case WPA_SEL(WPA_ASE_NONE):
2709		return "NONE";
2710	}
2711	return "?";
2712#undef WPA_SEL
2713}
2714
2715static void
2716printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2717{
2718	u_int8_t len = ie[1];
2719
2720	printf("%s", tag);
2721	if (verbose) {
2722		const char *sep;
2723		int n;
2724
2725		ie += 6, len -= 4;		/* NB: len is payload only */
2726
2727		printf("<v%u", LE_READ_2(ie));
2728		ie += 2, len -= 2;
2729
2730		printf(" mc:%s", wpa_cipher(ie));
2731		ie += 4, len -= 4;
2732
2733		/* unicast ciphers */
2734		n = LE_READ_2(ie);
2735		ie += 2, len -= 2;
2736		sep = " uc:";
2737		for (; n > 0; n--) {
2738			printf("%s%s", sep, wpa_cipher(ie));
2739			ie += 4, len -= 4;
2740			sep = "+";
2741		}
2742
2743		/* key management algorithms */
2744		n = LE_READ_2(ie);
2745		ie += 2, len -= 2;
2746		sep = " km:";
2747		for (; n > 0; n--) {
2748			printf("%s%s", sep, wpa_keymgmt(ie));
2749			ie += 4, len -= 4;
2750			sep = "+";
2751		}
2752
2753		if (len > 2)		/* optional capabilities */
2754			printf(", caps 0x%x", LE_READ_2(ie));
2755		printf(">");
2756	}
2757}
2758
2759static const char *
2760rsn_cipher(const u_int8_t *sel)
2761{
2762#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
2763	u_int32_t w = LE_READ_4(sel);
2764
2765	switch (w) {
2766	case RSN_SEL(RSN_CSE_NULL):
2767		return "NONE";
2768	case RSN_SEL(RSN_CSE_WEP40):
2769		return "WEP40";
2770	case RSN_SEL(RSN_CSE_WEP104):
2771		return "WEP104";
2772	case RSN_SEL(RSN_CSE_TKIP):
2773		return "TKIP";
2774	case RSN_SEL(RSN_CSE_CCMP):
2775		return "AES-CCMP";
2776	case RSN_SEL(RSN_CSE_WRAP):
2777		return "AES-OCB";
2778	}
2779	return "?";
2780#undef WPA_SEL
2781}
2782
2783static const char *
2784rsn_keymgmt(const u_int8_t *sel)
2785{
2786#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
2787	u_int32_t w = LE_READ_4(sel);
2788
2789	switch (w) {
2790	case RSN_SEL(RSN_ASE_8021X_UNSPEC):
2791		return "8021X-UNSPEC";
2792	case RSN_SEL(RSN_ASE_8021X_PSK):
2793		return "8021X-PSK";
2794	case RSN_SEL(RSN_ASE_NONE):
2795		return "NONE";
2796	}
2797	return "?";
2798#undef RSN_SEL
2799}
2800
2801static void
2802printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2803{
2804	printf("%s", tag);
2805	if (verbose) {
2806		const char *sep;
2807		int n;
2808
2809		ie += 2, ielen -= 2;
2810
2811		printf("<v%u", LE_READ_2(ie));
2812		ie += 2, ielen -= 2;
2813
2814		printf(" mc:%s", rsn_cipher(ie));
2815		ie += 4, ielen -= 4;
2816
2817		/* unicast ciphers */
2818		n = LE_READ_2(ie);
2819		ie += 2, ielen -= 2;
2820		sep = " uc:";
2821		for (; n > 0; n--) {
2822			printf("%s%s", sep, rsn_cipher(ie));
2823			ie += 4, ielen -= 4;
2824			sep = "+";
2825		}
2826
2827		/* key management algorithms */
2828		n = LE_READ_2(ie);
2829		ie += 2, ielen -= 2;
2830		sep = " km:";
2831		for (; n > 0; n--) {
2832			printf("%s%s", sep, rsn_keymgmt(ie));
2833			ie += 4, ielen -= 4;
2834			sep = "+";
2835		}
2836
2837		if (ielen > 2)		/* optional capabilities */
2838			printf(", caps 0x%x", LE_READ_2(ie));
2839		/* XXXPMKID */
2840		printf(">");
2841	}
2842}
2843
2844/* XXX move to a public include file */
2845#define IEEE80211_WPS_DEV_PASS_ID	0x1012
2846#define IEEE80211_WPS_SELECTED_REG	0x1041
2847#define IEEE80211_WPS_SETUP_STATE	0x1044
2848#define IEEE80211_WPS_UUID_E		0x1047
2849#define IEEE80211_WPS_VERSION		0x104a
2850
2851#define BE_READ_2(p)					\
2852	((u_int16_t)					\
2853	 ((((const u_int8_t *)(p))[1]      ) |		\
2854	  (((const u_int8_t *)(p))[0] <<  8)))
2855
2856static void
2857printwpsie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2858{
2859#define	N(a)	(sizeof(a) / sizeof(a[0]))
2860	u_int8_t len = ie[1];
2861
2862	printf("%s", tag);
2863	if (verbose) {
2864		static const char *dev_pass_id[] = {
2865			"D",	/* Default (PIN) */
2866			"U",	/* User-specified */
2867			"M",	/* Machine-specified */
2868			"K",	/* Rekey */
2869			"P",	/* PushButton */
2870			"R"	/* Registrar-specified */
2871		};
2872		int n;
2873
2874		ie +=6, len -= 4;		/* NB: len is payload only */
2875
2876		/* WPS IE in Beacon and Probe Resp frames have different fields */
2877		printf("<");
2878		while (len) {
2879			uint16_t tlv_type = BE_READ_2(ie);
2880			uint16_t tlv_len  = BE_READ_2(ie + 2);
2881
2882			ie += 4, len -= 4;
2883
2884			switch (tlv_type) {
2885			case IEEE80211_WPS_VERSION:
2886				printf("v:%d.%d", *ie >> 4, *ie & 0xf);
2887				break;
2888			case IEEE80211_WPS_SETUP_STATE:
2889				/* Only 1 and 2 are valid */
2890				if (*ie == 0 || *ie >= 3)
2891					printf(" state:B");
2892				else
2893					printf(" st:%s", *ie == 1 ? "N" : "C");
2894				break;
2895			case IEEE80211_WPS_SELECTED_REG:
2896				printf(" sel:%s", *ie ? "T" : "F");
2897				break;
2898			case IEEE80211_WPS_DEV_PASS_ID:
2899				n = LE_READ_2(ie);
2900				if (n < N(dev_pass_id))
2901					printf(" dpi:%s", dev_pass_id[n]);
2902				break;
2903			case IEEE80211_WPS_UUID_E:
2904				printf(" uuid-e:");
2905				for (n = 0; n < (tlv_len - 1); n++)
2906					printf("%02x-", ie[n]);
2907				printf("%02x", ie[n]);
2908				break;
2909			}
2910			ie += tlv_len, len -= tlv_len;
2911		}
2912		printf(">");
2913	}
2914#undef N
2915}
2916
2917static void
2918printtdmaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2919{
2920	printf("%s", tag);
2921	if (verbose && ielen >= sizeof(struct ieee80211_tdma_param)) {
2922		const struct ieee80211_tdma_param *tdma =
2923		   (const struct ieee80211_tdma_param *) ie;
2924
2925		/* XXX tstamp */
2926		printf("<v%u slot:%u slotcnt:%u slotlen:%u bintval:%u inuse:0x%x>",
2927		    tdma->tdma_version, tdma->tdma_slot, tdma->tdma_slotcnt,
2928		    LE_READ_2(&tdma->tdma_slotlen), tdma->tdma_bintval,
2929		    tdma->tdma_inuse[0]);
2930	}
2931}
2932
2933/*
2934 * Copy the ssid string contents into buf, truncating to fit.  If the
2935 * ssid is entirely printable then just copy intact.  Otherwise convert
2936 * to hexadecimal.  If the result is truncated then replace the last
2937 * three characters with "...".
2938 */
2939static int
2940copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
2941{
2942	const u_int8_t *p;
2943	size_t maxlen;
2944	int i;
2945
2946	if (essid_len > bufsize)
2947		maxlen = bufsize;
2948	else
2949		maxlen = essid_len;
2950	/* determine printable or not */
2951	for (i = 0, p = essid; i < maxlen; i++, p++) {
2952		if (*p < ' ' || *p > 0x7e)
2953			break;
2954	}
2955	if (i != maxlen) {		/* not printable, print as hex */
2956		if (bufsize < 3)
2957			return 0;
2958		strlcpy(buf, "0x", bufsize);
2959		bufsize -= 2;
2960		p = essid;
2961		for (i = 0; i < maxlen && bufsize >= 2; i++) {
2962			sprintf(&buf[2+2*i], "%02x", p[i]);
2963			bufsize -= 2;
2964		}
2965		if (i != essid_len)
2966			memcpy(&buf[2+2*i-3], "...", 3);
2967	} else {			/* printable, truncate as needed */
2968		memcpy(buf, essid, maxlen);
2969		if (maxlen != essid_len)
2970			memcpy(&buf[maxlen-3], "...", 3);
2971	}
2972	return maxlen;
2973}
2974
2975static void
2976printssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2977{
2978	char ssid[2*IEEE80211_NWID_LEN+1];
2979
2980	printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid);
2981}
2982
2983static void
2984printrates(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2985{
2986	const char *sep;
2987	int i;
2988
2989	printf("%s", tag);
2990	sep = "<";
2991	for (i = 2; i < ielen; i++) {
2992		printf("%s%s%d", sep,
2993		    ie[i] & IEEE80211_RATE_BASIC ? "B" : "",
2994		    ie[i] & IEEE80211_RATE_VAL);
2995		sep = ",";
2996	}
2997	printf(">");
2998}
2999
3000static void
3001printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3002{
3003	const struct ieee80211_country_ie *cie =
3004	   (const struct ieee80211_country_ie *) ie;
3005	int i, nbands, schan, nchan;
3006
3007	printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]);
3008	nbands = (cie->len - 3) / sizeof(cie->band[0]);
3009	for (i = 0; i < nbands; i++) {
3010		schan = cie->band[i].schan;
3011		nchan = cie->band[i].nchan;
3012		if (nchan != 1)
3013			printf(" %u-%u,%u", schan, schan + nchan-1,
3014			    cie->band[i].maxtxpwr);
3015		else
3016			printf(" %u,%u", schan, cie->band[i].maxtxpwr);
3017	}
3018	printf(">");
3019}
3020
3021/* unaligned little endian access */
3022#define LE_READ_4(p)					\
3023	((u_int32_t)					\
3024	 ((((const u_int8_t *)(p))[0]      ) |		\
3025	  (((const u_int8_t *)(p))[1] <<  8) |		\
3026	  (((const u_int8_t *)(p))[2] << 16) |		\
3027	  (((const u_int8_t *)(p))[3] << 24)))
3028
3029static __inline int
3030iswpaoui(const u_int8_t *frm)
3031{
3032	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
3033}
3034
3035static __inline int
3036iswmeinfo(const u_int8_t *frm)
3037{
3038	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
3039		frm[6] == WME_INFO_OUI_SUBTYPE;
3040}
3041
3042static __inline int
3043iswmeparam(const u_int8_t *frm)
3044{
3045	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
3046		frm[6] == WME_PARAM_OUI_SUBTYPE;
3047}
3048
3049static __inline int
3050isatherosoui(const u_int8_t *frm)
3051{
3052	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
3053}
3054
3055static __inline int
3056istdmaoui(const uint8_t *frm)
3057{
3058	return frm[1] > 3 && LE_READ_4(frm+2) == ((TDMA_OUI_TYPE<<24)|TDMA_OUI);
3059}
3060
3061static __inline int
3062iswpsoui(const uint8_t *frm)
3063{
3064	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPS_OUI_TYPE<<24)|WPA_OUI);
3065}
3066
3067static const char *
3068iename(int elemid)
3069{
3070	switch (elemid) {
3071	case IEEE80211_ELEMID_FHPARMS:	return " FHPARMS";
3072	case IEEE80211_ELEMID_CFPARMS:	return " CFPARMS";
3073	case IEEE80211_ELEMID_TIM:	return " TIM";
3074	case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS";
3075	case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE";
3076	case IEEE80211_ELEMID_PWRCNSTR:	return " PWRCNSTR";
3077	case IEEE80211_ELEMID_PWRCAP:	return " PWRCAP";
3078	case IEEE80211_ELEMID_TPCREQ:	return " TPCREQ";
3079	case IEEE80211_ELEMID_TPCREP:	return " TPCREP";
3080	case IEEE80211_ELEMID_SUPPCHAN:	return " SUPPCHAN";
3081	case IEEE80211_ELEMID_CSA:	return " CSA";
3082	case IEEE80211_ELEMID_MEASREQ:	return " MEASREQ";
3083	case IEEE80211_ELEMID_MEASREP:	return " MEASREP";
3084	case IEEE80211_ELEMID_QUIET:	return " QUIET";
3085	case IEEE80211_ELEMID_IBSSDFS:	return " IBSSDFS";
3086	case IEEE80211_ELEMID_TPC:	return " TPC";
3087	case IEEE80211_ELEMID_CCKM:	return " CCKM";
3088	}
3089	return " ???";
3090}
3091
3092static void
3093printies(const u_int8_t *vp, int ielen, int maxcols)
3094{
3095	while (ielen > 0) {
3096		switch (vp[0]) {
3097		case IEEE80211_ELEMID_SSID:
3098			if (verbose)
3099				printssid(" SSID", vp, 2+vp[1], maxcols);
3100			break;
3101		case IEEE80211_ELEMID_RATES:
3102		case IEEE80211_ELEMID_XRATES:
3103			if (verbose)
3104				printrates(vp[0] == IEEE80211_ELEMID_RATES ?
3105				    " RATES" : " XRATES", vp, 2+vp[1], maxcols);
3106			break;
3107		case IEEE80211_ELEMID_DSPARMS:
3108			if (verbose)
3109				printf(" DSPARMS<%u>", vp[2]);
3110			break;
3111		case IEEE80211_ELEMID_COUNTRY:
3112			if (verbose)
3113				printcountry(" COUNTRY", vp, 2+vp[1], maxcols);
3114			break;
3115		case IEEE80211_ELEMID_ERP:
3116			if (verbose)
3117				printf(" ERP<0x%x>", vp[2]);
3118			break;
3119		case IEEE80211_ELEMID_VENDOR:
3120			if (iswpaoui(vp))
3121				printwpaie(" WPA", vp, 2+vp[1], maxcols);
3122			else if (iswmeinfo(vp))
3123				printwmeinfo(" WME", vp, 2+vp[1], maxcols);
3124			else if (iswmeparam(vp))
3125				printwmeparam(" WME", vp, 2+vp[1], maxcols);
3126			else if (isatherosoui(vp))
3127				printathie(" ATH", vp, 2+vp[1], maxcols);
3128			else if (iswpsoui(vp))
3129				printwpsie(" WPS", vp, 2+vp[1], maxcols);
3130			else if (istdmaoui(vp))
3131				printtdmaie(" TDMA", vp, 2+vp[1], maxcols);
3132			else if (verbose)
3133				printie(" VEN", vp, 2+vp[1], maxcols);
3134			break;
3135		case IEEE80211_ELEMID_RSN:
3136			printrsnie(" RSN", vp, 2+vp[1], maxcols);
3137			break;
3138		case IEEE80211_ELEMID_HTCAP:
3139			printhtcap(" HTCAP", vp, 2+vp[1], maxcols);
3140			break;
3141		case IEEE80211_ELEMID_HTINFO:
3142			if (verbose)
3143				printhtinfo(" HTINFO", vp, 2+vp[1], maxcols);
3144			break;
3145		case IEEE80211_ELEMID_MESHID:
3146			if (verbose)
3147				printssid(" MESHID", vp, 2+vp[1], maxcols);
3148			break;
3149		case IEEE80211_ELEMID_MESHCONF:
3150			printmeshconf(" MESHCONF", vp, 2+vp[1], maxcols);
3151			break;
3152		default:
3153			if (verbose)
3154				printie(iename(vp[0]), vp, 2+vp[1], maxcols);
3155			break;
3156		}
3157		ielen -= 2+vp[1];
3158		vp += 2+vp[1];
3159	}
3160}
3161
3162static void
3163printmimo(const struct ieee80211_mimo_info *mi)
3164{
3165	/* NB: don't muddy display unless there's something to show */
3166	if (mi->rssi[0] != 0 || mi->rssi[1] != 0 || mi->rssi[2] != 0) {
3167		/* XXX ignore EVM for now */
3168		printf(" (rssi %d:%d:%d nf %d:%d:%d)",
3169		    mi->rssi[0], mi->rssi[1], mi->rssi[2],
3170		    mi->noise[0], mi->noise[1], mi->noise[2]);
3171	}
3172}
3173
3174static void
3175list_scan(int s)
3176{
3177	uint8_t buf[24*1024];
3178	char ssid[IEEE80211_NWID_LEN+1];
3179	const uint8_t *cp;
3180	int len, ssidmax, idlen;
3181
3182	if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0)
3183		errx(1, "unable to get scan results");
3184	if (len < sizeof(struct ieee80211req_scan_result))
3185		return;
3186
3187	getchaninfo(s);
3188
3189	ssidmax = verbose ? IEEE80211_NWID_LEN - 1 : 14;
3190	printf("%-*.*s  %-17.17s  %4s %4s  %-7s  %3s %4s\n"
3191		, ssidmax, ssidmax, "SSID/MESH ID"
3192		, "BSSID"
3193		, "CHAN"
3194		, "RATE"
3195		, " S:N"
3196		, "INT"
3197		, "CAPS"
3198	);
3199	cp = buf;
3200	do {
3201		const struct ieee80211req_scan_result *sr;
3202		const uint8_t *vp, *idp;
3203
3204		sr = (const struct ieee80211req_scan_result *) cp;
3205		vp = cp + sr->isr_ie_off;
3206		if (sr->isr_meshid_len) {
3207			idp = vp + sr->isr_ssid_len;
3208			idlen = sr->isr_meshid_len;
3209		} else {
3210			idp = vp;
3211			idlen = sr->isr_ssid_len;
3212		}
3213		printf("%-*.*s  %s  %3d  %3dM %3d:%-3d  %3d %-4.4s"
3214			, ssidmax
3215			  , copy_essid(ssid, ssidmax, idp, idlen)
3216			  , ssid
3217			, ether_ntoa((const struct ether_addr *) sr->isr_bssid)
3218			, ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
3219			, getmaxrate(sr->isr_rates, sr->isr_nrates)
3220			, (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise
3221			, sr->isr_intval
3222			, getcaps(sr->isr_capinfo)
3223		);
3224		printies(vp + sr->isr_ssid_len + sr->isr_meshid_len,
3225		    sr->isr_ie_len, 24);
3226		printf("\n");
3227		cp += sr->isr_len, len -= sr->isr_len;
3228	} while (len >= sizeof(struct ieee80211req_scan_result));
3229}
3230
3231static void
3232scan_and_wait(int s)
3233{
3234	struct ieee80211_scan_req sr;
3235	struct ieee80211req ireq;
3236	int sroute;
3237
3238	sroute = socket(PF_ROUTE, SOCK_RAW, 0);
3239	if (sroute < 0) {
3240		perror("socket(PF_ROUTE,SOCK_RAW)");
3241		return;
3242	}
3243	(void) memset(&ireq, 0, sizeof(ireq));
3244	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3245	ireq.i_type = IEEE80211_IOC_SCAN_REQ;
3246
3247	memset(&sr, 0, sizeof(sr));
3248	sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
3249		    | IEEE80211_IOC_SCAN_BGSCAN
3250		    | IEEE80211_IOC_SCAN_NOPICK
3251		    | IEEE80211_IOC_SCAN_ONCE;
3252	sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
3253	sr.sr_nssid = 0;
3254
3255	ireq.i_data = &sr;
3256	ireq.i_len = sizeof(sr);
3257	/*
3258	 * NB: only root can trigger a scan so ignore errors. Also ignore
3259	 * possible errors from net80211, even if no new scan could be
3260	 * started there might still be a valid scan cache.
3261	 */
3262	if (ioctl(s, SIOCS80211, &ireq) == 0) {
3263		char buf[2048];
3264		struct if_announcemsghdr *ifan;
3265		struct rt_msghdr *rtm;
3266
3267		do {
3268			if (read(sroute, buf, sizeof(buf)) < 0) {
3269				perror("read(PF_ROUTE)");
3270				break;
3271			}
3272			rtm = (struct rt_msghdr *) buf;
3273			if (rtm->rtm_version != RTM_VERSION)
3274				break;
3275			ifan = (struct if_announcemsghdr *) rtm;
3276		} while (rtm->rtm_type != RTM_IEEE80211 ||
3277		    ifan->ifan_what != RTM_IEEE80211_SCAN);
3278	}
3279	close(sroute);
3280}
3281
3282static
3283DECL_CMD_FUNC(set80211scan, val, d)
3284{
3285	scan_and_wait(s);
3286	list_scan(s);
3287}
3288
3289static enum ieee80211_opmode get80211opmode(int s);
3290
3291static int
3292gettxseq(const struct ieee80211req_sta_info *si)
3293{
3294	int i, txseq;
3295
3296	if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3297		return si->isi_txseqs[0];
3298	/* XXX not right but usually what folks want */
3299	txseq = 0;
3300	for (i = 0; i < IEEE80211_TID_SIZE; i++)
3301		if (si->isi_txseqs[i] > txseq)
3302			txseq = si->isi_txseqs[i];
3303	return txseq;
3304}
3305
3306static int
3307getrxseq(const struct ieee80211req_sta_info *si)
3308{
3309	int i, rxseq;
3310
3311	if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3312		return si->isi_rxseqs[0];
3313	/* XXX not right but usually what folks want */
3314	rxseq = 0;
3315	for (i = 0; i < IEEE80211_TID_SIZE; i++)
3316		if (si->isi_rxseqs[i] > rxseq)
3317			rxseq = si->isi_rxseqs[i];
3318	return rxseq;
3319}
3320
3321static void
3322list_stations(int s)
3323{
3324	union {
3325		struct ieee80211req_sta_req req;
3326		uint8_t buf[24*1024];
3327	} u;
3328	enum ieee80211_opmode opmode = get80211opmode(s);
3329	const uint8_t *cp;
3330	int len;
3331
3332	/* broadcast address =>'s get all stations */
3333	(void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
3334	if (opmode == IEEE80211_M_STA) {
3335		/*
3336		 * Get information about the associated AP.
3337		 */
3338		(void) get80211(s, IEEE80211_IOC_BSSID,
3339		    u.req.is_u.macaddr, IEEE80211_ADDR_LEN);
3340	}
3341	if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0)
3342		errx(1, "unable to get station information");
3343	if (len < sizeof(struct ieee80211req_sta_info))
3344		return;
3345
3346	getchaninfo(s);
3347
3348	if (opmode == IEEE80211_M_MBSS)
3349		printf("%-17.17s %4s %5s %5s %7s %4s %4s %4s %6s %6s\n"
3350			, "ADDR"
3351			, "CHAN"
3352			, "LOCAL"
3353			, "PEER"
3354			, "STATE"
3355			, "RATE"
3356			, "RSSI"
3357			, "IDLE"
3358			, "TXSEQ"
3359			, "RXSEQ"
3360		);
3361	else
3362		printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %-7s\n"
3363			, "ADDR"
3364			, "AID"
3365			, "CHAN"
3366			, "RATE"
3367			, "RSSI"
3368			, "IDLE"
3369			, "TXSEQ"
3370			, "RXSEQ"
3371			, "CAPS"
3372			, "FLAG"
3373		);
3374	cp = (const uint8_t *) u.req.info;
3375	do {
3376		const struct ieee80211req_sta_info *si;
3377
3378		si = (const struct ieee80211req_sta_info *) cp;
3379		if (si->isi_len < sizeof(*si))
3380			break;
3381		if (opmode == IEEE80211_M_MBSS)
3382			printf("%s %4d %5x %5x %7.7s %3dM %4.1f %4d %6d %6d"
3383				, ether_ntoa((const struct ether_addr*)
3384				    si->isi_macaddr)
3385				, ieee80211_mhz2ieee(si->isi_freq,
3386				    si->isi_flags)
3387				, si->isi_localid
3388				, si->isi_peerid
3389				, mesh_linkstate_string(si->isi_peerstate)
3390				, si->isi_txmbps/2
3391				, si->isi_rssi/2.
3392				, si->isi_inact
3393				, gettxseq(si)
3394				, getrxseq(si)
3395			);
3396		else
3397			printf("%s %4u %4d %3dM %4.1f %4d %6d %6d %-4.4s %-7.7s"
3398				, ether_ntoa((const struct ether_addr*)
3399				    si->isi_macaddr)
3400				, IEEE80211_AID(si->isi_associd)
3401				, ieee80211_mhz2ieee(si->isi_freq,
3402				    si->isi_flags)
3403				, si->isi_txmbps/2
3404				, si->isi_rssi/2.
3405				, si->isi_inact
3406				, gettxseq(si)
3407				, getrxseq(si)
3408				, getcaps(si->isi_capinfo)
3409				, getflags(si->isi_state)
3410			);
3411		printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
3412		printmimo(&si->isi_mimo);
3413		printf("\n");
3414		cp += si->isi_len, len -= si->isi_len;
3415	} while (len >= sizeof(struct ieee80211req_sta_info));
3416}
3417
3418static const char *
3419mesh_linkstate_string(uint8_t state)
3420{
3421#define	N(a)	(sizeof(a) / sizeof(a[0]))
3422	static const char *state_names[] = {
3423	    [0] = "IDLE",
3424	    [1] = "OPEN-TX",
3425	    [2] = "OPEN-RX",
3426	    [3] = "CONF-RX",
3427	    [4] = "ESTAB",
3428	    [5] = "HOLDING",
3429	};
3430
3431	if (state >= N(state_names)) {
3432		static char buf[10];
3433		snprintf(buf, sizeof(buf), "#%u", state);
3434		return buf;
3435	} else
3436		return state_names[state];
3437#undef N
3438}
3439
3440static const char *
3441get_chaninfo(const struct ieee80211_channel *c, int precise,
3442	char buf[], size_t bsize)
3443{
3444	buf[0] = '\0';
3445	if (IEEE80211_IS_CHAN_FHSS(c))
3446		strlcat(buf, " FHSS", bsize);
3447	if (IEEE80211_IS_CHAN_A(c))
3448		strlcat(buf, " 11a", bsize);
3449	else if (IEEE80211_IS_CHAN_ANYG(c))
3450		strlcat(buf, " 11g", bsize);
3451	else if (IEEE80211_IS_CHAN_B(c))
3452		strlcat(buf, " 11b", bsize);
3453	if (IEEE80211_IS_CHAN_HALF(c))
3454		strlcat(buf, "/10MHz", bsize);
3455	if (IEEE80211_IS_CHAN_QUARTER(c))
3456		strlcat(buf, "/5MHz", bsize);
3457	if (IEEE80211_IS_CHAN_TURBO(c))
3458		strlcat(buf, " Turbo", bsize);
3459	if (precise) {
3460		if (IEEE80211_IS_CHAN_HT20(c))
3461			strlcat(buf, " ht/20", bsize);
3462		else if (IEEE80211_IS_CHAN_HT40D(c))
3463			strlcat(buf, " ht/40-", bsize);
3464		else if (IEEE80211_IS_CHAN_HT40U(c))
3465			strlcat(buf, " ht/40+", bsize);
3466	} else {
3467		if (IEEE80211_IS_CHAN_HT(c))
3468			strlcat(buf, " ht", bsize);
3469	}
3470	return buf;
3471}
3472
3473static void
3474print_chaninfo(const struct ieee80211_channel *c, int verb)
3475{
3476	char buf[14];
3477
3478	if (verb)
3479		printf("Channel %3u : %u%c%c%c%c%c MHz%-14.14s",
3480		    ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
3481		    IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
3482		    IEEE80211_IS_CHAN_DFS(c) ? 'D' : ' ',
3483		    IEEE80211_IS_CHAN_RADAR(c) ? 'R' : ' ',
3484		    IEEE80211_IS_CHAN_CWINT(c) ? 'I' : ' ',
3485		    IEEE80211_IS_CHAN_CACDONE(c) ? 'C' : ' ',
3486		    get_chaninfo(c, verb, buf, sizeof(buf)));
3487	else
3488	printf("Channel %3u : %u%c MHz%-14.14s",
3489	    ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
3490	    IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
3491	    get_chaninfo(c, verb, buf, sizeof(buf)));
3492
3493}
3494
3495static int
3496chanpref(const struct ieee80211_channel *c)
3497{
3498	if (IEEE80211_IS_CHAN_HT40(c))
3499		return 40;
3500	if (IEEE80211_IS_CHAN_HT20(c))
3501		return 30;
3502	if (IEEE80211_IS_CHAN_HALF(c))
3503		return 10;
3504	if (IEEE80211_IS_CHAN_QUARTER(c))
3505		return 5;
3506	if (IEEE80211_IS_CHAN_TURBO(c))
3507		return 25;
3508	if (IEEE80211_IS_CHAN_A(c))
3509		return 20;
3510	if (IEEE80211_IS_CHAN_G(c))
3511		return 20;
3512	if (IEEE80211_IS_CHAN_B(c))
3513		return 15;
3514	if (IEEE80211_IS_CHAN_PUREG(c))
3515		return 15;
3516	return 0;
3517}
3518
3519static void
3520print_channels(int s, const struct ieee80211req_chaninfo *chans,
3521	int allchans, int verb)
3522{
3523	struct ieee80211req_chaninfo *achans;
3524	uint8_t reported[IEEE80211_CHAN_BYTES];
3525	const struct ieee80211_channel *c;
3526	int i, half;
3527
3528	achans = malloc(IEEE80211_CHANINFO_SPACE(chans));
3529	if (achans == NULL)
3530		errx(1, "no space for active channel list");
3531	achans->ic_nchans = 0;
3532	memset(reported, 0, sizeof(reported));
3533	if (!allchans) {
3534		struct ieee80211req_chanlist active;
3535
3536		if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0)
3537			errx(1, "unable to get active channel list");
3538		for (i = 0; i < chans->ic_nchans; i++) {
3539			c = &chans->ic_chans[i];
3540			if (!isset(active.ic_channels, c->ic_ieee))
3541				continue;
3542			/*
3543			 * Suppress compatible duplicates unless
3544			 * verbose.  The kernel gives us it's
3545			 * complete channel list which has separate
3546			 * entries for 11g/11b and 11a/turbo.
3547			 */
3548			if (isset(reported, c->ic_ieee) && !verb) {
3549				/* XXX we assume duplicates are adjacent */
3550				achans->ic_chans[achans->ic_nchans-1] = *c;
3551			} else {
3552				achans->ic_chans[achans->ic_nchans++] = *c;
3553				setbit(reported, c->ic_ieee);
3554			}
3555		}
3556	} else {
3557		for (i = 0; i < chans->ic_nchans; i++) {
3558			c = &chans->ic_chans[i];
3559			/* suppress duplicates as above */
3560			if (isset(reported, c->ic_ieee) && !verb) {
3561				/* XXX we assume duplicates are adjacent */
3562				struct ieee80211_channel *a =
3563				    &achans->ic_chans[achans->ic_nchans-1];
3564				if (chanpref(c) > chanpref(a))
3565					*a = *c;
3566			} else {
3567				achans->ic_chans[achans->ic_nchans++] = *c;
3568				setbit(reported, c->ic_ieee);
3569			}
3570		}
3571	}
3572	half = achans->ic_nchans / 2;
3573	if (achans->ic_nchans % 2)
3574		half++;
3575
3576	for (i = 0; i < achans->ic_nchans / 2; i++) {
3577		print_chaninfo(&achans->ic_chans[i], verb);
3578		print_chaninfo(&achans->ic_chans[half+i], verb);
3579		printf("\n");
3580	}
3581	if (achans->ic_nchans % 2) {
3582		print_chaninfo(&achans->ic_chans[i], verb);
3583		printf("\n");
3584	}
3585	free(achans);
3586}
3587
3588static void
3589list_channels(int s, int allchans)
3590{
3591	getchaninfo(s);
3592	print_channels(s, chaninfo, allchans, verbose);
3593}
3594
3595static void
3596print_txpow(const struct ieee80211_channel *c)
3597{
3598	printf("Channel %3u : %u MHz %3.1f reg %2d  ",
3599	    c->ic_ieee, c->ic_freq,
3600	    c->ic_maxpower/2., c->ic_maxregpower);
3601}
3602
3603static void
3604print_txpow_verbose(const struct ieee80211_channel *c)
3605{
3606	print_chaninfo(c, 1);
3607	printf("min %4.1f dBm  max %3.1f dBm  reg %2d dBm",
3608	    c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
3609	/* indicate where regulatory cap limits power use */
3610	if (c->ic_maxpower > 2*c->ic_maxregpower)
3611		printf(" <");
3612}
3613
3614static void
3615list_txpow(int s)
3616{
3617	struct ieee80211req_chaninfo *achans;
3618	uint8_t reported[IEEE80211_CHAN_BYTES];
3619	struct ieee80211_channel *c, *prev;
3620	int i, half;
3621
3622	getchaninfo(s);
3623	achans = malloc(IEEE80211_CHANINFO_SPACE(chaninfo));
3624	if (achans == NULL)
3625		errx(1, "no space for active channel list");
3626	achans->ic_nchans = 0;
3627	memset(reported, 0, sizeof(reported));
3628	for (i = 0; i < chaninfo->ic_nchans; i++) {
3629		c = &chaninfo->ic_chans[i];
3630		/* suppress duplicates as above */
3631		if (isset(reported, c->ic_ieee) && !verbose) {
3632			/* XXX we assume duplicates are adjacent */
3633			prev = &achans->ic_chans[achans->ic_nchans-1];
3634			/* display highest power on channel */
3635			if (c->ic_maxpower > prev->ic_maxpower)
3636				*prev = *c;
3637		} else {
3638			achans->ic_chans[achans->ic_nchans++] = *c;
3639			setbit(reported, c->ic_ieee);
3640		}
3641	}
3642	if (!verbose) {
3643		half = achans->ic_nchans / 2;
3644		if (achans->ic_nchans % 2)
3645			half++;
3646
3647		for (i = 0; i < achans->ic_nchans / 2; i++) {
3648			print_txpow(&achans->ic_chans[i]);
3649			print_txpow(&achans->ic_chans[half+i]);
3650			printf("\n");
3651		}
3652		if (achans->ic_nchans % 2) {
3653			print_txpow(&achans->ic_chans[i]);
3654			printf("\n");
3655		}
3656	} else {
3657		for (i = 0; i < achans->ic_nchans; i++) {
3658			print_txpow_verbose(&achans->ic_chans[i]);
3659			printf("\n");
3660		}
3661	}
3662	free(achans);
3663}
3664
3665static void
3666list_keys(int s)
3667{
3668}
3669
3670#define	IEEE80211_C_BITS \
3671	"\20\1STA\002803ENCAP\7FF\10TURBOP\11IBSS\12PMGT" \
3672	"\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \
3673	"\21MONITOR\22DFS\23MBSS\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
3674	"\37TXFRAG\40TDMA"
3675
3676static void
3677list_capabilities(int s)
3678{
3679	struct ieee80211_devcaps_req *dc;
3680
3681	if (verbose)
3682		dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
3683	else
3684		dc = malloc(IEEE80211_DEVCAPS_SIZE(1));
3685	if (dc == NULL)
3686		errx(1, "no space for device capabilities");
3687	dc->dc_chaninfo.ic_nchans = verbose ? MAXCHAN : 1;
3688	getdevcaps(s, dc);
3689	printb("drivercaps", dc->dc_drivercaps, IEEE80211_C_BITS);
3690	if (dc->dc_cryptocaps != 0 || verbose) {
3691		putchar('\n');
3692		printb("cryptocaps", dc->dc_cryptocaps, IEEE80211_CRYPTO_BITS);
3693	}
3694	if (dc->dc_htcaps != 0 || verbose) {
3695		putchar('\n');
3696		printb("htcaps", dc->dc_htcaps, IEEE80211_HTCAP_BITS);
3697	}
3698	putchar('\n');
3699	if (verbose) {
3700		chaninfo = &dc->dc_chaninfo;	/* XXX */
3701		print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, verbose);
3702	}
3703	free(dc);
3704}
3705
3706static int
3707get80211wme(int s, int param, int ac, int *val)
3708{
3709	struct ieee80211req ireq;
3710
3711	(void) memset(&ireq, 0, sizeof(ireq));
3712	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3713	ireq.i_type = param;
3714	ireq.i_len = ac;
3715	if (ioctl(s, SIOCG80211, &ireq) < 0) {
3716		warn("cannot get WME parameter %d, ac %d%s",
3717		    param, ac & IEEE80211_WMEPARAM_VAL,
3718		    ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : "");
3719		return -1;
3720	}
3721	*val = ireq.i_val;
3722	return 0;
3723}
3724
3725static void
3726list_wme_aci(int s, const char *tag, int ac)
3727{
3728	int val;
3729
3730	printf("\t%s", tag);
3731
3732	/* show WME BSS parameters */
3733	if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1)
3734		printf(" cwmin %2u", val);
3735	if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1)
3736		printf(" cwmax %2u", val);
3737	if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1)
3738		printf(" aifs %2u", val);
3739	if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1)
3740		printf(" txopLimit %3u", val);
3741	if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) {
3742		if (val)
3743			printf(" acm");
3744		else if (verbose)
3745			printf(" -acm");
3746	}
3747	/* !BSS only */
3748	if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
3749		if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) {
3750			if (!val)
3751				printf(" -ack");
3752			else if (verbose)
3753				printf(" ack");
3754		}
3755	}
3756	printf("\n");
3757}
3758
3759static void
3760list_wme(int s)
3761{
3762	static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
3763	int ac;
3764
3765	if (verbose) {
3766		/* display both BSS and local settings */
3767		for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
3768	again:
3769			if (ac & IEEE80211_WMEPARAM_BSS)
3770				list_wme_aci(s, "     ", ac);
3771			else
3772				list_wme_aci(s, acnames[ac], ac);
3773			if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
3774				ac |= IEEE80211_WMEPARAM_BSS;
3775				goto again;
3776			} else
3777				ac &= ~IEEE80211_WMEPARAM_BSS;
3778		}
3779	} else {
3780		/* display only channel settings */
3781		for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++)
3782			list_wme_aci(s, acnames[ac], ac);
3783	}
3784}
3785
3786static void
3787list_roam(int s)
3788{
3789	const struct ieee80211_roamparam *rp;
3790	int mode;
3791
3792	getroam(s);
3793	for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
3794		rp = &roamparams.params[mode];
3795		if (rp->rssi == 0 && rp->rate == 0)
3796			continue;
3797		if (mode == IEEE80211_MODE_11NA || mode == IEEE80211_MODE_11NG) {
3798			if (rp->rssi & 1)
3799				LINE_CHECK("roam:%-7.7s rssi %2u.5dBm  MCS %2u    ",
3800				    modename[mode], rp->rssi/2,
3801				    rp->rate &~ IEEE80211_RATE_MCS);
3802			else
3803				LINE_CHECK("roam:%-7.7s rssi %4udBm  MCS %2u    ",
3804				    modename[mode], rp->rssi/2,
3805				    rp->rate &~ IEEE80211_RATE_MCS);
3806		} else {
3807			if (rp->rssi & 1)
3808				LINE_CHECK("roam:%-7.7s rssi %2u.5dBm rate %2u Mb/s",
3809				    modename[mode], rp->rssi/2, rp->rate/2);
3810			else
3811				LINE_CHECK("roam:%-7.7s rssi %4udBm rate %2u Mb/s",
3812				    modename[mode], rp->rssi/2, rp->rate/2);
3813		}
3814	}
3815}
3816
3817static void
3818list_txparams(int s)
3819{
3820	const struct ieee80211_txparam *tp;
3821	int mode;
3822
3823	gettxparams(s);
3824	for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
3825		tp = &txparams.params[mode];
3826		if (tp->mgmtrate == 0 && tp->mcastrate == 0)
3827			continue;
3828		if (mode == IEEE80211_MODE_11NA || mode == IEEE80211_MODE_11NG) {
3829			if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
3830				LINE_CHECK("%-7.7s ucast NONE    mgmt %2u MCS  "
3831				    "mcast %2u MCS  maxretry %u",
3832				    modename[mode],
3833				    tp->mgmtrate &~ IEEE80211_RATE_MCS,
3834				    tp->mcastrate &~ IEEE80211_RATE_MCS,
3835				    tp->maxretry);
3836			else
3837				LINE_CHECK("%-7.7s ucast %2u MCS  mgmt %2u MCS  "
3838				    "mcast %2u MCS  maxretry %u",
3839				    modename[mode],
3840				    tp->ucastrate &~ IEEE80211_RATE_MCS,
3841				    tp->mgmtrate &~ IEEE80211_RATE_MCS,
3842				    tp->mcastrate &~ IEEE80211_RATE_MCS,
3843				    tp->maxretry);
3844		} else {
3845			if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
3846				LINE_CHECK("%-7.7s ucast NONE    mgmt %2u Mb/s "
3847				    "mcast %2u Mb/s maxretry %u",
3848				    modename[mode],
3849				    tp->mgmtrate/2,
3850				    tp->mcastrate/2, tp->maxretry);
3851			else
3852				LINE_CHECK("%-7.7s ucast %2u Mb/s mgmt %2u Mb/s "
3853				    "mcast %2u Mb/s maxretry %u",
3854				    modename[mode],
3855				    tp->ucastrate/2, tp->mgmtrate/2,
3856				    tp->mcastrate/2, tp->maxretry);
3857		}
3858	}
3859}
3860
3861static void
3862printpolicy(int policy)
3863{
3864	switch (policy) {
3865	case IEEE80211_MACCMD_POLICY_OPEN:
3866		printf("policy: open\n");
3867		break;
3868	case IEEE80211_MACCMD_POLICY_ALLOW:
3869		printf("policy: allow\n");
3870		break;
3871	case IEEE80211_MACCMD_POLICY_DENY:
3872		printf("policy: deny\n");
3873		break;
3874	case IEEE80211_MACCMD_POLICY_RADIUS:
3875		printf("policy: radius\n");
3876		break;
3877	default:
3878		printf("policy: unknown (%u)\n", policy);
3879		break;
3880	}
3881}
3882
3883static void
3884list_mac(int s)
3885{
3886	struct ieee80211req ireq;
3887	struct ieee80211req_maclist *acllist;
3888	int i, nacls, policy, len;
3889	uint8_t *data;
3890	char c;
3891
3892	(void) memset(&ireq, 0, sizeof(ireq));
3893	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
3894	ireq.i_type = IEEE80211_IOC_MACCMD;
3895	ireq.i_val = IEEE80211_MACCMD_POLICY;
3896	if (ioctl(s, SIOCG80211, &ireq) < 0) {
3897		if (errno == EINVAL) {
3898			printf("No acl policy loaded\n");
3899			return;
3900		}
3901		err(1, "unable to get mac policy");
3902	}
3903	policy = ireq.i_val;
3904	if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
3905		c = '*';
3906	} else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
3907		c = '+';
3908	} else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
3909		c = '-';
3910	} else if (policy == IEEE80211_MACCMD_POLICY_RADIUS) {
3911		c = 'r';		/* NB: should never have entries */
3912	} else {
3913		printf("policy: unknown (%u)\n", policy);
3914		c = '?';
3915	}
3916	if (verbose || c == '?')
3917		printpolicy(policy);
3918
3919	ireq.i_val = IEEE80211_MACCMD_LIST;
3920	ireq.i_len = 0;
3921	if (ioctl(s, SIOCG80211, &ireq) < 0)
3922		err(1, "unable to get mac acl list size");
3923	if (ireq.i_len == 0) {		/* NB: no acls */
3924		if (!(verbose || c == '?'))
3925			printpolicy(policy);
3926		return;
3927	}
3928	len = ireq.i_len;
3929
3930	data = malloc(len);
3931	if (data == NULL)
3932		err(1, "out of memory for acl list");
3933
3934	ireq.i_data = data;
3935	if (ioctl(s, SIOCG80211, &ireq) < 0)
3936		err(1, "unable to get mac acl list");
3937	nacls = len / sizeof(*acllist);
3938	acllist = (struct ieee80211req_maclist *) data;
3939	for (i = 0; i < nacls; i++)
3940		printf("%c%s\n", c, ether_ntoa(
3941			(const struct ether_addr *) acllist[i].ml_macaddr));
3942	free(data);
3943}
3944
3945static void
3946print_regdomain(const struct ieee80211_regdomain *reg, int verb)
3947{
3948	if ((reg->regdomain != 0 &&
3949	    reg->regdomain != reg->country) || verb) {
3950		const struct regdomain *rd =
3951		    lib80211_regdomain_findbysku(getregdata(), reg->regdomain);
3952		if (rd == NULL)
3953			LINE_CHECK("regdomain %d", reg->regdomain);
3954		else
3955			LINE_CHECK("regdomain %s", rd->name);
3956	}
3957	if (reg->country != 0 || verb) {
3958		const struct country *cc =
3959		    lib80211_country_findbycc(getregdata(), reg->country);
3960		if (cc == NULL)
3961			LINE_CHECK("country %d", reg->country);
3962		else
3963			LINE_CHECK("country %s", cc->isoname);
3964	}
3965	if (reg->location == 'I')
3966		LINE_CHECK("indoor");
3967	else if (reg->location == 'O')
3968		LINE_CHECK("outdoor");
3969	else if (verb)
3970		LINE_CHECK("anywhere");
3971	if (reg->ecm)
3972		LINE_CHECK("ecm");
3973	else if (verb)
3974		LINE_CHECK("-ecm");
3975}
3976
3977static void
3978list_regdomain(int s, int channelsalso)
3979{
3980	getregdomain(s);
3981	if (channelsalso) {
3982		getchaninfo(s);
3983		spacer = ':';
3984		print_regdomain(&regdomain, 1);
3985		LINE_BREAK();
3986		print_channels(s, chaninfo, 1/*allchans*/, 1/*verbose*/);
3987	} else
3988		print_regdomain(&regdomain, verbose);
3989}
3990
3991static void
3992list_mesh(int s)
3993{
3994	struct ieee80211req ireq;
3995	struct ieee80211req_mesh_route routes[128];
3996	struct ieee80211req_mesh_route *rt;
3997
3998	(void) memset(&ireq, 0, sizeof(ireq));
3999	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4000	ireq.i_type = IEEE80211_IOC_MESH_RTCMD;
4001	ireq.i_val = IEEE80211_MESH_RTCMD_LIST;
4002	ireq.i_data = &routes;
4003	ireq.i_len = sizeof(routes);
4004	if (ioctl(s, SIOCG80211, &ireq) < 0)
4005	 	err(1, "unable to get the Mesh routing table");
4006
4007	printf("%-17.17s %-17.17s %4s %4s %4s %6s %s\n"
4008		, "DEST"
4009		, "NEXT HOP"
4010		, "HOPS"
4011		, "METRIC"
4012		, "LIFETIME"
4013		, "MSEQ"
4014		, "FLAGS");
4015
4016	for (rt = &routes[0]; rt - &routes[0] < ireq.i_len / sizeof(*rt); rt++){
4017		printf("%s ",
4018		    ether_ntoa((const struct ether_addr *)rt->imr_dest));
4019		printf("%s %4u   %4u   %6u %6u    %c%c\n",
4020			ether_ntoa((const struct ether_addr *)rt->imr_nexthop),
4021			rt->imr_nhops, rt->imr_metric, rt->imr_lifetime,
4022			rt->imr_lastmseq,
4023			(rt->imr_flags & IEEE80211_MESHRT_FLAGS_DISCOVER) ?
4024			    'D' :
4025			(rt->imr_flags & IEEE80211_MESHRT_FLAGS_VALID) ?
4026			    'V' : '!',
4027			(rt->imr_flags & IEEE80211_MESHRT_FLAGS_PROXY) ?
4028			    'P' : ' ');
4029	}
4030}
4031
4032static
4033DECL_CMD_FUNC(set80211list, arg, d)
4034{
4035#define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
4036
4037	LINE_INIT('\t');
4038
4039	if (iseq(arg, "sta"))
4040		list_stations(s);
4041	else if (iseq(arg, "scan") || iseq(arg, "ap"))
4042		list_scan(s);
4043	else if (iseq(arg, "chan") || iseq(arg, "freq"))
4044		list_channels(s, 1);
4045	else if (iseq(arg, "active"))
4046		list_channels(s, 0);
4047	else if (iseq(arg, "keys"))
4048		list_keys(s);
4049	else if (iseq(arg, "caps"))
4050		list_capabilities(s);
4051	else if (iseq(arg, "wme") || iseq(arg, "wmm"))
4052		list_wme(s);
4053	else if (iseq(arg, "mac"))
4054		list_mac(s);
4055	else if (iseq(arg, "txpow"))
4056		list_txpow(s);
4057	else if (iseq(arg, "roam"))
4058		list_roam(s);
4059	else if (iseq(arg, "txparam") || iseq(arg, "txparm"))
4060		list_txparams(s);
4061	else if (iseq(arg, "regdomain"))
4062		list_regdomain(s, 1);
4063	else if (iseq(arg, "countries"))
4064		list_countries();
4065	else if (iseq(arg, "mesh"))
4066		list_mesh(s);
4067	else
4068		errx(1, "Don't know how to list %s for %s", arg, name);
4069	LINE_BREAK();
4070#undef iseq
4071}
4072
4073static enum ieee80211_opmode
4074get80211opmode(int s)
4075{
4076	struct ifmediareq ifmr;
4077
4078	(void) memset(&ifmr, 0, sizeof(ifmr));
4079	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
4080
4081	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
4082		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
4083			if (ifmr.ifm_current & IFM_FLAG0)
4084				return IEEE80211_M_AHDEMO;
4085			else
4086				return IEEE80211_M_IBSS;
4087		}
4088		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
4089			return IEEE80211_M_HOSTAP;
4090		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
4091			return IEEE80211_M_MONITOR;
4092		if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
4093			return IEEE80211_M_MBSS;
4094	}
4095	return IEEE80211_M_STA;
4096}
4097
4098#if 0
4099static void
4100printcipher(int s, struct ieee80211req *ireq, int keylenop)
4101{
4102	switch (ireq->i_val) {
4103	case IEEE80211_CIPHER_WEP:
4104		ireq->i_type = keylenop;
4105		if (ioctl(s, SIOCG80211, ireq) != -1)
4106			printf("WEP-%s",
4107			    ireq->i_len <= 5 ? "40" :
4108			    ireq->i_len <= 13 ? "104" : "128");
4109		else
4110			printf("WEP");
4111		break;
4112	case IEEE80211_CIPHER_TKIP:
4113		printf("TKIP");
4114		break;
4115	case IEEE80211_CIPHER_AES_OCB:
4116		printf("AES-OCB");
4117		break;
4118	case IEEE80211_CIPHER_AES_CCM:
4119		printf("AES-CCM");
4120		break;
4121	case IEEE80211_CIPHER_CKIP:
4122		printf("CKIP");
4123		break;
4124	case IEEE80211_CIPHER_NONE:
4125		printf("NONE");
4126		break;
4127	default:
4128		printf("UNKNOWN (0x%x)", ireq->i_val);
4129		break;
4130	}
4131}
4132#endif
4133
4134static void
4135printkey(const struct ieee80211req_key *ik)
4136{
4137	static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
4138	int keylen = ik->ik_keylen;
4139	int printcontents;
4140
4141	printcontents = printkeys &&
4142		(memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
4143	if (printcontents)
4144		LINE_BREAK();
4145	switch (ik->ik_type) {
4146	case IEEE80211_CIPHER_WEP:
4147		/* compatibility */
4148		LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
4149		    keylen <= 5 ? "40-bit" :
4150		    keylen <= 13 ? "104-bit" : "128-bit");
4151		break;
4152	case IEEE80211_CIPHER_TKIP:
4153		if (keylen > 128/8)
4154			keylen -= 128/8;	/* ignore MIC for now */
4155		LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4156		break;
4157	case IEEE80211_CIPHER_AES_OCB:
4158		LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4159		break;
4160	case IEEE80211_CIPHER_AES_CCM:
4161		LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4162		break;
4163	case IEEE80211_CIPHER_CKIP:
4164		LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4165		break;
4166	case IEEE80211_CIPHER_NONE:
4167		LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4168		break;
4169	default:
4170		LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
4171			ik->ik_type, ik->ik_keyix+1, 8*keylen);
4172		break;
4173	}
4174	if (printcontents) {
4175		int i;
4176
4177		printf(" <");
4178		for (i = 0; i < keylen; i++)
4179			printf("%02x", ik->ik_keydata[i]);
4180		printf(">");
4181		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
4182		    (ik->ik_keyrsc != 0 || verbose))
4183			printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
4184		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
4185		    (ik->ik_keytsc != 0 || verbose))
4186			printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
4187		if (ik->ik_flags != 0 && verbose) {
4188			const char *sep = " ";
4189
4190			if (ik->ik_flags & IEEE80211_KEY_XMIT)
4191				printf("%stx", sep), sep = "+";
4192			if (ik->ik_flags & IEEE80211_KEY_RECV)
4193				printf("%srx", sep), sep = "+";
4194			if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
4195				printf("%sdef", sep), sep = "+";
4196		}
4197		LINE_BREAK();
4198	}
4199}
4200
4201static void
4202printrate(const char *tag, int v, int defrate, int defmcs)
4203{
4204	if ((v & IEEE80211_RATE_MCS) == 0) {
4205		if (v != defrate) {
4206			if (v & 1)
4207				LINE_CHECK("%s %d.5", tag, v/2);
4208			else
4209				LINE_CHECK("%s %d", tag, v/2);
4210		}
4211	} else {
4212		if (v != defmcs)
4213			LINE_CHECK("%s %d", tag, v &~ 0x80);
4214	}
4215}
4216
4217static int
4218getid(int s, int ix, void *data, size_t len, int *plen, int mesh)
4219{
4220	struct ieee80211req ireq;
4221
4222	(void) memset(&ireq, 0, sizeof(ireq));
4223	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4224	ireq.i_type = (!mesh) ? IEEE80211_IOC_SSID : IEEE80211_IOC_MESH_ID;
4225	ireq.i_val = ix;
4226	ireq.i_data = data;
4227	ireq.i_len = len;
4228	if (ioctl(s, SIOCG80211, &ireq) < 0)
4229		return -1;
4230	*plen = ireq.i_len;
4231	return 0;
4232}
4233
4234static void
4235ieee80211_status(int s)
4236{
4237	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
4238	enum ieee80211_opmode opmode = get80211opmode(s);
4239	int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode;
4240	uint8_t data[32];
4241	const struct ieee80211_channel *c;
4242	const struct ieee80211_roamparam *rp;
4243	const struct ieee80211_txparam *tp;
4244
4245	if (getid(s, -1, data, sizeof(data), &len, 0) < 0) {
4246		/* If we can't get the SSID, this isn't an 802.11 device. */
4247		return;
4248	}
4249
4250	/*
4251	 * Invalidate cached state so printing status for multiple
4252	 * if's doesn't reuse the first interfaces' cached state.
4253	 */
4254	gotcurchan = 0;
4255	gotroam = 0;
4256	gottxparams = 0;
4257	gothtconf = 0;
4258	gotregdomain = 0;
4259
4260	printf("\t");
4261	if (opmode == IEEE80211_M_MBSS) {
4262		printf("meshid ");
4263		getid(s, 0, data, sizeof(data), &len, 1);
4264		print_string(data, len);
4265	} else {
4266		if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
4267			num = 0;
4268		printf("ssid ");
4269		if (num > 1) {
4270			for (i = 0; i < num; i++) {
4271				if (getid(s, i, data, sizeof(data), &len, 0) >= 0 && len > 0) {
4272					printf(" %d:", i + 1);
4273					print_string(data, len);
4274				}
4275			}
4276		} else
4277			print_string(data, len);
4278	}
4279	c = getcurchan(s);
4280	if (c->ic_freq != IEEE80211_CHAN_ANY) {
4281		char buf[14];
4282		printf(" channel %d (%u MHz%s)", c->ic_ieee, c->ic_freq,
4283			get_chaninfo(c, 1, buf, sizeof(buf)));
4284	} else if (verbose)
4285		printf(" channel UNDEF");
4286
4287	if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 &&
4288	    (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
4289		printf(" bssid %s", ether_ntoa((struct ether_addr *)data));
4290
4291	if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) {
4292		printf("\n\tstationname ");
4293		print_string(data, len);
4294	}
4295
4296	spacer = ' ';		/* force first break */
4297	LINE_BREAK();
4298
4299	list_regdomain(s, 0);
4300
4301	wpa = 0;
4302	if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) {
4303		switch (val) {
4304		case IEEE80211_AUTH_NONE:
4305			LINE_CHECK("authmode NONE");
4306			break;
4307		case IEEE80211_AUTH_OPEN:
4308			LINE_CHECK("authmode OPEN");
4309			break;
4310		case IEEE80211_AUTH_SHARED:
4311			LINE_CHECK("authmode SHARED");
4312			break;
4313		case IEEE80211_AUTH_8021X:
4314			LINE_CHECK("authmode 802.1x");
4315			break;
4316		case IEEE80211_AUTH_WPA:
4317			if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0)
4318				wpa = 1;	/* default to WPA1 */
4319			switch (wpa) {
4320			case 2:
4321				LINE_CHECK("authmode WPA2/802.11i");
4322				break;
4323			case 3:
4324				LINE_CHECK("authmode WPA1+WPA2/802.11i");
4325				break;
4326			default:
4327				LINE_CHECK("authmode WPA");
4328				break;
4329			}
4330			break;
4331		case IEEE80211_AUTH_AUTO:
4332			LINE_CHECK("authmode AUTO");
4333			break;
4334		default:
4335			LINE_CHECK("authmode UNKNOWN (0x%x)", val);
4336			break;
4337		}
4338	}
4339
4340	if (wpa || verbose) {
4341		if (get80211val(s, IEEE80211_IOC_WPS, &val) != -1) {
4342			if (val)
4343				LINE_CHECK("wps");
4344			else if (verbose)
4345				LINE_CHECK("-wps");
4346		}
4347		if (get80211val(s, IEEE80211_IOC_TSN, &val) != -1) {
4348			if (val)
4349				LINE_CHECK("tsn");
4350			else if (verbose)
4351				LINE_CHECK("-tsn");
4352		}
4353		if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) {
4354			if (val)
4355				LINE_CHECK("countermeasures");
4356			else if (verbose)
4357				LINE_CHECK("-countermeasures");
4358		}
4359#if 0
4360		/* XXX not interesting with WPA done in user space */
4361		ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
4362		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4363		}
4364
4365		ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
4366		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4367			LINE_CHECK("mcastcipher ");
4368			printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
4369			spacer = ' ';
4370		}
4371
4372		ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
4373		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4374			LINE_CHECK("ucastcipher ");
4375			printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
4376		}
4377
4378		if (wpa & 2) {
4379			ireq.i_type = IEEE80211_IOC_RSNCAPS;
4380			if (ioctl(s, SIOCG80211, &ireq) != -1) {
4381				LINE_CHECK("RSN caps 0x%x", ireq.i_val);
4382				spacer = ' ';
4383			}
4384		}
4385
4386		ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
4387		if (ioctl(s, SIOCG80211, &ireq) != -1) {
4388		}
4389#endif
4390	}
4391
4392	if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 &&
4393	    wepmode != IEEE80211_WEP_NOSUP) {
4394
4395		switch (wepmode) {
4396		case IEEE80211_WEP_OFF:
4397			LINE_CHECK("privacy OFF");
4398			break;
4399		case IEEE80211_WEP_ON:
4400			LINE_CHECK("privacy ON");
4401			break;
4402		case IEEE80211_WEP_MIXED:
4403			LINE_CHECK("privacy MIXED");
4404			break;
4405		default:
4406			LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
4407			break;
4408		}
4409
4410		/*
4411		 * If we get here then we've got WEP support so we need
4412		 * to print WEP status.
4413		 */
4414
4415		if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) {
4416			warn("WEP support, but no tx key!");
4417			goto end;
4418		}
4419		if (val != -1)
4420			LINE_CHECK("deftxkey %d", val+1);
4421		else if (wepmode != IEEE80211_WEP_OFF || verbose)
4422			LINE_CHECK("deftxkey UNDEF");
4423
4424		if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) {
4425			warn("WEP support, but no NUMWEPKEYS support!");
4426			goto end;
4427		}
4428
4429		for (i = 0; i < num; i++) {
4430			struct ieee80211req_key ik;
4431
4432			memset(&ik, 0, sizeof(ik));
4433			ik.ik_keyix = i;
4434			if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) {
4435				warn("WEP support, but can get keys!");
4436				goto end;
4437			}
4438			if (ik.ik_keylen != 0) {
4439				if (verbose)
4440					LINE_BREAK();
4441				printkey(&ik);
4442			}
4443		}
4444end:
4445		;
4446	}
4447
4448	if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 &&
4449	    val != IEEE80211_POWERSAVE_NOSUP ) {
4450		if (val != IEEE80211_POWERSAVE_OFF || verbose) {
4451			switch (val) {
4452			case IEEE80211_POWERSAVE_OFF:
4453				LINE_CHECK("powersavemode OFF");
4454				break;
4455			case IEEE80211_POWERSAVE_CAM:
4456				LINE_CHECK("powersavemode CAM");
4457				break;
4458			case IEEE80211_POWERSAVE_PSP:
4459				LINE_CHECK("powersavemode PSP");
4460				break;
4461			case IEEE80211_POWERSAVE_PSP_CAM:
4462				LINE_CHECK("powersavemode PSP-CAM");
4463				break;
4464			}
4465			if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1)
4466				LINE_CHECK("powersavesleep %d", val);
4467		}
4468	}
4469
4470	if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) {
4471		if (val & 1)
4472			LINE_CHECK("txpower %d.5", val/2);
4473		else
4474			LINE_CHECK("txpower %d", val/2);
4475	}
4476	if (verbose) {
4477		if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1)
4478			LINE_CHECK("txpowmax %.1f", val/2.);
4479	}
4480
4481	if (get80211val(s, IEEE80211_IOC_DOTD, &val) != -1) {
4482		if (val)
4483			LINE_CHECK("dotd");
4484		else if (verbose)
4485			LINE_CHECK("-dotd");
4486	}
4487
4488	if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) {
4489		if (val != IEEE80211_RTS_MAX || verbose)
4490			LINE_CHECK("rtsthreshold %d", val);
4491	}
4492
4493	if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) {
4494		if (val != IEEE80211_FRAG_MAX || verbose)
4495			LINE_CHECK("fragthreshold %d", val);
4496	}
4497	if (opmode == IEEE80211_M_STA || verbose) {
4498		if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) {
4499			if (val != IEEE80211_HWBMISS_MAX || verbose)
4500				LINE_CHECK("bmiss %d", val);
4501		}
4502	}
4503
4504	if (!verbose) {
4505		gettxparams(s);
4506		tp = &txparams.params[chan2mode(c)];
4507		printrate("ucastrate", tp->ucastrate,
4508		    IEEE80211_FIXED_RATE_NONE, IEEE80211_FIXED_RATE_NONE);
4509		printrate("mcastrate", tp->mcastrate, 2*1,
4510		    IEEE80211_RATE_MCS|0);
4511		printrate("mgmtrate", tp->mgmtrate, 2*1,
4512		    IEEE80211_RATE_MCS|0);
4513		if (tp->maxretry != 6)		/* XXX */
4514			LINE_CHECK("maxretry %d", tp->maxretry);
4515	} else {
4516		LINE_BREAK();
4517		list_txparams(s);
4518	}
4519
4520	bgscaninterval = -1;
4521	(void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval);
4522
4523	if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) {
4524		if (val != bgscaninterval || verbose)
4525			LINE_CHECK("scanvalid %u", val);
4526	}
4527
4528	bgscan = 0;
4529	if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) {
4530		if (bgscan)
4531			LINE_CHECK("bgscan");
4532		else if (verbose)
4533			LINE_CHECK("-bgscan");
4534	}
4535	if (bgscan || verbose) {
4536		if (bgscaninterval != -1)
4537			LINE_CHECK("bgscanintvl %u", bgscaninterval);
4538		if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1)
4539			LINE_CHECK("bgscanidle %u", val);
4540		if (!verbose) {
4541			getroam(s);
4542			rp = &roamparams.params[chan2mode(c)];
4543			if (rp->rssi & 1)
4544				LINE_CHECK("roam:rssi %u.5", rp->rssi/2);
4545			else
4546				LINE_CHECK("roam:rssi %u", rp->rssi/2);
4547			LINE_CHECK("roam:rate %u", rp->rate/2);
4548		} else {
4549			LINE_BREAK();
4550			list_roam(s);
4551			LINE_BREAK();
4552		}
4553	}
4554
4555	if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
4556		if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) {
4557			if (val)
4558				LINE_CHECK("pureg");
4559			else if (verbose)
4560				LINE_CHECK("-pureg");
4561		}
4562		if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) {
4563			switch (val) {
4564			case IEEE80211_PROTMODE_OFF:
4565				LINE_CHECK("protmode OFF");
4566				break;
4567			case IEEE80211_PROTMODE_CTS:
4568				LINE_CHECK("protmode CTS");
4569				break;
4570			case IEEE80211_PROTMODE_RTSCTS:
4571				LINE_CHECK("protmode RTSCTS");
4572				break;
4573			default:
4574				LINE_CHECK("protmode UNKNOWN (0x%x)", val);
4575				break;
4576			}
4577		}
4578	}
4579
4580	if (IEEE80211_IS_CHAN_HT(c) || verbose) {
4581		gethtconf(s);
4582		switch (htconf & 3) {
4583		case 0:
4584		case 2:
4585			LINE_CHECK("-ht");
4586			break;
4587		case 1:
4588			LINE_CHECK("ht20");
4589			break;
4590		case 3:
4591			if (verbose)
4592				LINE_CHECK("ht");
4593			break;
4594		}
4595		if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) {
4596			if (!val)
4597				LINE_CHECK("-htcompat");
4598			else if (verbose)
4599				LINE_CHECK("htcompat");
4600		}
4601		if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) {
4602			switch (val) {
4603			case 0:
4604				LINE_CHECK("-ampdu");
4605				break;
4606			case 1:
4607				LINE_CHECK("ampdutx -ampdurx");
4608				break;
4609			case 2:
4610				LINE_CHECK("-ampdutx ampdurx");
4611				break;
4612			case 3:
4613				if (verbose)
4614					LINE_CHECK("ampdu");
4615				break;
4616			}
4617		}
4618		if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) {
4619			switch (val) {
4620			case IEEE80211_HTCAP_MAXRXAMPDU_8K:
4621				LINE_CHECK("ampdulimit 8k");
4622				break;
4623			case IEEE80211_HTCAP_MAXRXAMPDU_16K:
4624				LINE_CHECK("ampdulimit 16k");
4625				break;
4626			case IEEE80211_HTCAP_MAXRXAMPDU_32K:
4627				LINE_CHECK("ampdulimit 32k");
4628				break;
4629			case IEEE80211_HTCAP_MAXRXAMPDU_64K:
4630				LINE_CHECK("ampdulimit 64k");
4631				break;
4632			}
4633		}
4634		if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) {
4635			switch (val) {
4636			case IEEE80211_HTCAP_MPDUDENSITY_NA:
4637				if (verbose)
4638					LINE_CHECK("ampdudensity NA");
4639				break;
4640			case IEEE80211_HTCAP_MPDUDENSITY_025:
4641				LINE_CHECK("ampdudensity .25");
4642				break;
4643			case IEEE80211_HTCAP_MPDUDENSITY_05:
4644				LINE_CHECK("ampdudensity .5");
4645				break;
4646			case IEEE80211_HTCAP_MPDUDENSITY_1:
4647				LINE_CHECK("ampdudensity 1");
4648				break;
4649			case IEEE80211_HTCAP_MPDUDENSITY_2:
4650				LINE_CHECK("ampdudensity 2");
4651				break;
4652			case IEEE80211_HTCAP_MPDUDENSITY_4:
4653				LINE_CHECK("ampdudensity 4");
4654				break;
4655			case IEEE80211_HTCAP_MPDUDENSITY_8:
4656				LINE_CHECK("ampdudensity 8");
4657				break;
4658			case IEEE80211_HTCAP_MPDUDENSITY_16:
4659				LINE_CHECK("ampdudensity 16");
4660				break;
4661			}
4662		}
4663		if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) {
4664			switch (val) {
4665			case 0:
4666				LINE_CHECK("-amsdu");
4667				break;
4668			case 1:
4669				LINE_CHECK("amsdutx -amsdurx");
4670				break;
4671			case 2:
4672				LINE_CHECK("-amsdutx amsdurx");
4673				break;
4674			case 3:
4675				if (verbose)
4676					LINE_CHECK("amsdu");
4677				break;
4678			}
4679		}
4680		/* XXX amsdu limit */
4681		if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) {
4682			if (val)
4683				LINE_CHECK("shortgi");
4684			else if (verbose)
4685				LINE_CHECK("-shortgi");
4686		}
4687		if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) {
4688			if (val == IEEE80211_PROTMODE_OFF)
4689				LINE_CHECK("htprotmode OFF");
4690			else if (val != IEEE80211_PROTMODE_RTSCTS)
4691				LINE_CHECK("htprotmode UNKNOWN (0x%x)", val);
4692			else if (verbose)
4693				LINE_CHECK("htprotmode RTSCTS");
4694		}
4695		if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) {
4696			if (val)
4697				LINE_CHECK("puren");
4698			else if (verbose)
4699				LINE_CHECK("-puren");
4700		}
4701		if (get80211val(s, IEEE80211_IOC_SMPS, &val) != -1) {
4702			if (val == IEEE80211_HTCAP_SMPS_DYNAMIC)
4703				LINE_CHECK("smpsdyn");
4704			else if (val == IEEE80211_HTCAP_SMPS_ENA)
4705				LINE_CHECK("smps");
4706			else if (verbose)
4707				LINE_CHECK("-smps");
4708		}
4709		if (get80211val(s, IEEE80211_IOC_RIFS, &val) != -1) {
4710			if (val)
4711				LINE_CHECK("rifs");
4712			else if (verbose)
4713				LINE_CHECK("-rifs");
4714		}
4715	}
4716
4717	if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) {
4718		if (wme)
4719			LINE_CHECK("wme");
4720		else if (verbose)
4721			LINE_CHECK("-wme");
4722	} else
4723		wme = 0;
4724
4725	if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) {
4726		if (val)
4727			LINE_CHECK("burst");
4728		else if (verbose)
4729			LINE_CHECK("-burst");
4730	}
4731
4732	if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) {
4733		if (val)
4734			LINE_CHECK("ff");
4735		else if (verbose)
4736			LINE_CHECK("-ff");
4737	}
4738	if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) {
4739		if (val)
4740			LINE_CHECK("dturbo");
4741		else if (verbose)
4742			LINE_CHECK("-dturbo");
4743	}
4744	if (get80211val(s, IEEE80211_IOC_DWDS, &val) != -1) {
4745		if (val)
4746			LINE_CHECK("dwds");
4747		else if (verbose)
4748			LINE_CHECK("-dwds");
4749	}
4750
4751	if (opmode == IEEE80211_M_HOSTAP) {
4752		if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) {
4753			if (val)
4754				LINE_CHECK("hidessid");
4755			else if (verbose)
4756				LINE_CHECK("-hidessid");
4757		}
4758		if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) {
4759			if (!val)
4760				LINE_CHECK("-apbridge");
4761			else if (verbose)
4762				LINE_CHECK("apbridge");
4763		}
4764		if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1)
4765			LINE_CHECK("dtimperiod %u", val);
4766
4767		if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) {
4768			if (!val)
4769				LINE_CHECK("-doth");
4770			else if (verbose)
4771				LINE_CHECK("doth");
4772		}
4773		if (get80211val(s, IEEE80211_IOC_DFS, &val) != -1) {
4774			if (!val)
4775				LINE_CHECK("-dfs");
4776			else if (verbose)
4777				LINE_CHECK("dfs");
4778		}
4779		if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) {
4780			if (!val)
4781				LINE_CHECK("-inact");
4782			else if (verbose)
4783				LINE_CHECK("inact");
4784		}
4785	} else {
4786		if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) {
4787			if (val != IEEE80211_ROAMING_AUTO || verbose) {
4788				switch (val) {
4789				case IEEE80211_ROAMING_DEVICE:
4790					LINE_CHECK("roaming DEVICE");
4791					break;
4792				case IEEE80211_ROAMING_AUTO:
4793					LINE_CHECK("roaming AUTO");
4794					break;
4795				case IEEE80211_ROAMING_MANUAL:
4796					LINE_CHECK("roaming MANUAL");
4797					break;
4798				default:
4799					LINE_CHECK("roaming UNKNOWN (0x%x)",
4800						val);
4801					break;
4802				}
4803			}
4804		}
4805	}
4806
4807	if (opmode == IEEE80211_M_AHDEMO) {
4808		if (get80211val(s, IEEE80211_IOC_TDMA_SLOT, &val) != -1)
4809			LINE_CHECK("tdmaslot %u", val);
4810		if (get80211val(s, IEEE80211_IOC_TDMA_SLOTCNT, &val) != -1)
4811			LINE_CHECK("tdmaslotcnt %u", val);
4812		if (get80211val(s, IEEE80211_IOC_TDMA_SLOTLEN, &val) != -1)
4813			LINE_CHECK("tdmaslotlen %u", val);
4814		if (get80211val(s, IEEE80211_IOC_TDMA_BINTERVAL, &val) != -1)
4815			LINE_CHECK("tdmabintval %u", val);
4816	} else if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) {
4817		/* XXX default define not visible */
4818		if (val != 100 || verbose)
4819			LINE_CHECK("bintval %u", val);
4820	}
4821
4822	if (wme && verbose) {
4823		LINE_BREAK();
4824		list_wme(s);
4825	}
4826
4827	if (opmode == IEEE80211_M_MBSS) {
4828		if (get80211val(s, IEEE80211_IOC_MESH_TTL, &val) != -1) {
4829			LINE_CHECK("meshttl %u", val);
4830		}
4831		if (get80211val(s, IEEE80211_IOC_MESH_AP, &val) != -1) {
4832			if (val)
4833				LINE_CHECK("meshpeering");
4834			else
4835				LINE_CHECK("-meshpeering");
4836		}
4837		if (get80211val(s, IEEE80211_IOC_MESH_FWRD, &val) != -1) {
4838			if (val)
4839				LINE_CHECK("meshforward");
4840			else
4841				LINE_CHECK("-meshforward");
4842		}
4843		if (get80211val(s, IEEE80211_IOC_MESH_GATE, &val) != -1) {
4844			if (val)
4845				LINE_CHECK("meshgate");
4846			else
4847				LINE_CHECK("-meshgate");
4848		}
4849		if (get80211len(s, IEEE80211_IOC_MESH_PR_METRIC, data, 12,
4850		    &len) != -1) {
4851			data[len] = '\0';
4852			LINE_CHECK("meshmetric %s", data);
4853		}
4854		if (get80211len(s, IEEE80211_IOC_MESH_PR_PATH, data, 12,
4855		    &len) != -1) {
4856			data[len] = '\0';
4857			LINE_CHECK("meshpath %s", data);
4858		}
4859		if (get80211val(s, IEEE80211_IOC_HWMP_ROOTMODE, &val) != -1) {
4860			switch (val) {
4861			case IEEE80211_HWMP_ROOTMODE_DISABLED:
4862				LINE_CHECK("hwmprootmode DISABLED");
4863				break;
4864			case IEEE80211_HWMP_ROOTMODE_NORMAL:
4865				LINE_CHECK("hwmprootmode NORMAL");
4866				break;
4867			case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
4868				LINE_CHECK("hwmprootmode PROACTIVE");
4869				break;
4870			case IEEE80211_HWMP_ROOTMODE_RANN:
4871				LINE_CHECK("hwmprootmode RANN");
4872				break;
4873			default:
4874				LINE_CHECK("hwmprootmode UNKNOWN(%d)", val);
4875				break;
4876			}
4877		}
4878		if (get80211val(s, IEEE80211_IOC_HWMP_MAXHOPS, &val) != -1) {
4879			LINE_CHECK("hwmpmaxhops %u", val);
4880		}
4881	}
4882
4883	LINE_BREAK();
4884}
4885
4886static int
4887get80211(int s, int type, void *data, int len)
4888{
4889	struct ieee80211req ireq;
4890
4891	(void) memset(&ireq, 0, sizeof(ireq));
4892	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4893	ireq.i_type = type;
4894	ireq.i_data = data;
4895	ireq.i_len = len;
4896	return ioctl(s, SIOCG80211, &ireq);
4897}
4898
4899static int
4900get80211len(int s, int type, void *data, int len, int *plen)
4901{
4902	struct ieee80211req ireq;
4903
4904	(void) memset(&ireq, 0, sizeof(ireq));
4905	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4906	ireq.i_type = type;
4907	ireq.i_len = len;
4908	assert(ireq.i_len == len);	/* NB: check for 16-bit truncation */
4909	ireq.i_data = data;
4910	if (ioctl(s, SIOCG80211, &ireq) < 0)
4911		return -1;
4912	*plen = ireq.i_len;
4913	return 0;
4914}
4915
4916static int
4917get80211val(int s, int type, int *val)
4918{
4919	struct ieee80211req ireq;
4920
4921	(void) memset(&ireq, 0, sizeof(ireq));
4922	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4923	ireq.i_type = type;
4924	if (ioctl(s, SIOCG80211, &ireq) < 0)
4925		return -1;
4926	*val = ireq.i_val;
4927	return 0;
4928}
4929
4930static void
4931set80211(int s, int type, int val, int len, void *data)
4932{
4933	struct ieee80211req	ireq;
4934
4935	(void) memset(&ireq, 0, sizeof(ireq));
4936	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4937	ireq.i_type = type;
4938	ireq.i_val = val;
4939	ireq.i_len = len;
4940	assert(ireq.i_len == len);	/* NB: check for 16-bit truncation */
4941	ireq.i_data = data;
4942	if (ioctl(s, SIOCS80211, &ireq) < 0)
4943		err(1, "SIOCS80211");
4944}
4945
4946static const char *
4947get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
4948{
4949	int len;
4950	int hexstr;
4951	u_int8_t *p;
4952
4953	len = *lenp;
4954	p = buf;
4955	hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
4956	if (hexstr)
4957		val += 2;
4958	for (;;) {
4959		if (*val == '\0')
4960			break;
4961		if (sep != NULL && strchr(sep, *val) != NULL) {
4962			val++;
4963			break;
4964		}
4965		if (hexstr) {
4966			if (!isxdigit((u_char)val[0])) {
4967				warnx("bad hexadecimal digits");
4968				return NULL;
4969			}
4970			if (!isxdigit((u_char)val[1])) {
4971				warnx("odd count hexadecimal digits");
4972				return NULL;
4973			}
4974		}
4975		if (p >= buf + len) {
4976			if (hexstr)
4977				warnx("hexadecimal digits too long");
4978			else
4979				warnx("string too long");
4980			return NULL;
4981		}
4982		if (hexstr) {
4983#define	tohex(x)	(isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
4984			*p++ = (tohex((u_char)val[0]) << 4) |
4985			    tohex((u_char)val[1]);
4986#undef tohex
4987			val += 2;
4988		} else
4989			*p++ = *val++;
4990	}
4991	len = p - buf;
4992	/* The string "-" is treated as the empty string. */
4993	if (!hexstr && len == 1 && buf[0] == '-') {
4994		len = 0;
4995		memset(buf, 0, *lenp);
4996	} else if (len < *lenp)
4997		memset(p, 0, *lenp - len);
4998	*lenp = len;
4999	return val;
5000}
5001
5002static void
5003print_string(const u_int8_t *buf, int len)
5004{
5005	int i;
5006	int hasspc;
5007
5008	i = 0;
5009	hasspc = 0;
5010	for (; i < len; i++) {
5011		if (!isprint(buf[i]) && buf[i] != '\0')
5012			break;
5013		if (isspace(buf[i]))
5014			hasspc++;
5015	}
5016	if (i == len) {
5017		if (hasspc || len == 0 || buf[0] == '\0')
5018			printf("\"%.*s\"", len, buf);
5019		else
5020			printf("%.*s", len, buf);
5021	} else {
5022		printf("0x");
5023		for (i = 0; i < len; i++)
5024			printf("%02x", buf[i]);
5025	}
5026}
5027
5028/*
5029 * Virtual AP cloning support.
5030 */
5031static struct ieee80211_clone_params params = {
5032	.icp_opmode	= IEEE80211_M_STA,	/* default to station mode */
5033};
5034
5035static void
5036wlan_create(int s, struct ifreq *ifr)
5037{
5038	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
5039
5040	if (params.icp_parent[0] == '\0')
5041		errx(1, "must specify a parent device (wlandev) when creating "
5042		    "a wlan device");
5043	if (params.icp_opmode == IEEE80211_M_WDS &&
5044	    memcmp(params.icp_bssid, zerobssid, sizeof(zerobssid)) == 0)
5045		errx(1, "no bssid specified for WDS (use wlanbssid)");
5046	ifr->ifr_data = (caddr_t) &params;
5047	if (ioctl(s, SIOCIFCREATE2, ifr) < 0)
5048		err(1, "SIOCIFCREATE2");
5049}
5050
5051static
5052DECL_CMD_FUNC(set80211clone_wlandev, arg, d)
5053{
5054	strlcpy(params.icp_parent, arg, IFNAMSIZ);
5055}
5056
5057static
5058DECL_CMD_FUNC(set80211clone_wlanbssid, arg, d)
5059{
5060	const struct ether_addr *ea;
5061
5062	ea = ether_aton(arg);
5063	if (ea == NULL)
5064		errx(1, "%s: cannot parse bssid", arg);
5065	memcpy(params.icp_bssid, ea->octet, IEEE80211_ADDR_LEN);
5066}
5067
5068static
5069DECL_CMD_FUNC(set80211clone_wlanaddr, arg, d)
5070{
5071	const struct ether_addr *ea;
5072
5073	ea = ether_aton(arg);
5074	if (ea == NULL)
5075		errx(1, "%s: cannot parse address", arg);
5076	memcpy(params.icp_macaddr, ea->octet, IEEE80211_ADDR_LEN);
5077	params.icp_flags |= IEEE80211_CLONE_MACADDR;
5078}
5079
5080static
5081DECL_CMD_FUNC(set80211clone_wlanmode, arg, d)
5082{
5083#define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
5084	if (iseq(arg, "sta"))
5085		params.icp_opmode = IEEE80211_M_STA;
5086	else if (iseq(arg, "ahdemo") || iseq(arg, "adhoc-demo"))
5087		params.icp_opmode = IEEE80211_M_AHDEMO;
5088	else if (iseq(arg, "ibss") || iseq(arg, "adhoc"))
5089		params.icp_opmode = IEEE80211_M_IBSS;
5090	else if (iseq(arg, "ap") || iseq(arg, "host"))
5091		params.icp_opmode = IEEE80211_M_HOSTAP;
5092	else if (iseq(arg, "wds"))
5093		params.icp_opmode = IEEE80211_M_WDS;
5094	else if (iseq(arg, "monitor"))
5095		params.icp_opmode = IEEE80211_M_MONITOR;
5096	else if (iseq(arg, "tdma")) {
5097		params.icp_opmode = IEEE80211_M_AHDEMO;
5098		params.icp_flags |= IEEE80211_CLONE_TDMA;
5099	} else if (iseq(arg, "mesh") || iseq(arg, "mp")) /* mesh point */
5100		params.icp_opmode = IEEE80211_M_MBSS;
5101	else
5102		errx(1, "Don't know to create %s for %s", arg, name);
5103#undef iseq
5104}
5105
5106static void
5107set80211clone_beacons(const char *val, int d, int s, const struct afswtch *rafp)
5108{
5109	/* NB: inverted sense */
5110	if (d)
5111		params.icp_flags &= ~IEEE80211_CLONE_NOBEACONS;
5112	else
5113		params.icp_flags |= IEEE80211_CLONE_NOBEACONS;
5114}
5115
5116static void
5117set80211clone_bssid(const char *val, int d, int s, const struct afswtch *rafp)
5118{
5119	if (d)
5120		params.icp_flags |= IEEE80211_CLONE_BSSID;
5121	else
5122		params.icp_flags &= ~IEEE80211_CLONE_BSSID;
5123}
5124
5125static void
5126set80211clone_wdslegacy(const char *val, int d, int s, const struct afswtch *rafp)
5127{
5128	if (d)
5129		params.icp_flags |= IEEE80211_CLONE_WDSLEGACY;
5130	else
5131		params.icp_flags &= ~IEEE80211_CLONE_WDSLEGACY;
5132}
5133
5134static struct cmd ieee80211_cmds[] = {
5135	DEF_CMD_ARG("ssid",		set80211ssid),
5136	DEF_CMD_ARG("nwid",		set80211ssid),
5137	DEF_CMD_ARG("meshid",		set80211meshid),
5138	DEF_CMD_ARG("stationname",	set80211stationname),
5139	DEF_CMD_ARG("station",		set80211stationname),	/* BSD/OS */
5140	DEF_CMD_ARG("channel",		set80211channel),
5141	DEF_CMD_ARG("authmode",		set80211authmode),
5142	DEF_CMD_ARG("powersavemode",	set80211powersavemode),
5143	DEF_CMD("powersave",	1,	set80211powersave),
5144	DEF_CMD("-powersave",	0,	set80211powersave),
5145	DEF_CMD_ARG("powersavesleep", 	set80211powersavesleep),
5146	DEF_CMD_ARG("wepmode",		set80211wepmode),
5147	DEF_CMD("wep",		1,	set80211wep),
5148	DEF_CMD("-wep",		0,	set80211wep),
5149	DEF_CMD_ARG("deftxkey",		set80211weptxkey),
5150	DEF_CMD_ARG("weptxkey",		set80211weptxkey),
5151	DEF_CMD_ARG("wepkey",		set80211wepkey),
5152	DEF_CMD_ARG("nwkey",		set80211nwkey),		/* NetBSD */
5153	DEF_CMD("-nwkey",	0,	set80211wep),		/* NetBSD */
5154	DEF_CMD_ARG("rtsthreshold",	set80211rtsthreshold),
5155	DEF_CMD_ARG("protmode",		set80211protmode),
5156	DEF_CMD_ARG("txpower",		set80211txpower),
5157	DEF_CMD_ARG("roaming",		set80211roaming),
5158	DEF_CMD("wme",		1,	set80211wme),
5159	DEF_CMD("-wme",		0,	set80211wme),
5160	DEF_CMD("wmm",		1,	set80211wme),
5161	DEF_CMD("-wmm",		0,	set80211wme),
5162	DEF_CMD("hidessid",	1,	set80211hidessid),
5163	DEF_CMD("-hidessid",	0,	set80211hidessid),
5164	DEF_CMD("apbridge",	1,	set80211apbridge),
5165	DEF_CMD("-apbridge",	0,	set80211apbridge),
5166	DEF_CMD_ARG("chanlist",		set80211chanlist),
5167	DEF_CMD_ARG("bssid",		set80211bssid),
5168	DEF_CMD_ARG("ap",		set80211bssid),
5169	DEF_CMD("scan",	0,		set80211scan),
5170	DEF_CMD_ARG("list",		set80211list),
5171	DEF_CMD_ARG2("cwmin",		set80211cwmin),
5172	DEF_CMD_ARG2("cwmax",		set80211cwmax),
5173	DEF_CMD_ARG2("aifs",		set80211aifs),
5174	DEF_CMD_ARG2("txoplimit",	set80211txoplimit),
5175	DEF_CMD_ARG("acm",		set80211acm),
5176	DEF_CMD_ARG("-acm",		set80211noacm),
5177	DEF_CMD_ARG("ack",		set80211ackpolicy),
5178	DEF_CMD_ARG("-ack",		set80211noackpolicy),
5179	DEF_CMD_ARG2("bss:cwmin",	set80211bsscwmin),
5180	DEF_CMD_ARG2("bss:cwmax",	set80211bsscwmax),
5181	DEF_CMD_ARG2("bss:aifs",	set80211bssaifs),
5182	DEF_CMD_ARG2("bss:txoplimit",	set80211bsstxoplimit),
5183	DEF_CMD_ARG("dtimperiod",	set80211dtimperiod),
5184	DEF_CMD_ARG("bintval",		set80211bintval),
5185	DEF_CMD("mac:open",	IEEE80211_MACCMD_POLICY_OPEN,	set80211maccmd),
5186	DEF_CMD("mac:allow",	IEEE80211_MACCMD_POLICY_ALLOW,	set80211maccmd),
5187	DEF_CMD("mac:deny",	IEEE80211_MACCMD_POLICY_DENY,	set80211maccmd),
5188	DEF_CMD("mac:radius",	IEEE80211_MACCMD_POLICY_RADIUS,	set80211maccmd),
5189	DEF_CMD("mac:flush",	IEEE80211_MACCMD_FLUSH,		set80211maccmd),
5190	DEF_CMD("mac:detach",	IEEE80211_MACCMD_DETACH,	set80211maccmd),
5191	DEF_CMD_ARG("mac:add",		set80211addmac),
5192	DEF_CMD_ARG("mac:del",		set80211delmac),
5193	DEF_CMD_ARG("mac:kick",		set80211kickmac),
5194	DEF_CMD("pureg",	1,	set80211pureg),
5195	DEF_CMD("-pureg",	0,	set80211pureg),
5196	DEF_CMD("ff",		1,	set80211fastframes),
5197	DEF_CMD("-ff",		0,	set80211fastframes),
5198	DEF_CMD("dturbo",	1,	set80211dturbo),
5199	DEF_CMD("-dturbo",	0,	set80211dturbo),
5200	DEF_CMD("bgscan",	1,	set80211bgscan),
5201	DEF_CMD("-bgscan",	0,	set80211bgscan),
5202	DEF_CMD_ARG("bgscanidle",	set80211bgscanidle),
5203	DEF_CMD_ARG("bgscanintvl",	set80211bgscanintvl),
5204	DEF_CMD_ARG("scanvalid",	set80211scanvalid),
5205	DEF_CMD("quiet",        1,      set80211quiet),
5206	DEF_CMD("-quiet",       0,      set80211quiet),
5207	DEF_CMD_ARG("quiet_count",      set80211quietcount),
5208	DEF_CMD_ARG("quiet_period",     set80211quietperiod),
5209	DEF_CMD_ARG("quiet_dur",        set80211quietduration),
5210	DEF_CMD_ARG("quiet_offset",     set80211quietoffset),
5211	DEF_CMD_ARG("roam:rssi",	set80211roamrssi),
5212	DEF_CMD_ARG("roam:rate",	set80211roamrate),
5213	DEF_CMD_ARG("mcastrate",	set80211mcastrate),
5214	DEF_CMD_ARG("ucastrate",	set80211ucastrate),
5215	DEF_CMD_ARG("mgtrate",		set80211mgtrate),
5216	DEF_CMD_ARG("mgmtrate",		set80211mgtrate),
5217	DEF_CMD_ARG("maxretry",		set80211maxretry),
5218	DEF_CMD_ARG("fragthreshold",	set80211fragthreshold),
5219	DEF_CMD("burst",	1,	set80211burst),
5220	DEF_CMD("-burst",	0,	set80211burst),
5221	DEF_CMD_ARG("bmiss",		set80211bmissthreshold),
5222	DEF_CMD_ARG("bmissthreshold",	set80211bmissthreshold),
5223	DEF_CMD("shortgi",	1,	set80211shortgi),
5224	DEF_CMD("-shortgi",	0,	set80211shortgi),
5225	DEF_CMD("ampdurx",	2,	set80211ampdu),
5226	DEF_CMD("-ampdurx",	-2,	set80211ampdu),
5227	DEF_CMD("ampdutx",	1,	set80211ampdu),
5228	DEF_CMD("-ampdutx",	-1,	set80211ampdu),
5229	DEF_CMD("ampdu",	3,	set80211ampdu),		/* NB: tx+rx */
5230	DEF_CMD("-ampdu",	-3,	set80211ampdu),
5231	DEF_CMD_ARG("ampdulimit",	set80211ampdulimit),
5232	DEF_CMD_ARG("ampdudensity",	set80211ampdudensity),
5233	DEF_CMD("amsdurx",	2,	set80211amsdu),
5234	DEF_CMD("-amsdurx",	-2,	set80211amsdu),
5235	DEF_CMD("amsdutx",	1,	set80211amsdu),
5236	DEF_CMD("-amsdutx",	-1,	set80211amsdu),
5237	DEF_CMD("amsdu",	3,	set80211amsdu),		/* NB: tx+rx */
5238	DEF_CMD("-amsdu",	-3,	set80211amsdu),
5239	DEF_CMD_ARG("amsdulimit",	set80211amsdulimit),
5240	DEF_CMD("puren",	1,	set80211puren),
5241	DEF_CMD("-puren",	0,	set80211puren),
5242	DEF_CMD("doth",		1,	set80211doth),
5243	DEF_CMD("-doth",	0,	set80211doth),
5244	DEF_CMD("dfs",		1,	set80211dfs),
5245	DEF_CMD("-dfs",		0,	set80211dfs),
5246	DEF_CMD("htcompat",	1,	set80211htcompat),
5247	DEF_CMD("-htcompat",	0,	set80211htcompat),
5248	DEF_CMD("dwds",		1,	set80211dwds),
5249	DEF_CMD("-dwds",	0,	set80211dwds),
5250	DEF_CMD("inact",	1,	set80211inact),
5251	DEF_CMD("-inact",	0,	set80211inact),
5252	DEF_CMD("tsn",		1,	set80211tsn),
5253	DEF_CMD("-tsn",		0,	set80211tsn),
5254	DEF_CMD_ARG("regdomain",	set80211regdomain),
5255	DEF_CMD_ARG("country",		set80211country),
5256	DEF_CMD("indoor",	'I',	set80211location),
5257	DEF_CMD("-indoor",	'O',	set80211location),
5258	DEF_CMD("outdoor",	'O',	set80211location),
5259	DEF_CMD("-outdoor",	'I',	set80211location),
5260	DEF_CMD("anywhere",	' ',	set80211location),
5261	DEF_CMD("ecm",		1,	set80211ecm),
5262	DEF_CMD("-ecm",		0,	set80211ecm),
5263	DEF_CMD("dotd",		1,	set80211dotd),
5264	DEF_CMD("-dotd",	0,	set80211dotd),
5265	DEF_CMD_ARG("htprotmode",	set80211htprotmode),
5266	DEF_CMD("ht20",		1,	set80211htconf),
5267	DEF_CMD("-ht20",	0,	set80211htconf),
5268	DEF_CMD("ht40",		3,	set80211htconf),	/* NB: 20+40 */
5269	DEF_CMD("-ht40",	0,	set80211htconf),
5270	DEF_CMD("ht",		3,	set80211htconf),	/* NB: 20+40 */
5271	DEF_CMD("-ht",		0,	set80211htconf),
5272	DEF_CMD("rifs",		1,	set80211rifs),
5273	DEF_CMD("-rifs",	0,	set80211rifs),
5274	DEF_CMD("smps",		IEEE80211_HTCAP_SMPS_ENA,	set80211smps),
5275	DEF_CMD("smpsdyn",	IEEE80211_HTCAP_SMPS_DYNAMIC,	set80211smps),
5276	DEF_CMD("-smps",	IEEE80211_HTCAP_SMPS_OFF,	set80211smps),
5277	/* XXX for testing */
5278	DEF_CMD_ARG("chanswitch",	set80211chanswitch),
5279
5280	DEF_CMD_ARG("tdmaslot",		set80211tdmaslot),
5281	DEF_CMD_ARG("tdmaslotcnt",	set80211tdmaslotcnt),
5282	DEF_CMD_ARG("tdmaslotlen",	set80211tdmaslotlen),
5283	DEF_CMD_ARG("tdmabintval",	set80211tdmabintval),
5284
5285	DEF_CMD_ARG("meshttl",		set80211meshttl),
5286	DEF_CMD("meshforward",	1,	set80211meshforward),
5287	DEF_CMD("-meshforward",	0,	set80211meshforward),
5288	DEF_CMD("meshgate",	1,	set80211meshgate),
5289	DEF_CMD("-meshgate",	0,	set80211meshgate),
5290	DEF_CMD("meshpeering",	1,	set80211meshpeering),
5291	DEF_CMD("-meshpeering",	0,	set80211meshpeering),
5292	DEF_CMD_ARG("meshmetric",	set80211meshmetric),
5293	DEF_CMD_ARG("meshpath",		set80211meshpath),
5294	DEF_CMD("meshrt:flush",	IEEE80211_MESH_RTCMD_FLUSH,	set80211meshrtcmd),
5295	DEF_CMD_ARG("meshrt:add",	set80211addmeshrt),
5296	DEF_CMD_ARG("meshrt:del",	set80211delmeshrt),
5297	DEF_CMD_ARG("hwmprootmode",	set80211hwmprootmode),
5298	DEF_CMD_ARG("hwmpmaxhops",	set80211hwmpmaxhops),
5299
5300	/* vap cloning support */
5301	DEF_CLONE_CMD_ARG("wlanaddr",	set80211clone_wlanaddr),
5302	DEF_CLONE_CMD_ARG("wlanbssid",	set80211clone_wlanbssid),
5303	DEF_CLONE_CMD_ARG("wlandev",	set80211clone_wlandev),
5304	DEF_CLONE_CMD_ARG("wlanmode",	set80211clone_wlanmode),
5305	DEF_CLONE_CMD("beacons", 1,	set80211clone_beacons),
5306	DEF_CLONE_CMD("-beacons", 0,	set80211clone_beacons),
5307	DEF_CLONE_CMD("bssid",	1,	set80211clone_bssid),
5308	DEF_CLONE_CMD("-bssid",	0,	set80211clone_bssid),
5309	DEF_CLONE_CMD("wdslegacy", 1,	set80211clone_wdslegacy),
5310	DEF_CLONE_CMD("-wdslegacy", 0,	set80211clone_wdslegacy),
5311};
5312static struct afswtch af_ieee80211 = {
5313	.af_name	= "af_ieee80211",
5314	.af_af		= AF_UNSPEC,
5315	.af_other_status = ieee80211_status,
5316};
5317
5318static __constructor void
5319ieee80211_ctor(void)
5320{
5321#define	N(a)	(sizeof(a) / sizeof(a[0]))
5322	int i;
5323
5324	for (i = 0; i < N(ieee80211_cmds);  i++)
5325		cmd_register(&ieee80211_cmds[i]);
5326	af_register(&af_ieee80211);
5327	clone_setdefcallback("wlan", wlan_create);
5328#undef N
5329}
5330