ifieee80211.c revision 175952
177218Sphk/*
277218Sphk * Copyright 2001 The Aerospace Corporation.  All rights reserved.
377218Sphk *
477218Sphk * Redistribution and use in source and binary forms, with or without
577218Sphk * modification, are permitted provided that the following conditions
677218Sphk * are met:
777218Sphk * 1. Redistributions of source code must retain the above copyright
877218Sphk *    notice, this list of conditions and the following disclaimer.
977218Sphk * 2. Redistributions in binary form must reproduce the above copyright
1077218Sphk *    notice, this list of conditions and the following disclaimer in the
1177218Sphk *    documentation and/or other materials provided with the distribution.
1291454Sbrooks * 3. The name of The Aerospace Corporation may not be used to endorse or
1391454Sbrooks *    promote products derived from this software.
1477218Sphk *
1577218Sphk * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION ``AS IS'' AND
1677218Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1777218Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1877218Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AEROSPACE CORPORATION BE LIABLE
1977218Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2077218Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2177218Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2277218Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2377218Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2477218Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2577218Sphk * SUCH DAMAGE.
2677218Sphk *
2777218Sphk * $FreeBSD: head/sbin/ifconfig/ifieee80211.c 175952 2008-02-03 18:21:13Z sam $
2877218Sphk */
2977218Sphk
3077218Sphk/*-
3177218Sphk * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
3277218Sphk * All rights reserved.
3377218Sphk *
3477218Sphk * This code is derived from software contributed to The NetBSD Foundation
3577218Sphk * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
3677218Sphk * NASA Ames Research Center.
3777218Sphk *
3877218Sphk * Redistribution and use in source and binary forms, with or without
3977218Sphk * modification, are permitted provided that the following conditions
4077218Sphk * are met:
4177218Sphk * 1. Redistributions of source code must retain the above copyright
4277218Sphk *    notice, this list of conditions and the following disclaimer.
4377218Sphk * 2. Redistributions in binary form must reproduce the above copyright
4477218Sphk *    notice, this list of conditions and the following disclaimer in the
4577218Sphk *    documentation and/or other materials provided with the distribution.
4677218Sphk * 3. All advertising materials mentioning features or use of this software
4777218Sphk *    must display the following acknowledgement:
4877218Sphk *	This product includes software developed by the NetBSD
4977218Sphk *	Foundation, Inc. and its contributors.
5077218Sphk * 4. Neither the name of The NetBSD Foundation nor the names of its
5177218Sphk *    contributors may be used to endorse or promote products derived
5277218Sphk *    from this software without specific prior written permission.
5377218Sphk *
5477218Sphk * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
5577218Sphk * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
5677218Sphk * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
5777218Sphk * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
5877218Sphk * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5977218Sphk * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
6077218Sphk * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
6177218Sphk * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
6277218Sphk * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
6377218Sphk * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
6477218Sphk * POSSIBILITY OF SUCH DAMAGE.
6577218Sphk */
6677218Sphk
6777218Sphk#include <sys/param.h>
6877218Sphk#include <sys/ioctl.h>
6977218Sphk#include <sys/socket.h>
7077218Sphk#include <sys/sysctl.h>
7177218Sphk#include <sys/time.h>
7277218Sphk
7377218Sphk#include <net/ethernet.h>
7477218Sphk#include <net/if.h>
7577218Sphk#include <net/if_dl.h>
7677218Sphk#include <net/if_types.h>
77138593Ssam#include <net/if_media.h>
7877218Sphk#include <net/route.h>
79138593Ssam
80116957Ssam#include <net80211/ieee80211.h>
81120178Ssam#include <net80211/ieee80211_crypto.h>
82116957Ssam#include <net80211/ieee80211_ioctl.h>
8377218Sphk
8477218Sphk#include <ctype.h>
8577218Sphk#include <err.h>
8677218Sphk#include <errno.h>
8777218Sphk#include <fcntl.h>
88146873Sjhb#include <inttypes.h>
8977218Sphk#include <stdio.h>
9077218Sphk#include <stdlib.h>
9177218Sphk#include <string.h>
9277218Sphk#include <unistd.h>
93155931Ssam#include <stdarg.h>
94173275Ssam#include <stddef.h>		/* NB: for offsetof */
9577218Sphk
9677218Sphk#include "ifconfig.h"
9777218Sphk
98173275Ssam#define	MAXCOL	78
99173275Ssamstatic	int col;
100173275Ssamstatic	char spacer;
101173275Ssam
102173275Ssamstatic void LINE_INIT(char c);
103173275Ssamstatic void LINE_BREAK(void);
104173275Ssamstatic void LINE_CHECK(const char *fmt, ...);
105173275Ssam
106173275Ssam/* XXX need max array size */
107173275Ssamstatic const int htrates[16] = {
108173275Ssam	13,		/* IFM_IEEE80211_MCS0 */
109173275Ssam	26,		/* IFM_IEEE80211_MCS1 */
110173275Ssam	39,		/* IFM_IEEE80211_MCS2 */
111173275Ssam	52,		/* IFM_IEEE80211_MCS3 */
112173275Ssam	78,		/* IFM_IEEE80211_MCS4 */
113173275Ssam	104,		/* IFM_IEEE80211_MCS5 */
114173275Ssam	117,		/* IFM_IEEE80211_MCS6 */
115173275Ssam	130,		/* IFM_IEEE80211_MCS7 */
116173275Ssam	26,		/* IFM_IEEE80211_MCS8 */
117173275Ssam	52,		/* IFM_IEEE80211_MCS9 */
118173275Ssam	78,		/* IFM_IEEE80211_MCS10 */
119173275Ssam	104,		/* IFM_IEEE80211_MCS11 */
120173275Ssam	156,		/* IFM_IEEE80211_MCS12 */
121173275Ssam	208,		/* IFM_IEEE80211_MCS13 */
122173275Ssam	234,		/* IFM_IEEE80211_MCS14 */
123173275Ssam	260,		/* IFM_IEEE80211_MCS15 */
124173275Ssam};
125173275Ssam
126173275Ssamstatic int get80211(int s, int type, void *data, int len);
127173275Ssamstatic int get80211len(int s, int type, void *data, int len, int *plen);
128173275Ssamstatic int get80211val(int s, int type, int *val);
129170531Ssamstatic void set80211(int s, int type, int val, int len, void *data);
13077218Sphkstatic const char *get_string(const char *val, const char *sep,
13177218Sphk    u_int8_t *buf, int *lenp);
13277218Sphkstatic void print_string(const u_int8_t *buf, int len);
13377218Sphk
134170531Ssamstatic struct ieee80211req_chaninfo chaninfo;
135170531Ssamstatic struct ifmediareq *ifmr;
136173275Ssamstatic struct ieee80211_channel curchan;
137173275Ssamstatic int gotcurchan = 0;
138173275Ssamstatic int htconf = 0;
139173275Ssamstatic	int gothtconf = 0;
140170531Ssam
141173275Ssamstatic void
142173275Ssamgethtconf(int s)
143173275Ssam{
144173275Ssam	if (gothtconf)
145173275Ssam		return;
146173275Ssam	if (get80211val(s, IEEE80211_IOC_HTCONF, &htconf) < 0)
147173275Ssam		warn("unable to get HT configuration information");
148173275Ssam	gothtconf = 1;
149173275Ssam}
150173275Ssam
151170531Ssam/*
152170531Ssam * Collect channel info from the kernel.  We use this (mostly)
153170531Ssam * to handle mapping between frequency and IEEE channel number.
154170531Ssam */
155170531Ssamstatic void
156170531Ssamgetchaninfo(int s)
157170531Ssam{
158170531Ssam	if (chaninfo.ic_nchans != 0)
159170531Ssam		return;
160173275Ssam	if (get80211(s, IEEE80211_IOC_CHANINFO, &chaninfo, sizeof(chaninfo)) < 0)
161170531Ssam		errx(1, "unable to get channel information");
162170531Ssam
163170531Ssam	ifmr = ifmedia_getstate(s);
164173275Ssam	gethtconf(s);
165170531Ssam}
166170531Ssam
167170531Ssam/*
168170531Ssam * Given the channel at index i with attributes from,
169170531Ssam * check if there is a channel with attributes to in
170170531Ssam * the channel table.  With suitable attributes this
171170531Ssam * allows the caller to look for promotion; e.g. from
172170531Ssam * 11b > 11g.
173170531Ssam */
174138593Ssamstatic int
175170531Ssamcanpromote(int i, int from, int to)
176170531Ssam{
177170531Ssam	const struct ieee80211_channel *fc = &chaninfo.ic_chans[i];
178170531Ssam	int j;
179170531Ssam
180170531Ssam	if ((fc->ic_flags & from) != from)
181170531Ssam		return i;
182170531Ssam	/* NB: quick check exploiting ordering of chans w/ same frequency */
183170531Ssam	if (i+1 < chaninfo.ic_nchans &&
184170531Ssam	    chaninfo.ic_chans[i+1].ic_freq == fc->ic_freq &&
185170531Ssam	    (chaninfo.ic_chans[i+1].ic_flags & to) == to)
186170531Ssam		return i+1;
187170531Ssam	/* brute force search in case channel list is not ordered */
188170531Ssam	for (j = 0; j < chaninfo.ic_nchans; j++) {
189170531Ssam		const struct ieee80211_channel *tc = &chaninfo.ic_chans[j];
190170531Ssam		if (j != i &&
191170531Ssam		    tc->ic_freq == fc->ic_freq && (tc->ic_flags & to) == to)
192170531Ssam		return j;
193170531Ssam	}
194170531Ssam	return i;
195170531Ssam}
196170531Ssam
197170531Ssam/*
198170531Ssam * Handle channel promotion.  When a channel is specified with
199170531Ssam * only a frequency we want to promote it to the ``best'' channel
200170531Ssam * available.  The channel list has separate entries for 11b, 11g,
201170531Ssam * 11a, and 11n[ga] channels so specifying a frequency w/o any
202170531Ssam * attributes requires we upgrade, e.g. from 11b -> 11g.  This
203170531Ssam * gets complicated when the channel is specified on the same
204170531Ssam * command line with a media request that constrains the available
205170531Ssam * channe list (e.g. mode 11a); we want to honor that to avoid
206170531Ssam * confusing behaviour.
207170531Ssam */
208170531Ssamstatic int
209170531Ssampromote(int i)
210170531Ssam{
211170531Ssam	/*
212170531Ssam	 * Query the current mode of the interface in case it's
213170531Ssam	 * constrained (e.g. to 11a).  We must do this carefully
214170531Ssam	 * as there may be a pending ifmedia request in which case
215170531Ssam	 * asking the kernel will give us the wrong answer.  This
216170531Ssam	 * is an unfortunate side-effect of the way ifconfig is
217170531Ssam	 * structure for modularity (yech).
218170531Ssam	 *
219170531Ssam	 * NB: ifmr is actually setup in getchaninfo (above); we
220170531Ssam	 *     assume it's called coincident with to this call so
221170531Ssam	 *     we have a ``current setting''; otherwise we must pass
222170531Ssam	 *     the socket descriptor down to here so we can make
223170531Ssam	 *     the ifmedia_getstate call ourselves.
224170531Ssam	 */
225170531Ssam	int chanmode = ifmr != NULL ? IFM_MODE(ifmr->ifm_current) : IFM_AUTO;
226170531Ssam
227170531Ssam	/* when ambiguous promote to ``best'' */
228170531Ssam	/* NB: we abitrarily pick HT40+ over HT40- */
229170531Ssam	if (chanmode != IFM_IEEE80211_11B)
230170531Ssam		i = canpromote(i, IEEE80211_CHAN_B, IEEE80211_CHAN_G);
231173275Ssam	if (chanmode != IFM_IEEE80211_11G && (htconf & 1)) {
232170531Ssam		i = canpromote(i, IEEE80211_CHAN_G,
233170531Ssam			IEEE80211_CHAN_G | IEEE80211_CHAN_HT20);
234173275Ssam		if (htconf & 2) {
235173275Ssam			i = canpromote(i, IEEE80211_CHAN_G,
236173275Ssam				IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D);
237173275Ssam			i = canpromote(i, IEEE80211_CHAN_G,
238173275Ssam				IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U);
239173275Ssam		}
240170531Ssam	}
241173275Ssam	if (chanmode != IFM_IEEE80211_11A && (htconf & 1)) {
242170531Ssam		i = canpromote(i, IEEE80211_CHAN_A,
243170531Ssam			IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
244173275Ssam		if (htconf & 2) {
245173275Ssam			i = canpromote(i, IEEE80211_CHAN_A,
246173275Ssam				IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
247173275Ssam			i = canpromote(i, IEEE80211_CHAN_A,
248173275Ssam				IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
249173275Ssam		}
250170531Ssam	}
251170531Ssam	return i;
252170531Ssam}
253170531Ssam
254170531Ssamstatic void
255170531Ssammapfreq(struct ieee80211_channel *chan, int freq, int flags)
256170531Ssam{
257170531Ssam	int i;
258170531Ssam
259170531Ssam	for (i = 0; i < chaninfo.ic_nchans; i++) {
260170531Ssam		const struct ieee80211_channel *c = &chaninfo.ic_chans[i];
261170531Ssam
262170531Ssam		if (c->ic_freq == freq && (c->ic_flags & flags) == flags) {
263170531Ssam			if (flags == 0) {
264170531Ssam				/* when ambiguous promote to ``best'' */
265170531Ssam				c = &chaninfo.ic_chans[promote(i)];
266170531Ssam			}
267170531Ssam			*chan = *c;
268170531Ssam			return;
269170531Ssam		}
270170531Ssam	}
271170531Ssam	errx(1, "unknown/undefined frequency %u/0x%x", freq, flags);
272170531Ssam}
273170531Ssam
274170531Ssamstatic void
275170531Ssammapchan(struct ieee80211_channel *chan, int ieee, int flags)
276170531Ssam{
277170531Ssam	int i;
278170531Ssam
279170531Ssam	for (i = 0; i < chaninfo.ic_nchans; i++) {
280170531Ssam		const struct ieee80211_channel *c = &chaninfo.ic_chans[i];
281170531Ssam
282170531Ssam		if (c->ic_ieee == ieee && (c->ic_flags & flags) == flags) {
283170531Ssam			if (flags == 0) {
284170531Ssam				/* when ambiguous promote to ``best'' */
285170531Ssam				c = &chaninfo.ic_chans[promote(i)];
286170531Ssam			}
287170531Ssam			*chan = *c;
288170531Ssam			return;
289170531Ssam		}
290170531Ssam	}
291173275Ssam	errx(1, "unknown/undefined channel number %d flags 0x%x", ieee, flags);
292170531Ssam}
293170531Ssam
294173275Ssamstatic const struct ieee80211_channel *
295173275Ssamgetcurchan(int s)
296173275Ssam{
297173275Ssam	if (gotcurchan)
298173275Ssam		return &curchan;
299173275Ssam	if (get80211(s, IEEE80211_IOC_CURCHAN, &curchan, sizeof(curchan)) < 0) {
300173275Ssam		int val;
301173275Ssam		/* fall back to legacy ioctl */
302173275Ssam		if (get80211val(s, IEEE80211_IOC_CHANNEL, &val) < 0)
303173275Ssam			errx(-1, "cannot figure out current channel");
304173275Ssam		getchaninfo(s);
305173275Ssam		mapchan(&curchan, val, 0);
306173275Ssam	}
307173275Ssam	gotcurchan = 1;
308173275Ssam	return &curchan;
309173275Ssam}
310173275Ssam
311170531Ssamstatic int
312170531Ssamieee80211_mhz2ieee(int freq, int flags)
313170531Ssam{
314170531Ssam	struct ieee80211_channel chan;
315170531Ssam	mapfreq(&chan, freq, flags);
316170531Ssam	return chan.ic_ieee;
317170531Ssam}
318170531Ssam
319170531Ssamstatic int
320138593Ssamisanyarg(const char *arg)
321138593Ssam{
322173275Ssam	return (strncmp(arg, "-", 1) == 0 ||
323173275Ssam	    strncasecmp(arg, "any", 3) == 0 || strncasecmp(arg, "off", 3) == 0);
324138593Ssam}
325138593Ssam
326138593Ssamstatic void
32777218Sphkset80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
32877218Sphk{
32977218Sphk	int		ssid;
33077218Sphk	int		len;
331151883Sbrooks	u_int8_t	data[IEEE80211_NWID_LEN];
33277218Sphk
33377218Sphk	ssid = 0;
334121827Sbrooks	len = strlen(val);
33588748Sambrisko	if (len > 2 && isdigit(val[0]) && val[1] == ':') {
33688748Sambrisko		ssid = atoi(val)-1;
33788748Sambrisko		val += 2;
33888748Sambrisko	}
33977218Sphk
34077218Sphk	bzero(data, sizeof(data));
34177218Sphk	len = sizeof(data);
342151883Sbrooks	if (get_string(val, NULL, data, &len) == NULL)
343151883Sbrooks		exit(1);
34477218Sphk
34577218Sphk	set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
34677218Sphk}
34777218Sphk
348138593Ssamstatic void
34977218Sphkset80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
35077218Sphk{
35177218Sphk	int			len;
35277218Sphk	u_int8_t		data[33];
35377218Sphk
35477218Sphk	bzero(data, sizeof(data));
35577218Sphk	len = sizeof(data);
35677218Sphk	get_string(val, NULL, data, &len);
35777218Sphk
35877218Sphk	set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
35977218Sphk}
36077218Sphk
361138593Ssam/*
362170531Ssam * Parse a channel specification for attributes/flags.
363170531Ssam * The syntax is:
364170531Ssam *	freq/xx		channel width (5,10,20,40,40+,40-)
365170531Ssam *	freq:mode	channel mode (a,b,g,h,n,t,s,d)
366170531Ssam *
367170531Ssam * These can be combined in either order; e.g. 2437:ng/40.
368170531Ssam * Modes are case insensitive.
369170531Ssam *
370170531Ssam * The result is not validated here; it's assumed to be
371170531Ssam * checked against the channel table fetched from the kernel.
372170531Ssam */
373170531Ssamstatic int
374173275Ssamgetchannelflags(const char *val, int freq)
375138593Ssam{
376170531Ssam#define	_CHAN_HT	0x80000000
377170531Ssam	const char *cp;
378170531Ssam	int flags;
379138593Ssam
380170531Ssam	flags = 0;
381166015Ssam
382170531Ssam	cp = strchr(val, ':');
383170531Ssam	if (cp != NULL) {
384170531Ssam		for (cp++; isalpha((int) *cp); cp++) {
385170531Ssam			/* accept mixed case */
386170531Ssam			int c = *cp;
387170531Ssam			if (isupper(c))
388170531Ssam				c = tolower(c);
389170531Ssam			switch (c) {
390170531Ssam			case 'a':		/* 802.11a */
391170531Ssam				flags |= IEEE80211_CHAN_A;
392170531Ssam				break;
393170531Ssam			case 'b':		/* 802.11b */
394170531Ssam				flags |= IEEE80211_CHAN_B;
395170531Ssam				break;
396170531Ssam			case 'g':		/* 802.11g */
397170531Ssam				flags |= IEEE80211_CHAN_G;
398170531Ssam				break;
399170531Ssam			case 'h':		/* ht = 802.11n */
400170531Ssam			case 'n':		/* 802.11n */
401170531Ssam				flags |= _CHAN_HT;	/* NB: private */
402170531Ssam				break;
403170531Ssam			case 'd':		/* dt = Atheros Dynamic Turbo */
404170531Ssam				flags |= IEEE80211_CHAN_TURBO;
405170531Ssam				break;
406170531Ssam			case 't':		/* ht, dt, st, t */
407170531Ssam				/* dt and unadorned t specify Dynamic Turbo */
408170531Ssam				if ((flags & (IEEE80211_CHAN_STURBO|_CHAN_HT)) == 0)
409170531Ssam					flags |= IEEE80211_CHAN_TURBO;
410170531Ssam				break;
411170531Ssam			case 's':		/* st = Atheros Static Turbo */
412170531Ssam				flags |= IEEE80211_CHAN_STURBO;
413170531Ssam				break;
414170531Ssam			default:
415173275Ssam				errx(-1, "%s: Invalid channel attribute %c\n",
416170531Ssam				    val, *cp);
417170531Ssam			}
418170531Ssam		}
419170531Ssam	}
420170531Ssam	cp = strchr(val, '/');
421170531Ssam	if (cp != NULL) {
422170531Ssam		char *ep;
423170531Ssam		u_long cw = strtoul(cp+1, &ep, 10);
424166015Ssam
425170531Ssam		switch (cw) {
426170531Ssam		case 5:
427170531Ssam			flags |= IEEE80211_CHAN_QUARTER;
428170531Ssam			break;
429170531Ssam		case 10:
430170531Ssam			flags |= IEEE80211_CHAN_HALF;
431170531Ssam			break;
432170531Ssam		case 20:
433170531Ssam			/* NB: this may be removed below */
434170531Ssam			flags |= IEEE80211_CHAN_HT20;
435170531Ssam			break;
436170531Ssam		case 40:
437170531Ssam			if (ep != NULL && *ep == '+')
438170531Ssam				flags |= IEEE80211_CHAN_HT40U;
439170531Ssam			else if (ep != NULL && *ep == '-')
440170531Ssam				flags |= IEEE80211_CHAN_HT40D;
441170531Ssam			break;
442170531Ssam		default:
443173275Ssam			errx(-1, "%s: Invalid channel width\n", val);
444170531Ssam		}
445165570Ssam	}
446170531Ssam	/*
447170531Ssam	 * Cleanup specifications.
448170531Ssam	 */
449170531Ssam	if ((flags & _CHAN_HT) == 0) {
450170531Ssam		/*
451170531Ssam		 * If user specified freq/20 or freq/40 quietly remove
452170531Ssam		 * HT cw attributes depending on channel use.  To give
453170531Ssam		 * an explicit 20/40 width for an HT channel you must
454170531Ssam		 * indicate it is an HT channel since all HT channels
455170531Ssam		 * are also usable for legacy operation; e.g. freq:n/40.
456170531Ssam		 */
457170531Ssam		flags &= ~IEEE80211_CHAN_HT;
458170531Ssam	} else {
459170531Ssam		/*
460170531Ssam		 * Remove private indicator that this is an HT channel
461170531Ssam		 * and if no explicit channel width has been given
462170531Ssam		 * provide the default settings.
463170531Ssam		 */
464170531Ssam		flags &= ~_CHAN_HT;
465173275Ssam		if ((flags & IEEE80211_CHAN_HT) == 0) {
466173275Ssam			struct ieee80211_channel chan;
467173275Ssam			/*
468173275Ssam			 * Consult the channel list to see if we can use
469173275Ssam			 * HT40+ or HT40- (if both the map routines choose).
470173275Ssam			 */
471173275Ssam			if (freq > 255)
472173275Ssam				mapfreq(&chan, freq, 0);
473173275Ssam			else
474173275Ssam				mapchan(&chan, freq, 0);
475173275Ssam			flags |= (chan.ic_flags & IEEE80211_CHAN_HT);
476173275Ssam		}
477170531Ssam	}
478170531Ssam	return flags;
479170531Ssam#undef _CHAN_HT
480138593Ssam}
481138593Ssam
482138593Ssamstatic void
48377218Sphkset80211channel(const char *val, int d, int s, const struct afswtch *rafp)
48477218Sphk{
485170531Ssam	struct ieee80211_channel chan;
486170531Ssam
487170531Ssam	memset(&chan, 0, sizeof(chan));
488138593Ssam	if (!isanyarg(val)) {
489173275Ssam		int v, flags;
490170531Ssam
491170531Ssam		getchaninfo(s);
492173275Ssam		v = atoi(val);
493173275Ssam		flags = getchannelflags(val, v);
494170531Ssam		if (v > 255) {		/* treat as frequency */
495170531Ssam			mapfreq(&chan, v, flags);
496170531Ssam		} else {
497170531Ssam			mapchan(&chan, v, flags);
498170531Ssam		}
499170531Ssam	} else {
500170531Ssam		chan.ic_freq = IEEE80211_CHAN_ANY;
501170531Ssam	}
502170531Ssam	set80211(s, IEEE80211_IOC_CURCHAN, 0, sizeof(chan), &chan);
50377218Sphk}
50477218Sphk
505138593Ssamstatic void
50677218Sphkset80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
50777218Sphk{
50877218Sphk	int	mode;
50977218Sphk
51091454Sbrooks	if (strcasecmp(val, "none") == 0) {
51177218Sphk		mode = IEEE80211_AUTH_NONE;
51291454Sbrooks	} else if (strcasecmp(val, "open") == 0) {
51377218Sphk		mode = IEEE80211_AUTH_OPEN;
51491454Sbrooks	} else if (strcasecmp(val, "shared") == 0) {
51577218Sphk		mode = IEEE80211_AUTH_SHARED;
516138593Ssam	} else if (strcasecmp(val, "8021x") == 0) {
517138593Ssam		mode = IEEE80211_AUTH_8021X;
518138593Ssam	} else if (strcasecmp(val, "wpa") == 0) {
519138593Ssam		mode = IEEE80211_AUTH_WPA;
52077218Sphk	} else {
521150708Sru		errx(1, "unknown authmode");
52277218Sphk	}
52377218Sphk
52477218Sphk	set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
52577218Sphk}
52677218Sphk
527138593Ssamstatic void
52877218Sphkset80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
52977218Sphk{
53077218Sphk	int	mode;
53177218Sphk
53291454Sbrooks	if (strcasecmp(val, "off") == 0) {
53377218Sphk		mode = IEEE80211_POWERSAVE_OFF;
53491454Sbrooks	} else if (strcasecmp(val, "on") == 0) {
53577218Sphk		mode = IEEE80211_POWERSAVE_ON;
53691454Sbrooks	} else if (strcasecmp(val, "cam") == 0) {
53777218Sphk		mode = IEEE80211_POWERSAVE_CAM;
53891454Sbrooks	} else if (strcasecmp(val, "psp") == 0) {
53977218Sphk		mode = IEEE80211_POWERSAVE_PSP;
54091454Sbrooks	} else if (strcasecmp(val, "psp-cam") == 0) {
54177218Sphk		mode = IEEE80211_POWERSAVE_PSP_CAM;
54277218Sphk	} else {
543150708Sru		errx(1, "unknown powersavemode");
54477218Sphk	}
54577218Sphk
54677218Sphk	set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
54777218Sphk}
54877218Sphk
549138593Ssamstatic void
55077218Sphkset80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
55177218Sphk{
55277218Sphk	if (d == 0)
55377218Sphk		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
55477218Sphk		    0, NULL);
55577218Sphk	else
55677218Sphk		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
55777218Sphk		    0, NULL);
55877218Sphk}
55977218Sphk
560138593Ssamstatic void
56177218Sphkset80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
56277218Sphk{
56377218Sphk	set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
56477218Sphk}
56577218Sphk
566138593Ssamstatic void
56777218Sphkset80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
56877218Sphk{
56977218Sphk	int	mode;
57077218Sphk
57191454Sbrooks	if (strcasecmp(val, "off") == 0) {
57277218Sphk		mode = IEEE80211_WEP_OFF;
57391454Sbrooks	} else if (strcasecmp(val, "on") == 0) {
57477218Sphk		mode = IEEE80211_WEP_ON;
57591454Sbrooks	} else if (strcasecmp(val, "mixed") == 0) {
57677218Sphk		mode = IEEE80211_WEP_MIXED;
57777218Sphk	} else {
578150708Sru		errx(1, "unknown wep mode");
57977218Sphk	}
58077218Sphk
58177218Sphk	set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
58277218Sphk}
58377218Sphk
584138593Ssamstatic void
58577218Sphkset80211wep(const char *val, int d, int s, const struct afswtch *rafp)
58677218Sphk{
58777218Sphk	set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
58877218Sphk}
58977218Sphk
590139493Ssamstatic int
591139493Ssamisundefarg(const char *arg)
592139493Ssam{
593139493Ssam	return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0);
594139493Ssam}
595139493Ssam
596138593Ssamstatic void
59777218Sphkset80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
59877218Sphk{
599139493Ssam	if (isundefarg(val))
600139493Ssam		set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
601139493Ssam	else
602139493Ssam		set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
60377218Sphk}
60477218Sphk
605138593Ssamstatic void
60677218Sphkset80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
60777218Sphk{
60877218Sphk	int		key = 0;
60977218Sphk	int		len;
610120178Ssam	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
61177218Sphk
61291454Sbrooks	if (isdigit(val[0]) && val[1] == ':') {
61377218Sphk		key = atoi(val)-1;
61477218Sphk		val += 2;
61577218Sphk	}
61677218Sphk
61777218Sphk	bzero(data, sizeof(data));
61877218Sphk	len = sizeof(data);
61977218Sphk	get_string(val, NULL, data, &len);
62077218Sphk
62177218Sphk	set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
62277218Sphk}
62377218Sphk
62477218Sphk/*
625148686Sstefanf * This function is purely a NetBSD compatability interface.  The NetBSD
626148686Sstefanf * interface is too inflexible, but it's there so we'll support it since
62777218Sphk * it's not all that hard.
62877218Sphk */
629138593Ssamstatic void
63077218Sphkset80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
63177218Sphk{
63277218Sphk	int		txkey;
63377218Sphk	int		i, len;
634120178Ssam	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
63577218Sphk
63677218Sphk	set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
63777218Sphk
63891454Sbrooks	if (isdigit(val[0]) && val[1] == ':') {
63977218Sphk		txkey = val[0]-'0'-1;
64077218Sphk		val += 2;
64177218Sphk
64291454Sbrooks		for (i = 0; i < 4; i++) {
64377218Sphk			bzero(data, sizeof(data));
64477218Sphk			len = sizeof(data);
64577218Sphk			val = get_string(val, ",", data, &len);
646151827Sbrooks			if (val == NULL)
647151827Sbrooks				exit(1);
64877218Sphk
64977218Sphk			set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
65077218Sphk		}
65177218Sphk	} else {
65277218Sphk		bzero(data, sizeof(data));
65377218Sphk		len = sizeof(data);
65477218Sphk		get_string(val, NULL, data, &len);
65577218Sphk		txkey = 0;
65677218Sphk
65777218Sphk		set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
65877218Sphk
65977218Sphk		bzero(data, sizeof(data));
66091454Sbrooks		for (i = 1; i < 4; i++)
66177218Sphk			set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
66277218Sphk	}
66377218Sphk
66477218Sphk	set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
66577218Sphk}
66677218Sphk
667138593Ssamstatic void
668127649Ssamset80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
669127649Ssam{
670148416Ssam	set80211(s, IEEE80211_IOC_RTSTHRESHOLD,
671148416Ssam		isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL);
672127649Ssam}
673127649Ssam
674138593Ssamstatic void
675127649Ssamset80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
676127649Ssam{
677127649Ssam	int	mode;
678127649Ssam
679127649Ssam	if (strcasecmp(val, "off") == 0) {
680127649Ssam		mode = IEEE80211_PROTMODE_OFF;
681127649Ssam	} else if (strcasecmp(val, "cts") == 0) {
682127649Ssam		mode = IEEE80211_PROTMODE_CTS;
683173275Ssam	} else if (strncasecmp(val, "rtscts", 3) == 0) {
684127649Ssam		mode = IEEE80211_PROTMODE_RTSCTS;
685127649Ssam	} else {
686150708Sru		errx(1, "unknown protection mode");
687127649Ssam	}
688127649Ssam
689127649Ssam	set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
690127649Ssam}
691127649Ssam
692138593Ssamstatic void
693173275Ssamset80211htprotmode(const char *val, int d, int s, const struct afswtch *rafp)
694173275Ssam{
695173275Ssam	int	mode;
696173275Ssam
697173275Ssam	if (strcasecmp(val, "off") == 0) {
698173275Ssam		mode = IEEE80211_PROTMODE_OFF;
699173275Ssam	} else if (strncasecmp(val, "rts", 3) == 0) {
700173275Ssam		mode = IEEE80211_PROTMODE_RTSCTS;
701173275Ssam	} else {
702173275Ssam		errx(1, "unknown protection mode");
703173275Ssam	}
704173275Ssam
705173275Ssam	set80211(s, IEEE80211_IOC_HTPROTMODE, mode, 0, NULL);
706173275Ssam}
707173275Ssam
708173275Ssamstatic void
709127649Ssamset80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
710127649Ssam{
711173275Ssam	double v = atof(val);
712173275Ssam	int txpow;
713173275Ssam
714173275Ssam	txpow = (int) (2*v);
715173275Ssam	if (txpow != 2*v)
716173275Ssam		errx(-1, "invalid tx power (must be .5 dBm units)");
717173275Ssam	set80211(s, IEEE80211_IOC_TXPOWER, txpow, 0, NULL);
718127649Ssam}
719127649Ssam
720138593Ssam#define	IEEE80211_ROAMING_DEVICE	0
721138593Ssam#define	IEEE80211_ROAMING_AUTO		1
722138593Ssam#define	IEEE80211_ROAMING_MANUAL	2
723138593Ssam
724138593Ssamstatic void
725138593Ssamset80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
72677218Sphk{
727138593Ssam	int mode;
72877218Sphk
729138593Ssam	if (strcasecmp(val, "device") == 0) {
730138593Ssam		mode = IEEE80211_ROAMING_DEVICE;
731138593Ssam	} else if (strcasecmp(val, "auto") == 0) {
732138593Ssam		mode = IEEE80211_ROAMING_AUTO;
733138593Ssam	} else if (strcasecmp(val, "manual") == 0) {
734138593Ssam		mode = IEEE80211_ROAMING_MANUAL;
735138593Ssam	} else {
736150708Sru		errx(1, "unknown roaming mode");
737138593Ssam	}
738138593Ssam	set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
739138593Ssam}
740138593Ssam
741138593Ssamstatic void
742138593Ssamset80211wme(const char *val, int d, int s, const struct afswtch *rafp)
743138593Ssam{
744138593Ssam	set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
745138593Ssam}
746138593Ssam
747138593Ssamstatic void
748138593Ssamset80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
749138593Ssam{
750138593Ssam	set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
751138593Ssam}
752138593Ssam
753138593Ssamstatic void
754138593Ssamset80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
755138593Ssam{
756138593Ssam	set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
757138593Ssam}
758138593Ssam
759138593Ssamstatic void
760170531Ssamset80211fastframes(const char *val, int d, int s, const struct afswtch *rafp)
761170531Ssam{
762170531Ssam	set80211(s, IEEE80211_IOC_FF, d, 0, NULL);
763170531Ssam}
764170531Ssam
765170531Ssamstatic void
766170531Ssamset80211dturbo(const char *val, int d, int s, const struct afswtch *rafp)
767170531Ssam{
768170531Ssam	set80211(s, IEEE80211_IOC_TURBOP, d, 0, NULL);
769170531Ssam}
770170531Ssam
771170531Ssamstatic void
772138593Ssamset80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
773138593Ssam{
774138593Ssam	struct ieee80211req_chanlist chanlist;
775138593Ssam#define	MAXCHAN	(sizeof(chanlist.ic_channels)*NBBY)
776138593Ssam	char *temp, *cp, *tp;
777138593Ssam
778138593Ssam	temp = malloc(strlen(val) + 1);
779138593Ssam	if (temp == NULL)
780138593Ssam		errx(1, "malloc failed");
781138593Ssam	strcpy(temp, val);
782138593Ssam	memset(&chanlist, 0, sizeof(chanlist));
783138593Ssam	cp = temp;
784138593Ssam	for (;;) {
785173275Ssam		int first, last, f, c;
786138593Ssam
787138593Ssam		tp = strchr(cp, ',');
788138593Ssam		if (tp != NULL)
789138593Ssam			*tp++ = '\0';
790138593Ssam		switch (sscanf(cp, "%u-%u", &first, &last)) {
791138593Ssam		case 1:
792138593Ssam			if (first > MAXCHAN)
793146873Sjhb				errx(-1, "channel %u out of range, max %zu",
794138593Ssam					first, MAXCHAN);
795138593Ssam			setbit(chanlist.ic_channels, first);
796138593Ssam			break;
797138593Ssam		case 2:
798138593Ssam			if (first > MAXCHAN)
799146873Sjhb				errx(-1, "channel %u out of range, max %zu",
800138593Ssam					first, MAXCHAN);
801138593Ssam			if (last > MAXCHAN)
802146873Sjhb				errx(-1, "channel %u out of range, max %zu",
803138593Ssam					last, MAXCHAN);
804138593Ssam			if (first > last)
805138593Ssam				errx(-1, "void channel range, %u > %u",
806138593Ssam					first, last);
807138593Ssam			for (f = first; f <= last; f++)
808138593Ssam				setbit(chanlist.ic_channels, f);
809138593Ssam			break;
810138593Ssam		}
811138593Ssam		if (tp == NULL)
812138593Ssam			break;
813173275Ssam		c = *tp;
814173275Ssam		while (isspace(c))
815138593Ssam			tp++;
816173275Ssam		if (!isdigit(c))
817138593Ssam			break;
818138593Ssam		cp = tp;
819138593Ssam	}
820170531Ssam	set80211(s, IEEE80211_IOC_CHANLIST, 0, sizeof(chanlist), &chanlist);
821138593Ssam#undef MAXCHAN
822138593Ssam}
823138593Ssam
824138593Ssamstatic void
825138593Ssamset80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
826138593Ssam{
827138593Ssam
828138593Ssam	if (!isanyarg(val)) {
829138593Ssam		char *temp;
830138593Ssam		struct sockaddr_dl sdl;
831138593Ssam
832155702Ssam		temp = malloc(strlen(val) + 2); /* ':' and '\0' */
833138593Ssam		if (temp == NULL)
834138593Ssam			errx(1, "malloc failed");
835138593Ssam		temp[0] = ':';
836138593Ssam		strcpy(temp + 1, val);
837138593Ssam		sdl.sdl_len = sizeof(sdl);
838138593Ssam		link_addr(temp, &sdl);
839138593Ssam		free(temp);
840138593Ssam		if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
841138593Ssam			errx(1, "malformed link-level address");
842138593Ssam		set80211(s, IEEE80211_IOC_BSSID, 0,
843138593Ssam			IEEE80211_ADDR_LEN, LLADDR(&sdl));
844138593Ssam	} else {
845138593Ssam		uint8_t zerobssid[IEEE80211_ADDR_LEN];
846138593Ssam		memset(zerobssid, 0, sizeof(zerobssid));
847138593Ssam		set80211(s, IEEE80211_IOC_BSSID, 0,
848138593Ssam			IEEE80211_ADDR_LEN, zerobssid);
849138593Ssam	}
850138593Ssam}
851138593Ssam
852138593Ssamstatic int
853138593Ssamgetac(const char *ac)
854138593Ssam{
855138593Ssam	if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0)
856138593Ssam		return WME_AC_BE;
857138593Ssam	if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0)
858138593Ssam		return WME_AC_BK;
859138593Ssam	if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0)
860138593Ssam		return WME_AC_VI;
861138593Ssam	if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0)
862138593Ssam		return WME_AC_VO;
863138593Ssam	errx(1, "unknown wme access class %s", ac);
864138593Ssam}
865138593Ssam
866138593Ssamstatic
867138593SsamDECL_CMD_FUNC2(set80211cwmin, ac, val)
868138593Ssam{
869138593Ssam	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
870138593Ssam}
871138593Ssam
872138593Ssamstatic
873138593SsamDECL_CMD_FUNC2(set80211cwmax, ac, val)
874138593Ssam{
875138593Ssam	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
876138593Ssam}
877138593Ssam
878138593Ssamstatic
879138593SsamDECL_CMD_FUNC2(set80211aifs, ac, val)
880138593Ssam{
881138593Ssam	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
882138593Ssam}
883138593Ssam
884138593Ssamstatic
885138593SsamDECL_CMD_FUNC2(set80211txoplimit, ac, val)
886138593Ssam{
887138593Ssam	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
888138593Ssam}
889138593Ssam
890138593Ssamstatic
891148621SsamDECL_CMD_FUNC(set80211acm, ac, d)
892138593Ssam{
893148621Ssam	set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL);
894138593Ssam}
895148621Ssamstatic
896148621SsamDECL_CMD_FUNC(set80211noacm, ac, d)
897148621Ssam{
898148621Ssam	set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL);
899148621Ssam}
900138593Ssam
901138593Ssamstatic
902148621SsamDECL_CMD_FUNC(set80211ackpolicy, ac, d)
903138593Ssam{
904148621Ssam	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL);
905138593Ssam}
906148621Ssamstatic
907148621SsamDECL_CMD_FUNC(set80211noackpolicy, ac, d)
908148621Ssam{
909148621Ssam	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL);
910148621Ssam}
911138593Ssam
912138593Ssamstatic
913138593SsamDECL_CMD_FUNC2(set80211bsscwmin, ac, val)
914138593Ssam{
915138593Ssam	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
916138593Ssam		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
917138593Ssam}
918138593Ssam
919138593Ssamstatic
920138593SsamDECL_CMD_FUNC2(set80211bsscwmax, ac, val)
921138593Ssam{
922138593Ssam	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
923138593Ssam		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
924138593Ssam}
925138593Ssam
926138593Ssamstatic
927138593SsamDECL_CMD_FUNC2(set80211bssaifs, ac, val)
928138593Ssam{
929138593Ssam	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
930138593Ssam		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
931138593Ssam}
932138593Ssam
933138593Ssamstatic
934138593SsamDECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
935138593Ssam{
936138593Ssam	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
937138593Ssam		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
938138593Ssam}
939138593Ssam
940138593Ssamstatic
941138593SsamDECL_CMD_FUNC(set80211dtimperiod, val, d)
942138593Ssam{
943138593Ssam	set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
944138593Ssam}
945138593Ssam
946138593Ssamstatic
947138593SsamDECL_CMD_FUNC(set80211bintval, val, d)
948138593Ssam{
949138593Ssam	set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
950138593Ssam}
951138593Ssam
952138593Ssamstatic void
953138593Ssamset80211macmac(int s, int op, const char *val)
954138593Ssam{
955138593Ssam	char *temp;
956138593Ssam	struct sockaddr_dl sdl;
957138593Ssam
958155702Ssam	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
959138593Ssam	if (temp == NULL)
960138593Ssam		errx(1, "malloc failed");
961138593Ssam	temp[0] = ':';
962138593Ssam	strcpy(temp + 1, val);
963138593Ssam	sdl.sdl_len = sizeof(sdl);
964138593Ssam	link_addr(temp, &sdl);
965138593Ssam	free(temp);
966138593Ssam	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
967138593Ssam		errx(1, "malformed link-level address");
968138593Ssam	set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
969138593Ssam}
970138593Ssam
971138593Ssamstatic
972138593SsamDECL_CMD_FUNC(set80211addmac, val, d)
973138593Ssam{
974138593Ssam	set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
975138593Ssam}
976138593Ssam
977138593Ssamstatic
978138593SsamDECL_CMD_FUNC(set80211delmac, val, d)
979138593Ssam{
980138593Ssam	set80211macmac(s, IEEE80211_IOC_DELMAC, val);
981138593Ssam}
982138593Ssam
983138593Ssamstatic
984149029SsamDECL_CMD_FUNC(set80211kickmac, val, d)
985149029Ssam{
986149029Ssam	char *temp;
987149029Ssam	struct sockaddr_dl sdl;
988149029Ssam	struct ieee80211req_mlme mlme;
989149029Ssam
990155702Ssam	temp = malloc(strlen(val) + 2); /* ':' and '\0' */
991149029Ssam	if (temp == NULL)
992149029Ssam		errx(1, "malloc failed");
993149029Ssam	temp[0] = ':';
994149029Ssam	strcpy(temp + 1, val);
995149029Ssam	sdl.sdl_len = sizeof(sdl);
996149029Ssam	link_addr(temp, &sdl);
997149029Ssam	free(temp);
998149029Ssam	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
999149029Ssam		errx(1, "malformed link-level address");
1000149029Ssam	memset(&mlme, 0, sizeof(mlme));
1001149029Ssam	mlme.im_op = IEEE80211_MLME_DEAUTH;
1002149029Ssam	mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE;
1003149029Ssam	memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN);
1004170531Ssam	set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), &mlme);
1005149029Ssam}
1006149029Ssam
1007149029Ssamstatic
1008138593SsamDECL_CMD_FUNC(set80211maccmd, val, d)
1009138593Ssam{
1010138593Ssam	set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
1011138593Ssam}
1012138593Ssam
1013147795Ssamstatic void
1014147795Ssamset80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
1015147795Ssam{
1016147795Ssam	set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
1017147795Ssam}
1018147795Ssam
1019153422Ssamstatic void
1020170531Ssamset80211bgscan(const char *val, int d, int s, const struct afswtch *rafp)
1021153422Ssam{
1022170531Ssam	set80211(s, IEEE80211_IOC_BGSCAN, d, 0, NULL);
1023153422Ssam}
1024153422Ssam
1025148416Ssamstatic
1026170531SsamDECL_CMD_FUNC(set80211bgscanidle, val, d)
1027170531Ssam{
1028170531Ssam	set80211(s, IEEE80211_IOC_BGSCAN_IDLE, atoi(val), 0, NULL);
1029170531Ssam}
1030170531Ssam
1031170531Ssamstatic
1032170531SsamDECL_CMD_FUNC(set80211bgscanintvl, val, d)
1033170531Ssam{
1034170531Ssam	set80211(s, IEEE80211_IOC_BGSCAN_INTERVAL, atoi(val), 0, NULL);
1035170531Ssam}
1036170531Ssam
1037170531Ssamstatic
1038170531SsamDECL_CMD_FUNC(set80211scanvalid, val, d)
1039170531Ssam{
1040170531Ssam	set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL);
1041170531Ssam}
1042170531Ssam
1043170531Ssamstatic
1044170531SsamDECL_CMD_FUNC(set80211roamrssi11a, val, d)
1045170531Ssam{
1046170531Ssam	set80211(s, IEEE80211_IOC_ROAM_RSSI_11A, atoi(val), 0, NULL);
1047170531Ssam}
1048170531Ssam
1049170531Ssamstatic
1050170531SsamDECL_CMD_FUNC(set80211roamrssi11b, val, d)
1051170531Ssam{
1052170531Ssam	set80211(s, IEEE80211_IOC_ROAM_RSSI_11B, atoi(val), 0, NULL);
1053170531Ssam}
1054170531Ssam
1055170531Ssamstatic
1056170531SsamDECL_CMD_FUNC(set80211roamrssi11g, val, d)
1057170531Ssam{
1058170531Ssam	set80211(s, IEEE80211_IOC_ROAM_RSSI_11G, atoi(val), 0, NULL);
1059170531Ssam}
1060170531Ssam
1061170531Ssamstatic
1062170531SsamDECL_CMD_FUNC(set80211roamrate11a, val, d)
1063170531Ssam{
1064170531Ssam	set80211(s, IEEE80211_IOC_ROAM_RATE_11A, 2*atoi(val), 0, NULL);
1065170531Ssam}
1066170531Ssam
1067170531Ssamstatic
1068170531SsamDECL_CMD_FUNC(set80211roamrate11b, val, d)
1069170531Ssam{
1070170531Ssam	set80211(s, IEEE80211_IOC_ROAM_RATE_11B, 2*atoi(val), 0, NULL);
1071170531Ssam}
1072170531Ssam
1073170531Ssamstatic
1074170531SsamDECL_CMD_FUNC(set80211roamrate11g, val, d)
1075170531Ssam{
1076170531Ssam	set80211(s, IEEE80211_IOC_ROAM_RATE_11G, 2*atoi(val), 0, NULL);
1077170531Ssam}
1078170531Ssam
1079170531Ssamstatic
1080153354SsamDECL_CMD_FUNC(set80211mcastrate, val, d)
1081153354Ssam{
1082170531Ssam	set80211(s, IEEE80211_IOC_MCAST_RATE, 2*atoi(val), 0, NULL);
1083153354Ssam}
1084153354Ssam
1085153354Ssamstatic
1086148416SsamDECL_CMD_FUNC(set80211fragthreshold, val, d)
1087148416Ssam{
1088148416Ssam	set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
1089148416Ssam		isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
1090148416Ssam}
1091148416Ssam
1092160687Ssamstatic
1093160687SsamDECL_CMD_FUNC(set80211bmissthreshold, val, d)
1094160687Ssam{
1095160687Ssam	set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
1096160687Ssam		isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
1097160687Ssam}
1098160687Ssam
1099170531Ssamstatic void
1100170531Ssamset80211burst(const char *val, int d, int s, const struct afswtch *rafp)
1101170531Ssam{
1102170531Ssam	set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
1103170531Ssam}
1104170531Ssam
1105170531Ssamstatic void
1106170531Ssamset80211doth(const char *val, int d, int s, const struct afswtch *rafp)
1107170531Ssam{
1108170531Ssam	set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL);
1109170531Ssam}
1110170531Ssam
1111173275Ssamstatic void
1112173275Ssamset80211shortgi(const char *val, int d, int s, const struct afswtch *rafp)
1113173275Ssam{
1114173275Ssam	set80211(s, IEEE80211_IOC_SHORTGI,
1115173275Ssam		d ? (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) : 0,
1116173275Ssam		0, NULL);
1117173275Ssam}
1118173275Ssam
1119173275Ssamstatic void
1120173275Ssamset80211ampdu(const char *val, int d, int s, const struct afswtch *rafp)
1121173275Ssam{
1122173275Ssam	int ampdu;
1123173275Ssam
1124173275Ssam	if (get80211val(s, IEEE80211_IOC_AMPDU, &ampdu) < 0)
1125173275Ssam		errx(-1, "cannot get AMPDU setting");
1126173275Ssam	if (d < 0) {
1127173275Ssam		d = -d;
1128173275Ssam		ampdu &= ~d;
1129173275Ssam	} else
1130173275Ssam		ampdu |= d;
1131173275Ssam	set80211(s, IEEE80211_IOC_AMPDU, ampdu, 0, NULL);
1132173275Ssam}
1133173275Ssam
1134173275Ssamstatic
1135173275SsamDECL_CMD_FUNC(set80211ampdulimit, val, d)
1136173275Ssam{
1137173275Ssam	int v;
1138173275Ssam
1139173275Ssam	switch (atoi(val)) {
1140173275Ssam	case 8:
1141173275Ssam	case 8*1024:
1142173275Ssam		v = IEEE80211_HTCAP_MAXRXAMPDU_8K;
1143173275Ssam		break;
1144173275Ssam	case 16:
1145173275Ssam	case 16*1024:
1146173275Ssam		v = IEEE80211_HTCAP_MAXRXAMPDU_16K;
1147173275Ssam		break;
1148173275Ssam	case 32:
1149173275Ssam	case 32*1024:
1150173275Ssam		v = IEEE80211_HTCAP_MAXRXAMPDU_32K;
1151173275Ssam		break;
1152173275Ssam	case 64:
1153173275Ssam	case 64*1024:
1154173275Ssam		v = IEEE80211_HTCAP_MAXRXAMPDU_64K;
1155173275Ssam		break;
1156173275Ssam	default:
1157173275Ssam		errx(-1, "invalid A-MPDU limit %s", val);
1158173275Ssam	}
1159173275Ssam	set80211(s, IEEE80211_IOC_AMPDU_LIMIT, v, 0, NULL);
1160173275Ssam}
1161173275Ssam
1162173275Ssamstatic
1163173275SsamDECL_CMD_FUNC(set80211ampdudensity, val, d)
1164173275Ssam{
1165173275Ssam	int v;
1166173275Ssam
1167173275Ssam	if (isanyarg(val))
1168173275Ssam		v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1169173275Ssam	else switch ((int)(atof(val)*4)) {
1170173275Ssam	case 0:
1171173275Ssam		v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1172173275Ssam		break;
1173173275Ssam	case 1:
1174173275Ssam		v = IEEE80211_HTCAP_MPDUDENSITY_025;
1175173275Ssam		break;
1176173275Ssam	case 2:
1177173275Ssam		v = IEEE80211_HTCAP_MPDUDENSITY_05;
1178173275Ssam		break;
1179173275Ssam	case 4:
1180173275Ssam		v = IEEE80211_HTCAP_MPDUDENSITY_1;
1181173275Ssam		break;
1182173275Ssam	case 8:
1183173275Ssam		v = IEEE80211_HTCAP_MPDUDENSITY_2;
1184173275Ssam		break;
1185173275Ssam	case 16:
1186173275Ssam		v = IEEE80211_HTCAP_MPDUDENSITY_4;
1187173275Ssam		break;
1188173275Ssam	case 32:
1189173275Ssam		v = IEEE80211_HTCAP_MPDUDENSITY_8;
1190173275Ssam		break;
1191173275Ssam	case 64:
1192173275Ssam		v = IEEE80211_HTCAP_MPDUDENSITY_16;
1193173275Ssam		break;
1194173275Ssam	default:
1195173275Ssam		errx(-1, "invalid A-MPDU density %s", val);
1196173275Ssam	}
1197173275Ssam	set80211(s, IEEE80211_IOC_AMPDU_DENSITY, v, 0, NULL);
1198173275Ssam}
1199173275Ssam
1200173275Ssamstatic void
1201173275Ssamset80211amsdu(const char *val, int d, int s, const struct afswtch *rafp)
1202173275Ssam{
1203173275Ssam	int amsdu;
1204173275Ssam
1205173275Ssam	if (get80211val(s, IEEE80211_IOC_AMSDU, &amsdu) < 0)
1206173275Ssam		errx(-1, "cannot get AMSDU setting");
1207173275Ssam	if (d < 0) {
1208173275Ssam		d = -d;
1209173275Ssam		amsdu &= ~d;
1210173275Ssam	} else
1211173275Ssam		amsdu |= d;
1212173275Ssam	set80211(s, IEEE80211_IOC_AMSDU, amsdu, 0, NULL);
1213173275Ssam}
1214173275Ssam
1215173275Ssamstatic
1216173275SsamDECL_CMD_FUNC(set80211amsdulimit, val, d)
1217173275Ssam{
1218173275Ssam	set80211(s, IEEE80211_IOC_AMSDU_LIMIT, atoi(val), 0, NULL);
1219173275Ssam}
1220173275Ssam
1221173275Ssamstatic void
1222173275Ssamset80211puren(const char *val, int d, int s, const struct afswtch *rafp)
1223173275Ssam{
1224173275Ssam	set80211(s, IEEE80211_IOC_PUREN, d, 0, NULL);
1225173275Ssam}
1226173275Ssam
1227173275Ssamstatic void
1228173275Ssamset80211htcompat(const char *val, int d, int s, const struct afswtch *rafp)
1229173275Ssam{
1230173275Ssam	set80211(s, IEEE80211_IOC_HTCOMPAT, d, 0, NULL);
1231173275Ssam}
1232173275Ssam
1233173275Ssamstatic void
1234173275Ssamset80211htconf(const char *val, int d, int s, const struct afswtch *rafp)
1235173275Ssam{
1236173275Ssam	set80211(s, IEEE80211_IOC_HTCONF, d, 0, NULL);
1237173275Ssam	htconf = d;
1238173275Ssam}
1239173275Ssam
1240173275Ssamstatic void
1241173275Ssamset80211inact(const char *val, int d, int s, const struct afswtch *rafp)
1242173275Ssam{
1243173275Ssam	set80211(s, IEEE80211_IOC_INACTIVITY, d, 0, NULL);
1244173275Ssam}
1245173275Ssam
1246173275Ssamstatic void
1247173275SsamLINE_INIT(char c)
1248173275Ssam{
1249173275Ssam	spacer = c;
1250173275Ssam	if (c == '\t')
1251173275Ssam		col = 8;
1252173275Ssam	else
1253173275Ssam		col = 1;
1254173275Ssam}
1255173275Ssam
1256173275Ssamstatic void
1257173275SsamLINE_BREAK(void)
1258173275Ssam{
1259173275Ssam	if (spacer != '\t') {
1260173275Ssam		printf("\n");
1261173275Ssam		spacer = '\t';
1262173275Ssam	}
1263173275Ssam	col = 8;		/* 8-col tab */
1264173275Ssam}
1265173275Ssam
1266173275Ssamstatic void
1267173275SsamLINE_CHECK(const char *fmt, ...)
1268173275Ssam{
1269173275Ssam	char buf[80];
1270173275Ssam	va_list ap;
1271173275Ssam	int n;
1272173275Ssam
1273173275Ssam	va_start(ap, fmt);
1274173275Ssam	n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
1275173275Ssam	va_end(ap);
1276173275Ssam	col += 1+n;
1277173275Ssam	if (col > MAXCOL) {
1278173275Ssam		LINE_BREAK();
1279173275Ssam		col += n;
1280173275Ssam	}
1281173275Ssam	buf[0] = spacer;
1282173275Ssam	printf("%s", buf);
1283173275Ssam	spacer = ' ';
1284173275Ssam}
1285173275Ssam
1286138593Ssamstatic int
1287170531Ssamgetmaxrate(const uint8_t rates[15], uint8_t nrates)
1288138593Ssam{
1289138593Ssam	int i, maxrate = -1;
1290138593Ssam
1291138593Ssam	for (i = 0; i < nrates; i++) {
1292138593Ssam		int rate = rates[i] & IEEE80211_RATE_VAL;
1293138593Ssam		if (rate > maxrate)
1294138593Ssam			maxrate = rate;
1295138593Ssam	}
1296138593Ssam	return maxrate / 2;
1297138593Ssam}
1298138593Ssam
1299138593Ssamstatic const char *
1300138593Ssamgetcaps(int capinfo)
1301138593Ssam{
1302138593Ssam	static char capstring[32];
1303138593Ssam	char *cp = capstring;
1304138593Ssam
1305138593Ssam	if (capinfo & IEEE80211_CAPINFO_ESS)
1306138593Ssam		*cp++ = 'E';
1307138593Ssam	if (capinfo & IEEE80211_CAPINFO_IBSS)
1308138593Ssam		*cp++ = 'I';
1309138593Ssam	if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
1310138593Ssam		*cp++ = 'c';
1311138593Ssam	if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
1312138593Ssam		*cp++ = 'C';
1313138593Ssam	if (capinfo & IEEE80211_CAPINFO_PRIVACY)
1314138593Ssam		*cp++ = 'P';
1315138593Ssam	if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
1316138593Ssam		*cp++ = 'S';
1317138593Ssam	if (capinfo & IEEE80211_CAPINFO_PBCC)
1318138593Ssam		*cp++ = 'B';
1319138593Ssam	if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
1320138593Ssam		*cp++ = 'A';
1321138593Ssam	if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
1322138593Ssam		*cp++ = 's';
1323138593Ssam	if (capinfo & IEEE80211_CAPINFO_RSN)
1324138593Ssam		*cp++ = 'R';
1325138593Ssam	if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
1326138593Ssam		*cp++ = 'D';
1327138593Ssam	*cp = '\0';
1328138593Ssam	return capstring;
1329138593Ssam}
1330138593Ssam
1331159885Ssamstatic const char *
1332159885Ssamgetflags(int flags)
1333159885Ssam{
1334159885Ssam/* XXX need these publicly defined or similar */
1335159885Ssam#define	IEEE80211_NODE_AUTH	0x0001		/* authorized for data */
1336159885Ssam#define	IEEE80211_NODE_QOS	0x0002		/* QoS enabled */
1337159885Ssam#define	IEEE80211_NODE_ERP	0x0004		/* ERP enabled */
1338159885Ssam#define	IEEE80211_NODE_PWR_MGT	0x0010		/* power save mode enabled */
1339170531Ssam#define	IEEE80211_NODE_HT	0x0040		/* HT enabled */
1340173275Ssam#define	IEEE80211_NODE_HTCOMPAT	0x0080		/* HT setup w/ vendor OUI's */
1341173275Ssam#define	IEEE80211_NODE_WPS	0x0100		/* WPS association */
1342173275Ssam#define	IEEE80211_NODE_TSN	0x0200		/* TSN association */
1343173275Ssam
1344159885Ssam	static char flagstring[32];
1345159885Ssam	char *cp = flagstring;
1346159885Ssam
1347159885Ssam	if (flags & IEEE80211_NODE_AUTH)
1348159885Ssam		*cp++ = 'A';
1349159885Ssam	if (flags & IEEE80211_NODE_QOS)
1350159885Ssam		*cp++ = 'Q';
1351159885Ssam	if (flags & IEEE80211_NODE_ERP)
1352159885Ssam		*cp++ = 'E';
1353159885Ssam	if (flags & IEEE80211_NODE_PWR_MGT)
1354159885Ssam		*cp++ = 'P';
1355173275Ssam	if (flags & IEEE80211_NODE_HT) {
1356170531Ssam		*cp++ = 'H';
1357173275Ssam		if (flags & IEEE80211_NODE_HTCOMPAT)
1358173275Ssam			*cp++ = '+';
1359173275Ssam	}
1360173275Ssam	if (flags & IEEE80211_NODE_WPS)
1361173275Ssam		*cp++ = 'W';
1362173275Ssam	if (flags & IEEE80211_NODE_TSN)
1363173275Ssam		*cp++ = 'T';
1364159885Ssam	*cp = '\0';
1365159885Ssam	return flagstring;
1366173275Ssam#undef IEEE80211_NODE_TSN
1367173275Ssam#undef IEEE80211_NODE_WPS
1368173275Ssam#undef IEEE80211_NODE_HTCOMPAT
1369170531Ssam#undef IEEE80211_NODE_HT
1370159885Ssam#undef IEEE80211_NODE_AUTH
1371159885Ssam#undef IEEE80211_NODE_QOS
1372159885Ssam#undef IEEE80211_NODE_ERP
1373159885Ssam#undef IEEE80211_NODE_PWR_MGT
1374159885Ssam}
1375159885Ssam
1376138593Ssamstatic void
1377138593Ssamprintie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
1378138593Ssam{
1379138593Ssam	printf("%s", tag);
1380138593Ssam	if (verbose) {
1381138593Ssam		maxlen -= strlen(tag)+2;
1382138593Ssam		if (2*ielen > maxlen)
1383138593Ssam			maxlen--;
1384138593Ssam		printf("<");
1385138593Ssam		for (; ielen > 0; ie++, ielen--) {
1386138593Ssam			if (maxlen-- <= 0)
1387138593Ssam				break;
1388138593Ssam			printf("%02x", *ie);
1389138593Ssam		}
1390138593Ssam		if (ielen != 0)
1391138593Ssam			printf("-");
1392138593Ssam		printf(">");
1393138593Ssam	}
1394138593Ssam}
1395138593Ssam
1396170531Ssam#define LE_READ_2(p)					\
1397170531Ssam	((u_int16_t)					\
1398170531Ssam	 ((((const u_int8_t *)(p))[0]      ) |		\
1399170531Ssam	  (((const u_int8_t *)(p))[1] <<  8)))
1400170531Ssam#define LE_READ_4(p)					\
1401170531Ssam	((u_int32_t)					\
1402170531Ssam	 ((((const u_int8_t *)(p))[0]      ) |		\
1403170531Ssam	  (((const u_int8_t *)(p))[1] <<  8) |		\
1404170531Ssam	  (((const u_int8_t *)(p))[2] << 16) |		\
1405170531Ssam	  (((const u_int8_t *)(p))[3] << 24)))
1406170531Ssam
1407138593Ssam/*
1408170531Ssam * NB: The decoding routines assume a properly formatted ie
1409170531Ssam *     which should be safe as the kernel only retains them
1410170531Ssam *     if they parse ok.
1411170531Ssam */
1412170531Ssam
1413170531Ssamstatic void
1414173275Ssamprintwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1415170531Ssam{
1416170531Ssam#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
1417170531Ssam	static const char *acnames[] = { "BE", "BK", "VO", "VI" };
1418173275Ssam	const struct ieee80211_wme_param *wme =
1419173275Ssam	    (const struct ieee80211_wme_param *) ie;
1420170531Ssam	int i;
1421170531Ssam
1422170531Ssam	printf("%s", tag);
1423173275Ssam	if (!verbose)
1424173275Ssam		return;
1425173275Ssam	printf("<qosinfo 0x%x", wme->param_qosInfo);
1426173275Ssam	ie += offsetof(struct ieee80211_wme_param, params_acParams);
1427173275Ssam	for (i = 0; i < WME_NUM_AC; i++) {
1428173275Ssam		const struct ieee80211_wme_acparams *ac =
1429173275Ssam		    &wme->params_acParams[i];
1430173275Ssam
1431173275Ssam		printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]"
1432173275Ssam			, acnames[i]
1433173275Ssam			, MS(ac->acp_aci_aifsn, WME_PARAM_ACM) ? "acm " : ""
1434173275Ssam			, MS(ac->acp_aci_aifsn, WME_PARAM_AIFSN)
1435173275Ssam			, MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMIN)
1436173275Ssam			, MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMAX)
1437173275Ssam			, LE_READ_2(&ac->acp_txop)
1438173275Ssam		);
1439170531Ssam	}
1440173275Ssam	printf(">");
1441170531Ssam#undef MS
1442170531Ssam}
1443170531Ssam
1444170531Ssamstatic void
1445173275Ssamprintwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1446173275Ssam{
1447173275Ssam	printf("%s", tag);
1448173275Ssam	if (verbose) {
1449173275Ssam		const struct ieee80211_wme_info *wme =
1450173275Ssam		    (const struct ieee80211_wme_info *) ie;
1451173275Ssam		printf("<version 0x%x info 0x%x>",
1452173275Ssam		    wme->wme_version, wme->wme_info);
1453173275Ssam	}
1454173275Ssam}
1455173275Ssam
1456173275Ssamstatic void
1457173275Ssamprinthtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1458173275Ssam{
1459173275Ssam	printf("%s", tag);
1460173275Ssam	if (verbose) {
1461173275Ssam		const struct ieee80211_ie_htcap *htcap =
1462173275Ssam		    (const struct ieee80211_ie_htcap *) ie;
1463173275Ssam		const char *sep;
1464173275Ssam		int i, j;
1465173275Ssam
1466173275Ssam		printf("<cap 0x%x param 0x%x",
1467173275Ssam		    LE_READ_2(&htcap->hc_cap), htcap->hc_param);
1468173275Ssam		printf(" mcsset[");
1469173275Ssam		sep = "";
1470173275Ssam		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
1471173275Ssam			if (isset(htcap->hc_mcsset, i)) {
1472173275Ssam				for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
1473173275Ssam					if (isclr(htcap->hc_mcsset, j))
1474173275Ssam						break;
1475173275Ssam				j--;
1476173275Ssam				if (i == j)
1477173275Ssam					printf("%s%u", sep, i);
1478173275Ssam				else
1479173275Ssam					printf("%s%u-%u", sep, i, j);
1480173275Ssam				i += j-i;
1481173275Ssam				sep = ",";
1482173275Ssam			}
1483173275Ssam		printf("] extcap 0x%x txbf 0x%x antenna 0x%x>",
1484173275Ssam		    LE_READ_2(&htcap->hc_extcap),
1485173275Ssam		    LE_READ_4(&htcap->hc_txbf),
1486173275Ssam		    htcap->hc_antenna);
1487173275Ssam	}
1488173275Ssam}
1489173275Ssam
1490173275Ssamstatic void
1491173275Ssamprinthtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1492173275Ssam{
1493173275Ssam	printf("%s", tag);
1494173275Ssam	if (verbose) {
1495173275Ssam		const struct ieee80211_ie_htinfo *htinfo =
1496173275Ssam		    (const struct ieee80211_ie_htinfo *) ie;
1497173275Ssam		const char *sep;
1498173275Ssam		int i, j;
1499173275Ssam
1500173275Ssam		printf("<ctl %u, %x,%x,%x,%x", htinfo->hi_ctrlchannel,
1501173275Ssam		    htinfo->hi_byte1, htinfo->hi_byte2, htinfo->hi_byte3,
1502173275Ssam		    LE_READ_2(&htinfo->hi_byte45));
1503173275Ssam		printf(" basicmcs[");
1504173275Ssam		sep = "";
1505173275Ssam		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
1506173275Ssam			if (isset(htinfo->hi_basicmcsset, i)) {
1507173275Ssam				for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
1508173275Ssam					if (isclr(htinfo->hi_basicmcsset, j))
1509173275Ssam						break;
1510173275Ssam				j--;
1511173275Ssam				if (i == j)
1512173275Ssam					printf("%s%u", sep, i);
1513173275Ssam				else
1514173275Ssam					printf("%s%u-%u", sep, i, j);
1515173275Ssam				i += j-i;
1516173275Ssam				sep = ",";
1517173275Ssam			}
1518173275Ssam		printf("]>");
1519173275Ssam	}
1520173275Ssam}
1521173275Ssam
1522173275Ssamstatic void
1523170531Ssamprintathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1524170531Ssam{
1525170531Ssam
1526170531Ssam	printf("%s", tag);
1527170531Ssam	if (verbose) {
1528170531Ssam		const struct ieee80211_ath_ie *ath =
1529170531Ssam			(const struct ieee80211_ath_ie *)ie;
1530170531Ssam
1531170531Ssam		printf("<");
1532170531Ssam		if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME)
1533170531Ssam			printf("DTURBO,");
1534170531Ssam		if (ath->ath_capability & ATHEROS_CAP_COMPRESSION)
1535170531Ssam			printf("COMP,");
1536170531Ssam		if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME)
1537170531Ssam			printf("FF,");
1538170531Ssam		if (ath->ath_capability & ATHEROS_CAP_XR)
1539170531Ssam			printf("XR,");
1540170531Ssam		if (ath->ath_capability & ATHEROS_CAP_AR)
1541170531Ssam			printf("AR,");
1542170531Ssam		if (ath->ath_capability & ATHEROS_CAP_BURST)
1543170531Ssam			printf("BURST,");
1544170531Ssam		if (ath->ath_capability & ATHEROS_CAP_WME)
1545170531Ssam			printf("WME,");
1546170531Ssam		if (ath->ath_capability & ATHEROS_CAP_BOOST)
1547170531Ssam			printf("BOOST,");
1548170531Ssam		printf("0x%x>", LE_READ_2(ath->ath_defkeyix));
1549170531Ssam	}
1550170531Ssam}
1551170531Ssam
1552170531Ssamstatic const char *
1553170531Ssamwpa_cipher(const u_int8_t *sel)
1554170531Ssam{
1555170531Ssam#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
1556170531Ssam	u_int32_t w = LE_READ_4(sel);
1557170531Ssam
1558170531Ssam	switch (w) {
1559170531Ssam	case WPA_SEL(WPA_CSE_NULL):
1560170531Ssam		return "NONE";
1561170531Ssam	case WPA_SEL(WPA_CSE_WEP40):
1562170531Ssam		return "WEP40";
1563170531Ssam	case WPA_SEL(WPA_CSE_WEP104):
1564170531Ssam		return "WEP104";
1565170531Ssam	case WPA_SEL(WPA_CSE_TKIP):
1566170531Ssam		return "TKIP";
1567170531Ssam	case WPA_SEL(WPA_CSE_CCMP):
1568170531Ssam		return "AES-CCMP";
1569170531Ssam	}
1570170531Ssam	return "?";		/* NB: so 1<< is discarded */
1571170531Ssam#undef WPA_SEL
1572170531Ssam}
1573170531Ssam
1574170531Ssamstatic const char *
1575170531Ssamwpa_keymgmt(const u_int8_t *sel)
1576170531Ssam{
1577170531Ssam#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
1578170531Ssam	u_int32_t w = LE_READ_4(sel);
1579170531Ssam
1580170531Ssam	switch (w) {
1581170531Ssam	case WPA_SEL(WPA_ASE_8021X_UNSPEC):
1582170531Ssam		return "8021X-UNSPEC";
1583170531Ssam	case WPA_SEL(WPA_ASE_8021X_PSK):
1584170531Ssam		return "8021X-PSK";
1585170531Ssam	case WPA_SEL(WPA_ASE_NONE):
1586170531Ssam		return "NONE";
1587170531Ssam	}
1588170531Ssam	return "?";
1589170531Ssam#undef WPA_SEL
1590170531Ssam}
1591170531Ssam
1592170531Ssamstatic void
1593170531Ssamprintwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1594170531Ssam{
1595170531Ssam	u_int8_t len = ie[1];
1596170531Ssam
1597170531Ssam	printf("%s", tag);
1598170531Ssam	if (verbose) {
1599170531Ssam		const char *sep;
1600170531Ssam		int n;
1601170531Ssam
1602170531Ssam		ie += 6, len -= 4;		/* NB: len is payload only */
1603170531Ssam
1604170531Ssam		printf("<v%u", LE_READ_2(ie));
1605170531Ssam		ie += 2, len -= 2;
1606170531Ssam
1607170531Ssam		printf(" mc:%s", wpa_cipher(ie));
1608170531Ssam		ie += 4, len -= 4;
1609170531Ssam
1610170531Ssam		/* unicast ciphers */
1611170531Ssam		n = LE_READ_2(ie);
1612170531Ssam		ie += 2, len -= 2;
1613170531Ssam		sep = " uc:";
1614170531Ssam		for (; n > 0; n--) {
1615170531Ssam			printf("%s%s", sep, wpa_cipher(ie));
1616170531Ssam			ie += 4, len -= 4;
1617170531Ssam			sep = "+";
1618170531Ssam		}
1619170531Ssam
1620170531Ssam		/* key management algorithms */
1621170531Ssam		n = LE_READ_2(ie);
1622170531Ssam		ie += 2, len -= 2;
1623170531Ssam		sep = " km:";
1624170531Ssam		for (; n > 0; n--) {
1625170531Ssam			printf("%s%s", sep, wpa_keymgmt(ie));
1626170531Ssam			ie += 4, len -= 4;
1627170531Ssam			sep = "+";
1628170531Ssam		}
1629170531Ssam
1630170531Ssam		if (len > 2)		/* optional capabilities */
1631170531Ssam			printf(", caps 0x%x", LE_READ_2(ie));
1632170531Ssam		printf(">");
1633170531Ssam	}
1634170531Ssam}
1635170531Ssam
1636170531Ssamstatic const char *
1637170531Ssamrsn_cipher(const u_int8_t *sel)
1638170531Ssam{
1639170531Ssam#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
1640170531Ssam	u_int32_t w = LE_READ_4(sel);
1641170531Ssam
1642170531Ssam	switch (w) {
1643170531Ssam	case RSN_SEL(RSN_CSE_NULL):
1644170531Ssam		return "NONE";
1645170531Ssam	case RSN_SEL(RSN_CSE_WEP40):
1646170531Ssam		return "WEP40";
1647170531Ssam	case RSN_SEL(RSN_CSE_WEP104):
1648170531Ssam		return "WEP104";
1649170531Ssam	case RSN_SEL(RSN_CSE_TKIP):
1650170531Ssam		return "TKIP";
1651170531Ssam	case RSN_SEL(RSN_CSE_CCMP):
1652170531Ssam		return "AES-CCMP";
1653170531Ssam	case RSN_SEL(RSN_CSE_WRAP):
1654170531Ssam		return "AES-OCB";
1655170531Ssam	}
1656170531Ssam	return "?";
1657170531Ssam#undef WPA_SEL
1658170531Ssam}
1659170531Ssam
1660170531Ssamstatic const char *
1661170531Ssamrsn_keymgmt(const u_int8_t *sel)
1662170531Ssam{
1663170531Ssam#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
1664170531Ssam	u_int32_t w = LE_READ_4(sel);
1665170531Ssam
1666170531Ssam	switch (w) {
1667170531Ssam	case RSN_SEL(RSN_ASE_8021X_UNSPEC):
1668170531Ssam		return "8021X-UNSPEC";
1669170531Ssam	case RSN_SEL(RSN_ASE_8021X_PSK):
1670170531Ssam		return "8021X-PSK";
1671170531Ssam	case RSN_SEL(RSN_ASE_NONE):
1672170531Ssam		return "NONE";
1673170531Ssam	}
1674170531Ssam	return "?";
1675170531Ssam#undef RSN_SEL
1676170531Ssam}
1677170531Ssam
1678170531Ssamstatic void
1679170531Ssamprintrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1680170531Ssam{
1681170531Ssam	printf("%s", tag);
1682170531Ssam	if (verbose) {
1683170531Ssam		const char *sep;
1684170531Ssam		int n;
1685170531Ssam
1686173275Ssam		ie += 2, ielen -= 2;
1687170531Ssam
1688170531Ssam		printf("<v%u", LE_READ_2(ie));
1689173275Ssam		ie += 2, ielen -= 2;
1690170531Ssam
1691170531Ssam		printf(" mc:%s", rsn_cipher(ie));
1692173275Ssam		ie += 4, ielen -= 4;
1693170531Ssam
1694170531Ssam		/* unicast ciphers */
1695170531Ssam		n = LE_READ_2(ie);
1696173275Ssam		ie += 2, ielen -= 2;
1697170531Ssam		sep = " uc:";
1698170531Ssam		for (; n > 0; n--) {
1699170531Ssam			printf("%s%s", sep, rsn_cipher(ie));
1700173275Ssam			ie += 4, ielen -= 4;
1701170531Ssam			sep = "+";
1702170531Ssam		}
1703170531Ssam
1704170531Ssam		/* key management algorithms */
1705170531Ssam		n = LE_READ_2(ie);
1706173275Ssam		ie += 2, ielen -= 2;
1707170531Ssam		sep = " km:";
1708170531Ssam		for (; n > 0; n--) {
1709170531Ssam			printf("%s%s", sep, rsn_keymgmt(ie));
1710173275Ssam			ie += 4, ielen -= 4;
1711170531Ssam			sep = "+";
1712170531Ssam		}
1713170531Ssam
1714173275Ssam		if (ielen > 2)		/* optional capabilities */
1715170531Ssam			printf(", caps 0x%x", LE_READ_2(ie));
1716170531Ssam		/* XXXPMKID */
1717170531Ssam		printf(">");
1718170531Ssam	}
1719170531Ssam}
1720170531Ssam
1721170531Ssam/*
1722138593Ssam * Copy the ssid string contents into buf, truncating to fit.  If the
1723138593Ssam * ssid is entirely printable then just copy intact.  Otherwise convert
1724138593Ssam * to hexadecimal.  If the result is truncated then replace the last
1725138593Ssam * three characters with "...".
1726138593Ssam */
1727146873Sjhbstatic int
1728138593Ssamcopy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
1729138593Ssam{
1730138593Ssam	const u_int8_t *p;
1731138593Ssam	size_t maxlen;
1732138593Ssam	int i;
1733138593Ssam
1734138593Ssam	if (essid_len > bufsize)
1735138593Ssam		maxlen = bufsize;
1736138593Ssam	else
1737138593Ssam		maxlen = essid_len;
1738138593Ssam	/* determine printable or not */
1739138593Ssam	for (i = 0, p = essid; i < maxlen; i++, p++) {
1740138593Ssam		if (*p < ' ' || *p > 0x7e)
1741138593Ssam			break;
1742138593Ssam	}
1743138593Ssam	if (i != maxlen) {		/* not printable, print as hex */
1744138593Ssam		if (bufsize < 3)
1745138593Ssam			return 0;
1746138593Ssam		strlcpy(buf, "0x", bufsize);
1747138593Ssam		bufsize -= 2;
1748138593Ssam		p = essid;
1749138593Ssam		for (i = 0; i < maxlen && bufsize >= 2; i++) {
1750147489Savatar			sprintf(&buf[2+2*i], "%02x", p[i]);
1751138593Ssam			bufsize -= 2;
1752138593Ssam		}
1753147489Savatar		if (i != essid_len)
1754147489Savatar			memcpy(&buf[2+2*i-3], "...", 3);
1755138593Ssam	} else {			/* printable, truncate as needed */
1756138593Ssam		memcpy(buf, essid, maxlen);
1757147489Savatar		if (maxlen != essid_len)
1758147489Savatar			memcpy(&buf[maxlen-3], "...", 3);
1759138593Ssam	}
1760138593Ssam	return maxlen;
1761138593Ssam}
1762138593Ssam
1763173275Ssamstatic void
1764173275Ssamprintssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1765173275Ssam{
1766173275Ssam	char ssid[2*IEEE80211_NWID_LEN+1];
1767173275Ssam
1768173275Ssam	printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid);
1769173275Ssam}
1770173275Ssam
1771173275Ssamstatic void
1772173275Ssamprintrates(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1773173275Ssam{
1774173275Ssam	const char *sep;
1775173275Ssam	int i;
1776173275Ssam
1777173275Ssam	printf("%s", tag);
1778173275Ssam	sep = "<";
1779173275Ssam	for (i = 2; i < ielen; i++) {
1780173275Ssam		printf("%s%s%d", sep,
1781173275Ssam		    ie[i] & IEEE80211_RATE_BASIC ? "B" : "",
1782173275Ssam		    ie[i] & IEEE80211_RATE_VAL);
1783173275Ssam		sep = ",";
1784173275Ssam	}
1785173275Ssam	printf(">");
1786173275Ssam}
1787173275Ssam
1788173275Ssamstatic void
1789173275Ssamprintcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1790173275Ssam{
1791173275Ssam	const struct ieee80211_country_ie *cie =
1792173275Ssam	   (const struct ieee80211_country_ie *) ie;
1793173275Ssam	int i, nbands, schan, nchan;
1794173275Ssam
1795173275Ssam	printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]);
1796173275Ssam	nbands = (cie->len - 3) / sizeof(cie->band[0]);
1797173275Ssam	for (i = 0; i < nbands; i++) {
1798173275Ssam		schan = cie->band[i].schan;
1799173275Ssam		nchan = cie->band[i].nchan;
1800173275Ssam		if (nchan != 1)
1801173275Ssam			printf(" %u-%u,%u", schan, schan + nchan-1,
1802173275Ssam			    cie->band[i].maxtxpwr);
1803173275Ssam		else
1804173275Ssam			printf(" %u,%u", schan, cie->band[i].maxtxpwr);
1805173275Ssam	}
1806173275Ssam	printf(">");
1807173275Ssam}
1808173275Ssam
1809148686Sstefanf/* unaligned little endian access */
1810138593Ssam#define LE_READ_4(p)					\
1811138593Ssam	((u_int32_t)					\
1812138593Ssam	 ((((const u_int8_t *)(p))[0]      ) |		\
1813138593Ssam	  (((const u_int8_t *)(p))[1] <<  8) |		\
1814138593Ssam	  (((const u_int8_t *)(p))[2] << 16) |		\
1815138593Ssam	  (((const u_int8_t *)(p))[3] << 24)))
1816138593Ssam
1817138593Ssamstatic int __inline
1818138593Ssamiswpaoui(const u_int8_t *frm)
1819138593Ssam{
1820138593Ssam	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
1821138593Ssam}
1822138593Ssam
1823138593Ssamstatic int __inline
1824173275Ssamiswmeinfo(const u_int8_t *frm)
1825138593Ssam{
1826173275Ssam	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
1827173275Ssam		frm[6] == WME_INFO_OUI_SUBTYPE;
1828138593Ssam}
1829138593Ssam
1830138593Ssamstatic int __inline
1831173275Ssamiswmeparam(const u_int8_t *frm)
1832173275Ssam{
1833173275Ssam	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
1834173275Ssam		frm[6] == WME_PARAM_OUI_SUBTYPE;
1835173275Ssam}
1836173275Ssam
1837173275Ssamstatic int __inline
1838138593Ssamisatherosoui(const u_int8_t *frm)
1839138593Ssam{
1840138593Ssam	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
1841138593Ssam}
1842138593Ssam
1843173275Ssamstatic const char *
1844173275Ssamiename(int elemid)
1845173275Ssam{
1846173275Ssam	switch (elemid) {
1847173275Ssam	case IEEE80211_ELEMID_FHPARMS:	return " FHPARMS";
1848173275Ssam	case IEEE80211_ELEMID_CFPARMS:	return " CFPARMS";
1849173275Ssam	case IEEE80211_ELEMID_TIM:	return " TIM";
1850173275Ssam	case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS";
1851173275Ssam	case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE";
1852173275Ssam	case IEEE80211_ELEMID_PWRCNSTR:	return " PWRCNSTR";
1853173275Ssam	case IEEE80211_ELEMID_PWRCAP:	return " PWRCAP";
1854173275Ssam	case IEEE80211_ELEMID_TPCREQ:	return " TPCREQ";
1855173275Ssam	case IEEE80211_ELEMID_TPCREP:	return " TPCREP";
1856173275Ssam	case IEEE80211_ELEMID_SUPPCHAN:	return " SUPPCHAN";
1857173275Ssam	case IEEE80211_ELEMID_CHANSWITCHANN:return " CSA";
1858173275Ssam	case IEEE80211_ELEMID_MEASREQ:	return " MEASREQ";
1859173275Ssam	case IEEE80211_ELEMID_MEASREP:	return " MEASREP";
1860173275Ssam	case IEEE80211_ELEMID_QUIET:	return " QUIET";
1861173275Ssam	case IEEE80211_ELEMID_IBSSDFS:	return " IBSSDFS";
1862173275Ssam	case IEEE80211_ELEMID_TPC:	return " TPC";
1863173275Ssam	case IEEE80211_ELEMID_CCKM:	return " CCKM";
1864173275Ssam	}
1865173275Ssam	return " ???";
1866173275Ssam}
1867173275Ssam
1868138593Ssamstatic void
1869138593Ssamprinties(const u_int8_t *vp, int ielen, int maxcols)
1870138593Ssam{
1871138593Ssam	while (ielen > 0) {
1872138593Ssam		switch (vp[0]) {
1873173275Ssam		case IEEE80211_ELEMID_SSID:
1874173275Ssam			if (verbose)
1875173275Ssam				printssid(" SSID", vp, 2+vp[1], maxcols);
1876173275Ssam			break;
1877173275Ssam		case IEEE80211_ELEMID_RATES:
1878173275Ssam		case IEEE80211_ELEMID_XRATES:
1879173275Ssam			if (verbose)
1880173275Ssam				printrates(vp[0] == IEEE80211_ELEMID_RATES ?
1881173275Ssam				    " RATES" : " XRATES", vp, 2+vp[1], maxcols);
1882173275Ssam			break;
1883173275Ssam		case IEEE80211_ELEMID_DSPARMS:
1884173275Ssam			if (verbose)
1885173275Ssam				printf(" DSPARMS<%u>", vp[2]);
1886173275Ssam			break;
1887173275Ssam		case IEEE80211_ELEMID_COUNTRY:
1888173275Ssam			if (verbose)
1889173275Ssam				printcountry(" COUNTRY", vp, 2+vp[1], maxcols);
1890173275Ssam			break;
1891173275Ssam		case IEEE80211_ELEMID_ERP:
1892173275Ssam			if (verbose)
1893173275Ssam				printf(" ERP<0x%x>", vp[2]);
1894173275Ssam			break;
1895138593Ssam		case IEEE80211_ELEMID_VENDOR:
1896138593Ssam			if (iswpaoui(vp))
1897170531Ssam				printwpaie(" WPA", vp, 2+vp[1], maxcols);
1898173275Ssam			else if (iswmeinfo(vp))
1899173275Ssam				printwmeinfo(" WME", vp, 2+vp[1], maxcols);
1900173275Ssam			else if (iswmeparam(vp))
1901173275Ssam				printwmeparam(" WME", vp, 2+vp[1], maxcols);
1902139492Ssam			else if (isatherosoui(vp))
1903170531Ssam				printathie(" ATH", vp, 2+vp[1], maxcols);
1904173275Ssam			else if (verbose)
1905138593Ssam				printie(" VEN", vp, 2+vp[1], maxcols);
1906138593Ssam			break;
1907138593Ssam		case IEEE80211_ELEMID_RSN:
1908170531Ssam			printrsnie(" RSN", vp, 2+vp[1], maxcols);
1909138593Ssam			break;
1910173275Ssam		case IEEE80211_ELEMID_HTCAP:
1911173275Ssam			printhtcap(" HTCAP", vp, 2+vp[1], maxcols);
1912173275Ssam			break;
1913173275Ssam		case IEEE80211_ELEMID_HTINFO:
1914173275Ssam			if (verbose)
1915173275Ssam				printhtinfo(" HTINFO", vp, 2+vp[1], maxcols);
1916173275Ssam			break;
1917138593Ssam		default:
1918173275Ssam			if (verbose)
1919173275Ssam				printie(iename(vp[0]), vp, 2+vp[1], maxcols);
1920138593Ssam			break;
1921138593Ssam		}
1922138593Ssam		ielen -= 2+vp[1];
1923138593Ssam		vp += 2+vp[1];
1924138593Ssam	}
1925138593Ssam}
1926138593Ssam
1927138593Ssamstatic void
1928138593Ssamlist_scan(int s)
1929138593Ssam{
1930138593Ssam	uint8_t buf[24*1024];
1931153892Srwatson	char ssid[IEEE80211_NWID_LEN+1];
1932173275Ssam	const uint8_t *cp;
1933154522Ssam	int len, ssidmax;
1934138593Ssam
1935173275Ssam	if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0)
1936138593Ssam		errx(1, "unable to get scan results");
1937138593Ssam	if (len < sizeof(struct ieee80211req_scan_result))
1938138593Ssam		return;
1939138593Ssam
1940170531Ssam	getchaninfo(s);
1941170531Ssam
1942154522Ssam	ssidmax = verbose ? IEEE80211_NWID_LEN : 14;
1943170531Ssam	printf("%-*.*s  %-17.17s  %4s %4s  %-7s  %3s %4s\n"
1944154522Ssam		, ssidmax, ssidmax, "SSID"
1945138593Ssam		, "BSSID"
1946138593Ssam		, "CHAN"
1947138593Ssam		, "RATE"
1948170531Ssam		, " S:N"
1949138593Ssam		, "INT"
1950138593Ssam		, "CAPS"
1951138593Ssam	);
1952138593Ssam	cp = buf;
1953138593Ssam	do {
1954170531Ssam		const struct ieee80211req_scan_result *sr;
1955170531Ssam		const uint8_t *vp;
1956138593Ssam
1957170531Ssam		sr = (const struct ieee80211req_scan_result *) cp;
1958173275Ssam		vp = cp + sr->isr_ie_off;
1959170531Ssam		printf("%-*.*s  %s  %3d  %3dM %3d:%-3d  %3d %-4.4s"
1960154522Ssam			, ssidmax
1961155461Ssam			  , copy_essid(ssid, ssidmax, vp, sr->isr_ssid_len)
1962154522Ssam			  , ssid
1963138593Ssam			, ether_ntoa((const struct ether_addr *) sr->isr_bssid)
1964165570Ssam			, ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
1965138593Ssam			, getmaxrate(sr->isr_rates, sr->isr_nrates)
1966170531Ssam			, (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise
1967138593Ssam			, sr->isr_intval
1968138593Ssam			, getcaps(sr->isr_capinfo)
1969138593Ssam		);
1970170531Ssam		printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);
1971138593Ssam		printf("\n");
1972138593Ssam		cp += sr->isr_len, len -= sr->isr_len;
1973138593Ssam	} while (len >= sizeof(struct ieee80211req_scan_result));
1974138593Ssam}
1975138593Ssam
1976138593Ssam#include <net80211/ieee80211_freebsd.h>
1977138593Ssam
1978138593Ssamstatic void
1979138593Ssamscan_and_wait(int s)
1980138593Ssam{
1981138593Ssam	struct ieee80211req ireq;
1982138593Ssam	int sroute;
1983138593Ssam
1984138593Ssam	sroute = socket(PF_ROUTE, SOCK_RAW, 0);
1985138593Ssam	if (sroute < 0) {
1986138593Ssam		perror("socket(PF_ROUTE,SOCK_RAW)");
1987138593Ssam		return;
1988138593Ssam	}
1989138593Ssam	(void) memset(&ireq, 0, sizeof(ireq));
1990138593Ssam	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1991138593Ssam	ireq.i_type = IEEE80211_IOC_SCAN_REQ;
1992138593Ssam	/* NB: only root can trigger a scan so ignore errors */
1993138593Ssam	if (ioctl(s, SIOCS80211, &ireq) >= 0) {
1994138593Ssam		char buf[2048];
1995138593Ssam		struct if_announcemsghdr *ifan;
1996138593Ssam		struct rt_msghdr *rtm;
1997138593Ssam
1998138593Ssam		do {
1999138593Ssam			if (read(sroute, buf, sizeof(buf)) < 0) {
2000138593Ssam				perror("read(PF_ROUTE)");
2001138593Ssam				break;
2002138593Ssam			}
2003138593Ssam			rtm = (struct rt_msghdr *) buf;
2004138593Ssam			if (rtm->rtm_version != RTM_VERSION)
2005138593Ssam				break;
2006138593Ssam			ifan = (struct if_announcemsghdr *) rtm;
2007138593Ssam		} while (rtm->rtm_type != RTM_IEEE80211 ||
2008138593Ssam		    ifan->ifan_what != RTM_IEEE80211_SCAN);
2009138593Ssam	}
2010138593Ssam	close(sroute);
2011138593Ssam}
2012138593Ssam
2013138593Ssamstatic
2014138593SsamDECL_CMD_FUNC(set80211scan, val, d)
2015138593Ssam{
2016138593Ssam	scan_and_wait(s);
2017138593Ssam	list_scan(s);
2018138593Ssam}
2019138593Ssam
2020161147Ssamstatic enum ieee80211_opmode get80211opmode(int s);
2021161147Ssam
2022173275Ssamstatic int
2023173275Ssamgettxseq(const struct ieee80211req_sta_info *si)
2024173275Ssam{
2025173275Ssam#define	IEEE80211_NODE_QOS	0x0002		/* QoS enabled */
2026173275Ssam
2027173275Ssam	int i, txseq;
2028173275Ssam
2029173275Ssam	if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
2030173275Ssam		return si->isi_txseqs[0];
2031173275Ssam	/* XXX not right but usually what folks want */
2032173275Ssam	txseq = 0;
2033173275Ssam	for (i = 0; i < IEEE80211_TID_SIZE; i++)
2034173275Ssam		if (si->isi_txseqs[i] > txseq)
2035173275Ssam			txseq = si->isi_txseqs[i];
2036173275Ssam	return txseq;
2037173275Ssam#undef IEEE80211_NODE_QOS
2038173275Ssam}
2039173275Ssam
2040173275Ssamstatic int
2041173275Ssamgetrxseq(const struct ieee80211req_sta_info *si)
2042173275Ssam{
2043173275Ssam#define	IEEE80211_NODE_QOS	0x0002		/* QoS enabled */
2044173275Ssam
2045173275Ssam	int i, rxseq;
2046173275Ssam
2047173275Ssam	if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
2048173275Ssam		return si->isi_rxseqs[0];
2049173275Ssam	/* XXX not right but usually what folks want */
2050173275Ssam	rxseq = 0;
2051173275Ssam	for (i = 0; i < IEEE80211_TID_SIZE; i++)
2052173275Ssam		if (si->isi_rxseqs[i] > rxseq)
2053173275Ssam			rxseq = si->isi_rxseqs[i];
2054173275Ssam	return rxseq;
2055173275Ssam#undef IEEE80211_NODE_QOS
2056173275Ssam}
2057173275Ssam
2058173275Ssamstatic int
2059173275Ssamgettxrate(int txrate, int chanflags)
2060173275Ssam{
2061173275Ssam	if (txrate & 0x80) {
2062173275Ssam		txrate = htrates[txrate & 0xf];
2063173275Ssam		/* NB: could bump this more based on short gi */
2064173275Ssam		return chanflags & IEEE80211_CHAN_HT40 ? txrate : txrate / 2;
2065173275Ssam	} else
2066173275Ssam		return (txrate & IEEE80211_RATE_VAL) / 2;
2067173275Ssam}
2068173275Ssam
2069138593Ssamstatic void
2070138593Ssamlist_stations(int s)
2071138593Ssam{
2072161147Ssam	union {
2073161147Ssam		struct ieee80211req_sta_req req;
2074161147Ssam		uint8_t buf[24*1024];
2075161147Ssam	} u;
2076161147Ssam	enum ieee80211_opmode opmode = get80211opmode(s);
2077170531Ssam	const uint8_t *cp;
2078138593Ssam	int len;
2079138593Ssam
2080161147Ssam	/* broadcast address =>'s get all stations */
2081161147Ssam	(void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
2082161147Ssam	if (opmode == IEEE80211_M_STA) {
2083161147Ssam		/*
2084161147Ssam		 * Get information about the associated AP.
2085161147Ssam		 */
2086173275Ssam		(void) get80211(s, IEEE80211_IOC_BSSID,
2087173275Ssam		    u.req.is_u.macaddr, IEEE80211_ADDR_LEN);
2088161147Ssam	}
2089173275Ssam	if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0)
2090138593Ssam		errx(1, "unable to get station information");
2091138593Ssam	if (len < sizeof(struct ieee80211req_sta_info))
2092138593Ssam		return;
2093138593Ssam
2094170531Ssam	getchaninfo(s);
2095170531Ssam
2096159885Ssam	printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %4s\n"
2097138593Ssam		, "ADDR"
2098138593Ssam		, "AID"
2099138593Ssam		, "CHAN"
2100138593Ssam		, "RATE"
2101138593Ssam		, "RSSI"
2102138593Ssam		, "IDLE"
2103138593Ssam		, "TXSEQ"
2104138593Ssam		, "RXSEQ"
2105138593Ssam		, "CAPS"
2106159885Ssam		, "FLAG"
2107138593Ssam	);
2108170531Ssam	cp = (const uint8_t *) u.req.info;
2109138593Ssam	do {
2110170531Ssam		const struct ieee80211req_sta_info *si;
2111138593Ssam
2112170531Ssam		si = (const struct ieee80211req_sta_info *) cp;
2113161147Ssam		if (si->isi_len < sizeof(*si))
2114161147Ssam			break;
2115170531Ssam		printf("%s %4u %4d %3dM %3.1f %4d %6d %6d %-4.4s %-4.4s"
2116138593Ssam			, ether_ntoa((const struct ether_addr*) si->isi_macaddr)
2117138593Ssam			, IEEE80211_AID(si->isi_associd)
2118166015Ssam			, ieee80211_mhz2ieee(si->isi_freq, si->isi_flags)
2119173275Ssam			, gettxrate(si->isi_txrate, si->isi_flags)
2120170531Ssam			, si->isi_rssi/2.
2121138593Ssam			, si->isi_inact
2122173275Ssam			, gettxseq(si)
2123173275Ssam			, getrxseq(si)
2124138593Ssam			, getcaps(si->isi_capinfo)
2125159885Ssam			, getflags(si->isi_state)
2126138593Ssam		);
2127170531Ssam		printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
2128138593Ssam		printf("\n");
2129138593Ssam		cp += si->isi_len, len -= si->isi_len;
2130138593Ssam	} while (len >= sizeof(struct ieee80211req_sta_info));
2131138593Ssam}
2132138593Ssam
2133170531Ssamstatic const char *
2134170531Ssamget_chaninfo(const struct ieee80211_channel *c, int precise,
2135170531Ssam	char buf[], size_t bsize)
2136138593Ssam{
2137138593Ssam	buf[0] = '\0';
2138138593Ssam	if (IEEE80211_IS_CHAN_FHSS(c))
2139170531Ssam		strlcat(buf, " FHSS", bsize);
2140165570Ssam	if (IEEE80211_IS_CHAN_A(c)) {
2141165570Ssam		if (IEEE80211_IS_CHAN_HALF(c))
2142170531Ssam			strlcat(buf, " 11a/10Mhz", bsize);
2143165570Ssam		else if (IEEE80211_IS_CHAN_QUARTER(c))
2144170531Ssam			strlcat(buf, " 11a/5Mhz", bsize);
2145165570Ssam		else
2146170531Ssam			strlcat(buf, " 11a", bsize);
2147165570Ssam	}
2148166015Ssam	if (IEEE80211_IS_CHAN_ANYG(c)) {
2149166015Ssam		if (IEEE80211_IS_CHAN_HALF(c))
2150170531Ssam			strlcat(buf, " 11g/10Mhz", bsize);
2151166015Ssam		else if (IEEE80211_IS_CHAN_QUARTER(c))
2152170531Ssam			strlcat(buf, " 11g/5Mhz", bsize);
2153166015Ssam		else
2154170531Ssam			strlcat(buf, " 11g", bsize);
2155166015Ssam	} else if (IEEE80211_IS_CHAN_B(c))
2156170531Ssam		strlcat(buf, " 11b", bsize);
2157170531Ssam	if (IEEE80211_IS_CHAN_TURBO(c))
2158170531Ssam		strlcat(buf, " Turbo", bsize);
2159170531Ssam	if (precise) {
2160170531Ssam		if (IEEE80211_IS_CHAN_HT20(c))
2161170531Ssam			strlcat(buf, " ht/20", bsize);
2162170531Ssam		else if (IEEE80211_IS_CHAN_HT40D(c))
2163170531Ssam			strlcat(buf, " ht/40-", bsize);
2164170531Ssam		else if (IEEE80211_IS_CHAN_HT40U(c))
2165170531Ssam			strlcat(buf, " ht/40+", bsize);
2166170531Ssam	} else {
2167170531Ssam		if (IEEE80211_IS_CHAN_HT(c))
2168170531Ssam			strlcat(buf, " ht", bsize);
2169170531Ssam	}
2170170531Ssam	return buf;
2171170531Ssam}
2172170531Ssam
2173170531Ssamstatic void
2174173275Ssamprint_chaninfo(const struct ieee80211_channel *c, int verb)
2175170531Ssam{
2176170531Ssam	char buf[14];
2177170531Ssam
2178138593Ssam	printf("Channel %3u : %u%c Mhz%-14.14s",
2179165570Ssam		ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
2180170531Ssam		IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
2181173275Ssam		get_chaninfo(c, verb, buf, sizeof(buf)));
2182138593Ssam}
2183138593Ssam
2184138593Ssamstatic void
2185173275Ssamprint_channels(int s, const struct ieee80211req_chaninfo *chans,
2186173275Ssam	int allchans, int verb)
2187138593Ssam{
2188138593Ssam	struct ieee80211req_chaninfo achans;
2189170531Ssam	uint8_t reported[IEEE80211_CHAN_BYTES];
2190138593Ssam	const struct ieee80211_channel *c;
2191170531Ssam	int i, half;
2192138593Ssam
2193170531Ssam	memset(&achans, 0, sizeof(achans));
2194170531Ssam	memset(reported, 0, sizeof(reported));
2195138593Ssam	if (!allchans) {
2196138593Ssam		struct ieee80211req_chanlist active;
2197138593Ssam
2198173275Ssam		if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0)
2199138593Ssam			errx(1, "unable to get active channel list");
2200138593Ssam		memset(&achans, 0, sizeof(achans));
2201173275Ssam		for (i = 0; i < chans->ic_nchans; i++) {
2202173275Ssam			c = &chans->ic_chans[i];
2203170531Ssam			if (!isset(active.ic_channels, c->ic_ieee))
2204170531Ssam				continue;
2205170531Ssam			/*
2206170531Ssam			 * Suppress compatible duplicates unless
2207170531Ssam			 * verbose.  The kernel gives us it's
2208170531Ssam			 * complete channel list which has separate
2209170531Ssam			 * entries for 11g/11b and 11a/turbo.
2210170531Ssam			 */
2211173275Ssam			if (isset(reported, c->ic_ieee) && !verb) {
2212170531Ssam				/* XXX we assume duplicates are adjacent */
2213170531Ssam				achans.ic_chans[achans.ic_nchans-1] = *c;
2214170531Ssam			} else {
2215138593Ssam				achans.ic_chans[achans.ic_nchans++] = *c;
2216170531Ssam				setbit(reported, c->ic_ieee);
2217170531Ssam			}
2218138593Ssam		}
2219170531Ssam	} else {
2220173275Ssam		for (i = 0; i < chans->ic_nchans; i++) {
2221173275Ssam			c = &chans->ic_chans[i];
2222170531Ssam			/* suppress duplicates as above */
2223173275Ssam			if (isset(reported, c->ic_ieee) && !verb) {
2224170531Ssam				/* XXX we assume duplicates are adjacent */
2225170531Ssam				achans.ic_chans[achans.ic_nchans-1] = *c;
2226170531Ssam			} else {
2227170531Ssam				achans.ic_chans[achans.ic_nchans++] = *c;
2228170531Ssam				setbit(reported, c->ic_ieee);
2229170531Ssam			}
2230170531Ssam		}
2231170531Ssam	}
2232138593Ssam	half = achans.ic_nchans / 2;
2233138593Ssam	if (achans.ic_nchans % 2)
2234138593Ssam		half++;
2235170531Ssam
2236138593Ssam	for (i = 0; i < achans.ic_nchans / 2; i++) {
2237173275Ssam		print_chaninfo(&achans.ic_chans[i], verb);
2238173275Ssam		print_chaninfo(&achans.ic_chans[half+i], verb);
2239138593Ssam		printf("\n");
2240138593Ssam	}
2241138593Ssam	if (achans.ic_nchans % 2) {
2242173275Ssam		print_chaninfo(&achans.ic_chans[i], verb);
2243138593Ssam		printf("\n");
2244138593Ssam	}
2245138593Ssam}
2246138593Ssam
2247138593Ssamstatic void
2248173275Ssamlist_channels(int s, int allchans)
2249173275Ssam{
2250173275Ssam	getchaninfo(s);
2251173275Ssam	print_channels(s, &chaninfo, allchans, verbose);
2252173275Ssam}
2253173275Ssam
2254173275Ssamstatic void
2255170531Ssamprint_txpow(const struct ieee80211_channel *c)
2256170531Ssam{
2257170531Ssam	printf("Channel %3u : %u Mhz %3.1f reg %2d  ",
2258170531Ssam	    c->ic_ieee, c->ic_freq,
2259170531Ssam	    c->ic_maxpower/2., c->ic_maxregpower);
2260170531Ssam}
2261170531Ssam
2262170531Ssamstatic void
2263170531Ssamprint_txpow_verbose(const struct ieee80211_channel *c)
2264170531Ssam{
2265173275Ssam	print_chaninfo(c, 1);
2266170531Ssam	printf("min %4.1f dBm  max %3.1f dBm  reg %2d dBm",
2267170531Ssam	    c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
2268170531Ssam	/* indicate where regulatory cap limits power use */
2269170531Ssam	if (c->ic_maxpower > 2*c->ic_maxregpower)
2270170531Ssam		printf(" <");
2271170531Ssam}
2272170531Ssam
2273170531Ssamstatic void
2274170531Ssamlist_txpow(int s)
2275170531Ssam{
2276170531Ssam	struct ieee80211req_chaninfo achans;
2277170531Ssam	uint8_t reported[IEEE80211_CHAN_BYTES];
2278170531Ssam	struct ieee80211_channel *c, *prev;
2279170531Ssam	int i, half;
2280170531Ssam
2281170531Ssam	getchaninfo(s);
2282170531Ssam	memset(&achans, 0, sizeof(achans));
2283170531Ssam	memset(reported, 0, sizeof(reported));
2284170531Ssam	for (i = 0; i < chaninfo.ic_nchans; i++) {
2285170531Ssam		c = &chaninfo.ic_chans[i];
2286170531Ssam		/* suppress duplicates as above */
2287170531Ssam		if (isset(reported, c->ic_ieee) && !verbose) {
2288170531Ssam			/* XXX we assume duplicates are adjacent */
2289170531Ssam			prev = &achans.ic_chans[achans.ic_nchans-1];
2290170531Ssam			/* display highest power on channel */
2291170531Ssam			if (c->ic_maxpower > prev->ic_maxpower)
2292170531Ssam				*prev = *c;
2293170531Ssam		} else {
2294170531Ssam			achans.ic_chans[achans.ic_nchans++] = *c;
2295170531Ssam			setbit(reported, c->ic_ieee);
2296170531Ssam		}
2297170531Ssam	}
2298170531Ssam	if (!verbose) {
2299170531Ssam		half = achans.ic_nchans / 2;
2300170531Ssam		if (achans.ic_nchans % 2)
2301170531Ssam			half++;
2302170531Ssam
2303170531Ssam		for (i = 0; i < achans.ic_nchans / 2; i++) {
2304170531Ssam			print_txpow(&achans.ic_chans[i]);
2305170531Ssam			print_txpow(&achans.ic_chans[half+i]);
2306170531Ssam			printf("\n");
2307170531Ssam		}
2308170531Ssam		if (achans.ic_nchans % 2) {
2309170531Ssam			print_txpow(&achans.ic_chans[i]);
2310170531Ssam			printf("\n");
2311170531Ssam		}
2312170531Ssam	} else {
2313170531Ssam		for (i = 0; i < achans.ic_nchans; i++) {
2314170531Ssam			print_txpow_verbose(&achans.ic_chans[i]);
2315170531Ssam			printf("\n");
2316170531Ssam		}
2317170531Ssam	}
2318170531Ssam}
2319170531Ssam
2320170531Ssamstatic void
2321138593Ssamlist_keys(int s)
2322138593Ssam{
2323138593Ssam}
2324138593Ssam
2325138593Ssam#define	IEEE80211_C_BITS \
2326170531Ssam"\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\7FF\10TURBOP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \
2327138593Ssam"\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \
2328170531Ssam"\31WPA2\32BURST\33WME\34WDS\36BGSCAN\37TXFRAG"
2329138593Ssam
2330138593Ssamstatic void
2331138593Ssamlist_capabilities(int s)
2332138593Ssam{
2333138593Ssam	struct ieee80211req ireq;
2334138593Ssam	u_int32_t caps;
2335138593Ssam
2336138593Ssam	(void) memset(&ireq, 0, sizeof(ireq));
2337138593Ssam	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2338138593Ssam	ireq.i_type = IEEE80211_IOC_DRIVER_CAPS;
2339138593Ssam	if (ioctl(s, SIOCG80211, &ireq) < 0)
2340138593Ssam		errx(1, "unable to get driver capabilities");
2341138593Ssam	caps = (((u_int16_t) ireq.i_val) << 16) | ((u_int16_t) ireq.i_len);
2342138593Ssam	printb(name, caps, IEEE80211_C_BITS);
2343138593Ssam	putchar('\n');
2344138593Ssam}
2345138593Ssam
2346173275Ssamstatic int
2347173275Ssamget80211wme(int s, int param, int ac, int *val)
2348173275Ssam{
2349173275Ssam	struct ieee80211req ireq;
2350173275Ssam
2351173275Ssam	(void) memset(&ireq, 0, sizeof(ireq));
2352173275Ssam	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2353173275Ssam	ireq.i_type = param;
2354173275Ssam	ireq.i_len = ac;
2355173275Ssam	if (ioctl(s, SIOCG80211, &ireq) < 0) {
2356173275Ssam		warn("cannot get WME parameter %d, ac %d%s",
2357173275Ssam		    param, ac & IEEE80211_WMEPARAM_VAL,
2358173275Ssam		    ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : "");
2359173275Ssam		return -1;
2360173275Ssam	}
2361173275Ssam	*val = ireq.i_val;
2362173275Ssam	return 0;
2363173275Ssam}
2364173275Ssam
2365138593Ssamstatic void
2366138593Ssamlist_wme(int s)
2367138593Ssam{
2368138593Ssam	static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
2369173275Ssam	int ac, val;
2370138593Ssam
2371138593Ssam	for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
2372138593Ssamagain:
2373173275Ssam		if (ac & IEEE80211_WMEPARAM_BSS)
2374138593Ssam			printf("\t%s", "     ");
2375138593Ssam		else
2376138593Ssam			printf("\t%s", acnames[ac]);
2377138593Ssam
2378138593Ssam		/* show WME BSS parameters */
2379173275Ssam		if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1)
2380173275Ssam			printf(" cwmin %2u", val);
2381173275Ssam		if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1)
2382173275Ssam			printf(" cwmax %2u", val);
2383173275Ssam		if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1)
2384173275Ssam			printf(" aifs %2u", val);
2385173275Ssam		if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1)
2386173275Ssam			printf(" txopLimit %3u", val);
2387173275Ssam		if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) {
2388173275Ssam			if (val)
2389138593Ssam				printf(" acm");
2390138593Ssam			else if (verbose)
2391138593Ssam				printf(" -acm");
2392138593Ssam		}
2393138593Ssam		/* !BSS only */
2394173275Ssam		if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
2395173275Ssam			if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) {
2396173275Ssam				if (!val)
2397138593Ssam					printf(" -ack");
2398138593Ssam				else if (verbose)
2399138593Ssam					printf(" ack");
2400138593Ssam			}
2401138593Ssam		}
2402138593Ssam		printf("\n");
2403173275Ssam		if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
2404173275Ssam			ac |= IEEE80211_WMEPARAM_BSS;
2405138593Ssam			goto again;
2406138593Ssam		} else
2407173275Ssam			ac &= ~IEEE80211_WMEPARAM_BSS;
2408138593Ssam	}
2409138593Ssam}
2410138593Ssam
2411149029Ssamstatic void
2412173275Ssamprintpolicy(int policy)
2413173275Ssam{
2414173275Ssam	switch (policy) {
2415173275Ssam	case IEEE80211_MACCMD_POLICY_OPEN:
2416173275Ssam		printf("policy: open\n");
2417173275Ssam		break;
2418173275Ssam	case IEEE80211_MACCMD_POLICY_ALLOW:
2419173275Ssam		printf("policy: allow\n");
2420173275Ssam		break;
2421173275Ssam	case IEEE80211_MACCMD_POLICY_DENY:
2422173275Ssam		printf("policy: deny\n");
2423173275Ssam		break;
2424173275Ssam	default:
2425173275Ssam		printf("policy: unknown (%u)\n", policy);
2426173275Ssam		break;
2427173275Ssam	}
2428173275Ssam}
2429173275Ssam
2430173275Ssamstatic void
2431149029Ssamlist_mac(int s)
2432149029Ssam{
2433149029Ssam	struct ieee80211req ireq;
2434149029Ssam	struct ieee80211req_maclist *acllist;
2435173275Ssam	int i, nacls, policy, len;
2436173275Ssam	uint8_t *data;
2437149029Ssam	char c;
2438149029Ssam
2439149029Ssam	(void) memset(&ireq, 0, sizeof(ireq));
2440149029Ssam	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
2441149029Ssam	ireq.i_type = IEEE80211_IOC_MACCMD;
2442149029Ssam	ireq.i_val = IEEE80211_MACCMD_POLICY;
2443149029Ssam	if (ioctl(s, SIOCG80211, &ireq) < 0) {
2444149029Ssam		if (errno == EINVAL) {
2445149029Ssam			printf("No acl policy loaded\n");
2446149029Ssam			return;
2447149029Ssam		}
2448149029Ssam		err(1, "unable to get mac policy");
2449149029Ssam	}
2450149029Ssam	policy = ireq.i_val;
2451149029Ssam	if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
2452149029Ssam		c = '*';
2453149029Ssam	} else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
2454149029Ssam		c = '+';
2455149029Ssam	} else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
2456149029Ssam		c = '-';
2457149029Ssam	} else {
2458149029Ssam		printf("policy: unknown (%u)\n", policy);
2459149029Ssam		c = '?';
2460149029Ssam	}
2461173275Ssam	if (verbose || c == '?')
2462173275Ssam		printpolicy(policy);
2463173275Ssam
2464175952Ssam	ireq.i_val = IEEE80211_MACCMD_LIST;
2465175952Ssam	ireq.i_len = 0;
2466175952Ssam	if (ioctl(s, SIOCG80211, &ireq) < 0)
2467173275Ssam		err(1, "unable to get mac acl list size");
2468175952Ssam	if (ireq.i_len == 0) {		/* NB: no acls */
2469173275Ssam		if (!(verbose || c == '?'))
2470173275Ssam			printpolicy(policy);
2471173275Ssam		return;
2472173275Ssam	}
2473175952Ssam	len = ireq.i_len;
2474173275Ssam
2475173275Ssam	data = malloc(len);
2476173275Ssam	if (data == NULL)
2477173275Ssam		err(1, "out of memory for acl list");
2478173275Ssam
2479175952Ssam	ireq.i_data = data;
2480175952Ssam	if (ioctl(s, SIOCG80211, &ireq) < 0)
2481173275Ssam		err(1, "unable to get mac acl list");
2482173275Ssam	nacls = len / sizeof(*acllist);
2483173275Ssam	acllist = (struct ieee80211req_maclist *) data;
2484149029Ssam	for (i = 0; i < nacls; i++)
2485149029Ssam		printf("%c%s\n", c, ether_ntoa(
2486149029Ssam			(const struct ether_addr *) acllist[i].ml_macaddr));
2487173275Ssam	free(data);
2488149029Ssam}
2489149029Ssam
2490138593Ssamstatic
2491138593SsamDECL_CMD_FUNC(set80211list, arg, d)
2492138593Ssam{
2493138593Ssam#define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
2494138593Ssam
2495173275Ssam	LINE_INIT('\t');
2496173275Ssam
2497138593Ssam	if (iseq(arg, "sta"))
2498138593Ssam		list_stations(s);
2499138593Ssam	else if (iseq(arg, "scan") || iseq(arg, "ap"))
2500138593Ssam		list_scan(s);
2501138593Ssam	else if (iseq(arg, "chan") || iseq(arg, "freq"))
2502138593Ssam		list_channels(s, 1);
2503138593Ssam	else if (iseq(arg, "active"))
2504138593Ssam		list_channels(s, 0);
2505138593Ssam	else if (iseq(arg, "keys"))
2506138593Ssam		list_keys(s);
2507138593Ssam	else if (iseq(arg, "caps"))
2508138593Ssam		list_capabilities(s);
2509138593Ssam	else if (iseq(arg, "wme"))
2510138593Ssam		list_wme(s);
2511149029Ssam	else if (iseq(arg, "mac"))
2512149029Ssam		list_mac(s);
2513170531Ssam	else if (iseq(arg, "txpow"))
2514170531Ssam		list_txpow(s);
2515138593Ssam	else
2516138593Ssam		errx(1, "Don't know how to list %s for %s", arg, name);
2517138593Ssam#undef iseq
2518138593Ssam}
2519138593Ssam
2520138593Ssamstatic enum ieee80211_opmode
2521138593Ssamget80211opmode(int s)
2522138593Ssam{
2523138593Ssam	struct ifmediareq ifmr;
2524138593Ssam
2525138593Ssam	(void) memset(&ifmr, 0, sizeof(ifmr));
2526138593Ssam	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
2527138593Ssam
2528138593Ssam	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
2529138593Ssam		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC)
2530138593Ssam			return IEEE80211_M_IBSS;	/* XXX ahdemo */
2531138593Ssam		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
2532138593Ssam			return IEEE80211_M_HOSTAP;
2533138593Ssam		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
2534138593Ssam			return IEEE80211_M_MONITOR;
2535138593Ssam	}
2536138593Ssam	return IEEE80211_M_STA;
2537138593Ssam}
2538138593Ssam
2539138593Ssam#if 0
2540138593Ssamstatic void
2541138593Ssamprintcipher(int s, struct ieee80211req *ireq, int keylenop)
2542138593Ssam{
2543138593Ssam	switch (ireq->i_val) {
2544138593Ssam	case IEEE80211_CIPHER_WEP:
2545138593Ssam		ireq->i_type = keylenop;
2546138593Ssam		if (ioctl(s, SIOCG80211, ireq) != -1)
2547138593Ssam			printf("WEP-%s",
2548138593Ssam			    ireq->i_len <= 5 ? "40" :
2549138593Ssam			    ireq->i_len <= 13 ? "104" : "128");
2550138593Ssam		else
2551138593Ssam			printf("WEP");
2552138593Ssam		break;
2553138593Ssam	case IEEE80211_CIPHER_TKIP:
2554138593Ssam		printf("TKIP");
2555138593Ssam		break;
2556138593Ssam	case IEEE80211_CIPHER_AES_OCB:
2557138593Ssam		printf("AES-OCB");
2558138593Ssam		break;
2559138593Ssam	case IEEE80211_CIPHER_AES_CCM:
2560138593Ssam		printf("AES-CCM");
2561138593Ssam		break;
2562138593Ssam	case IEEE80211_CIPHER_CKIP:
2563138593Ssam		printf("CKIP");
2564138593Ssam		break;
2565138593Ssam	case IEEE80211_CIPHER_NONE:
2566138593Ssam		printf("NONE");
2567138593Ssam		break;
2568138593Ssam	default:
2569138593Ssam		printf("UNKNOWN (0x%x)", ireq->i_val);
2570138593Ssam		break;
2571138593Ssam	}
2572138593Ssam}
2573138593Ssam#endif
2574138593Ssam
2575155931Ssamstatic void
2576138593Ssamprintkey(const struct ieee80211req_key *ik)
2577138593Ssam{
2578138593Ssam	static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
2579138593Ssam	int keylen = ik->ik_keylen;
2580138593Ssam	int printcontents;
2581138593Ssam
2582148001Srwatson	printcontents = printkeys &&
2583138593Ssam		(memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
2584138593Ssam	if (printcontents)
2585138593Ssam		LINE_BREAK();
2586138593Ssam	switch (ik->ik_type) {
2587138593Ssam	case IEEE80211_CIPHER_WEP:
2588138593Ssam		/* compatibility */
2589155931Ssam		LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
2590138593Ssam		    keylen <= 5 ? "40-bit" :
2591138593Ssam		    keylen <= 13 ? "104-bit" : "128-bit");
2592138593Ssam		break;
2593138593Ssam	case IEEE80211_CIPHER_TKIP:
2594138593Ssam		if (keylen > 128/8)
2595138593Ssam			keylen -= 128/8;	/* ignore MIC for now */
2596155931Ssam		LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2597138593Ssam		break;
2598138593Ssam	case IEEE80211_CIPHER_AES_OCB:
2599155931Ssam		LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2600138593Ssam		break;
2601138593Ssam	case IEEE80211_CIPHER_AES_CCM:
2602155931Ssam		LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2603138593Ssam		break;
2604138593Ssam	case IEEE80211_CIPHER_CKIP:
2605155931Ssam		LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2606138593Ssam		break;
2607138593Ssam	case IEEE80211_CIPHER_NONE:
2608155931Ssam		LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2609138593Ssam		break;
2610138593Ssam	default:
2611155931Ssam		LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
2612138593Ssam			ik->ik_type, ik->ik_keyix+1, 8*keylen);
2613138593Ssam		break;
2614138593Ssam	}
2615138593Ssam	if (printcontents) {
2616138593Ssam		int i;
2617138593Ssam
2618138593Ssam		printf(" <");
2619138593Ssam		for (i = 0; i < keylen; i++)
2620138593Ssam			printf("%02x", ik->ik_keydata[i]);
2621138593Ssam		printf(">");
2622138593Ssam		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
2623138593Ssam		    (ik->ik_keyrsc != 0 || verbose))
2624146873Sjhb			printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
2625138593Ssam		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
2626138593Ssam		    (ik->ik_keytsc != 0 || verbose))
2627146873Sjhb			printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
2628138593Ssam		if (ik->ik_flags != 0 && verbose) {
2629138593Ssam			const char *sep = " ";
2630138593Ssam
2631138593Ssam			if (ik->ik_flags & IEEE80211_KEY_XMIT)
2632138593Ssam				printf("%stx", sep), sep = "+";
2633138593Ssam			if (ik->ik_flags & IEEE80211_KEY_RECV)
2634138593Ssam				printf("%srx", sep), sep = "+";
2635138593Ssam			if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
2636138593Ssam				printf("%sdef", sep), sep = "+";
2637138593Ssam		}
2638138593Ssam		LINE_BREAK();
2639138593Ssam	}
2640138593Ssam}
2641138593Ssam
2642138593Ssamstatic void
2643173275Ssamprintrate(const char *tag, int v, int defrate, int defmcs)
2644138593Ssam{
2645173275Ssam	if (v == 11)
2646173275Ssam		LINE_CHECK("%s 5.5", tag);
2647173275Ssam	else if (v & 0x80) {
2648173275Ssam		if (v != defmcs)
2649173275Ssam			LINE_CHECK("%s %d", tag, v &~ 0x80);
2650173275Ssam	} else {
2651173275Ssam		if (v != defrate)
2652173275Ssam			LINE_CHECK("%s %d", tag, v/2);
2653173275Ssam	}
2654173275Ssam}
2655173275Ssam
2656173275Ssamstatic int
2657173275Ssamgetssid(int s, int ix, void *data, size_t len, int *plen)
2658173275Ssam{
2659138593Ssam	struct ieee80211req ireq;
2660138593Ssam
2661138593Ssam	(void) memset(&ireq, 0, sizeof(ireq));
2662138593Ssam	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2663173275Ssam	ireq.i_type = IEEE80211_IOC_SSID;
2664173275Ssam	ireq.i_val = ix;
2665173275Ssam	ireq.i_data = data;
2666173275Ssam	ireq.i_len = len;
2667173275Ssam	if (ioctl(s, SIOCG80211, &ireq) < 0)
2668173275Ssam		return -1;
2669173275Ssam	*plen = ireq.i_len;
2670173275Ssam	return 0;
2671173275Ssam}
267277218Sphk
2673173275Ssamstatic void
2674173275Ssamprintrssi(const char *tag, int rssi)
2675173275Ssam{
2676173275Ssam	if (rssi & 1)
2677173275Ssam		LINE_CHECK("%s %u.5", tag, rssi/2);
2678173275Ssam	else
2679173275Ssam		LINE_CHECK("%s %u", tag, rssi/2);
2680173275Ssam}
2681138593Ssam
2682173275Ssamstatic void
2683173275Ssamieee80211_status(int s)
2684173275Ssam{
2685173275Ssam	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
2686173275Ssam	enum ieee80211_opmode opmode = get80211opmode(s);
2687173275Ssam	int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode;
2688173275Ssam	uint8_t data[32];
2689173275Ssam	const struct ieee80211_channel *c;
2690173275Ssam
2691173275Ssam	if (getssid(s, -1, data, sizeof(data), &len) < 0) {
2692148686Sstefanf		/* If we can't get the SSID, this isn't an 802.11 device. */
269377218Sphk		return;
269477218Sphk	}
2695173275Ssam
2696173275Ssam	/*
2697173275Ssam	 * Invalidate cached state so printing status for multiple
2698173275Ssam	 * if's doesn't reuse the first interfaces' cached state.
2699173275Ssam	 */
2700173275Ssam	gotcurchan = 0;
2701173275Ssam	gothtconf = 0;
2702173275Ssam
2703173275Ssam	if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
2704173275Ssam		num = 0;
2705138593Ssam	printf("\tssid ");
2706138593Ssam	if (num > 1) {
2707173275Ssam		for (i = 0; i < num; i++) {
2708173275Ssam			if (getssid(s, i, data, sizeof(data), &len) >= 0 && len > 0) {
2709173275Ssam				printf(" %d:", i + 1);
2710173275Ssam				print_string(data, len);
2711138593Ssam			}
271288748Sambrisko		}
2713138593Ssam	} else
2714173275Ssam		print_string(data, len);
271577218Sphk
2716173275Ssam	c = getcurchan(s);
2717170531Ssam	if (c->ic_freq != IEEE80211_CHAN_ANY) {
2718170531Ssam		char buf[14];
2719170531Ssam		printf(" channel %d (%u Mhz%s)", c->ic_ieee, c->ic_freq,
2720170531Ssam			get_chaninfo(c, 1, buf, sizeof(buf)));
2721138593Ssam	} else if (verbose)
2722138593Ssam		printf(" channel UNDEF");
2723138593Ssam
2724173275Ssam	if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 &&
2725173275Ssam	    (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
2726173275Ssam		printf(" bssid %s", ether_ntoa((struct ether_addr *)data));
2727138593Ssam
2728173275Ssam	if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) {
2729138593Ssam		printf("\n\tstationname ");
2730173275Ssam		print_string(data, len);
273177218Sphk	}
273277218Sphk
2733138593Ssam	spacer = ' ';		/* force first break */
2734138593Ssam	LINE_BREAK();
273577218Sphk
2736173275Ssam	wpa = 0;
2737173275Ssam	if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) {
2738173275Ssam		switch (val) {
2739173275Ssam		case IEEE80211_AUTH_NONE:
2740173275Ssam			LINE_CHECK("authmode NONE");
2741173275Ssam			break;
2742173275Ssam		case IEEE80211_AUTH_OPEN:
2743173275Ssam			LINE_CHECK("authmode OPEN");
2744173275Ssam			break;
2745173275Ssam		case IEEE80211_AUTH_SHARED:
2746173275Ssam			LINE_CHECK("authmode SHARED");
2747173275Ssam			break;
2748173275Ssam		case IEEE80211_AUTH_8021X:
2749173275Ssam			LINE_CHECK("authmode 802.1x");
2750173275Ssam			break;
2751173275Ssam		case IEEE80211_AUTH_WPA:
2752173275Ssam			if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0)
2753173275Ssam				wpa = 1;	/* default to WPA1 */
2754173275Ssam			switch (wpa) {
2755173275Ssam			case 2:
2756173275Ssam				LINE_CHECK("authmode WPA2/802.11i");
275777218Sphk				break;
2758173275Ssam			case 3:
2759173275Ssam				LINE_CHECK("authmode WPA1+WPA2/802.11i");
276077218Sphk				break;
2761127649Ssam			default:
2762173275Ssam				LINE_CHECK("authmode WPA");
2763127649Ssam				break;
2764173275Ssam			}
2765173275Ssam			break;
2766173275Ssam		case IEEE80211_AUTH_AUTO:
2767173275Ssam			LINE_CHECK("authmode AUTO");
2768173275Ssam			break;
2769173275Ssam		default:
2770173275Ssam			LINE_CHECK("authmode UNKNOWN (0x%x)", val);
2771173275Ssam			break;
2772127649Ssam		}
2773127649Ssam	}
2774127649Ssam
2775173275Ssam	if (wpa || verbose) {
2776173275Ssam		if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) {
2777173275Ssam			if (val)
2778173275Ssam				LINE_CHECK("countermeasures");
2779173275Ssam			else if (verbose)
2780173275Ssam				LINE_CHECK("-countermeasures");
2781173275Ssam		}
2782173275Ssam	}
2783138593Ssam
2784173275Ssam	if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 &&
2785173275Ssam	    wepmode != IEEE80211_WEP_NOSUP) {
2786173275Ssam		int firstkey;
2787173275Ssam
2788138718Ssam		switch (wepmode) {
2789173275Ssam		case IEEE80211_WEP_OFF:
2790173275Ssam			LINE_CHECK("privacy OFF");
2791173275Ssam			break;
2792173275Ssam		case IEEE80211_WEP_ON:
2793173275Ssam			LINE_CHECK("privacy ON");
2794173275Ssam			break;
2795173275Ssam		case IEEE80211_WEP_MIXED:
2796173275Ssam			LINE_CHECK("privacy MIXED");
2797173275Ssam			break;
2798173275Ssam		default:
2799173275Ssam			LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
2800173275Ssam			break;
280177218Sphk		}
280277218Sphk
280377218Sphk		/*
280477218Sphk		 * If we get here then we've got WEP support so we need
280577218Sphk		 * to print WEP status.
280691454Sbrooks		 */
280777218Sphk
2808173275Ssam		if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) {
280977218Sphk			warn("WEP support, but no tx key!");
281077218Sphk			goto end;
281177218Sphk		}
2812173275Ssam		if (val != -1)
2813173275Ssam			LINE_CHECK("deftxkey %d", val+1);
2814138718Ssam		else if (wepmode != IEEE80211_WEP_OFF || verbose)
2815155931Ssam			LINE_CHECK("deftxkey UNDEF");
281677218Sphk
2817173275Ssam		if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) {
281877218Sphk			warn("WEP support, but no NUMWEPKEYS support!");
281977218Sphk			goto end;
282077218Sphk		}
282177218Sphk
2822138593Ssam		firstkey = 1;
2823138593Ssam		for (i = 0; i < num; i++) {
2824138593Ssam			struct ieee80211req_key ik;
282577218Sphk
2826138593Ssam			memset(&ik, 0, sizeof(ik));
2827138593Ssam			ik.ik_keyix = i;
2828173275Ssam			if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) {
282977218Sphk				warn("WEP support, but can get keys!");
283077218Sphk				goto end;
283177218Sphk			}
2832138593Ssam			if (ik.ik_keylen != 0) {
2833138593Ssam				if (verbose)
2834138593Ssam					LINE_BREAK();
2835138593Ssam				printkey(&ik);
2836138593Ssam				firstkey = 0;
2837138593Ssam			}
2838138593Ssam		}
2839173275Ssamend:
2840173275Ssam		;
2841138593Ssam	}
2842138593Ssam
2843173275Ssam	if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 &&
2844173275Ssam	    val != IEEE80211_POWERSAVE_NOSUP ) {
2845173275Ssam		if (val != IEEE80211_POWERSAVE_OFF || verbose) {
2846173275Ssam			switch (val) {
2847173275Ssam			case IEEE80211_POWERSAVE_OFF:
2848173275Ssam				LINE_CHECK("powersavemode OFF");
2849173275Ssam				break;
2850173275Ssam			case IEEE80211_POWERSAVE_CAM:
2851173275Ssam				LINE_CHECK("powersavemode CAM");
2852173275Ssam				break;
2853173275Ssam			case IEEE80211_POWERSAVE_PSP:
2854173275Ssam				LINE_CHECK("powersavemode PSP");
2855173275Ssam				break;
2856173275Ssam			case IEEE80211_POWERSAVE_PSP_CAM:
2857173275Ssam				LINE_CHECK("powersavemode PSP-CAM");
2858173275Ssam				break;
2859138593Ssam			}
2860173275Ssam			if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1)
2861173275Ssam				LINE_CHECK("powersavesleep %d", val);
2862138593Ssam		}
2863138593Ssam	}
2864138593Ssam
2865173275Ssam	if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) {
2866173275Ssam		if (val & 1)
2867173275Ssam			LINE_CHECK("txpower %d.5", val/2);
2868173275Ssam		else
2869173275Ssam			LINE_CHECK("txpower %d", val/2);
2870173275Ssam	}
2871138593Ssam	if (verbose) {
2872173275Ssam		if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1)
2873173275Ssam			LINE_CHECK("txpowmax %.1f", val/2.);
2874138593Ssam	}
2875138593Ssam
2876173275Ssam	if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) {
2877173275Ssam		if (val != IEEE80211_RTS_MAX || verbose)
2878173275Ssam			LINE_CHECK("rtsthreshold %d", val);
2879138593Ssam	}
2880138593Ssam
2881173275Ssam	if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) {
2882173275Ssam		if (val != IEEE80211_FRAG_MAX || verbose)
2883173275Ssam			LINE_CHECK("fragthreshold %d", val);
2884170531Ssam	}
2885173275Ssam	if (opmode == IEEE80211_M_STA || verbose) {
2886173275Ssam		if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) {
2887173275Ssam			if (val != IEEE80211_HWBMISS_MAX || verbose)
2888173275Ssam				LINE_CHECK("bmiss %d", val);
2889153354Ssam		}
2890153354Ssam	}
2891153354Ssam
2892173275Ssam	if (get80211val(s, IEEE80211_IOC_MCAST_RATE, &val) != -1)
2893173275Ssam		printrate("mcastrate", val, 2*1, 0/*XXX*/);
2894170531Ssam
2895173275Ssam	bgscaninterval = -1;
2896173275Ssam	(void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval);
2897173275Ssam
2898173275Ssam	if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) {
2899173275Ssam		if (val != bgscaninterval || verbose)
2900173275Ssam			LINE_CHECK("scanvalid %u", val);
2901148416Ssam	}
2902148416Ssam
2903173275Ssam	bgscan = 0;
2904173275Ssam	if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) {
2905173275Ssam		if (bgscan)
2906170531Ssam			LINE_CHECK("bgscan");
2907170531Ssam		else if (verbose)
2908170531Ssam			LINE_CHECK("-bgscan");
2909160687Ssam	}
2910170531Ssam	if (bgscan || verbose) {
2911170531Ssam		if (bgscaninterval != -1)
2912170531Ssam			LINE_CHECK("bgscanintvl %u", bgscaninterval);
2913173275Ssam		if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1)
2914173275Ssam			LINE_CHECK("bgscanidle %u", val);
2915170531Ssam		if (IEEE80211_IS_CHAN_A(c) || verbose) {
2916173275Ssam			if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11A, &val) != -1)
2917173275Ssam				printrssi("roam:rssi11a", val);
2918173275Ssam			if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11A, &val) != -1)
2919173275Ssam				printrate("roam:rate11a", val, -1, -1);
2920170531Ssam		}
2921170531Ssam		if (IEEE80211_IS_CHAN_B(c) || verbose) {
2922173275Ssam			if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11B, &val) != -1)
2923173275Ssam				printrssi("roam:rssi11b", val);
2924173275Ssam			if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11B, &val) != -1)
2925173275Ssam				printrate("roam:rate11b", val, -1, -1);
2926170531Ssam		}
2927170531Ssam		if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
2928173275Ssam			if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11G, &val) != -1)
2929173275Ssam				printrssi("roam:rssi11g", val);
2930173275Ssam			if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11G, &val) != -1)
2931173275Ssam				printrate("roam:rate11g", val, -1, -1);
2932170531Ssam		}
2933170531Ssam	}
2934160687Ssam
2935165570Ssam	if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
2936173275Ssam		if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) {
2937173275Ssam			if (val)
2938155931Ssam				LINE_CHECK("pureg");
2939147795Ssam			else if (verbose)
2940155931Ssam				LINE_CHECK("-pureg");
2941147795Ssam		}
2942173275Ssam		if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) {
2943173275Ssam			switch (val) {
2944173275Ssam			case IEEE80211_PROTMODE_OFF:
2945173275Ssam				LINE_CHECK("protmode OFF");
2946173275Ssam				break;
2947173275Ssam			case IEEE80211_PROTMODE_CTS:
2948173275Ssam				LINE_CHECK("protmode CTS");
2949173275Ssam				break;
2950173275Ssam			case IEEE80211_PROTMODE_RTSCTS:
2951173275Ssam				LINE_CHECK("protmode RTSCTS");
2952173275Ssam				break;
2953173275Ssam			default:
2954173275Ssam				LINE_CHECK("protmode UNKNOWN (0x%x)", val);
2955173275Ssam				break;
2956138593Ssam			}
2957138593Ssam		}
2958138593Ssam	}
2959138593Ssam
2960173275Ssam	if (IEEE80211_IS_CHAN_HT(c) || verbose) {
2961173275Ssam		gethtconf(s);
2962173275Ssam		switch (htconf & 3) {
2963173275Ssam		case 0:
2964173275Ssam		case 2:
2965173275Ssam			LINE_CHECK("-ht");
2966173275Ssam			break;
2967173275Ssam		case 1:
2968173275Ssam			LINE_CHECK("ht20");
2969173275Ssam			break;
2970173275Ssam		case 3:
2971173275Ssam			if (verbose)
2972173275Ssam				LINE_CHECK("ht");
2973173275Ssam			break;
2974173275Ssam		}
2975173275Ssam		if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) {
2976173275Ssam			if (!val)
2977173275Ssam				LINE_CHECK("-htcompat");
2978173275Ssam			else if (verbose)
2979173275Ssam				LINE_CHECK("htcompat");
2980173275Ssam		}
2981173275Ssam		if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) {
2982173275Ssam			switch (val) {
2983173275Ssam			case 0:
2984173275Ssam				LINE_CHECK("-ampdu");
2985173275Ssam				break;
2986173275Ssam			case 1:
2987173275Ssam				LINE_CHECK("ampdutx -ampdurx");
2988173275Ssam				break;
2989173275Ssam			case 2:
2990173275Ssam				LINE_CHECK("-ampdutx ampdurx");
2991173275Ssam				break;
2992173275Ssam			case 3:
2993173275Ssam				if (verbose)
2994173275Ssam					LINE_CHECK("ampdu");
2995173275Ssam				break;
2996173275Ssam			}
2997173275Ssam		}
2998173275Ssam		if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) {
2999173275Ssam			switch (val) {
3000173275Ssam			case IEEE80211_HTCAP_MAXRXAMPDU_8K:
3001173275Ssam				LINE_CHECK("ampdulimit 8k");
3002173275Ssam				break;
3003173275Ssam			case IEEE80211_HTCAP_MAXRXAMPDU_16K:
3004173275Ssam				LINE_CHECK("ampdulimit 16k");
3005173275Ssam				break;
3006173275Ssam			case IEEE80211_HTCAP_MAXRXAMPDU_32K:
3007173275Ssam				LINE_CHECK("ampdulimit 32k");
3008173275Ssam				break;
3009173275Ssam			case IEEE80211_HTCAP_MAXRXAMPDU_64K:
3010173275Ssam				LINE_CHECK("ampdulimit 64k");
3011173275Ssam				break;
3012173275Ssam			}
3013173275Ssam		}
3014173275Ssam		if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) {
3015173275Ssam			switch (val) {
3016173275Ssam			case IEEE80211_HTCAP_MPDUDENSITY_NA:
3017173275Ssam				if (verbose)
3018173275Ssam					LINE_CHECK("ampdudensity -");
3019173275Ssam				break;
3020173275Ssam			case IEEE80211_HTCAP_MPDUDENSITY_025:
3021173275Ssam				LINE_CHECK("ampdudensity .25");
3022173275Ssam				break;
3023173275Ssam			case IEEE80211_HTCAP_MPDUDENSITY_05:
3024173275Ssam				LINE_CHECK("ampdudensity .5");
3025173275Ssam				break;
3026173275Ssam			case IEEE80211_HTCAP_MPDUDENSITY_1:
3027173275Ssam				LINE_CHECK("ampdudensity 1");
3028173275Ssam				break;
3029173275Ssam			case IEEE80211_HTCAP_MPDUDENSITY_2:
3030173275Ssam				LINE_CHECK("ampdudensity 2");
3031173275Ssam				break;
3032173275Ssam			case IEEE80211_HTCAP_MPDUDENSITY_4:
3033173275Ssam				LINE_CHECK("ampdudensity 4");
3034173275Ssam				break;
3035173275Ssam			case IEEE80211_HTCAP_MPDUDENSITY_8:
3036173275Ssam				LINE_CHECK("ampdudensity 8");
3037173275Ssam				break;
3038173275Ssam			case IEEE80211_HTCAP_MPDUDENSITY_16:
3039173275Ssam				LINE_CHECK("ampdudensity 16");
3040173275Ssam				break;
3041173275Ssam			}
3042173275Ssam		}
3043173275Ssam		if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) {
3044173275Ssam			switch (val) {
3045173275Ssam			case 0:
3046173275Ssam				LINE_CHECK("-amsdu");
3047173275Ssam				break;
3048173275Ssam			case 1:
3049173275Ssam				LINE_CHECK("amsdutx -amsdurx");
3050173275Ssam				break;
3051173275Ssam			case 2:
3052173275Ssam				LINE_CHECK("-amsdutx amsdurx");
3053173275Ssam				break;
3054173275Ssam			case 3:
3055173275Ssam				if (verbose)
3056173275Ssam					LINE_CHECK("amsdu");
3057173275Ssam				break;
3058173275Ssam			}
3059173275Ssam		}
3060173275Ssam		/* XXX amsdu limit */
3061173275Ssam		/* XXX 20/40 */
3062173275Ssam		if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) {
3063173275Ssam			if (val)
3064173275Ssam				LINE_CHECK("shortgi");
3065173275Ssam			else if (verbose)
3066173275Ssam				LINE_CHECK("-shortgi");
3067173275Ssam		}
3068173275Ssam		if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) {
3069173275Ssam			if (val == IEEE80211_PROTMODE_OFF)
3070173275Ssam				LINE_CHECK("htprotmode OFF");
3071173275Ssam			else if (val != IEEE80211_PROTMODE_RTSCTS)
3072173275Ssam				LINE_CHECK("htprotmode UNKNOWN (0x%x)", val);
3073173275Ssam			else if (verbose)
3074173275Ssam				LINE_CHECK("htprotmode RTSCTS");
3075173275Ssam		}
3076173275Ssam		if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) {
3077173275Ssam			if (val)
3078173275Ssam				LINE_CHECK("puren");
3079173275Ssam			else if (verbose)
3080173275Ssam				LINE_CHECK("-puren");
3081173275Ssam		}
3082173275Ssam	}
3083173275Ssam
3084173275Ssam	if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) {
3085138593Ssam		if (wme)
3086155931Ssam			LINE_CHECK("wme");
3087138593Ssam		else if (verbose)
3088155931Ssam			LINE_CHECK("-wme");
3089138593Ssam	} else
3090138593Ssam		wme = 0;
3091138593Ssam
3092173275Ssam	if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) {
3093173275Ssam		if (val)
3094155931Ssam			LINE_CHECK("burst");
3095153422Ssam		else if (verbose)
3096155931Ssam			LINE_CHECK("-burst");
3097153422Ssam	}
3098153422Ssam
3099173275Ssam	if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) {
3100173275Ssam		if (val)
3101170531Ssam			LINE_CHECK("ff");
3102170531Ssam		else if (verbose)
3103170531Ssam			LINE_CHECK("-ff");
3104170531Ssam	}
3105173275Ssam	if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) {
3106173275Ssam		if (val)
3107170531Ssam			LINE_CHECK("dturbo");
3108170531Ssam		else if (verbose)
3109170531Ssam			LINE_CHECK("-dturbo");
3110170531Ssam	}
3111170531Ssam
3112138593Ssam	if (opmode == IEEE80211_M_HOSTAP) {
3113173275Ssam		if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) {
3114173275Ssam			if (val)
3115168075Ssam				LINE_CHECK("hidessid");
3116138593Ssam			else if (verbose)
3117168075Ssam				LINE_CHECK("-hidessid");
3118138593Ssam		}
3119173275Ssam		if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) {
3120173275Ssam			if (!val)
3121155931Ssam				LINE_CHECK("-apbridge");
3122138593Ssam			else if (verbose)
3123155931Ssam				LINE_CHECK("apbridge");
3124138593Ssam		}
3125173275Ssam		if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1)
3126173275Ssam			LINE_CHECK("dtimperiod %u", val);
3127138593Ssam
3128173275Ssam		if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) {
3129173275Ssam			if (!val)
3130170531Ssam				LINE_CHECK("-doth");
3131170531Ssam			else if (verbose)
3132170531Ssam				LINE_CHECK("doth");
3133170531Ssam		}
3134173275Ssam		if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) {
3135173275Ssam			if (!val)
3136173275Ssam				LINE_CHECK("-inact");
3137173275Ssam			else if (verbose)
3138173275Ssam				LINE_CHECK("inact");
3139173275Ssam		}
3140138593Ssam	} else {
3141173275Ssam		if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) {
3142173275Ssam			if (val != IEEE80211_ROAMING_AUTO || verbose) {
3143173275Ssam				switch (val) {
3144138593Ssam				case IEEE80211_ROAMING_DEVICE:
3145155931Ssam					LINE_CHECK("roaming DEVICE");
3146138593Ssam					break;
3147138593Ssam				case IEEE80211_ROAMING_AUTO:
3148155931Ssam					LINE_CHECK("roaming AUTO");
3149138593Ssam					break;
3150138593Ssam				case IEEE80211_ROAMING_MANUAL:
3151155931Ssam					LINE_CHECK("roaming MANUAL");
3152138593Ssam					break;
3153138593Ssam				default:
3154155931Ssam					LINE_CHECK("roaming UNKNOWN (0x%x)",
3155173275Ssam						val);
3156138593Ssam					break;
3157138593Ssam				}
3158138593Ssam			}
3159138593Ssam		}
3160138593Ssam	}
3161173275Ssam	if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) {
3162173275Ssam		/* XXX default define not visible */
3163173275Ssam		if (val != 100 || verbose)
3164173275Ssam			LINE_CHECK("bintval %u", val);
3165138593Ssam	}
3166138593Ssam
3167138593Ssam	if (wme && verbose) {
3168138593Ssam		LINE_BREAK();
3169138593Ssam		list_wme(s);
3170138593Ssam	}
3171173275Ssam	LINE_BREAK();
3172173275Ssam}
3173138593Ssam
3174173275Ssamstatic int
3175173275Ssamget80211(int s, int type, void *data, int len)
3176173275Ssam{
3177173275Ssam	struct ieee80211req ireq;
3178138593Ssam
3179173275Ssam	(void) memset(&ireq, 0, sizeof(ireq));
3180173275Ssam	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3181173275Ssam	ireq.i_type = type;
3182173275Ssam	ireq.i_data = data;
3183173275Ssam	ireq.i_len = len;
3184173275Ssam	return ioctl(s, SIOCG80211, &ireq);
3185173275Ssam}
3186138593Ssam
3187173275Ssamstatic int
3188173275Ssamget80211len(int s, int type, void *data, int len, int *plen)
3189173275Ssam{
3190173275Ssam	struct ieee80211req ireq;
3191138593Ssam
3192173275Ssam	(void) memset(&ireq, 0, sizeof(ireq));
3193173275Ssam	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3194173275Ssam	ireq.i_type = type;
3195173275Ssam	ireq.i_len = len;
3196173275Ssam	ireq.i_data = data;
3197173275Ssam	if (ioctl(s, SIOCG80211, &ireq) < 0)
3198173275Ssam		return -1;
3199173275Ssam	*plen = ireq.i_len;
3200173275Ssam	return 0;
3201173275Ssam}
3202138593Ssam
3203173275Ssamstatic int
3204173275Ssamget80211val(int s, int type, int *val)
3205173275Ssam{
3206173275Ssam	struct ieee80211req ireq;
320777218Sphk
3208173275Ssam	(void) memset(&ireq, 0, sizeof(ireq));
3209173275Ssam	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3210173275Ssam	ireq.i_type = type;
3211173275Ssam	if (ioctl(s, SIOCG80211, &ireq) < 0)
3212173275Ssam		return -1;
3213173275Ssam	*val = ireq.i_val;
3214173275Ssam	return 0;
321577218Sphk}
321677218Sphk
321777218Sphkstatic void
3218170531Ssamset80211(int s, int type, int val, int len, void *data)
321977218Sphk{
322077218Sphk	struct ieee80211req	ireq;
322177218Sphk
322277218Sphk	(void) memset(&ireq, 0, sizeof(ireq));
322377218Sphk	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
322477218Sphk	ireq.i_type = type;
322577218Sphk	ireq.i_val = val;
322677218Sphk	ireq.i_len = len;
322777218Sphk	ireq.i_data = data;
322891454Sbrooks	if (ioctl(s, SIOCS80211, &ireq) < 0)
322977218Sphk		err(1, "SIOCS80211");
323077218Sphk}
323177218Sphk
323277218Sphkstatic const char *
323377218Sphkget_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
323477218Sphk{
323577218Sphk	int len;
323677218Sphk	int hexstr;
323777218Sphk	u_int8_t *p;
323877218Sphk
323977218Sphk	len = *lenp;
324077218Sphk	p = buf;
324177218Sphk	hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
324277218Sphk	if (hexstr)
324377218Sphk		val += 2;
324477218Sphk	for (;;) {
324577218Sphk		if (*val == '\0')
324677218Sphk			break;
324777218Sphk		if (sep != NULL && strchr(sep, *val) != NULL) {
324877218Sphk			val++;
324977218Sphk			break;
325077218Sphk		}
325177218Sphk		if (hexstr) {
3252127831Sphk			if (!isxdigit((u_char)val[0])) {
325377218Sphk				warnx("bad hexadecimal digits");
325477218Sphk				return NULL;
325577218Sphk			}
3256127831Sphk			if (!isxdigit((u_char)val[1])) {
3257127831Sphk				warnx("odd count hexadecimal digits");
3258127831Sphk				return NULL;
3259127831Sphk			}
326077218Sphk		}
3261127831Sphk		if (p >= buf + len) {
326277218Sphk			if (hexstr)
326377218Sphk				warnx("hexadecimal digits too long");
326477218Sphk			else
3265127831Sphk				warnx("string too long");
326677218Sphk			return NULL;
326777218Sphk		}
326877218Sphk		if (hexstr) {
326977218Sphk#define	tohex(x)	(isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
327077218Sphk			*p++ = (tohex((u_char)val[0]) << 4) |
327177218Sphk			    tohex((u_char)val[1]);
327277218Sphk#undef tohex
327377218Sphk			val += 2;
327477218Sphk		} else
327577218Sphk			*p++ = *val++;
327677218Sphk	}
327777218Sphk	len = p - buf;
327877218Sphk	/* The string "-" is treated as the empty string. */
3279165045Ssam	if (!hexstr && len == 1 && buf[0] == '-') {
328077218Sphk		len = 0;
3281165045Ssam		memset(buf, 0, *lenp);
3282165045Ssam	} else if (len < *lenp)
328377218Sphk		memset(p, 0, *lenp - len);
328477218Sphk	*lenp = len;
328577218Sphk	return val;
328677218Sphk}
328777218Sphk
328877218Sphkstatic void
328977218Sphkprint_string(const u_int8_t *buf, int len)
329077218Sphk{
329177218Sphk	int i;
329277218Sphk	int hasspc;
329377218Sphk
329477218Sphk	i = 0;
329577218Sphk	hasspc = 0;
329691454Sbrooks	for (; i < len; i++) {
329777218Sphk		if (!isprint(buf[i]) && buf[i] != '\0')
329877218Sphk			break;
329977218Sphk		if (isspace(buf[i]))
330077218Sphk			hasspc++;
330177218Sphk	}
330277218Sphk	if (i == len) {
330377218Sphk		if (hasspc || len == 0 || buf[0] == '\0')
330477218Sphk			printf("\"%.*s\"", len, buf);
330577218Sphk		else
330677218Sphk			printf("%.*s", len, buf);
330777218Sphk	} else {
330877218Sphk		printf("0x");
330977218Sphk		for (i = 0; i < len; i++)
331077218Sphk			printf("%02x", buf[i]);
331177218Sphk	}
331277218Sphk}
331377218Sphk
3314138593Ssamstatic struct cmd ieee80211_cmds[] = {
3315138593Ssam	DEF_CMD_ARG("ssid",		set80211ssid),
3316138593Ssam	DEF_CMD_ARG("nwid",		set80211ssid),
3317138593Ssam	DEF_CMD_ARG("stationname",	set80211stationname),
3318138593Ssam	DEF_CMD_ARG("station",		set80211stationname),	/* BSD/OS */
3319138593Ssam	DEF_CMD_ARG("channel",		set80211channel),
3320138593Ssam	DEF_CMD_ARG("authmode",		set80211authmode),
3321138593Ssam	DEF_CMD_ARG("powersavemode",	set80211powersavemode),
3322138593Ssam	DEF_CMD("powersave",	1,	set80211powersave),
3323138593Ssam	DEF_CMD("-powersave",	0,	set80211powersave),
3324138593Ssam	DEF_CMD_ARG("powersavesleep", 	set80211powersavesleep),
3325138593Ssam	DEF_CMD_ARG("wepmode",		set80211wepmode),
3326138593Ssam	DEF_CMD("wep",		1,	set80211wep),
3327138593Ssam	DEF_CMD("-wep",		0,	set80211wep),
3328139493Ssam	DEF_CMD_ARG("deftxkey",		set80211weptxkey),
3329138593Ssam	DEF_CMD_ARG("weptxkey",		set80211weptxkey),
3330138593Ssam	DEF_CMD_ARG("wepkey",		set80211wepkey),
3331138593Ssam	DEF_CMD_ARG("nwkey",		set80211nwkey),		/* NetBSD */
3332138593Ssam	DEF_CMD("-nwkey",	0,	set80211wep),		/* NetBSD */
3333138593Ssam	DEF_CMD_ARG("rtsthreshold",	set80211rtsthreshold),
3334138593Ssam	DEF_CMD_ARG("protmode",		set80211protmode),
3335138593Ssam	DEF_CMD_ARG("txpower",		set80211txpower),
3336138593Ssam	DEF_CMD_ARG("roaming",		set80211roaming),
3337138593Ssam	DEF_CMD("wme",		1,	set80211wme),
3338138593Ssam	DEF_CMD("-wme",		0,	set80211wme),
3339138593Ssam	DEF_CMD("hidessid",	1,	set80211hidessid),
3340138593Ssam	DEF_CMD("-hidessid",	0,	set80211hidessid),
3341138593Ssam	DEF_CMD("apbridge",	1,	set80211apbridge),
3342138593Ssam	DEF_CMD("-apbridge",	0,	set80211apbridge),
3343138593Ssam	DEF_CMD_ARG("chanlist",		set80211chanlist),
3344138593Ssam	DEF_CMD_ARG("bssid",		set80211bssid),
3345138593Ssam	DEF_CMD_ARG("ap",		set80211bssid),
3346138593Ssam	DEF_CMD("scan",	0,		set80211scan),
3347138593Ssam	DEF_CMD_ARG("list",		set80211list),
3348138593Ssam	DEF_CMD_ARG2("cwmin",		set80211cwmin),
3349138593Ssam	DEF_CMD_ARG2("cwmax",		set80211cwmax),
3350138593Ssam	DEF_CMD_ARG2("aifs",		set80211aifs),
3351138593Ssam	DEF_CMD_ARG2("txoplimit",	set80211txoplimit),
3352148621Ssam	DEF_CMD_ARG("acm",		set80211acm),
3353148621Ssam	DEF_CMD_ARG("-acm",		set80211noacm),
3354148621Ssam	DEF_CMD_ARG("ack",		set80211ackpolicy),
3355148621Ssam	DEF_CMD_ARG("-ack",		set80211noackpolicy),
3356138593Ssam	DEF_CMD_ARG2("bss:cwmin",	set80211bsscwmin),
3357138593Ssam	DEF_CMD_ARG2("bss:cwmax",	set80211bsscwmax),
3358138593Ssam	DEF_CMD_ARG2("bss:aifs",	set80211bssaifs),
3359138593Ssam	DEF_CMD_ARG2("bss:txoplimit",	set80211bsstxoplimit),
3360138593Ssam	DEF_CMD_ARG("dtimperiod",	set80211dtimperiod),
3361138593Ssam	DEF_CMD_ARG("bintval",		set80211bintval),
3362138593Ssam	DEF_CMD("mac:open",	IEEE80211_MACCMD_POLICY_OPEN,	set80211maccmd),
3363138593Ssam	DEF_CMD("mac:allow",	IEEE80211_MACCMD_POLICY_ALLOW,	set80211maccmd),
3364138593Ssam	DEF_CMD("mac:deny",	IEEE80211_MACCMD_POLICY_DENY,	set80211maccmd),
3365138593Ssam	DEF_CMD("mac:flush",	IEEE80211_MACCMD_FLUSH,		set80211maccmd),
3366138593Ssam	DEF_CMD("mac:detach",	IEEE80211_MACCMD_DETACH,	set80211maccmd),
3367138593Ssam	DEF_CMD_ARG("mac:add",		set80211addmac),
3368138593Ssam	DEF_CMD_ARG("mac:del",		set80211delmac),
3369138593Ssam	DEF_CMD_ARG("mac:kick",		set80211kickmac),
3370147795Ssam	DEF_CMD("pureg",	1,	set80211pureg),
3371147795Ssam	DEF_CMD("-pureg",	0,	set80211pureg),
3372170531Ssam	DEF_CMD("ff",		1,	set80211fastframes),
3373170531Ssam	DEF_CMD("-ff",		0,	set80211fastframes),
3374170531Ssam	DEF_CMD("dturbo",	1,	set80211dturbo),
3375170531Ssam	DEF_CMD("-dturbo",	0,	set80211dturbo),
3376170531Ssam	DEF_CMD("bgscan",	1,	set80211bgscan),
3377170531Ssam	DEF_CMD("-bgscan",	0,	set80211bgscan),
3378170531Ssam	DEF_CMD_ARG("bgscanidle",	set80211bgscanidle),
3379170531Ssam	DEF_CMD_ARG("bgscanintvl",	set80211bgscanintvl),
3380170531Ssam	DEF_CMD_ARG("scanvalid",	set80211scanvalid),
3381170531Ssam	DEF_CMD_ARG("roam:rssi11a",	set80211roamrssi11a),
3382170531Ssam	DEF_CMD_ARG("roam:rssi11b",	set80211roamrssi11b),
3383170531Ssam	DEF_CMD_ARG("roam:rssi11g",	set80211roamrssi11g),
3384170531Ssam	DEF_CMD_ARG("roam:rate11a",	set80211roamrate11a),
3385170531Ssam	DEF_CMD_ARG("roam:rate11b",	set80211roamrate11b),
3386170531Ssam	DEF_CMD_ARG("roam:rate11g",	set80211roamrate11g),
3387153354Ssam	DEF_CMD_ARG("mcastrate",	set80211mcastrate),
3388148416Ssam	DEF_CMD_ARG("fragthreshold",	set80211fragthreshold),
3389153422Ssam	DEF_CMD("burst",	1,	set80211burst),
3390153422Ssam	DEF_CMD("-burst",	0,	set80211burst),
3391160687Ssam	DEF_CMD_ARG("bmiss",		set80211bmissthreshold),
3392160687Ssam	DEF_CMD_ARG("bmissthreshold",	set80211bmissthreshold),
3393173275Ssam	DEF_CMD("shortgi",	1,	set80211shortgi),
3394173275Ssam	DEF_CMD("-shortgi",	0,	set80211shortgi),
3395173275Ssam	DEF_CMD("ampdurx",	2,	set80211ampdu),
3396173275Ssam	DEF_CMD("-ampdurx",	-2,	set80211ampdu),
3397173275Ssam	DEF_CMD("ampdutx",	1,	set80211ampdu),
3398173275Ssam	DEF_CMD("-ampdutx",	-1,	set80211ampdu),
3399173275Ssam	DEF_CMD("ampdu",	3,	set80211ampdu),		/* NB: tx+rx */
3400173275Ssam	DEF_CMD("-ampdu",	-3,	set80211ampdu),
3401173275Ssam	DEF_CMD_ARG("ampdulimit",	set80211ampdulimit),
3402173275Ssam	DEF_CMD_ARG("ampdudensity",	set80211ampdudensity),
3403173275Ssam	DEF_CMD("amsdurx",	2,	set80211amsdu),
3404173275Ssam	DEF_CMD("-amsdurx",	-2,	set80211amsdu),
3405173275Ssam	DEF_CMD("amsdutx",	1,	set80211amsdu),
3406173275Ssam	DEF_CMD("-amsdutx",	-1,	set80211amsdu),
3407173275Ssam	DEF_CMD("amsdu",	3,	set80211amsdu),		/* NB: tx+rx */
3408173275Ssam	DEF_CMD("-amsdu",	-3,	set80211amsdu),
3409173275Ssam	DEF_CMD_ARG("amsdulimit",	set80211amsdulimit),
3410173275Ssam	DEF_CMD("puren",	1,	set80211puren),
3411173275Ssam	DEF_CMD("-puren",	0,	set80211puren),
3412170531Ssam	DEF_CMD("doth",		1,	set80211doth),
3413170531Ssam	DEF_CMD("-doth",	0,	set80211doth),
3414173275Ssam	DEF_CMD("htcompat",	1,	set80211htcompat),
3415173275Ssam	DEF_CMD("-htcompat",	0,	set80211htcompat),
3416173275Ssam	DEF_CMD("inact",	1,	set80211inact),
3417173275Ssam	DEF_CMD("-inact",	0,	set80211inact),
3418173275Ssam	DEF_CMD_ARG("htprotmode",	set80211htprotmode),
3419173275Ssam	DEF_CMD("ht20",		1,	set80211htconf),
3420173275Ssam	DEF_CMD("-ht20",	0,	set80211htconf),
3421173275Ssam	DEF_CMD("ht40",		3,	set80211htconf),	/* NB: 20+40 */
3422173275Ssam	DEF_CMD("-ht40",	0,	set80211htconf),
3423173275Ssam	DEF_CMD("ht",		3,	set80211htconf),	/* NB: 20+40 */
3424173275Ssam	DEF_CMD("-ht",		0,	set80211htconf),
3425138593Ssam};
3426138593Ssamstatic struct afswtch af_ieee80211 = {
3427138593Ssam	.af_name	= "af_ieee80211",
3428138593Ssam	.af_af		= AF_UNSPEC,
3429139494Ssam	.af_other_status = ieee80211_status,
3430138593Ssam};
3431138593Ssam
3432138593Ssamstatic __constructor void
3433138593Ssamieee80211_ctor(void)
3434138593Ssam{
3435138593Ssam#define	N(a)	(sizeof(a) / sizeof(a[0]))
3436138593Ssam	int i;
3437138593Ssam
3438138593Ssam	for (i = 0; i < N(ieee80211_cmds);  i++)
3439138593Ssam		cmd_register(&ieee80211_cmds[i]);
3440138593Ssam	af_register(&af_ieee80211);
3441138593Ssam#undef N
3442138593Ssam}
3443