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