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