]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/ifconfig/ifieee80211.c
Optionally bind ktls threads to NUMA domains
[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                         /* VHT160 */
2409                         if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160MHZ(
2410                             dc->dc_vhtcaps)) {
2411                                 regdomain_addchans(ci, &rd->bands_11ac, reg,
2412                                     IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U |
2413                                     IEEE80211_CHAN_VHT160,
2414                                     &dc->dc_chaninfo);
2415                                 regdomain_addchans(ci, &rd->bands_11ac, reg,
2416                                     IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D |
2417                                     IEEE80211_CHAN_VHT160,
2418                                     &dc->dc_chaninfo);
2419                         }
2420
2421                         /* VHT80P80 */
2422                         if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160_80P80MHZ(
2423                             dc->dc_vhtcaps)) {
2424                                 regdomain_addchans(ci, &rd->bands_11ac, reg,
2425                                     IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U |
2426                                     IEEE80211_CHAN_VHT80P80,
2427                                     &dc->dc_chaninfo);
2428                                 regdomain_addchans(ci, &rd->bands_11ac, reg,
2429                                     IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D |
2430                                     IEEE80211_CHAN_VHT80P80,
2431                                     &dc->dc_chaninfo);
2432                         }
2433                 }
2434
2435                 if (!LIST_EMPTY(&rd->bands_11ng) && dc->dc_htcaps != 0) {
2436                         regdomain_addchans(ci, &rd->bands_11ng, reg,
2437                             IEEE80211_CHAN_G | IEEE80211_CHAN_HT20,
2438                             &dc->dc_chaninfo);
2439                         if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
2440                                 regdomain_addchans(ci, &rd->bands_11ng, reg,
2441                                     IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U,
2442                                     &dc->dc_chaninfo);
2443                                 regdomain_addchans(ci, &rd->bands_11ng, reg,
2444                                     IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D,
2445                                     &dc->dc_chaninfo);
2446                         }
2447                 }
2448                 qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]),
2449                     regdomain_sort);
2450         } else
2451                 memcpy(ci, &dc->dc_chaninfo,
2452                     IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
2453 }
2454
2455 static void
2456 list_countries(void)
2457 {
2458         struct regdata *rdp = getregdata();
2459         const struct country *cp;
2460         const struct regdomain *dp;
2461         int i;
2462
2463         i = 0;
2464         printf("\nCountry codes:\n");
2465         LIST_FOREACH(cp, &rdp->countries, next) {
2466                 printf("%2s %-15.15s%s", cp->isoname,
2467                     cp->name, ((i+1)%4) == 0 ? "\n" : " ");
2468                 i++;
2469         }
2470         i = 0;
2471         printf("\nRegulatory domains:\n");
2472         LIST_FOREACH(dp, &rdp->domains, next) {
2473                 printf("%-15.15s%s", dp->name, ((i+1)%4) == 0 ? "\n" : " ");
2474                 i++;
2475         }
2476         printf("\n");
2477 }
2478
2479 static void
2480 defaultcountry(const struct regdomain *rd)
2481 {
2482         struct regdata *rdp = getregdata();
2483         const struct country *cc;
2484
2485         cc = lib80211_country_findbycc(rdp, rd->cc->code);
2486         if (cc == NULL)
2487                 errx(1, "internal error, ISO country code %d not "
2488                     "defined for regdomain %s", rd->cc->code, rd->name);
2489         regdomain.country = cc->code;
2490         regdomain.isocc[0] = cc->isoname[0];
2491         regdomain.isocc[1] = cc->isoname[1];
2492 }
2493
2494 static
2495 DECL_CMD_FUNC(set80211regdomain, val, d)
2496 {
2497         struct regdata *rdp = getregdata();
2498         const struct regdomain *rd;
2499
2500         rd = lib80211_regdomain_findbyname(rdp, val);
2501         if (rd == NULL) {
2502                 char *eptr;
2503                 long sku = strtol(val, &eptr, 0);
2504
2505                 if (eptr != val)
2506                         rd = lib80211_regdomain_findbysku(rdp, sku);
2507                 if (eptr == val || rd == NULL)
2508                         errx(1, "unknown regdomain %s", val);
2509         }
2510         getregdomain(s);
2511         regdomain.regdomain = rd->sku;
2512         if (regdomain.country == 0 && rd->cc != NULL) {
2513                 /*
2514                  * No country code setup and there's a default
2515                  * one for this regdomain fill it in.
2516                  */
2517                 defaultcountry(rd);
2518         }
2519         callback_register(setregdomain_cb, &regdomain);
2520 }
2521
2522 static
2523 DECL_CMD_FUNC(set80211country, val, d)
2524 {
2525         struct regdata *rdp = getregdata();
2526         const struct country *cc;
2527
2528         cc = lib80211_country_findbyname(rdp, val);
2529         if (cc == NULL) {
2530                 char *eptr;
2531                 long code = strtol(val, &eptr, 0);
2532
2533                 if (eptr != val)
2534                         cc = lib80211_country_findbycc(rdp, code);
2535                 if (eptr == val || cc == NULL)
2536                         errx(1, "unknown ISO country code %s", val);
2537         }
2538         getregdomain(s);
2539         regdomain.regdomain = cc->rd->sku;
2540         regdomain.country = cc->code;
2541         regdomain.isocc[0] = cc->isoname[0];
2542         regdomain.isocc[1] = cc->isoname[1];
2543         callback_register(setregdomain_cb, &regdomain);
2544 }
2545
2546 static void
2547 set80211location(const char *val, int d, int s, const struct afswtch *rafp)
2548 {
2549         getregdomain(s);
2550         regdomain.location = d;
2551         callback_register(setregdomain_cb, &regdomain);
2552 }
2553
2554 static void
2555 set80211ecm(const char *val, int d, int s, const struct afswtch *rafp)
2556 {
2557         getregdomain(s);
2558         regdomain.ecm = d;
2559         callback_register(setregdomain_cb, &regdomain);
2560 }
2561
2562 static void
2563 LINE_INIT(char c)
2564 {
2565         spacer = c;
2566         if (c == '\t')
2567                 col = 8;
2568         else
2569                 col = 1;
2570 }
2571
2572 static void
2573 LINE_BREAK(void)
2574 {
2575         if (spacer != '\t') {
2576                 printf("\n");
2577                 spacer = '\t';
2578         }
2579         col = 8;                /* 8-col tab */
2580 }
2581
2582 static void
2583 LINE_CHECK(const char *fmt, ...)
2584 {
2585         char buf[80];
2586         va_list ap;
2587         int n;
2588
2589         va_start(ap, fmt);
2590         n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
2591         va_end(ap);
2592         col += 1+n;
2593         if (col > MAXCOL) {
2594                 LINE_BREAK();
2595                 col += n;
2596         }
2597         buf[0] = spacer;
2598         printf("%s", buf);
2599         spacer = ' ';
2600 }
2601
2602 static int
2603 getmaxrate(const uint8_t rates[15], uint8_t nrates)
2604 {
2605         int i, maxrate = -1;
2606
2607         for (i = 0; i < nrates; i++) {
2608                 int rate = rates[i] & IEEE80211_RATE_VAL;
2609                 if (rate > maxrate)
2610                         maxrate = rate;
2611         }
2612         return maxrate / 2;
2613 }
2614
2615 static const char *
2616 getcaps(int capinfo)
2617 {
2618         static char capstring[32];
2619         char *cp = capstring;
2620
2621         if (capinfo & IEEE80211_CAPINFO_ESS)
2622                 *cp++ = 'E';
2623         if (capinfo & IEEE80211_CAPINFO_IBSS)
2624                 *cp++ = 'I';
2625         if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
2626                 *cp++ = 'c';
2627         if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
2628                 *cp++ = 'C';
2629         if (capinfo & IEEE80211_CAPINFO_PRIVACY)
2630                 *cp++ = 'P';
2631         if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
2632                 *cp++ = 'S';
2633         if (capinfo & IEEE80211_CAPINFO_PBCC)
2634                 *cp++ = 'B';
2635         if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
2636                 *cp++ = 'A';
2637         if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
2638                 *cp++ = 's';
2639         if (capinfo & IEEE80211_CAPINFO_RSN)
2640                 *cp++ = 'R';
2641         if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
2642                 *cp++ = 'D';
2643         *cp = '\0';
2644         return capstring;
2645 }
2646
2647 static const char *
2648 getflags(int flags)
2649 {
2650         static char flagstring[32];
2651         char *cp = flagstring;
2652
2653         if (flags & IEEE80211_NODE_AUTH)
2654                 *cp++ = 'A';
2655         if (flags & IEEE80211_NODE_QOS)
2656                 *cp++ = 'Q';
2657         if (flags & IEEE80211_NODE_ERP)
2658                 *cp++ = 'E';
2659         if (flags & IEEE80211_NODE_PWR_MGT)
2660                 *cp++ = 'P';
2661         if (flags & IEEE80211_NODE_HT) {
2662                 *cp++ = 'H';
2663                 if (flags & IEEE80211_NODE_HTCOMPAT)
2664                         *cp++ = '+';
2665         }
2666         if (flags & IEEE80211_NODE_VHT)
2667                 *cp++ = 'V';
2668         if (flags & IEEE80211_NODE_WPS)
2669                 *cp++ = 'W';
2670         if (flags & IEEE80211_NODE_TSN)
2671                 *cp++ = 'N';
2672         if (flags & IEEE80211_NODE_AMPDU_TX)
2673                 *cp++ = 'T';
2674         if (flags & IEEE80211_NODE_AMPDU_RX)
2675                 *cp++ = 'R';
2676         if (flags & IEEE80211_NODE_MIMO_PS) {
2677                 *cp++ = 'M';
2678                 if (flags & IEEE80211_NODE_MIMO_RTS)
2679                         *cp++ = '+';
2680         }
2681         if (flags & IEEE80211_NODE_RIFS)
2682                 *cp++ = 'I';
2683         if (flags & IEEE80211_NODE_SGI40) {
2684                 *cp++ = 'S';
2685                 if (flags & IEEE80211_NODE_SGI20)
2686                         *cp++ = '+';
2687         } else if (flags & IEEE80211_NODE_SGI20)
2688                 *cp++ = 's';
2689         if (flags & IEEE80211_NODE_AMSDU_TX)
2690                 *cp++ = 't';
2691         if (flags & IEEE80211_NODE_AMSDU_RX)
2692                 *cp++ = 'r';
2693         if (flags & IEEE80211_NODE_UAPSD)
2694                 *cp++ = 'U';
2695         if (flags & IEEE80211_NODE_LDPC)
2696                 *cp++ = 'L';
2697         *cp = '\0';
2698         return flagstring;
2699 }
2700
2701 static void
2702 printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
2703 {
2704         printf("%s", tag);
2705         if (verbose) {
2706                 maxlen -= strlen(tag)+2;
2707                 if (2*ielen > maxlen)
2708                         maxlen--;
2709                 printf("<");
2710                 for (; ielen > 0; ie++, ielen--) {
2711                         if (maxlen-- <= 0)
2712                                 break;
2713                         printf("%02x", *ie);
2714                 }
2715                 if (ielen != 0)
2716                         printf("-");
2717                 printf(">");
2718         }
2719 }
2720
2721 #define LE_READ_2(p)                                    \
2722         ((u_int16_t)                                    \
2723          ((((const u_int8_t *)(p))[0]      ) |          \
2724           (((const u_int8_t *)(p))[1] <<  8)))
2725 #define LE_READ_4(p)                                    \
2726         ((u_int32_t)                                    \
2727          ((((const u_int8_t *)(p))[0]      ) |          \
2728           (((const u_int8_t *)(p))[1] <<  8) |          \
2729           (((const u_int8_t *)(p))[2] << 16) |          \
2730           (((const u_int8_t *)(p))[3] << 24)))
2731
2732 /*
2733  * NB: The decoding routines assume a properly formatted ie
2734  *     which should be safe as the kernel only retains them
2735  *     if they parse ok.
2736  */
2737
2738 static void
2739 printwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2740 {
2741         static const char *acnames[] = { "BE", "BK", "VO", "VI" };
2742         const struct ieee80211_wme_param *wme =
2743             (const struct ieee80211_wme_param *) ie;
2744         int i;
2745
2746         printf("%s", tag);
2747         if (!verbose)
2748                 return;
2749         printf("<qosinfo 0x%x", wme->param_qosInfo);
2750         ie += offsetof(struct ieee80211_wme_param, params_acParams);
2751         for (i = 0; i < WME_NUM_AC; i++) {
2752                 const struct ieee80211_wme_acparams *ac =
2753                     &wme->params_acParams[i];
2754
2755                 printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]", acnames[i],
2756                     _IEEE80211_MASKSHIFT(ac->acp_aci_aifsn, WME_PARAM_ACM) ?
2757                         "acm " : "",
2758                     _IEEE80211_MASKSHIFT(ac->acp_aci_aifsn, WME_PARAM_AIFSN),
2759                     _IEEE80211_MASKSHIFT(ac->acp_logcwminmax,
2760                         WME_PARAM_LOGCWMIN),
2761                     _IEEE80211_MASKSHIFT(ac->acp_logcwminmax,
2762                         WME_PARAM_LOGCWMAX),
2763                     LE_READ_2(&ac->acp_txop));
2764         }
2765         printf(">");
2766 }
2767
2768 static void
2769 printwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2770 {
2771         printf("%s", tag);
2772         if (verbose) {
2773                 const struct ieee80211_wme_info *wme =
2774                     (const struct ieee80211_wme_info *) ie;
2775                 printf("<version 0x%x info 0x%x>",
2776                     wme->wme_version, wme->wme_info);
2777         }
2778 }
2779
2780 static void
2781 printvhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2782 {
2783         printf("%s", tag);
2784         if (verbose) {
2785                 const struct ieee80211_ie_vhtcap *vhtcap =
2786                     (const struct ieee80211_ie_vhtcap *) ie;
2787                 uint32_t vhtcap_info = LE_READ_4(&vhtcap->vht_cap_info);
2788
2789                 printf("<cap 0x%08x", vhtcap_info);
2790                 printf(" rx_mcs_map 0x%x",
2791                     LE_READ_2(&vhtcap->supp_mcs.rx_mcs_map));
2792                 printf(" rx_highest %d",
2793                     LE_READ_2(&vhtcap->supp_mcs.rx_highest) & 0x1fff);
2794                 printf(" tx_mcs_map 0x%x",
2795                     LE_READ_2(&vhtcap->supp_mcs.tx_mcs_map));
2796                 printf(" tx_highest %d",
2797                     LE_READ_2(&vhtcap->supp_mcs.tx_highest) & 0x1fff);
2798
2799                 printf(">");
2800         }
2801 }
2802
2803 static void
2804 printvhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2805 {
2806         printf("%s", tag);
2807         if (verbose) {
2808                 const struct ieee80211_ie_vht_operation *vhtinfo =
2809                     (const struct ieee80211_ie_vht_operation *) ie;
2810
2811                 printf("<chw %d freq1_idx %d freq2_idx %d basic_mcs_set 0x%04x>",
2812                     vhtinfo->chan_width,
2813                     vhtinfo->center_freq_seg1_idx,
2814                     vhtinfo->center_freq_seg2_idx,
2815                     LE_READ_2(&vhtinfo->basic_mcs_set));
2816         }
2817 }
2818
2819 static void
2820 printvhtpwrenv(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2821 {
2822         printf("%s", tag);
2823         static const char *txpwrmap[] = {
2824                 "20",
2825                 "40",
2826                 "80",
2827                 "160",
2828         };
2829         if (verbose) {
2830                 const struct ieee80211_ie_vht_txpwrenv *vhtpwr =
2831                     (const struct ieee80211_ie_vht_txpwrenv *) ie;
2832                 int i, n;
2833                 const char *sep = "";
2834
2835                 /* Get count; trim at ielen */
2836                 n = (vhtpwr->tx_info &
2837                     IEEE80211_VHT_TXPWRENV_INFO_COUNT_MASK) + 1;
2838                 /* Trim at ielen */
2839                 if (n > ielen - 3)
2840                         n = ielen - 3;
2841                 printf("<tx_info 0x%02x pwr:[", vhtpwr->tx_info);
2842                 for (i = 0; i < n; i++) {
2843                         printf("%s%s:%.2f", sep, txpwrmap[i],
2844                             ((float) ((int8_t) ie[i+3])) / 2.0);
2845                         sep = " ";
2846                 }
2847
2848                 printf("]>");
2849         }
2850 }
2851
2852 static void
2853 printhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2854 {
2855         printf("%s", tag);
2856         if (verbose) {
2857                 const struct ieee80211_ie_htcap *htcap =
2858                     (const struct ieee80211_ie_htcap *) ie;
2859                 const char *sep;
2860                 int i, j;
2861
2862                 printf("<cap 0x%x param 0x%x",
2863                     LE_READ_2(&htcap->hc_cap), htcap->hc_param);
2864                 printf(" mcsset[");
2865                 sep = "";
2866                 for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2867                         if (isset(htcap->hc_mcsset, i)) {
2868                                 for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2869                                         if (isclr(htcap->hc_mcsset, j))
2870                                                 break;
2871                                 j--;
2872                                 if (i == j)
2873                                         printf("%s%u", sep, i);
2874                                 else
2875                                         printf("%s%u-%u", sep, i, j);
2876                                 i += j-i;
2877                                 sep = ",";
2878                         }
2879                 printf("] extcap 0x%x txbf 0x%x antenna 0x%x>",
2880                     LE_READ_2(&htcap->hc_extcap),
2881                     LE_READ_4(&htcap->hc_txbf),
2882                     htcap->hc_antenna);
2883         }
2884 }
2885
2886 static void
2887 printhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2888 {
2889         printf("%s", tag);
2890         if (verbose) {
2891                 const struct ieee80211_ie_htinfo *htinfo =
2892                     (const struct ieee80211_ie_htinfo *) ie;
2893                 const char *sep;
2894                 int i, j;
2895
2896                 printf("<ctl %u, %x,%x,%x,%x", htinfo->hi_ctrlchannel,
2897                     htinfo->hi_byte1, htinfo->hi_byte2, htinfo->hi_byte3,
2898                     LE_READ_2(&htinfo->hi_byte45));
2899                 printf(" basicmcs[");
2900                 sep = "";
2901                 for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2902                         if (isset(htinfo->hi_basicmcsset, i)) {
2903                                 for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2904                                         if (isclr(htinfo->hi_basicmcsset, j))
2905                                                 break;
2906                                 j--;
2907                                 if (i == j)
2908                                         printf("%s%u", sep, i);
2909                                 else
2910                                         printf("%s%u-%u", sep, i, j);
2911                                 i += j-i;
2912                                 sep = ",";
2913                         }
2914                 printf("]>");
2915         }
2916 }
2917
2918 static void
2919 printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2920 {
2921
2922         printf("%s", tag);
2923         if (verbose) {
2924                 const struct ieee80211_ath_ie *ath =
2925                         (const struct ieee80211_ath_ie *)ie;
2926
2927                 printf("<");
2928                 if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME)
2929                         printf("DTURBO,");
2930                 if (ath->ath_capability & ATHEROS_CAP_COMPRESSION)
2931                         printf("COMP,");
2932                 if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME)
2933                         printf("FF,");
2934                 if (ath->ath_capability & ATHEROS_CAP_XR)
2935                         printf("XR,");
2936                 if (ath->ath_capability & ATHEROS_CAP_AR)
2937                         printf("AR,");
2938                 if (ath->ath_capability & ATHEROS_CAP_BURST)
2939                         printf("BURST,");
2940                 if (ath->ath_capability & ATHEROS_CAP_WME)
2941                         printf("WME,");
2942                 if (ath->ath_capability & ATHEROS_CAP_BOOST)
2943                         printf("BOOST,");
2944                 printf("0x%x>", LE_READ_2(ath->ath_defkeyix));
2945         }
2946 }
2947
2948
2949 static void
2950 printmeshconf(const char *tag, const uint8_t *ie, size_t ielen, int maxlen)
2951 {
2952
2953         printf("%s", tag);
2954         if (verbose) {
2955                 const struct ieee80211_meshconf_ie *mconf =
2956                         (const struct ieee80211_meshconf_ie *)ie;
2957                 printf("<PATH:");
2958                 if (mconf->conf_pselid == IEEE80211_MESHCONF_PATH_HWMP)
2959                         printf("HWMP");
2960                 else
2961                         printf("UNKNOWN");
2962                 printf(" LINK:");
2963                 if (mconf->conf_pmetid == IEEE80211_MESHCONF_METRIC_AIRTIME)
2964                         printf("AIRTIME");
2965                 else
2966                         printf("UNKNOWN");
2967                 printf(" CONGESTION:");
2968                 if (mconf->conf_ccid == IEEE80211_MESHCONF_CC_DISABLED)
2969                         printf("DISABLED");
2970                 else
2971                         printf("UNKNOWN");
2972                 printf(" SYNC:");
2973                 if (mconf->conf_syncid == IEEE80211_MESHCONF_SYNC_NEIGHOFF)
2974                         printf("NEIGHOFF");
2975                 else
2976                         printf("UNKNOWN");
2977                 printf(" AUTH:");
2978                 if (mconf->conf_authid == IEEE80211_MESHCONF_AUTH_DISABLED)
2979                         printf("DISABLED");
2980                 else
2981                         printf("UNKNOWN");
2982                 printf(" FORM:0x%x CAPS:0x%x>", mconf->conf_form,
2983                     mconf->conf_cap);
2984         }
2985 }
2986
2987 static void
2988 printbssload(const char *tag, const uint8_t *ie, size_t ielen, int maxlen)
2989 {
2990         printf("%s", tag);
2991         if (verbose) {
2992                 const struct ieee80211_bss_load_ie *bssload =
2993                     (const struct ieee80211_bss_load_ie *) ie;
2994                 printf("<sta count %d, chan load %d, aac %d>",
2995                     LE_READ_2(&bssload->sta_count),
2996                     bssload->chan_load,
2997                     bssload->aac);
2998         }
2999 }
3000
3001 static void
3002 printapchanrep(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3003 {
3004         printf("%s", tag);
3005         if (verbose) {
3006                 const struct ieee80211_ap_chan_report_ie *ap =
3007                     (const struct ieee80211_ap_chan_report_ie *) ie;
3008                 const char *sep = "";
3009                 int i;
3010
3011                 printf("<class %u, chan:[", ap->i_class);
3012
3013                 for (i = 3; i < ielen; i++) {
3014                         printf("%s%u", sep, ie[i]);
3015                         sep = ",";
3016                 }
3017                 printf("]>");
3018         }
3019 }
3020
3021 static const char *
3022 wpa_cipher(const u_int8_t *sel)
3023 {
3024 #define WPA_SEL(x)      (((x)<<24)|WPA_OUI)
3025         u_int32_t w = LE_READ_4(sel);
3026
3027         switch (w) {
3028         case WPA_SEL(WPA_CSE_NULL):
3029                 return "NONE";
3030         case WPA_SEL(WPA_CSE_WEP40):
3031                 return "WEP40";
3032         case WPA_SEL(WPA_CSE_WEP104):
3033                 return "WEP104";
3034         case WPA_SEL(WPA_CSE_TKIP):
3035                 return "TKIP";
3036         case WPA_SEL(WPA_CSE_CCMP):
3037                 return "AES-CCMP";
3038         }
3039         return "?";             /* NB: so 1<< is discarded */
3040 #undef WPA_SEL
3041 }
3042
3043 static const char *
3044 wpa_keymgmt(const u_int8_t *sel)
3045 {
3046 #define WPA_SEL(x)      (((x)<<24)|WPA_OUI)
3047         u_int32_t w = LE_READ_4(sel);
3048
3049         switch (w) {
3050         case WPA_SEL(WPA_ASE_8021X_UNSPEC):
3051                 return "8021X-UNSPEC";
3052         case WPA_SEL(WPA_ASE_8021X_PSK):
3053                 return "8021X-PSK";
3054         case WPA_SEL(WPA_ASE_NONE):
3055                 return "NONE";
3056         }
3057         return "?";
3058 #undef WPA_SEL
3059 }
3060
3061 static void
3062 printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3063 {
3064         u_int8_t len = ie[1];
3065
3066         printf("%s", tag);
3067         if (verbose) {
3068                 const char *sep;
3069                 int n;
3070
3071                 ie += 6, len -= 4;              /* NB: len is payload only */
3072
3073                 printf("<v%u", LE_READ_2(ie));
3074                 ie += 2, len -= 2;
3075
3076                 printf(" mc:%s", wpa_cipher(ie));
3077                 ie += 4, len -= 4;
3078
3079                 /* unicast ciphers */
3080                 n = LE_READ_2(ie);
3081                 ie += 2, len -= 2;
3082                 sep = " uc:";
3083                 for (; n > 0; n--) {
3084                         printf("%s%s", sep, wpa_cipher(ie));
3085                         ie += 4, len -= 4;
3086                         sep = "+";
3087                 }
3088
3089                 /* key management algorithms */
3090                 n = LE_READ_2(ie);
3091                 ie += 2, len -= 2;
3092                 sep = " km:";
3093                 for (; n > 0; n--) {
3094                         printf("%s%s", sep, wpa_keymgmt(ie));
3095                         ie += 4, len -= 4;
3096                         sep = "+";
3097                 }
3098
3099                 if (len > 2)            /* optional capabilities */
3100                         printf(", caps 0x%x", LE_READ_2(ie));
3101                 printf(">");
3102         }
3103 }
3104
3105 static const char *
3106 rsn_cipher(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_CSE_NULL):
3113                 return "NONE";
3114         case RSN_SEL(RSN_CSE_WEP40):
3115                 return "WEP40";
3116         case RSN_SEL(RSN_CSE_WEP104):
3117                 return "WEP104";
3118         case RSN_SEL(RSN_CSE_TKIP):
3119                 return "TKIP";
3120         case RSN_SEL(RSN_CSE_CCMP):
3121                 return "AES-CCMP";
3122         case RSN_SEL(RSN_CSE_WRAP):
3123                 return "AES-OCB";
3124         }
3125         return "?";
3126 #undef WPA_SEL
3127 }
3128
3129 static const char *
3130 rsn_keymgmt(const u_int8_t *sel)
3131 {
3132 #define RSN_SEL(x)      (((x)<<24)|RSN_OUI)
3133         u_int32_t w = LE_READ_4(sel);
3134
3135         switch (w) {
3136         case RSN_SEL(RSN_ASE_8021X_UNSPEC):
3137                 return "8021X-UNSPEC";
3138         case RSN_SEL(RSN_ASE_8021X_PSK):
3139                 return "8021X-PSK";
3140         case RSN_SEL(RSN_ASE_NONE):
3141                 return "NONE";
3142         }
3143         return "?";
3144 #undef RSN_SEL
3145 }
3146
3147 static void
3148 printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3149 {
3150         printf("%s", tag);
3151         if (verbose) {
3152                 const char *sep;
3153                 int n;
3154
3155                 ie += 2, ielen -= 2;
3156
3157                 printf("<v%u", LE_READ_2(ie));
3158                 ie += 2, ielen -= 2;
3159
3160                 printf(" mc:%s", rsn_cipher(ie));
3161                 ie += 4, ielen -= 4;
3162
3163                 /* unicast ciphers */
3164                 n = LE_READ_2(ie);
3165                 ie += 2, ielen -= 2;
3166                 sep = " uc:";
3167                 for (; n > 0; n--) {
3168                         printf("%s%s", sep, rsn_cipher(ie));
3169                         ie += 4, ielen -= 4;
3170                         sep = "+";
3171                 }
3172
3173                 /* key management algorithms */
3174                 n = LE_READ_2(ie);
3175                 ie += 2, ielen -= 2;
3176                 sep = " km:";
3177                 for (; n > 0; n--) {
3178                         printf("%s%s", sep, rsn_keymgmt(ie));
3179                         ie += 4, ielen -= 4;
3180                         sep = "+";
3181                 }
3182
3183                 if (ielen > 2)          /* optional capabilities */
3184                         printf(", caps 0x%x", LE_READ_2(ie));
3185                 /* XXXPMKID */
3186                 printf(">");
3187         }
3188 }
3189
3190 #define BE_READ_2(p)                                    \
3191         ((u_int16_t)                                    \
3192          ((((const u_int8_t *)(p))[1]      ) |          \
3193           (((const u_int8_t *)(p))[0] <<  8)))
3194
3195 static void
3196 printwpsie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3197 {
3198         u_int8_t len = ie[1];
3199
3200         printf("%s", tag);
3201         if (verbose) {
3202                 static const char *dev_pass_id[] = {
3203                         "D",    /* Default (PIN) */
3204                         "U",    /* User-specified */
3205                         "M",    /* Machine-specified */
3206                         "K",    /* Rekey */
3207                         "P",    /* PushButton */
3208                         "R"     /* Registrar-specified */
3209                 };
3210                 int n;
3211                 int f;
3212
3213                 ie +=6, len -= 4;               /* NB: len is payload only */
3214
3215                 /* WPS IE in Beacon and Probe Resp frames have different fields */
3216                 printf("<");
3217                 while (len) {
3218                         uint16_t tlv_type = BE_READ_2(ie);
3219                         uint16_t tlv_len  = BE_READ_2(ie + 2);
3220                         uint16_t cfg_mthd;
3221
3222                         /* some devices broadcast invalid WPS frames */
3223                         if (tlv_len > len) {
3224                                 printf("bad frame length tlv_type=0x%02x "
3225                                     "tlv_len=%d len=%d", tlv_type, tlv_len,
3226                                     len);
3227                                 break;
3228                         }
3229
3230                         ie += 4, len -= 4;
3231
3232                         switch (tlv_type) {
3233                         case IEEE80211_WPS_ATTR_VERSION:
3234                                 printf("v:%d.%d", *ie >> 4, *ie & 0xf);
3235                                 break;
3236                         case IEEE80211_WPS_ATTR_AP_SETUP_LOCKED:
3237                                 printf(" ap_setup:%s", *ie ? "locked" :
3238                                     "unlocked");
3239                                 break;
3240                         case IEEE80211_WPS_ATTR_CONFIG_METHODS:
3241                         case IEEE80211_WPS_ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
3242                                 if (tlv_type == IEEE80211_WPS_ATTR_SELECTED_REGISTRAR_CONFIG_METHODS)
3243                                         printf(" sel_reg_cfg_mthd:");
3244                                 else
3245                                         printf(" cfg_mthd:" );
3246                                 cfg_mthd = BE_READ_2(ie);
3247                                 f = 0;
3248                                 for (n = 15; n >= 0; n--) {
3249                                         if (f) {
3250                                                 printf(",");
3251                                                 f = 0;
3252                                         }
3253                                         switch (cfg_mthd & (1 << n)) {
3254                                         case 0:
3255                                                 break;
3256                                         case IEEE80211_WPS_CONFIG_USBA:
3257                                                 printf("usba");
3258                                                 f++;
3259                                                 break;
3260                                         case IEEE80211_WPS_CONFIG_ETHERNET:
3261                                                 printf("ethernet");
3262                                                 f++;
3263                                                 break;
3264                                         case IEEE80211_WPS_CONFIG_LABEL:
3265                                                 printf("label");
3266                                                 f++;
3267                                                 break;
3268                                         case IEEE80211_WPS_CONFIG_DISPLAY:
3269                                                 if (!(cfg_mthd &
3270                                                     (IEEE80211_WPS_CONFIG_VIRT_DISPLAY |
3271                                                     IEEE80211_WPS_CONFIG_PHY_DISPLAY)))
3272                                                     {
3273                                                         printf("display");
3274                                                         f++;
3275                                                 }
3276                                                 break;
3277                                         case IEEE80211_WPS_CONFIG_EXT_NFC_TOKEN:
3278                                                 printf("ext_nfc_tokenk");
3279                                                 f++;
3280                                                 break;
3281                                         case IEEE80211_WPS_CONFIG_INT_NFC_TOKEN:
3282                                                 printf("int_nfc_token");
3283                                                 f++;
3284                                                 break;
3285                                         case IEEE80211_WPS_CONFIG_NFC_INTERFACE:
3286                                                 printf("nfc_interface");
3287                                                 f++;
3288                                                 break;
3289                                         case IEEE80211_WPS_CONFIG_PUSHBUTTON:
3290                                                 if (!(cfg_mthd &
3291                                                     (IEEE80211_WPS_CONFIG_VIRT_PUSHBUTTON |
3292                                                     IEEE80211_WPS_CONFIG_PHY_PUSHBUTTON))) {
3293                                                         printf("push_button");
3294                                                         f++;
3295                                                 }
3296                                                 break;
3297                                         case IEEE80211_WPS_CONFIG_KEYPAD:
3298                                                 printf("keypad");
3299                                                 f++;
3300                                                 break;
3301                                         case IEEE80211_WPS_CONFIG_VIRT_PUSHBUTTON:
3302                                                 printf("virtual_push_button");
3303                                                 f++;
3304                                                 break;
3305                                         case IEEE80211_WPS_CONFIG_PHY_PUSHBUTTON:
3306                                                 printf("physical_push_button");
3307                                                 f++;
3308                                                 break;
3309                                         case IEEE80211_WPS_CONFIG_P2PS:
3310                                                 printf("p2ps");
3311                                                 f++;
3312                                                 break;
3313                                         case IEEE80211_WPS_CONFIG_VIRT_DISPLAY:
3314                                                 printf("virtual_display");
3315                                                 f++;
3316                                                 break;
3317                                         case IEEE80211_WPS_CONFIG_PHY_DISPLAY:
3318                                                 printf("physical_display");
3319                                                 f++;
3320                                                 break;
3321                                         default:
3322                                                 printf("unknown_wps_config<%04x>",
3323                                                     cfg_mthd & (1 << n));
3324                                                 f++;
3325                                                 break;
3326                                         }
3327                                 }
3328                                 break;
3329                         case IEEE80211_WPS_ATTR_DEV_NAME:
3330                                 printf(" device_name:<%.*s>", tlv_len, ie);
3331                                 break;
3332                         case IEEE80211_WPS_ATTR_DEV_PASSWORD_ID:
3333                                 n = LE_READ_2(ie);
3334                                 if (n < nitems(dev_pass_id))
3335                                         printf(" dpi:%s", dev_pass_id[n]);
3336                                 break;
3337                         case IEEE80211_WPS_ATTR_MANUFACTURER:
3338                                 printf(" manufacturer:<%.*s>", tlv_len, ie);
3339                                 break;
3340                         case IEEE80211_WPS_ATTR_MODEL_NAME:
3341                                 printf(" model_name:<%.*s>", tlv_len, ie);
3342                                 break;
3343                         case IEEE80211_WPS_ATTR_MODEL_NUMBER:
3344                                 printf(" model_number:<%.*s>", tlv_len, ie);
3345                                 break;
3346                         case IEEE80211_WPS_ATTR_PRIMARY_DEV_TYPE:
3347                                 printf(" prim_dev:");
3348                                 for (n = 0; n < tlv_len; n++)
3349                                         printf("%02x", ie[n]);
3350                                 break;
3351                         case IEEE80211_WPS_ATTR_RF_BANDS:
3352                                 printf(" rf:");
3353                                 f = 0;
3354                                 for (n = 7; n >= 0; n--) {
3355                                         if (f) {
3356                                                 printf(",");
3357                                                 f = 0;
3358                                         }
3359                                         switch (*ie & (1 << n)) {
3360                                         case 0:
3361                                                 break;
3362                                         case IEEE80211_WPS_RF_BAND_24GHZ:
3363                                                 printf("2.4Ghz");
3364                                                 f++;
3365                                                 break;
3366                                         case IEEE80211_WPS_RF_BAND_50GHZ:
3367                                                 printf("5Ghz");
3368                                                 f++;
3369                                                 break;
3370                                         case IEEE80211_WPS_RF_BAND_600GHZ:
3371                                                 printf("60Ghz");
3372                                                 f++;
3373                                                 break;
3374                                         default:
3375                                                 printf("unknown<%02x>",
3376                                                     *ie & (1 << n));
3377                                                 f++;
3378                                                 break;
3379                                         }
3380                                 }
3381                                 break;
3382                         case IEEE80211_WPS_ATTR_RESPONSE_TYPE:
3383                                 printf(" resp_type:0x%02x", *ie);
3384                                 break;
3385                         case IEEE80211_WPS_ATTR_SELECTED_REGISTRAR:
3386                                 printf(" sel:%s", *ie ? "T" : "F");
3387                                 break;
3388                         case IEEE80211_WPS_ATTR_SERIAL_NUMBER:
3389                                 printf(" serial_number:<%.*s>", tlv_len, ie);
3390                                 break;
3391                         case IEEE80211_WPS_ATTR_UUID_E:
3392                                 printf(" uuid-e:");
3393                                 for (n = 0; n < (tlv_len - 1); n++)
3394                                         printf("%02x-", ie[n]);
3395                                 printf("%02x", ie[n]);
3396                                 break;
3397                         case IEEE80211_WPS_ATTR_VENDOR_EXT:
3398                                 printf(" vendor:");
3399                                 for (n = 0; n < tlv_len; n++)
3400                                         printf("%02x", ie[n]);
3401                                 break;
3402                         case IEEE80211_WPS_ATTR_WPS_STATE:
3403                                 switch (*ie) {
3404                                 case IEEE80211_WPS_STATE_NOT_CONFIGURED:
3405                                         printf(" state:N");
3406                                         break;
3407                                 case IEEE80211_WPS_STATE_CONFIGURED:
3408                                         printf(" state:C");
3409                                         break;
3410                                 default:
3411                                         printf(" state:B<%02x>", *ie);
3412                                         break;
3413                                 }
3414                                 break;
3415                         default:
3416                                 printf(" unknown_wps_attr:0x%x", tlv_type);
3417                                 break;
3418                         }
3419                         ie += tlv_len, len -= tlv_len;
3420                 }
3421                 printf(">");
3422         }
3423 }
3424
3425 static void
3426 printtdmaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3427 {
3428         printf("%s", tag);
3429         if (verbose && ielen >= sizeof(struct ieee80211_tdma_param)) {
3430                 const struct ieee80211_tdma_param *tdma =
3431                    (const struct ieee80211_tdma_param *) ie;
3432
3433                 /* XXX tstamp */
3434                 printf("<v%u slot:%u slotcnt:%u slotlen:%u bintval:%u inuse:0x%x>",
3435                     tdma->tdma_version, tdma->tdma_slot, tdma->tdma_slotcnt,
3436                     LE_READ_2(&tdma->tdma_slotlen), tdma->tdma_bintval,
3437                     tdma->tdma_inuse[0]);
3438         }
3439 }
3440
3441 /*
3442  * Copy the ssid string contents into buf, truncating to fit.  If the
3443  * ssid is entirely printable then just copy intact.  Otherwise convert
3444  * to hexadecimal.  If the result is truncated then replace the last
3445  * three characters with "...".
3446  */
3447 static int
3448 copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
3449 {
3450         const u_int8_t *p; 
3451         size_t maxlen;
3452         u_int i;
3453
3454         if (essid_len > bufsize)
3455                 maxlen = bufsize;
3456         else
3457                 maxlen = essid_len;
3458         /* determine printable or not */
3459         for (i = 0, p = essid; i < maxlen; i++, p++) {
3460                 if (*p < ' ' || *p > 0x7e)
3461                         break;
3462         }
3463         if (i != maxlen) {              /* not printable, print as hex */
3464                 if (bufsize < 3)
3465                         return 0;
3466                 strlcpy(buf, "0x", bufsize);
3467                 bufsize -= 2;
3468                 p = essid;
3469                 for (i = 0; i < maxlen && bufsize >= 2; i++) {
3470                         sprintf(&buf[2+2*i], "%02x", p[i]);
3471                         bufsize -= 2;
3472                 }
3473                 if (i != essid_len)
3474                         memcpy(&buf[2+2*i-3], "...", 3);
3475         } else {                        /* printable, truncate as needed */
3476                 memcpy(buf, essid, maxlen);
3477                 if (maxlen != essid_len)
3478                         memcpy(&buf[maxlen-3], "...", 3);
3479         }
3480         return maxlen;
3481 }
3482
3483 static void
3484 printssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3485 {
3486         char ssid[2*IEEE80211_NWID_LEN+1];
3487
3488         printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid);
3489 }
3490
3491 static void
3492 printrates(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3493 {
3494         const char *sep;
3495         int i;
3496
3497         printf("%s", tag);
3498         sep = "<";
3499         for (i = 2; i < ielen; i++) {
3500                 printf("%s%s%d", sep,
3501                     ie[i] & IEEE80211_RATE_BASIC ? "B" : "",
3502                     ie[i] & IEEE80211_RATE_VAL);
3503                 sep = ",";
3504         }
3505         printf(">");
3506 }
3507
3508 static void
3509 printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3510 {
3511         const struct ieee80211_country_ie *cie =
3512            (const struct ieee80211_country_ie *) ie;
3513         int i, nbands, schan, nchan;
3514
3515         printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]);
3516         nbands = (cie->len - 3) / sizeof(cie->band[0]);
3517         for (i = 0; i < nbands; i++) {
3518                 schan = cie->band[i].schan;
3519                 nchan = cie->band[i].nchan;
3520                 if (nchan != 1)
3521                         printf(" %u-%u,%u", schan, schan + nchan-1,
3522                             cie->band[i].maxtxpwr);
3523                 else
3524                         printf(" %u,%u", schan, cie->band[i].maxtxpwr);
3525         }
3526         printf(">");
3527 }
3528
3529 static __inline int
3530 iswpaoui(const u_int8_t *frm)
3531 {
3532         return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
3533 }
3534
3535 static __inline int
3536 iswmeinfo(const u_int8_t *frm)
3537 {
3538         return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
3539                 frm[6] == WME_INFO_OUI_SUBTYPE;
3540 }
3541
3542 static __inline int
3543 iswmeparam(const u_int8_t *frm)
3544 {
3545         return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
3546                 frm[6] == WME_PARAM_OUI_SUBTYPE;
3547 }
3548
3549 static __inline int
3550 isatherosoui(const u_int8_t *frm)
3551 {
3552         return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
3553 }
3554
3555 static __inline int
3556 istdmaoui(const uint8_t *frm)
3557 {
3558         return frm[1] > 3 && LE_READ_4(frm+2) == ((TDMA_OUI_TYPE<<24)|TDMA_OUI);
3559 }
3560
3561 static __inline int
3562 iswpsoui(const uint8_t *frm)
3563 {
3564         return frm[1] > 3 && LE_READ_4(frm+2) == ((WPS_OUI_TYPE<<24)|WPA_OUI);
3565 }
3566
3567 static const char *
3568 iename(int elemid)
3569 {
3570         static char iename_buf[64];
3571         switch (elemid) {
3572         case IEEE80211_ELEMID_FHPARMS:  return " FHPARMS";
3573         case IEEE80211_ELEMID_CFPARMS:  return " CFPARMS";
3574         case IEEE80211_ELEMID_TIM:      return " TIM";
3575         case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS";
3576         case IEEE80211_ELEMID_BSSLOAD:  return " BSSLOAD";
3577         case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE";
3578         case IEEE80211_ELEMID_PWRCNSTR: return " PWRCNSTR";
3579         case IEEE80211_ELEMID_PWRCAP:   return " PWRCAP";
3580         case IEEE80211_ELEMID_TPCREQ:   return " TPCREQ";
3581         case IEEE80211_ELEMID_TPCREP:   return " TPCREP";
3582         case IEEE80211_ELEMID_SUPPCHAN: return " SUPPCHAN";
3583         case IEEE80211_ELEMID_CSA:      return " CSA";
3584         case IEEE80211_ELEMID_MEASREQ:  return " MEASREQ";
3585         case IEEE80211_ELEMID_MEASREP:  return " MEASREP";
3586         case IEEE80211_ELEMID_QUIET:    return " QUIET";
3587         case IEEE80211_ELEMID_IBSSDFS:  return " IBSSDFS";
3588         case IEEE80211_ELEMID_RESERVED_47:
3589                                         return " RESERVED_47";
3590         case IEEE80211_ELEMID_MOBILITY_DOMAIN:
3591                                         return " MOBILITY_DOMAIN";
3592         case IEEE80211_ELEMID_RRM_ENACAPS:
3593                                         return " RRM_ENCAPS";
3594         case IEEE80211_ELEMID_OVERLAP_BSS_SCAN_PARAM:
3595                                         return " OVERLAP_BSS";
3596         case IEEE80211_ELEMID_TPC:      return " TPC";
3597         case IEEE80211_ELEMID_CCKM:     return " CCKM";
3598         case IEEE80211_ELEMID_EXTCAP:   return " EXTCAP";
3599         }
3600         snprintf(iename_buf, sizeof(iename_buf), " UNKNOWN_ELEMID_%d",
3601             elemid);
3602         return (const char *) iename_buf;
3603 }
3604
3605 static void
3606 printies(const u_int8_t *vp, int ielen, int maxcols)
3607 {
3608         while (ielen > 0) {
3609                 switch (vp[0]) {
3610                 case IEEE80211_ELEMID_SSID:
3611                         if (verbose)
3612                                 printssid(" SSID", vp, 2+vp[1], maxcols);
3613                         break;
3614                 case IEEE80211_ELEMID_RATES:
3615                 case IEEE80211_ELEMID_XRATES:
3616                         if (verbose)
3617                                 printrates(vp[0] == IEEE80211_ELEMID_RATES ?
3618                                     " RATES" : " XRATES", vp, 2+vp[1], maxcols);
3619                         break;
3620                 case IEEE80211_ELEMID_DSPARMS:
3621                         if (verbose)
3622                                 printf(" DSPARMS<%u>", vp[2]);
3623                         break;
3624                 case IEEE80211_ELEMID_COUNTRY:
3625                         if (verbose)
3626                                 printcountry(" COUNTRY", vp, 2+vp[1], maxcols);
3627                         break;
3628                 case IEEE80211_ELEMID_ERP:
3629                         if (verbose)
3630                                 printf(" ERP<0x%x>", vp[2]);
3631                         break;
3632                 case IEEE80211_ELEMID_VENDOR:
3633                         if (iswpaoui(vp))
3634                                 printwpaie(" WPA", vp, 2+vp[1], maxcols);
3635                         else if (iswmeinfo(vp))
3636                                 printwmeinfo(" WME", vp, 2+vp[1], maxcols);
3637                         else if (iswmeparam(vp))
3638                                 printwmeparam(" WME", vp, 2+vp[1], maxcols);
3639                         else if (isatherosoui(vp))
3640                                 printathie(" ATH", vp, 2+vp[1], maxcols);
3641                         else if (iswpsoui(vp))
3642                                 printwpsie(" WPS", vp, 2+vp[1], maxcols);
3643                         else if (istdmaoui(vp))
3644                                 printtdmaie(" TDMA", vp, 2+vp[1], maxcols);
3645                         else if (verbose)
3646                                 printie(" VEN", vp, 2+vp[1], maxcols);
3647                         break;
3648                 case IEEE80211_ELEMID_RSN:
3649                         printrsnie(" RSN", vp, 2+vp[1], maxcols);
3650                         break;
3651                 case IEEE80211_ELEMID_HTCAP:
3652                         printhtcap(" HTCAP", vp, 2+vp[1], maxcols);
3653                         break;
3654                 case IEEE80211_ELEMID_HTINFO:
3655                         if (verbose)
3656                                 printhtinfo(" HTINFO", vp, 2+vp[1], maxcols);
3657                         break;
3658                 case IEEE80211_ELEMID_MESHID:
3659                         if (verbose)
3660                                 printssid(" MESHID", vp, 2+vp[1], maxcols);
3661                         break;
3662                 case IEEE80211_ELEMID_MESHCONF:
3663                         printmeshconf(" MESHCONF", vp, 2+vp[1], maxcols);
3664                         break;
3665                 case IEEE80211_ELEMID_VHT_CAP:
3666                         printvhtcap(" VHTCAP", vp, 2+vp[1], maxcols);
3667                         break;
3668                 case IEEE80211_ELEMID_VHT_OPMODE:
3669                         printvhtinfo(" VHTOPMODE", vp, 2+vp[1], maxcols);
3670                         break;
3671                 case IEEE80211_ELEMID_VHT_PWR_ENV:
3672                         printvhtpwrenv(" VHTPWRENV", vp, 2+vp[1], maxcols);
3673                         break;
3674                 case IEEE80211_ELEMID_BSSLOAD:
3675                         printbssload(" BSSLOAD", vp, 2+vp[1], maxcols);
3676                         break;
3677                 case IEEE80211_ELEMID_APCHANREP:
3678                         printapchanrep(" APCHANREP", vp, 2+vp[1], maxcols);
3679                         break;
3680                 default:
3681                         if (verbose)
3682                                 printie(iename(vp[0]), vp, 2+vp[1], maxcols);
3683                         break;
3684                 }
3685                 ielen -= 2+vp[1];
3686                 vp += 2+vp[1];
3687         }
3688 }
3689
3690 static void
3691 printmimo(const struct ieee80211_mimo_info *mi)
3692 {
3693         int i;
3694         int r = 0;
3695
3696         for (i = 0; i < IEEE80211_MAX_CHAINS; i++) {
3697                 if (mi->ch[i].rssi != 0) {
3698                         r = 1;
3699                         break;
3700                 }
3701         }
3702
3703         /* NB: don't muddy display unless there's something to show */
3704         if (r == 0)
3705                 return;
3706
3707         /* XXX TODO: ignore EVM; secondary channels for now */
3708         printf(" (rssi %.1f:%.1f:%.1f:%.1f nf %d:%d:%d:%d)",
3709             mi->ch[0].rssi[0] / 2.0,
3710             mi->ch[1].rssi[0] / 2.0,
3711             mi->ch[2].rssi[0] / 2.0,
3712             mi->ch[3].rssi[0] / 2.0,
3713             mi->ch[0].noise[0],
3714             mi->ch[1].noise[0],
3715             mi->ch[2].noise[0],
3716             mi->ch[3].noise[0]);
3717 }
3718
3719 static void
3720 list_scan(int s)
3721 {
3722         uint8_t buf[24*1024];
3723         char ssid[IEEE80211_NWID_LEN+1];
3724         const uint8_t *cp;
3725         int len, idlen;
3726
3727         if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0)
3728                 errx(1, "unable to get scan results");
3729         if (len < sizeof(struct ieee80211req_scan_result))
3730                 return;
3731
3732         getchaninfo(s);
3733
3734         printf("%-*.*s  %-17.17s  %4s %4s   %-7s  %3s %4s\n"
3735                 , IEEE80211_NWID_LEN, IEEE80211_NWID_LEN, "SSID/MESH ID"
3736                 , "BSSID"
3737                 , "CHAN"
3738                 , "RATE"
3739                 , " S:N"
3740                 , "INT"
3741                 , "CAPS"
3742         );
3743         cp = buf;
3744         do {
3745                 const struct ieee80211req_scan_result *sr;
3746                 const uint8_t *vp, *idp;
3747
3748                 sr = (const struct ieee80211req_scan_result *) cp;
3749                 vp = cp + sr->isr_ie_off;
3750                 if (sr->isr_meshid_len) {
3751                         idp = vp + sr->isr_ssid_len;
3752                         idlen = sr->isr_meshid_len;
3753                 } else {
3754                         idp = vp;
3755                         idlen = sr->isr_ssid_len;
3756                 }
3757                 printf("%-*.*s  %s  %3d  %3dM %4d:%-4d %4d %-4.4s"
3758                         , IEEE80211_NWID_LEN
3759                           , copy_essid(ssid, IEEE80211_NWID_LEN, idp, idlen)
3760                           , ssid
3761                         , ether_ntoa((const struct ether_addr *) sr->isr_bssid)
3762                         , ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
3763                         , getmaxrate(sr->isr_rates, sr->isr_nrates)
3764                         , (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise
3765                         , sr->isr_intval
3766                         , getcaps(sr->isr_capinfo)
3767                 );
3768                 printies(vp + sr->isr_ssid_len + sr->isr_meshid_len,
3769                     sr->isr_ie_len, 24);
3770                 printf("\n");
3771                 cp += sr->isr_len, len -= sr->isr_len;
3772         } while (len >= sizeof(struct ieee80211req_scan_result));
3773 }
3774
3775 static void
3776 scan_and_wait(int s)
3777 {
3778         struct ieee80211_scan_req sr;
3779         struct ieee80211req ireq;
3780         int sroute;
3781
3782         sroute = socket(PF_ROUTE, SOCK_RAW, 0);
3783         if (sroute < 0) {
3784                 perror("socket(PF_ROUTE,SOCK_RAW)");
3785                 return;
3786         }
3787         (void) memset(&ireq, 0, sizeof(ireq));
3788         (void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
3789         ireq.i_type = IEEE80211_IOC_SCAN_REQ;
3790
3791         memset(&sr, 0, sizeof(sr));
3792         sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
3793                     | IEEE80211_IOC_SCAN_BGSCAN
3794                     | IEEE80211_IOC_SCAN_NOPICK
3795                     | IEEE80211_IOC_SCAN_ONCE;
3796         sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
3797         sr.sr_nssid = 0;
3798
3799         ireq.i_data = &sr;
3800         ireq.i_len = sizeof(sr);
3801         /*
3802          * NB: only root can trigger a scan so ignore errors. Also ignore
3803          * possible errors from net80211, even if no new scan could be
3804          * started there might still be a valid scan cache.
3805          */
3806         if (ioctl(s, SIOCS80211, &ireq) == 0) {
3807                 char buf[2048];
3808                 struct if_announcemsghdr *ifan;
3809                 struct rt_msghdr *rtm;
3810
3811                 do {
3812                         if (read(sroute, buf, sizeof(buf)) < 0) {
3813                                 perror("read(PF_ROUTE)");
3814                                 break;
3815                         }
3816                         rtm = (struct rt_msghdr *) buf;
3817                         if (rtm->rtm_version != RTM_VERSION)
3818                                 break;
3819                         ifan = (struct if_announcemsghdr *) rtm;
3820                 } while (rtm->rtm_type != RTM_IEEE80211 ||
3821                     ifan->ifan_what != RTM_IEEE80211_SCAN);
3822         }
3823         close(sroute);
3824 }
3825
3826 static
3827 DECL_CMD_FUNC(set80211scan, val, d)
3828 {
3829         scan_and_wait(s);
3830         list_scan(s);
3831 }
3832
3833 static enum ieee80211_opmode get80211opmode(int s);
3834
3835 static int
3836 gettxseq(const struct ieee80211req_sta_info *si)
3837 {
3838         int i, txseq;
3839
3840         if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3841                 return si->isi_txseqs[0];
3842         /* XXX not right but usually what folks want */
3843         txseq = 0;
3844         for (i = 0; i < IEEE80211_TID_SIZE; i++)
3845                 if (si->isi_txseqs[i] > txseq)
3846                         txseq = si->isi_txseqs[i];
3847         return txseq;
3848 }
3849
3850 static int
3851 getrxseq(const struct ieee80211req_sta_info *si)
3852 {
3853         int i, rxseq;
3854
3855         if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3856                 return si->isi_rxseqs[0];
3857         /* XXX not right but usually what folks want */
3858         rxseq = 0;
3859         for (i = 0; i < IEEE80211_TID_SIZE; i++)
3860                 if (si->isi_rxseqs[i] > rxseq)
3861                         rxseq = si->isi_rxseqs[i];
3862         return rxseq;
3863 }
3864
3865 static void
3866 list_stations(int s)
3867 {
3868         union {
3869                 struct ieee80211req_sta_req req;
3870                 uint8_t buf[24*1024];
3871         } u;
3872         enum ieee80211_opmode opmode = get80211opmode(s);
3873         const uint8_t *cp;
3874         int len;
3875
3876         /* broadcast address =>'s get all stations */
3877         (void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
3878         if (opmode == IEEE80211_M_STA) {
3879                 /*
3880                  * Get information about the associated AP.
3881                  */
3882                 (void) get80211(s, IEEE80211_IOC_BSSID,
3883                     u.req.is_u.macaddr, IEEE80211_ADDR_LEN);
3884         }
3885         if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0)
3886                 errx(1, "unable to get station information");
3887         if (len < sizeof(struct ieee80211req_sta_info))
3888                 return;
3889
3890         getchaninfo(s);
3891
3892         if (opmode == IEEE80211_M_MBSS)
3893                 printf("%-17.17s %4s %5s %5s %7s %4s %4s %4s %6s %6s\n"
3894                         , "ADDR"
3895                         , "CHAN"
3896                         , "LOCAL"
3897                         , "PEER"
3898                         , "STATE"
3899                         , "RATE"
3900                         , "RSSI"
3901                         , "IDLE"
3902                         , "TXSEQ"
3903                         , "RXSEQ"
3904                 );
3905         else
3906                 printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %-12s\n"
3907                         , "ADDR"
3908                         , "AID"
3909                         , "CHAN"
3910                         , "RATE"
3911                         , "RSSI"
3912                         , "IDLE"
3913                         , "TXSEQ"
3914                         , "RXSEQ"
3915                         , "CAPS"
3916                         , "FLAG"
3917                 );
3918         cp = (const uint8_t *) u.req.info;
3919         do {
3920                 const struct ieee80211req_sta_info *si;
3921
3922                 si = (const struct ieee80211req_sta_info *) cp;
3923                 if (si->isi_len < sizeof(*si))
3924                         break;
3925                 if (opmode == IEEE80211_M_MBSS)
3926                         printf("%s %4d %5x %5x %7.7s %3dM %4.1f %4d %6d %6d"
3927                                 , ether_ntoa((const struct ether_addr*)
3928                                     si->isi_macaddr)
3929                                 , ieee80211_mhz2ieee(si->isi_freq,
3930                                     si->isi_flags)
3931                                 , si->isi_localid
3932                                 , si->isi_peerid
3933                                 , mesh_linkstate_string(si->isi_peerstate)
3934                                 , si->isi_txmbps/2
3935                                 , si->isi_rssi/2.
3936                                 , si->isi_inact
3937                                 , gettxseq(si)
3938                                 , getrxseq(si)
3939                         );
3940                 else
3941                         printf("%s %4u %4d %3dM %4.1f %4d %6d %6d %-4.4s %-12.12s"
3942                                 , ether_ntoa((const struct ether_addr*)
3943                                     si->isi_macaddr)
3944                                 , IEEE80211_AID(si->isi_associd)
3945                                 , ieee80211_mhz2ieee(si->isi_freq,
3946                                     si->isi_flags)
3947                                 , si->isi_txmbps/2
3948                                 , si->isi_rssi/2.
3949                                 , si->isi_inact
3950                                 , gettxseq(si)
3951                                 , getrxseq(si)
3952                                 , getcaps(si->isi_capinfo)
3953                                 , getflags(si->isi_state)
3954                         );
3955                 printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
3956                 printmimo(&si->isi_mimo);
3957                 printf("\n");
3958                 cp += si->isi_len, len -= si->isi_len;
3959         } while (len >= sizeof(struct ieee80211req_sta_info));
3960 }
3961
3962 static const char *
3963 mesh_linkstate_string(uint8_t state)
3964 {
3965         static const char *state_names[] = {
3966             [0] = "IDLE",
3967             [1] = "OPEN-TX",
3968             [2] = "OPEN-RX",
3969             [3] = "CONF-RX",
3970             [4] = "ESTAB",
3971             [5] = "HOLDING",
3972         };
3973
3974         if (state >= nitems(state_names)) {
3975                 static char buf[10];
3976                 snprintf(buf, sizeof(buf), "#%u", state);
3977                 return buf;
3978         } else
3979                 return state_names[state];
3980 }
3981
3982 static const char *
3983 get_chaninfo(const struct ieee80211_channel *c, int precise,
3984         char buf[], size_t bsize)
3985 {
3986         buf[0] = '\0';
3987         if (IEEE80211_IS_CHAN_FHSS(c))
3988                 strlcat(buf, " FHSS", bsize);
3989         if (IEEE80211_IS_CHAN_A(c))
3990                 strlcat(buf, " 11a", bsize);
3991         else if (IEEE80211_IS_CHAN_ANYG(c))
3992                 strlcat(buf, " 11g", bsize);
3993         else if (IEEE80211_IS_CHAN_B(c))
3994                 strlcat(buf, " 11b", bsize);
3995         if (IEEE80211_IS_CHAN_HALF(c))
3996                 strlcat(buf, "/10MHz", bsize);
3997         if (IEEE80211_IS_CHAN_QUARTER(c))
3998                 strlcat(buf, "/5MHz", bsize);
3999         if (IEEE80211_IS_CHAN_TURBO(c))
4000                 strlcat(buf, " Turbo", bsize);
4001         if (precise) {
4002                 if (IEEE80211_IS_CHAN_VHT80P80(c))
4003                         strlcat(buf, " vht/80p80", bsize);
4004                 else if (IEEE80211_IS_CHAN_VHT160(c))
4005                         strlcat(buf, " vht/160", bsize);
4006                 else if (IEEE80211_IS_CHAN_VHT80(c) &&
4007                     IEEE80211_IS_CHAN_HT40D(c))
4008                         strlcat(buf, " vht/80-", bsize);
4009                 else if (IEEE80211_IS_CHAN_VHT80(c) &&
4010                     IEEE80211_IS_CHAN_HT40U(c))
4011                         strlcat(buf, " vht/80+", bsize);
4012                 else if (IEEE80211_IS_CHAN_VHT80(c))
4013                         strlcat(buf, " vht/80", bsize);
4014                 else if (IEEE80211_IS_CHAN_VHT40D(c))
4015                         strlcat(buf, " vht/40-", bsize);
4016                 else if (IEEE80211_IS_CHAN_VHT40U(c))
4017                         strlcat(buf, " vht/40+", bsize);
4018                 else if (IEEE80211_IS_CHAN_VHT20(c))
4019                         strlcat(buf, " vht/20", bsize);
4020                 else if (IEEE80211_IS_CHAN_HT20(c))
4021                         strlcat(buf, " ht/20", bsize);
4022                 else if (IEEE80211_IS_CHAN_HT40D(c))
4023                         strlcat(buf, " ht/40-", bsize);
4024                 else if (IEEE80211_IS_CHAN_HT40U(c))
4025                         strlcat(buf, " ht/40+", bsize);
4026         } else {
4027                 if (IEEE80211_IS_CHAN_VHT(c))
4028                         strlcat(buf, " vht", bsize);
4029                 else if (IEEE80211_IS_CHAN_HT(c))
4030                         strlcat(buf, " ht", bsize);
4031         }
4032         return buf;
4033 }
4034
4035 static void
4036 print_chaninfo(const struct ieee80211_channel *c, int verb)
4037 {
4038         char buf[14];
4039
4040         if (verb)
4041                 printf("Channel %3u : %u%c%c%c%c%c MHz%-14.14s",
4042                     ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
4043                     IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
4044                     IEEE80211_IS_CHAN_DFS(c) ? 'D' : ' ',
4045                     IEEE80211_IS_CHAN_RADAR(c) ? 'R' : ' ',
4046                     IEEE80211_IS_CHAN_CWINT(c) ? 'I' : ' ',
4047                     IEEE80211_IS_CHAN_CACDONE(c) ? 'C' : ' ',
4048                     get_chaninfo(c, verb, buf, sizeof(buf)));
4049         else
4050         printf("Channel %3u : %u%c MHz%-14.14s",
4051             ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
4052             IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
4053             get_chaninfo(c, verb, buf, sizeof(buf)));
4054
4055 }
4056
4057 static int
4058 chanpref(const struct ieee80211_channel *c)
4059 {
4060
4061         if (IEEE80211_IS_CHAN_VHT80P80(c))
4062                 return 90;
4063         if (IEEE80211_IS_CHAN_VHT160(c))
4064                 return 80;
4065         if (IEEE80211_IS_CHAN_VHT80(c))
4066                 return 70;
4067         if (IEEE80211_IS_CHAN_VHT40(c))
4068                 return 60;
4069         if (IEEE80211_IS_CHAN_VHT20(c))
4070                 return 50;
4071         if (IEEE80211_IS_CHAN_HT40(c))
4072                 return 40;
4073         if (IEEE80211_IS_CHAN_HT20(c))
4074                 return 30;
4075         if (IEEE80211_IS_CHAN_HALF(c))
4076                 return 10;
4077         if (IEEE80211_IS_CHAN_QUARTER(c))
4078                 return 5;
4079         if (IEEE80211_IS_CHAN_TURBO(c))
4080                 return 25;
4081         if (IEEE80211_IS_CHAN_A(c))
4082                 return 20;
4083         if (IEEE80211_IS_CHAN_G(c))
4084                 return 20;
4085         if (IEEE80211_IS_CHAN_B(c))
4086                 return 15;
4087         if (IEEE80211_IS_CHAN_PUREG(c))
4088                 return 15;
4089         return 0;
4090 }
4091
4092 static void
4093 print_channels(int s, const struct ieee80211req_chaninfo *chans,
4094         int allchans, int verb)
4095 {
4096         struct ieee80211req_chaninfo *achans;
4097         uint8_t reported[IEEE80211_CHAN_BYTES];
4098         const struct ieee80211_channel *c;
4099         int i, half;
4100
4101         achans = malloc(IEEE80211_CHANINFO_SPACE(chans));
4102         if (achans == NULL)
4103                 errx(1, "no space for active channel list");
4104         achans->ic_nchans = 0;
4105         memset(reported, 0, sizeof(reported));
4106         if (!allchans) {
4107                 struct ieee80211req_chanlist active;
4108
4109                 if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0)
4110                         errx(1, "unable to get active channel list");
4111                 for (i = 0; i < chans->ic_nchans; i++) {
4112                         c = &chans->ic_chans[i];
4113                         if (!isset(active.ic_channels, c->ic_ieee))
4114                                 continue;
4115                         /*
4116                          * Suppress compatible duplicates unless
4117                          * verbose.  The kernel gives us it's
4118                          * complete channel list which has separate
4119                          * entries for 11g/11b and 11a/turbo.
4120                          */
4121                         if (isset(reported, c->ic_ieee) && !verb) {
4122                                 /* XXX we assume duplicates are adjacent */
4123                                 achans->ic_chans[achans->ic_nchans-1] = *c;
4124                         } else {
4125                                 achans->ic_chans[achans->ic_nchans++] = *c;
4126                                 setbit(reported, c->ic_ieee);
4127                         }
4128                 }
4129         } else {
4130                 for (i = 0; i < chans->ic_nchans; i++) {
4131                         c = &chans->ic_chans[i];
4132                         /* suppress duplicates as above */
4133                         if (isset(reported, c->ic_ieee) && !verb) {
4134                                 /* XXX we assume duplicates are adjacent */
4135                                 struct ieee80211_channel *a =
4136                                     &achans->ic_chans[achans->ic_nchans-1];
4137                                 if (chanpref(c) > chanpref(a))
4138                                         *a = *c;
4139                         } else {
4140                                 achans->ic_chans[achans->ic_nchans++] = *c;
4141                                 setbit(reported, c->ic_ieee);
4142                         }
4143                 }
4144         }
4145         half = achans->ic_nchans / 2;
4146         if (achans->ic_nchans % 2)
4147                 half++;
4148
4149         for (i = 0; i < achans->ic_nchans / 2; i++) {
4150                 print_chaninfo(&achans->ic_chans[i], verb);
4151                 print_chaninfo(&achans->ic_chans[half+i], verb);
4152                 printf("\n");
4153         }
4154         if (achans->ic_nchans % 2) {
4155                 print_chaninfo(&achans->ic_chans[i], verb);
4156                 printf("\n");
4157         }
4158         free(achans);
4159 }
4160
4161 static void
4162 list_channels(int s, int allchans)
4163 {
4164         getchaninfo(s);
4165         print_channels(s, chaninfo, allchans, verbose);
4166 }
4167
4168 static void
4169 print_txpow(const struct ieee80211_channel *c)
4170 {
4171         printf("Channel %3u : %u MHz %3.1f reg %2d  ",
4172             c->ic_ieee, c->ic_freq,
4173             c->ic_maxpower/2., c->ic_maxregpower);
4174 }
4175
4176 static void
4177 print_txpow_verbose(const struct ieee80211_channel *c)
4178 {
4179         print_chaninfo(c, 1);
4180         printf("min %4.1f dBm  max %3.1f dBm  reg %2d dBm",
4181             c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
4182         /* indicate where regulatory cap limits power use */
4183         if (c->ic_maxpower > 2*c->ic_maxregpower)
4184                 printf(" <");
4185 }
4186
4187 static void
4188 list_txpow(int s)
4189 {
4190         struct ieee80211req_chaninfo *achans;
4191         uint8_t reported[IEEE80211_CHAN_BYTES];
4192         struct ieee80211_channel *c, *prev;
4193         int i, half;
4194
4195         getchaninfo(s);
4196         achans = malloc(IEEE80211_CHANINFO_SPACE(chaninfo));
4197         if (achans == NULL)
4198                 errx(1, "no space for active channel list");
4199         achans->ic_nchans = 0;
4200         memset(reported, 0, sizeof(reported));
4201         for (i = 0; i < chaninfo->ic_nchans; i++) {
4202                 c = &chaninfo->ic_chans[i];
4203                 /* suppress duplicates as above */
4204                 if (isset(reported, c->ic_ieee) && !verbose) {
4205                         /* XXX we assume duplicates are adjacent */
4206                         assert(achans->ic_nchans > 0);
4207                         prev = &achans->ic_chans[achans->ic_nchans-1];
4208                         /* display highest power on channel */
4209                         if (c->ic_maxpower > prev->ic_maxpower)
4210                                 *prev = *c;
4211                 } else {
4212                         achans->ic_chans[achans->ic_nchans++] = *c;
4213                         setbit(reported, c->ic_ieee);
4214                 }
4215         }
4216         if (!verbose) {
4217                 half = achans->ic_nchans / 2;
4218                 if (achans->ic_nchans % 2)
4219                         half++;
4220
4221                 for (i = 0; i < achans->ic_nchans / 2; i++) {
4222                         print_txpow(&achans->ic_chans[i]);
4223                         print_txpow(&achans->ic_chans[half+i]);
4224                         printf("\n");
4225                 }
4226                 if (achans->ic_nchans % 2) {
4227                         print_txpow(&achans->ic_chans[i]);
4228                         printf("\n");
4229                 }
4230         } else {
4231                 for (i = 0; i < achans->ic_nchans; i++) {
4232                         print_txpow_verbose(&achans->ic_chans[i]);
4233                         printf("\n");
4234                 }
4235         }
4236         free(achans);
4237 }
4238
4239 static void
4240 list_keys(int s)
4241 {
4242 }
4243
4244 static void
4245 list_capabilities(int s)
4246 {
4247         struct ieee80211_devcaps_req *dc;
4248
4249         if (verbose)
4250                 dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
4251         else
4252                 dc = malloc(IEEE80211_DEVCAPS_SIZE(1));
4253         if (dc == NULL)
4254                 errx(1, "no space for device capabilities");
4255         dc->dc_chaninfo.ic_nchans = verbose ? MAXCHAN : 1;
4256         getdevcaps(s, dc);
4257         printb("drivercaps", dc->dc_drivercaps, IEEE80211_C_BITS);
4258         if (dc->dc_cryptocaps != 0 || verbose) {
4259                 putchar('\n');
4260                 printb("cryptocaps", dc->dc_cryptocaps, IEEE80211_CRYPTO_BITS);
4261         }
4262         if (dc->dc_htcaps != 0 || verbose) {
4263                 putchar('\n');
4264                 printb("htcaps", dc->dc_htcaps, IEEE80211_HTCAP_BITS);
4265         }
4266         if (dc->dc_vhtcaps != 0 || verbose) {
4267                 putchar('\n');
4268                 printb("vhtcaps", dc->dc_vhtcaps, IEEE80211_VHTCAP_BITS);
4269         }
4270
4271         putchar('\n');
4272         if (verbose) {
4273                 chaninfo = &dc->dc_chaninfo;    /* XXX */
4274                 print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, verbose);
4275         }
4276         free(dc);
4277 }
4278
4279 static int
4280 get80211wme(int s, int param, int ac, int *val)
4281 {
4282         struct ieee80211req ireq;
4283
4284         (void) memset(&ireq, 0, sizeof(ireq));
4285         (void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
4286         ireq.i_type = param;
4287         ireq.i_len = ac;
4288         if (ioctl(s, SIOCG80211, &ireq) < 0) {
4289                 warn("cannot get WME parameter %d, ac %d%s",
4290                     param, ac & IEEE80211_WMEPARAM_VAL,
4291                     ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : "");
4292                 return -1;
4293         }
4294         *val = ireq.i_val;
4295         return 0;
4296 }
4297
4298 static void
4299 list_wme_aci(int s, const char *tag, int ac)
4300 {
4301         int val;
4302
4303         printf("\t%s", tag);
4304
4305         /* show WME BSS parameters */
4306         if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1)
4307                 printf(" cwmin %2u", val);
4308         if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1)
4309                 printf(" cwmax %2u", val);
4310         if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1)
4311                 printf(" aifs %2u", val);
4312         if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1)
4313                 printf(" txopLimit %3u", val);
4314         if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) {
4315                 if (val)
4316                         printf(" acm");
4317                 else if (verbose)
4318                         printf(" -acm");
4319         }
4320         /* !BSS only */
4321         if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
4322                 if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) {
4323                         if (!val)
4324                                 printf(" -ack");
4325                         else if (verbose)
4326                                 printf(" ack");
4327                 }
4328         }
4329         printf("\n");
4330 }
4331
4332 static void
4333 list_wme(int s)
4334 {
4335         static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
4336         int ac;
4337
4338         if (verbose) {
4339                 /* display both BSS and local settings */
4340                 for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
4341         again:
4342                         if (ac & IEEE80211_WMEPARAM_BSS)
4343                                 list_wme_aci(s, "     ", ac);
4344                         else
4345                                 list_wme_aci(s, acnames[ac], ac);
4346                         if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
4347                                 ac |= IEEE80211_WMEPARAM_BSS;
4348                                 goto again;
4349                         } else
4350                                 ac &= ~IEEE80211_WMEPARAM_BSS;
4351                 }
4352         } else {
4353                 /* display only channel settings */
4354                 for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++)
4355                         list_wme_aci(s, acnames[ac], ac);
4356         }
4357 }
4358
4359 static void
4360 list_roam(int s)
4361 {
4362         const struct ieee80211_roamparam *rp;
4363         int mode;
4364
4365         getroam(s);
4366         for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
4367                 rp = &roamparams.params[mode];
4368                 if (rp->rssi == 0 && rp->rate == 0)
4369                         continue;
4370                 if (mode == IEEE80211_MODE_11NA ||
4371                     mode == IEEE80211_MODE_11NG ||
4372                     mode == IEEE80211_MODE_VHT_2GHZ ||
4373                     mode == IEEE80211_MODE_VHT_5GHZ) {
4374                         if (rp->rssi & 1)
4375                                 LINE_CHECK("roam:%-7.7s rssi %2u.5dBm  MCS %2u    ",
4376                                     modename[mode], rp->rssi/2,
4377                                     rp->rate &~ IEEE80211_RATE_MCS);
4378                         else
4379                                 LINE_CHECK("roam:%-7.7s rssi %4udBm  MCS %2u    ",
4380                                     modename[mode], rp->rssi/2,
4381                                     rp->rate &~ IEEE80211_RATE_MCS);
4382                 } else {
4383                         if (rp->rssi & 1)
4384                                 LINE_CHECK("roam:%-7.7s rssi %2u.5dBm rate %2u Mb/s",
4385                                     modename[mode], rp->rssi/2, rp->rate/2);
4386                         else
4387                                 LINE_CHECK("roam:%-7.7s rssi %4udBm rate %2u Mb/s",
4388                                     modename[mode], rp->rssi/2, rp->rate/2);
4389                 }
4390         }
4391 }
4392
4393 /* XXX TODO: rate-to-string method... */
4394 static const char*
4395 get_mcs_mbs_rate_str(uint8_t rate)
4396 {
4397         return (rate & IEEE80211_RATE_MCS) ? "MCS " : "Mb/s";
4398 }
4399
4400 static uint8_t
4401 get_rate_value(uint8_t rate)
4402 {
4403         if (rate & IEEE80211_RATE_MCS)
4404                 return (rate &~ IEEE80211_RATE_MCS);
4405         return (rate / 2);
4406 }
4407
4408 static void
4409 list_txparams(int s)
4410 {
4411         const struct ieee80211_txparam *tp;
4412         int mode;
4413
4414         gettxparams(s);
4415         for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
4416                 tp = &txparams.params[mode];
4417                 if (tp->mgmtrate == 0 && tp->mcastrate == 0)
4418                         continue;
4419                 if (mode == IEEE80211_MODE_11NA ||
4420                     mode == IEEE80211_MODE_11NG ||
4421                     mode == IEEE80211_MODE_VHT_2GHZ ||
4422                     mode == IEEE80211_MODE_VHT_5GHZ) {
4423                         if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
4424                                 LINE_CHECK("%-7.7s ucast NONE    mgmt %2u %s "
4425                                     "mcast %2u %s maxretry %u",
4426                                     modename[mode],
4427                                     get_rate_value(tp->mgmtrate),
4428                                     get_mcs_mbs_rate_str(tp->mgmtrate),
4429                                     get_rate_value(tp->mcastrate),
4430                                     get_mcs_mbs_rate_str(tp->mcastrate),
4431                                     tp->maxretry);
4432                         else
4433                                 LINE_CHECK("%-7.7s ucast %2u MCS  mgmt %2u %s "
4434                                     "mcast %2u %s maxretry %u",
4435                                     modename[mode],
4436                                     tp->ucastrate &~ IEEE80211_RATE_MCS,
4437                                     get_rate_value(tp->mgmtrate),
4438                                     get_mcs_mbs_rate_str(tp->mgmtrate),
4439                                     get_rate_value(tp->mcastrate),
4440                                     get_mcs_mbs_rate_str(tp->mcastrate),
4441                                     tp->maxretry);
4442                 } else {
4443                         if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
4444                                 LINE_CHECK("%-7.7s ucast NONE    mgmt %2u Mb/s "
4445                                     "mcast %2u Mb/s maxretry %u",
4446                                     modename[mode],
4447                                     tp->mgmtrate/2,
4448                                     tp->mcastrate/2, tp->maxretry);
4449                         else
4450                                 LINE_CHECK("%-7.7s ucast %2u Mb/s mgmt %2u Mb/s "
4451                                     "mcast %2u Mb/s maxretry %u",
4452                                     modename[mode],
4453                                     tp->ucastrate/2, tp->mgmtrate/2,
4454                                     tp->mcastrate/2, tp->maxretry);
4455                 }
4456         }
4457 }
4458
4459 static void
4460 printpolicy(int policy)
4461 {
4462         switch (policy) {
4463         case IEEE80211_MACCMD_POLICY_OPEN:
4464                 printf("policy: open\n");
4465                 break;
4466         case IEEE80211_MACCMD_POLICY_ALLOW:
4467                 printf("policy: allow\n");
4468                 break;
4469         case IEEE80211_MACCMD_POLICY_DENY:
4470                 printf("policy: deny\n");
4471                 break;
4472         case IEEE80211_MACCMD_POLICY_RADIUS:
4473                 printf("policy: radius\n");
4474                 break;
4475         default:
4476                 printf("policy: unknown (%u)\n", policy);
4477                 break;
4478         }
4479 }
4480
4481 static void
4482 list_mac(int s)
4483 {
4484         struct ieee80211req ireq;
4485         struct ieee80211req_maclist *acllist;
4486         int i, nacls, policy, len;
4487         uint8_t *data;
4488         char c;
4489
4490         (void) memset(&ireq, 0, sizeof(ireq));
4491         (void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
4492         ireq.i_type = IEEE80211_IOC_MACCMD;
4493         ireq.i_val = IEEE80211_MACCMD_POLICY;
4494         if (ioctl(s, SIOCG80211, &ireq) < 0) {
4495                 if (errno == EINVAL) {
4496                         printf("No acl policy loaded\n");
4497                         return;
4498                 }
4499                 err(1, "unable to get mac policy");
4500         }
4501         policy = ireq.i_val;
4502         if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
4503                 c = '*';
4504         } else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
4505                 c = '+';
4506         } else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
4507                 c = '-';
4508         } else if (policy == IEEE80211_MACCMD_POLICY_RADIUS) {
4509                 c = 'r';                /* NB: should never have entries */
4510         } else {
4511                 printf("policy: unknown (%u)\n", policy);
4512                 c = '?';
4513         }
4514         if (verbose || c == '?')
4515                 printpolicy(policy);
4516
4517         ireq.i_val = IEEE80211_MACCMD_LIST;
4518         ireq.i_len = 0;
4519         if (ioctl(s, SIOCG80211, &ireq) < 0)
4520                 err(1, "unable to get mac acl list size");
4521         if (ireq.i_len == 0) {          /* NB: no acls */
4522                 if (!(verbose || c == '?'))
4523                         printpolicy(policy);
4524                 return;
4525         }
4526         len = ireq.i_len;
4527
4528         data = malloc(len);
4529         if (data == NULL)
4530                 err(1, "out of memory for acl list");
4531
4532         ireq.i_data = data;
4533         if (ioctl(s, SIOCG80211, &ireq) < 0)
4534                 err(1, "unable to get mac acl list");
4535         nacls = len / sizeof(*acllist);
4536         acllist = (struct ieee80211req_maclist *) data;
4537         for (i = 0; i < nacls; i++)
4538                 printf("%c%s\n", c, ether_ntoa(
4539                         (const struct ether_addr *) acllist[i].ml_macaddr));
4540         free(data);
4541 }
4542
4543 static void
4544 print_regdomain(const struct ieee80211_regdomain *reg, int verb)
4545 {
4546         if ((reg->regdomain != 0 &&
4547             reg->regdomain != reg->country) || verb) {
4548                 const struct regdomain *rd =
4549                     lib80211_regdomain_findbysku(getregdata(), reg->regdomain);
4550                 if (rd == NULL)
4551                         LINE_CHECK("regdomain %d", reg->regdomain);
4552                 else
4553                         LINE_CHECK("regdomain %s", rd->name);
4554         }
4555         if (reg->country != 0 || verb) {
4556                 const struct country *cc =
4557                     lib80211_country_findbycc(getregdata(), reg->country);
4558                 if (cc == NULL)
4559                         LINE_CHECK("country %d", reg->country);
4560                 else
4561                         LINE_CHECK("country %s", cc->isoname);
4562         }
4563         if (reg->location == 'I')
4564                 LINE_CHECK("indoor");
4565         else if (reg->location == 'O')
4566                 LINE_CHECK("outdoor");
4567         else if (verb)
4568                 LINE_CHECK("anywhere");
4569         if (reg->ecm)
4570                 LINE_CHECK("ecm");
4571         else if (verb)
4572                 LINE_CHECK("-ecm");
4573 }
4574
4575 static void
4576 list_regdomain(int s, int channelsalso)
4577 {
4578         getregdomain(s);
4579         if (channelsalso) {
4580                 getchaninfo(s);
4581                 spacer = ':';
4582                 print_regdomain(&regdomain, 1);
4583                 LINE_BREAK();
4584                 print_channels(s, chaninfo, 1/*allchans*/, 1/*verbose*/);
4585         } else
4586                 print_regdomain(&regdomain, verbose);
4587 }
4588
4589 static void
4590 list_mesh(int s)
4591 {
4592         struct ieee80211req ireq;
4593         struct ieee80211req_mesh_route routes[128];
4594         struct ieee80211req_mesh_route *rt;
4595
4596         (void) memset(&ireq, 0, sizeof(ireq));
4597         (void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
4598         ireq.i_type = IEEE80211_IOC_MESH_RTCMD;
4599         ireq.i_val = IEEE80211_MESH_RTCMD_LIST;
4600         ireq.i_data = &routes;
4601         ireq.i_len = sizeof(routes);
4602         if (ioctl(s, SIOCG80211, &ireq) < 0)
4603                 err(1, "unable to get the Mesh routing table");
4604
4605         printf("%-17.17s %-17.17s %4s %4s %4s %6s %s\n"
4606                 , "DEST"
4607                 , "NEXT HOP"
4608                 , "HOPS"
4609                 , "METRIC"
4610                 , "LIFETIME"
4611                 , "MSEQ"
4612                 , "FLAGS");
4613
4614         for (rt = &routes[0]; rt - &routes[0] < ireq.i_len / sizeof(*rt); rt++){
4615                 printf("%s ",
4616                     ether_ntoa((const struct ether_addr *)rt->imr_dest));
4617                 printf("%s %4u   %4u   %6u %6u    %c%c\n",
4618                         ether_ntoa((const struct ether_addr *)rt->imr_nexthop),
4619                         rt->imr_nhops, rt->imr_metric, rt->imr_lifetime,
4620                         rt->imr_lastmseq,
4621                         (rt->imr_flags & IEEE80211_MESHRT_FLAGS_DISCOVER) ?
4622                             'D' :
4623                         (rt->imr_flags & IEEE80211_MESHRT_FLAGS_VALID) ?
4624                             'V' : '!',
4625                         (rt->imr_flags & IEEE80211_MESHRT_FLAGS_PROXY) ?
4626                             'P' :
4627                         (rt->imr_flags & IEEE80211_MESHRT_FLAGS_GATE) ?
4628                             'G' :' ');
4629         }
4630 }
4631
4632 static
4633 DECL_CMD_FUNC(set80211list, arg, d)
4634 {
4635 #define iseq(a,b)       (strncasecmp(a,b,sizeof(b)-1) == 0)
4636
4637         LINE_INIT('\t');
4638
4639         if (iseq(arg, "sta"))
4640                 list_stations(s);
4641         else if (iseq(arg, "scan") || iseq(arg, "ap"))
4642                 list_scan(s);
4643         else if (iseq(arg, "chan") || iseq(arg, "freq"))
4644                 list_channels(s, 1);
4645         else if (iseq(arg, "active"))
4646                 list_channels(s, 0);
4647         else if (iseq(arg, "keys"))
4648                 list_keys(s);
4649         else if (iseq(arg, "caps"))
4650                 list_capabilities(s);
4651         else if (iseq(arg, "wme") || iseq(arg, "wmm"))
4652                 list_wme(s);
4653         else if (iseq(arg, "mac"))
4654                 list_mac(s);
4655         else if (iseq(arg, "txpow"))
4656                 list_txpow(s);
4657         else if (iseq(arg, "roam"))
4658                 list_roam(s);
4659         else if (iseq(arg, "txparam") || iseq(arg, "txparm"))
4660                 list_txparams(s);
4661         else if (iseq(arg, "regdomain"))
4662                 list_regdomain(s, 1);
4663         else if (iseq(arg, "countries"))
4664                 list_countries();
4665         else if (iseq(arg, "mesh"))
4666                 list_mesh(s);
4667         else
4668                 errx(1, "Don't know how to list %s for %s", arg, name);
4669         LINE_BREAK();
4670 #undef iseq
4671 }
4672
4673 static enum ieee80211_opmode
4674 get80211opmode(int s)
4675 {
4676         struct ifmediareq ifmr;
4677
4678         (void) memset(&ifmr, 0, sizeof(ifmr));
4679         (void) strlcpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
4680
4681         if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
4682                 if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
4683                         if (ifmr.ifm_current & IFM_FLAG0)
4684                                 return IEEE80211_M_AHDEMO;
4685                         else
4686                                 return IEEE80211_M_IBSS;
4687                 }
4688                 if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
4689                         return IEEE80211_M_HOSTAP;
4690                 if (ifmr.ifm_current & IFM_IEEE80211_IBSS)
4691                         return IEEE80211_M_IBSS;
4692                 if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
4693                         return IEEE80211_M_MONITOR;
4694                 if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
4695                         return IEEE80211_M_MBSS;
4696         }
4697         return IEEE80211_M_STA;
4698 }
4699
4700 #if 0
4701 static void
4702 printcipher(int s, struct ieee80211req *ireq, int keylenop)
4703 {
4704         switch (ireq->i_val) {
4705         case IEEE80211_CIPHER_WEP:
4706                 ireq->i_type = keylenop;
4707                 if (ioctl(s, SIOCG80211, ireq) != -1)
4708                         printf("WEP-%s", 
4709                             ireq->i_len <= 5 ? "40" :
4710                             ireq->i_len <= 13 ? "104" : "128");
4711                 else
4712                         printf("WEP");
4713                 break;
4714         case IEEE80211_CIPHER_TKIP:
4715                 printf("TKIP");
4716                 break;
4717         case IEEE80211_CIPHER_AES_OCB:
4718                 printf("AES-OCB");
4719                 break;
4720         case IEEE80211_CIPHER_AES_CCM:
4721                 printf("AES-CCM");
4722                 break;
4723         case IEEE80211_CIPHER_CKIP:
4724                 printf("CKIP");
4725                 break;
4726         case IEEE80211_CIPHER_NONE:
4727                 printf("NONE");
4728                 break;
4729         default:
4730                 printf("UNKNOWN (0x%x)", ireq->i_val);
4731                 break;
4732         }
4733 }
4734 #endif
4735
4736 static void
4737 printkey(const struct ieee80211req_key *ik)
4738 {
4739         static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
4740         u_int keylen = ik->ik_keylen;
4741         int printcontents;
4742
4743         printcontents = printkeys &&
4744                 (memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
4745         if (printcontents)
4746                 LINE_BREAK();
4747         switch (ik->ik_type) {
4748         case IEEE80211_CIPHER_WEP:
4749                 /* compatibility */
4750                 LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
4751                     keylen <= 5 ? "40-bit" :
4752                     keylen <= 13 ? "104-bit" : "128-bit");
4753                 break;
4754         case IEEE80211_CIPHER_TKIP:
4755                 if (keylen > 128/8)
4756                         keylen -= 128/8;        /* ignore MIC for now */
4757                 LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4758                 break;
4759         case IEEE80211_CIPHER_AES_OCB:
4760                 LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4761                 break;
4762         case IEEE80211_CIPHER_AES_CCM:
4763                 LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4764                 break;
4765         case IEEE80211_CIPHER_CKIP:
4766                 LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4767                 break;
4768         case IEEE80211_CIPHER_NONE:
4769                 LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
4770                 break;
4771         default:
4772                 LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
4773                         ik->ik_type, ik->ik_keyix+1, 8*keylen);
4774                 break;
4775         }
4776         if (printcontents) {
4777                 u_int i;
4778
4779                 printf(" <");
4780                 for (i = 0; i < keylen; i++)
4781                         printf("%02x", ik->ik_keydata[i]);
4782                 printf(">");
4783                 if (ik->ik_type != IEEE80211_CIPHER_WEP &&
4784                     (ik->ik_keyrsc != 0 || verbose))
4785                         printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
4786                 if (ik->ik_type != IEEE80211_CIPHER_WEP &&
4787                     (ik->ik_keytsc != 0 || verbose))
4788                         printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
4789                 if (ik->ik_flags != 0 && verbose) {
4790                         const char *sep = " ";
4791
4792                         if (ik->ik_flags & IEEE80211_KEY_XMIT)
4793                                 printf("%stx", sep), sep = "+";
4794                         if (ik->ik_flags & IEEE80211_KEY_RECV)
4795                                 printf("%srx", sep), sep = "+";
4796                         if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
4797                                 printf("%sdef", sep), sep = "+";
4798                 }
4799                 LINE_BREAK();
4800         }
4801 }
4802
4803 static void
4804 printrate(const char *tag, int v, int defrate, int defmcs)
4805 {
4806         if ((v & IEEE80211_RATE_MCS) == 0) {
4807                 if (v != defrate) {
4808                         if (v & 1)
4809                                 LINE_CHECK("%s %d.5", tag, v/2);
4810                         else
4811                                 LINE_CHECK("%s %d", tag, v/2);
4812                 }
4813         } else {
4814                 if (v != defmcs)
4815                         LINE_CHECK("%s %d", tag, v &~ 0x80);
4816         }
4817 }
4818
4819 static int
4820 getid(int s, int ix, void *data, size_t len, int *plen, int mesh)
4821 {
4822         struct ieee80211req ireq;
4823
4824         (void) memset(&ireq, 0, sizeof(ireq));
4825         (void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
4826         ireq.i_type = (!mesh) ? IEEE80211_IOC_SSID : IEEE80211_IOC_MESH_ID;
4827         ireq.i_val = ix;
4828         ireq.i_data = data;
4829         ireq.i_len = len;
4830         if (ioctl(s, SIOCG80211, &ireq) < 0)
4831                 return -1;
4832         *plen = ireq.i_len;
4833         return 0;
4834 }
4835
4836 static int
4837 getdevicename(int s, void *data, size_t len, int *plen)
4838 {
4839         struct ieee80211req ireq;
4840
4841         (void) memset(&ireq, 0, sizeof(ireq));
4842         (void) strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
4843         ireq.i_type = IEEE80211_IOC_IC_NAME;
4844         ireq.i_val = -1;
4845         ireq.i_data = data;
4846         ireq.i_len = len;
4847         if (ioctl(s, SIOCG80211, &ireq) < 0)
4848                 return (-1);
4849         *plen = ireq.i_len;
4850         return (0);
4851 }
4852
4853 static void
4854 ieee80211_status(int s)
4855 {
4856         static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
4857         enum ieee80211_opmode opmode = get80211opmode(s);
4858         int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode;
4859         uint8_t data[32];
4860         const struct ieee80211_channel *c;
4861         const struct ieee80211_roamparam *rp;
4862         const struct ieee80211_txparam *tp;
4863
4864         if (getid(s, -1, data, sizeof(data), &len, 0) < 0) {
4865                 /* If we can't get the SSID, this isn't an 802.11 device. */
4866                 return;
4867         }
4868
4869         /*
4870          * Invalidate cached state so printing status for multiple
4871          * if's doesn't reuse the first interfaces' cached state.
4872          */
4873         gotcurchan = 0;
4874         gotroam = 0;
4875         gottxparams = 0;
4876         gothtconf = 0;
4877         gotregdomain = 0;
4878
4879         printf("\t");
4880         if (opmode == IEEE80211_M_MBSS) {
4881                 printf("meshid ");
4882                 getid(s, 0, data, sizeof(data), &len, 1);
4883                 print_string(data, len);
4884         } else {
4885                 if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
4886                         num = 0;
4887                 printf("ssid ");
4888                 if (num > 1) {
4889                         for (i = 0; i < num; i++) {
4890                                 if (getid(s, i, data, sizeof(data), &len, 0) >= 0 && len > 0) {
4891                                         printf(" %d:", i + 1);
4892                                         print_string(data, len);
4893                                 }
4894                         }
4895                 } else
4896                         print_string(data, len);
4897         }
4898         c = getcurchan(s);
4899         if (c->ic_freq != IEEE80211_CHAN_ANY) {
4900                 char buf[14];
4901                 printf(" channel %d (%u MHz%s)", c->ic_ieee, c->ic_freq,
4902                         get_chaninfo(c, 1, buf, sizeof(buf)));
4903         } else if (verbose)
4904                 printf(" channel UNDEF");
4905
4906         if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 &&
4907             (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
4908                 printf(" bssid %s", ether_ntoa((struct ether_addr *)data));
4909
4910         if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) {
4911                 printf("\n\tstationname ");
4912                 print_string(data, len);
4913         }
4914
4915         spacer = ' ';           /* force first break */
4916         LINE_BREAK();
4917
4918         list_regdomain(s, 0);
4919
4920         wpa = 0;
4921         if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) {
4922                 switch (val) {
4923                 case IEEE80211_AUTH_NONE:
4924                         LINE_CHECK("authmode NONE");
4925                         break;
4926                 case IEEE80211_AUTH_OPEN:
4927                         LINE_CHECK("authmode OPEN");
4928                         break;
4929                 case IEEE80211_AUTH_SHARED:
4930                         LINE_CHECK("authmode SHARED");
4931                         break;
4932                 case IEEE80211_AUTH_8021X:
4933                         LINE_CHECK("authmode 802.1x");
4934                         break;
4935                 case IEEE80211_AUTH_WPA:
4936                         if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0)
4937                                 wpa = 1;        /* default to WPA1 */
4938                         switch (wpa) {
4939                         case 2:
4940                                 LINE_CHECK("authmode WPA2/802.11i");
4941                                 break;
4942                         case 3:
4943                                 LINE_CHECK("authmode WPA1+WPA2/802.11i");
4944                                 break;
4945                         default:
4946                                 LINE_CHECK("authmode WPA");
4947                                 break;
4948                         }
4949                         break;
4950                 case IEEE80211_AUTH_AUTO:
4951                         LINE_CHECK("authmode AUTO");
4952                         break;
4953                 default:
4954                         LINE_CHECK("authmode UNKNOWN (0x%x)", val);
4955                         break;
4956                 }
4957         }
4958
4959         if (wpa || verbose) {
4960                 if (get80211val(s, IEEE80211_IOC_WPS, &val) != -1) {
4961                         if (val)
4962                                 LINE_CHECK("wps");
4963                         else if (verbose)
4964                                 LINE_CHECK("-wps");
4965                 }
4966                 if (get80211val(s, IEEE80211_IOC_TSN, &val) != -1) {
4967                         if (val)
4968                                 LINE_CHECK("tsn");
4969                         else if (verbose)
4970                                 LINE_CHECK("-tsn");
4971                 }
4972                 if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) {
4973                         if (val)
4974                                 LINE_CHECK("countermeasures");
4975                         else if (verbose)
4976                                 LINE_CHECK("-countermeasures");
4977                 }
4978 #if 0
4979                 /* XXX not interesting with WPA done in user space */
4980                 ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
4981                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
4982                 }
4983
4984                 ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
4985                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
4986                         LINE_CHECK("mcastcipher ");
4987                         printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
4988                         spacer = ' ';
4989                 }
4990
4991                 ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
4992                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
4993                         LINE_CHECK("ucastcipher ");
4994                         printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
4995                 }
4996
4997                 if (wpa & 2) {
4998                         ireq.i_type = IEEE80211_IOC_RSNCAPS;
4999                         if (ioctl(s, SIOCG80211, &ireq) != -1) {
5000                                 LINE_CHECK("RSN caps 0x%x", ireq.i_val);
5001                                 spacer = ' ';
5002                         }
5003                 }
5004
5005                 ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
5006                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
5007                 }
5008 #endif
5009         }
5010
5011         if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 &&
5012             wepmode != IEEE80211_WEP_NOSUP) {
5013
5014                 switch (wepmode) {
5015                 case IEEE80211_WEP_OFF:
5016                         LINE_CHECK("privacy OFF");
5017                         break;
5018                 case IEEE80211_WEP_ON:
5019                         LINE_CHECK("privacy ON");
5020                         break;
5021                 case IEEE80211_WEP_MIXED:
5022                         LINE_CHECK("privacy MIXED");
5023                         break;
5024                 default:
5025                         LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
5026                         break;
5027                 }
5028
5029                 /*
5030                  * If we get here then we've got WEP support so we need
5031                  * to print WEP status.
5032                  */
5033
5034                 if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) {
5035                         warn("WEP support, but no tx key!");
5036                         goto end;
5037                 }
5038                 if (val != -1)
5039                         LINE_CHECK("deftxkey %d", val+1);
5040                 else if (wepmode != IEEE80211_WEP_OFF || verbose)
5041                         LINE_CHECK("deftxkey UNDEF");
5042
5043                 if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) {
5044                         warn("WEP support, but no NUMWEPKEYS support!");
5045                         goto end;
5046                 }
5047
5048                 for (i = 0; i < num; i++) {
5049                         struct ieee80211req_key ik;
5050
5051                         memset(&ik, 0, sizeof(ik));
5052                         ik.ik_keyix = i;
5053                         if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) {
5054                                 warn("WEP support, but can get keys!");
5055                                 goto end;
5056                         }
5057                         if (ik.ik_keylen != 0) {
5058                                 if (verbose)
5059                                         LINE_BREAK();
5060                                 printkey(&ik);
5061                         }
5062                 }
5063 end:
5064                 ;
5065         }
5066
5067         if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 &&
5068             val != IEEE80211_POWERSAVE_NOSUP ) {
5069                 if (val != IEEE80211_POWERSAVE_OFF || verbose) {
5070                         switch (val) {
5071                         case IEEE80211_POWERSAVE_OFF:
5072                                 LINE_CHECK("powersavemode OFF");
5073                                 break;
5074                         case IEEE80211_POWERSAVE_CAM:
5075                                 LINE_CHECK("powersavemode CAM");
5076                                 break;
5077                         case IEEE80211_POWERSAVE_PSP:
5078                                 LINE_CHECK("powersavemode PSP");
5079                                 break;
5080                         case IEEE80211_POWERSAVE_PSP_CAM:
5081                                 LINE_CHECK("powersavemode PSP-CAM");
5082                                 break;
5083                         }
5084                         if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1)
5085                                 LINE_CHECK("powersavesleep %d", val);
5086                 }
5087         }
5088
5089         if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) {
5090                 if (val & 1)
5091                         LINE_CHECK("txpower %d.5", val/2);
5092                 else
5093                         LINE_CHECK("txpower %d", val/2);
5094         }
5095         if (verbose) {
5096                 if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1)
5097                         LINE_CHECK("txpowmax %.1f", val/2.);
5098         }
5099
5100         if (get80211val(s, IEEE80211_IOC_DOTD, &val) != -1) {
5101                 if (val)
5102                         LINE_CHECK("dotd");
5103                 else if (verbose)
5104                         LINE_CHECK("-dotd");
5105         }
5106
5107         if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) {
5108                 if (val != IEEE80211_RTS_MAX || verbose)
5109                         LINE_CHECK("rtsthreshold %d", val);
5110         }
5111
5112         if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) {
5113                 if (val != IEEE80211_FRAG_MAX || verbose)
5114                         LINE_CHECK("fragthreshold %d", val);
5115         }
5116         if (opmode == IEEE80211_M_STA || verbose) {
5117                 if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) {
5118                         if (val != IEEE80211_HWBMISS_MAX || verbose)
5119                                 LINE_CHECK("bmiss %d", val);
5120                 }
5121         }
5122
5123         if (!verbose) {
5124                 gettxparams(s);
5125                 tp = &txparams.params[chan2mode(c)];
5126                 printrate("ucastrate", tp->ucastrate,
5127                     IEEE80211_FIXED_RATE_NONE, IEEE80211_FIXED_RATE_NONE);
5128                 printrate("mcastrate", tp->mcastrate, 2*1,
5129                     IEEE80211_RATE_MCS|0);
5130                 printrate("mgmtrate", tp->mgmtrate, 2*1,
5131                     IEEE80211_RATE_MCS|0);
5132                 if (tp->maxretry != 6)          /* XXX */
5133                         LINE_CHECK("maxretry %d", tp->maxretry);
5134         } else {
5135                 LINE_BREAK();
5136                 list_txparams(s);
5137         }
5138
5139         bgscaninterval = -1;
5140         (void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval);
5141
5142         if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) {
5143                 if (val != bgscaninterval || verbose)
5144                         LINE_CHECK("scanvalid %u", val);
5145         }
5146
5147         bgscan = 0;
5148         if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) {
5149                 if (bgscan)
5150                         LINE_CHECK("bgscan");
5151                 else if (verbose)
5152                         LINE_CHECK("-bgscan");
5153         }
5154         if (bgscan || verbose) {
5155                 if (bgscaninterval != -1)
5156                         LINE_CHECK("bgscanintvl %u", bgscaninterval);
5157                 if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1)
5158                         LINE_CHECK("bgscanidle %u", val);
5159                 if (!verbose) {
5160                         getroam(s);
5161                         rp = &roamparams.params[chan2mode(c)];
5162                         if (rp->rssi & 1)
5163                                 LINE_CHECK("roam:rssi %u.5", rp->rssi/2);
5164                         else
5165                                 LINE_CHECK("roam:rssi %u", rp->rssi/2);
5166                         LINE_CHECK("roam:rate %s%u",
5167                             (rp->rate & IEEE80211_RATE_MCS) ? "MCS " : "",
5168                             get_rate_value(rp->rate));
5169                 } else {
5170                         LINE_BREAK();
5171                         list_roam(s);
5172                         LINE_BREAK();
5173                 }
5174         }
5175
5176         if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
5177                 if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) {
5178                         if (val)
5179                                 LINE_CHECK("pureg");
5180                         else if (verbose)
5181                                 LINE_CHECK("-pureg");
5182                 }
5183                 if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) {
5184                         switch (val) {
5185                         case IEEE80211_PROTMODE_OFF:
5186                                 LINE_CHECK("protmode OFF");
5187                                 break;
5188                         case IEEE80211_PROTMODE_CTS:
5189                                 LINE_CHECK("protmode CTS");
5190                                 break;
5191                         case IEEE80211_PROTMODE_RTSCTS:
5192                                 LINE_CHECK("protmode RTSCTS");
5193                                 break;
5194                         default:
5195                                 LINE_CHECK("protmode UNKNOWN (0x%x)", val);
5196                                 break;
5197                         }
5198                 }
5199         }
5200
5201         if (IEEE80211_IS_CHAN_HT(c) || verbose) {
5202                 gethtconf(s);
5203                 switch (htconf & 3) {
5204                 case 0:
5205                 case 2:
5206                         LINE_CHECK("-ht");
5207                         break;
5208                 case 1:
5209                         LINE_CHECK("ht20");
5210                         break;
5211                 case 3:
5212                         if (verbose)
5213                                 LINE_CHECK("ht");
5214                         break;
5215                 }
5216                 if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) {
5217                         if (!val)
5218                                 LINE_CHECK("-htcompat");
5219                         else if (verbose)
5220                                 LINE_CHECK("htcompat");
5221                 }
5222                 if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) {
5223                         switch (val) {
5224                         case 0:
5225                                 LINE_CHECK("-ampdu");
5226                                 break;
5227                         case 1:
5228                                 LINE_CHECK("ampdutx -ampdurx");
5229                                 break;
5230                         case 2:
5231                                 LINE_CHECK("-ampdutx ampdurx");
5232                                 break;
5233                         case 3:
5234                                 if (verbose)
5235                                         LINE_CHECK("ampdu");
5236                                 break;
5237                         }
5238                 }
5239                 /* XXX 11ac density/size is different */
5240                 if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) {
5241                         switch (val) {
5242                         case IEEE80211_HTCAP_MAXRXAMPDU_8K:
5243                                 LINE_CHECK("ampdulimit 8k");
5244                                 break;
5245                         case IEEE80211_HTCAP_MAXRXAMPDU_16K:
5246                                 LINE_CHECK("ampdulimit 16k");
5247                                 break;
5248                         case IEEE80211_HTCAP_MAXRXAMPDU_32K:
5249                                 LINE_CHECK("ampdulimit 32k");
5250                                 break;
5251                         case IEEE80211_HTCAP_MAXRXAMPDU_64K:
5252                                 LINE_CHECK("ampdulimit 64k");
5253                                 break;
5254                         }
5255                 }
5256                 /* XXX 11ac density/size is different */
5257                 if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) {
5258                         switch (val) {
5259                         case IEEE80211_HTCAP_MPDUDENSITY_NA:
5260                                 if (verbose)
5261                                         LINE_CHECK("ampdudensity NA");
5262                                 break;
5263                         case IEEE80211_HTCAP_MPDUDENSITY_025:
5264                                 LINE_CHECK("ampdudensity .25");
5265                                 break;
5266                         case IEEE80211_HTCAP_MPDUDENSITY_05:
5267                                 LINE_CHECK("ampdudensity .5");
5268                                 break;
5269                         case IEEE80211_HTCAP_MPDUDENSITY_1:
5270                                 LINE_CHECK("ampdudensity 1");
5271                                 break;
5272                         case IEEE80211_HTCAP_MPDUDENSITY_2:
5273                                 LINE_CHECK("ampdudensity 2");
5274                                 break;
5275                         case IEEE80211_HTCAP_MPDUDENSITY_4:
5276                                 LINE_CHECK("ampdudensity 4");
5277                                 break;
5278                         case IEEE80211_HTCAP_MPDUDENSITY_8:
5279                                 LINE_CHECK("ampdudensity 8");
5280                                 break;
5281                         case IEEE80211_HTCAP_MPDUDENSITY_16:
5282                                 LINE_CHECK("ampdudensity 16");
5283                                 break;
5284                         }
5285                 }
5286                 if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) {
5287                         switch (val) {
5288                         case 0:
5289                                 LINE_CHECK("-amsdu");
5290                                 break;
5291                         case 1:
5292                                 LINE_CHECK("amsdutx -amsdurx");
5293                                 break;
5294                         case 2:
5295                                 LINE_CHECK("-amsdutx amsdurx");
5296                                 break;
5297                         case 3:
5298                                 if (verbose)
5299                                         LINE_CHECK("amsdu");
5300                                 break;
5301                         }
5302                 }
5303                 /* XXX amsdu limit */
5304                 if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) {
5305                         if (val)
5306                                 LINE_CHECK("shortgi");
5307                         else if (verbose)
5308                                 LINE_CHECK("-shortgi");
5309                 }
5310                 if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) {
5311                         if (val == IEEE80211_PROTMODE_OFF)
5312                                 LINE_CHECK("htprotmode OFF");
5313                         else if (val != IEEE80211_PROTMODE_RTSCTS)
5314                                 LINE_CHECK("htprotmode UNKNOWN (0x%x)", val);
5315                         else if (verbose)
5316                                 LINE_CHECK("htprotmode RTSCTS");
5317                 }
5318                 if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) {
5319                         if (val)
5320                                 LINE_CHECK("puren");
5321                         else if (verbose)
5322                                 LINE_CHECK("-puren");
5323                 }
5324                 if (get80211val(s, IEEE80211_IOC_SMPS, &val) != -1) {
5325                         if (val == IEEE80211_HTCAP_SMPS_DYNAMIC)
5326                                 LINE_CHECK("smpsdyn");
5327                         else if (val == IEEE80211_HTCAP_SMPS_ENA)
5328                                 LINE_CHECK("smps");
5329                         else if (verbose)
5330                                 LINE_CHECK("-smps");
5331                 }
5332                 if (get80211val(s, IEEE80211_IOC_RIFS, &val) != -1) {
5333                         if (val)
5334                                 LINE_CHECK("rifs");
5335                         else if (verbose)
5336                                 LINE_CHECK("-rifs");
5337                 }
5338
5339                 /* XXX VHT STBC? */
5340                 if (get80211val(s, IEEE80211_IOC_STBC, &val) != -1) {
5341                         switch (val) {
5342                         case 0:
5343                                 LINE_CHECK("-stbc");
5344                                 break;
5345                         case 1:
5346                                 LINE_CHECK("stbctx -stbcrx");
5347                                 break;
5348                         case 2:
5349                                 LINE_CHECK("-stbctx stbcrx");
5350                                 break;
5351                         case 3:
5352                                 if (verbose)
5353                                         LINE_CHECK("stbc");
5354                                 break;
5355                         }
5356                 }
5357                 if (get80211val(s, IEEE80211_IOC_LDPC, &val) != -1) {
5358                         switch (val) {
5359                         case 0:
5360                                 LINE_CHECK("-ldpc");
5361                                 break;
5362                         case 1:
5363                                 LINE_CHECK("ldpctx -ldpcrx");
5364                                 break;
5365                         case 2:
5366                                 LINE_CHECK("-ldpctx ldpcrx");
5367                                 break;
5368                         case 3:
5369                                 if (verbose)
5370                                         LINE_CHECK("ldpc");
5371                                 break;
5372                         }
5373                 }
5374                 if (get80211val(s, IEEE80211_IOC_UAPSD, &val) != -1) {
5375                         switch (val) {
5376                         case 0:
5377                                 LINE_CHECK("-uapsd");
5378                                 break;
5379                         case 1:
5380                                 LINE_CHECK("uapsd");
5381                                 break;
5382                         }
5383                 }
5384         }
5385
5386         if (IEEE80211_IS_CHAN_VHT(c) || verbose) {
5387                 getvhtconf(s);
5388                 if (vhtconf & IEEE80211_FVHT_VHT)
5389                         LINE_CHECK("vht");
5390                 else
5391                         LINE_CHECK("-vht");
5392                 if (vhtconf & IEEE80211_FVHT_USEVHT40)
5393                         LINE_CHECK("vht40");
5394                 else
5395                         LINE_CHECK("-vht40");
5396                 if (vhtconf & IEEE80211_FVHT_USEVHT80)
5397                         LINE_CHECK("vht80");
5398                 else
5399                         LINE_CHECK("-vht80");
5400                 if (vhtconf & IEEE80211_FVHT_USEVHT160)
5401                         LINE_CHECK("vht160");
5402                 else
5403                         LINE_CHECK("-vht160");
5404                 if (vhtconf & IEEE80211_FVHT_USEVHT80P80)
5405                         LINE_CHECK("vht80p80");
5406                 else
5407                         LINE_CHECK("-vht80p80");
5408         }
5409
5410         if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) {
5411                 if (wme)
5412                         LINE_CHECK("wme");
5413                 else if (verbose)
5414                         LINE_CHECK("-wme");
5415         } else
5416                 wme = 0;
5417
5418         if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) {
5419                 if (val)
5420                         LINE_CHECK("burst");
5421                 else if (verbose)
5422                         LINE_CHECK("-burst");
5423         }
5424
5425         if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) {
5426                 if (val)
5427                         LINE_CHECK("ff");
5428                 else if (verbose)
5429                         LINE_CHECK("-ff");
5430         }
5431         if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) {
5432                 if (val)
5433                         LINE_CHECK("dturbo");
5434                 else if (verbose)
5435                         LINE_CHECK("-dturbo");
5436         }
5437         if (get80211val(s, IEEE80211_IOC_DWDS, &val) != -1) {
5438                 if (val)
5439                         LINE_CHECK("dwds");
5440                 else if (verbose)
5441                         LINE_CHECK("-dwds");
5442         }
5443
5444         if (opmode == IEEE80211_M_HOSTAP) {
5445                 if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) {
5446                         if (val)
5447                                 LINE_CHECK("hidessid");
5448                         else if (verbose)
5449                                 LINE_CHECK("-hidessid");
5450                 }
5451                 if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) {
5452                         if (!val)
5453                                 LINE_CHECK("-apbridge");
5454                         else if (verbose)
5455                                 LINE_CHECK("apbridge");
5456                 }
5457                 if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1)
5458                         LINE_CHECK("dtimperiod %u", val);
5459
5460                 if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) {
5461                         if (!val)
5462                                 LINE_CHECK("-doth");
5463                         else if (verbose)
5464                                 LINE_CHECK("doth");
5465                 }
5466                 if (get80211val(s, IEEE80211_IOC_DFS, &val) != -1) {
5467                         if (!val)
5468                                 LINE_CHECK("-dfs");
5469                         else if (verbose)
5470                                 LINE_CHECK("dfs");
5471                 }
5472                 if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) {
5473                         if (!val)
5474                                 LINE_CHECK("-inact");
5475                         else if (verbose)
5476                                 LINE_CHECK("inact");
5477                 }
5478         } else {
5479                 if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) {
5480                         if (val != IEEE80211_ROAMING_AUTO || verbose) {
5481                                 switch (val) {
5482                                 case IEEE80211_ROAMING_DEVICE:
5483                                         LINE_CHECK("roaming DEVICE");
5484                                         break;
5485                                 case IEEE80211_ROAMING_AUTO:
5486                                         LINE_CHECK("roaming AUTO");
5487                                         break;
5488                                 case IEEE80211_ROAMING_MANUAL:
5489                                         LINE_CHECK("roaming MANUAL");
5490                                         break;
5491                                 default:
5492                                         LINE_CHECK("roaming UNKNOWN (0x%x)",
5493                                                 val);
5494                                         break;
5495                                 }
5496                         }
5497                 }
5498         }
5499
5500         if (opmode == IEEE80211_M_AHDEMO) {
5501                 if (get80211val(s, IEEE80211_IOC_TDMA_SLOT, &val) != -1)
5502                         LINE_CHECK("tdmaslot %u", val);
5503                 if (get80211val(s, IEEE80211_IOC_TDMA_SLOTCNT, &val) != -1)
5504                         LINE_CHECK("tdmaslotcnt %u", val);
5505                 if (get80211val(s, IEEE80211_IOC_TDMA_SLOTLEN, &val) != -1)
5506                         LINE_CHECK("tdmaslotlen %u", val);
5507                 if (get80211val(s, IEEE80211_IOC_TDMA_BINTERVAL, &val) != -1)
5508                         LINE_CHECK("tdmabintval %u", val);
5509         } else if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) {
5510                 /* XXX default define not visible */
5511                 if (val != 100 || verbose)
5512                         LINE_CHECK("bintval %u", val);
5513         }
5514
5515         if (wme && verbose) {
5516                 LINE_BREAK();
5517                 list_wme(s);
5518         }
5519
5520         if (opmode == IEEE80211_M_MBSS) {
5521                 if (get80211val(s, IEEE80211_IOC_MESH_TTL, &val) != -1) {
5522                         LINE_CHECK("meshttl %u", val);
5523                 }
5524                 if (get80211val(s, IEEE80211_IOC_MESH_AP, &val) != -1) {
5525                         if (val)
5526                                 LINE_CHECK("meshpeering");
5527                         else
5528                                 LINE_CHECK("-meshpeering");
5529                 }
5530                 if (get80211val(s, IEEE80211_IOC_MESH_FWRD, &val) != -1) {
5531                         if (val)
5532                                 LINE_CHECK("meshforward");
5533                         else
5534                                 LINE_CHECK("-meshforward");
5535                 }
5536                 if (get80211val(s, IEEE80211_IOC_MESH_GATE, &val) != -1) {
5537                         if (val)
5538                                 LINE_CHECK("meshgate");
5539                         else
5540                                 LINE_CHECK("-meshgate");
5541                 }
5542                 if (get80211len(s, IEEE80211_IOC_MESH_PR_METRIC, data, 12,
5543                     &len) != -1) {
5544                         data[len] = '\0';
5545                         LINE_CHECK("meshmetric %s", data);
5546                 }
5547                 if (get80211len(s, IEEE80211_IOC_MESH_PR_PATH, data, 12,
5548                     &len) != -1) {
5549                         data[len] = '\0';
5550                         LINE_CHECK("meshpath %s", data);
5551                 }
5552                 if (get80211val(s, IEEE80211_IOC_HWMP_ROOTMODE, &val) != -1) {
5553                         switch (val) {
5554                         case IEEE80211_HWMP_ROOTMODE_DISABLED:
5555                                 LINE_CHECK("hwmprootmode DISABLED");
5556                                 break;
5557                         case IEEE80211_HWMP_ROOTMODE_NORMAL:
5558                                 LINE_CHECK("hwmprootmode NORMAL");
5559                                 break;
5560                         case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
5561                                 LINE_CHECK("hwmprootmode PROACTIVE");
5562                                 break;
5563                         case IEEE80211_HWMP_ROOTMODE_RANN:
5564                                 LINE_CHECK("hwmprootmode RANN");
5565                                 break;
5566                         default:
5567                                 LINE_CHECK("hwmprootmode UNKNOWN(%d)", val);
5568                                 break;
5569                         }
5570                 }
5571                 if (get80211val(s, IEEE80211_IOC_HWMP_MAXHOPS, &val) != -1) {
5572                         LINE_CHECK("hwmpmaxhops %u", val);
5573                 }
5574         }
5575
5576         LINE_BREAK();
5577
5578         if (getdevicename(s, data, sizeof(data), &len) < 0)
5579                 return;
5580         LINE_CHECK("parent interface: %s", data);
5581
5582         LINE_BREAK();
5583 }
5584
5585 static int
5586 get80211(int s, int type, void *data, int len)
5587 {
5588
5589         return (lib80211_get80211(s, name, type, data, len));
5590 }
5591
5592 static int
5593 get80211len(int s, int type, void *data, int len, int *plen)
5594 {
5595
5596         return (lib80211_get80211len(s, name, type, data, len, plen));
5597 }
5598
5599 static int
5600 get80211val(int s, int type, int *val)
5601 {
5602
5603         return (lib80211_get80211val(s, name, type, val));
5604 }
5605
5606 static void
5607 set80211(int s, int type, int val, int len, void *data)
5608 {
5609         int ret;
5610
5611         ret = lib80211_set80211(s, name, type, val, len, data);
5612         if (ret < 0)
5613                 err(1, "SIOCS80211");
5614 }
5615
5616 static const char *
5617 get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
5618 {
5619         int len;
5620         int hexstr;
5621         u_int8_t *p;
5622
5623         len = *lenp;
5624         p = buf;
5625         hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
5626         if (hexstr)
5627                 val += 2;
5628         for (;;) {
5629                 if (*val == '\0')
5630                         break;
5631                 if (sep != NULL && strchr(sep, *val) != NULL) {
5632                         val++;
5633                         break;
5634                 }
5635                 if (hexstr) {
5636                         if (!isxdigit((u_char)val[0])) {
5637                                 warnx("bad hexadecimal digits");
5638                                 return NULL;
5639                         }
5640                         if (!isxdigit((u_char)val[1])) {
5641                                 warnx("odd count hexadecimal digits");
5642                                 return NULL;
5643                         }
5644                 }
5645                 if (p >= buf + len) {
5646                         if (hexstr)
5647                                 warnx("hexadecimal digits too long");
5648                         else
5649                                 warnx("string too long");
5650                         return NULL;
5651                 }
5652                 if (hexstr) {
5653 #define tohex(x)        (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
5654                         *p++ = (tohex((u_char)val[0]) << 4) |
5655                             tohex((u_char)val[1]);
5656 #undef tohex
5657                         val += 2;
5658                 } else
5659                         *p++ = *val++;
5660         }
5661         len = p - buf;
5662         /* The string "-" is treated as the empty string. */
5663         if (!hexstr && len == 1 && buf[0] == '-') {
5664                 len = 0;
5665                 memset(buf, 0, *lenp);
5666         } else if (len < *lenp)
5667                 memset(p, 0, *lenp - len);
5668         *lenp = len;
5669         return val;
5670 }
5671
5672 static void
5673 print_string(const u_int8_t *buf, int len)
5674 {
5675         int i;
5676         int hasspc;
5677         int utf8;
5678
5679         i = 0;
5680         hasspc = 0;
5681
5682         setlocale(LC_CTYPE, "");
5683         utf8 = strncmp("UTF-8", nl_langinfo(CODESET), 5) == 0;
5684
5685         for (; i < len; i++) {
5686                 if (!isprint(buf[i]) && buf[i] != '\0' && !utf8)
5687                         break;
5688                 if (isspace(buf[i]))
5689                         hasspc++;
5690         }
5691         if (i == len || utf8) {
5692                 if (hasspc || len == 0 || buf[0] == '\0')
5693                         printf("\"%.*s\"", len, buf);
5694                 else
5695                         printf("%.*s", len, buf);
5696         } else {
5697                 printf("0x");
5698                 for (i = 0; i < len; i++)
5699                         printf("%02x", buf[i]);
5700         }
5701 }
5702
5703 static void
5704 setdefregdomain(int s)
5705 {
5706         struct regdata *rdp = getregdata();
5707         const struct regdomain *rd;
5708
5709         /* Check if regdomain/country was already set by a previous call. */
5710         /* XXX is it possible? */
5711         if (regdomain.regdomain != 0 ||
5712             regdomain.country != CTRY_DEFAULT)
5713                 return;
5714
5715         getregdomain(s);
5716
5717         /* Check if it was already set by the driver. */
5718         if (regdomain.regdomain != 0 ||
5719             regdomain.country != CTRY_DEFAULT)
5720                 return;
5721
5722         /* Set FCC/US as default. */
5723         rd = lib80211_regdomain_findbysku(rdp, SKU_FCC);
5724         if (rd == NULL)
5725                 errx(1, "FCC regdomain was not found");
5726
5727         regdomain.regdomain = rd->sku;
5728         if (rd->cc != NULL)
5729                 defaultcountry(rd);
5730
5731         /* Send changes to net80211. */
5732         setregdomain_cb(s, &regdomain);
5733
5734         /* Cleanup (so it can be overriden by subsequent parameters). */
5735         regdomain.regdomain = 0;
5736         regdomain.country = CTRY_DEFAULT;
5737         regdomain.isocc[0] = 0;
5738         regdomain.isocc[1] = 0;
5739 }
5740
5741 /*
5742  * Virtual AP cloning support.
5743  */
5744 static struct ieee80211_clone_params params = {
5745         .icp_opmode     = IEEE80211_M_STA,      /* default to station mode */
5746 };
5747
5748 static void
5749 wlan_create(int s, struct ifreq *ifr)
5750 {
5751         static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
5752         char orig_name[IFNAMSIZ];
5753
5754         if (params.icp_parent[0] == '\0')
5755                 errx(1, "must specify a parent device (wlandev) when creating "
5756                     "a wlan device");
5757         if (params.icp_opmode == IEEE80211_M_WDS &&
5758             memcmp(params.icp_bssid, zerobssid, sizeof(zerobssid)) == 0)
5759                 errx(1, "no bssid specified for WDS (use wlanbssid)");
5760         ifr->ifr_data = (caddr_t) &params;
5761         ioctl_ifcreate(s, ifr);
5762
5763         /* XXX preserve original name for ifclonecreate(). */
5764         strlcpy(orig_name, name, sizeof(orig_name));
5765         strlcpy(name, ifr->ifr_name, sizeof(name));
5766
5767         setdefregdomain(s);
5768
5769         strlcpy(name, orig_name, sizeof(name));
5770 }
5771
5772 static
5773 DECL_CMD_FUNC(set80211clone_wlandev, arg, d)
5774 {
5775         strlcpy(params.icp_parent, arg, IFNAMSIZ);
5776 }
5777
5778 static
5779 DECL_CMD_FUNC(set80211clone_wlanbssid, arg, d)
5780 {
5781         const struct ether_addr *ea;
5782
5783         ea = ether_aton(arg);
5784         if (ea == NULL)
5785                 errx(1, "%s: cannot parse bssid", arg);
5786         memcpy(params.icp_bssid, ea->octet, IEEE80211_ADDR_LEN);
5787 }
5788
5789 static
5790 DECL_CMD_FUNC(set80211clone_wlanaddr, arg, d)
5791 {
5792         const struct ether_addr *ea;
5793
5794         ea = ether_aton(arg);
5795         if (ea == NULL)
5796                 errx(1, "%s: cannot parse address", arg);
5797         memcpy(params.icp_macaddr, ea->octet, IEEE80211_ADDR_LEN);
5798         params.icp_flags |= IEEE80211_CLONE_MACADDR;
5799 }
5800
5801 static
5802 DECL_CMD_FUNC(set80211clone_wlanmode, arg, d)
5803 {
5804 #define iseq(a,b)       (strncasecmp(a,b,sizeof(b)-1) == 0)
5805         if (iseq(arg, "sta"))
5806                 params.icp_opmode = IEEE80211_M_STA;
5807         else if (iseq(arg, "ahdemo") || iseq(arg, "adhoc-demo"))
5808                 params.icp_opmode = IEEE80211_M_AHDEMO;
5809         else if (iseq(arg, "ibss") || iseq(arg, "adhoc"))
5810                 params.icp_opmode = IEEE80211_M_IBSS;
5811         else if (iseq(arg, "ap") || iseq(arg, "host"))
5812                 params.icp_opmode = IEEE80211_M_HOSTAP;
5813         else if (iseq(arg, "wds"))
5814                 params.icp_opmode = IEEE80211_M_WDS;
5815         else if (iseq(arg, "monitor"))
5816                 params.icp_opmode = IEEE80211_M_MONITOR;
5817         else if (iseq(arg, "tdma")) {
5818                 params.icp_opmode = IEEE80211_M_AHDEMO;
5819                 params.icp_flags |= IEEE80211_CLONE_TDMA;
5820         } else if (iseq(arg, "mesh") || iseq(arg, "mp")) /* mesh point */
5821                 params.icp_opmode = IEEE80211_M_MBSS;
5822         else
5823                 errx(1, "Don't know to create %s for %s", arg, name);
5824 #undef iseq
5825 }
5826
5827 static void
5828 set80211clone_beacons(const char *val, int d, int s, const struct afswtch *rafp)
5829 {
5830         /* NB: inverted sense */
5831         if (d)
5832                 params.icp_flags &= ~IEEE80211_CLONE_NOBEACONS;
5833         else
5834                 params.icp_flags |= IEEE80211_CLONE_NOBEACONS;
5835 }
5836
5837 static void
5838 set80211clone_bssid(const char *val, int d, int s, const struct afswtch *rafp)
5839 {
5840         if (d)
5841                 params.icp_flags |= IEEE80211_CLONE_BSSID;
5842         else
5843                 params.icp_flags &= ~IEEE80211_CLONE_BSSID;
5844 }
5845
5846 static void
5847 set80211clone_wdslegacy(const char *val, int d, int s, const struct afswtch *rafp)
5848 {
5849         if (d)
5850                 params.icp_flags |= IEEE80211_CLONE_WDSLEGACY;
5851         else
5852                 params.icp_flags &= ~IEEE80211_CLONE_WDSLEGACY;
5853 }
5854
5855 static struct cmd ieee80211_cmds[] = {
5856         DEF_CMD_ARG("ssid",             set80211ssid),
5857         DEF_CMD_ARG("nwid",             set80211ssid),
5858         DEF_CMD_ARG("meshid",           set80211meshid),
5859         DEF_CMD_ARG("stationname",      set80211stationname),
5860         DEF_CMD_ARG("station",          set80211stationname),   /* BSD/OS */
5861         DEF_CMD_ARG("channel",          set80211channel),
5862         DEF_CMD_ARG("authmode",         set80211authmode),
5863         DEF_CMD_ARG("powersavemode",    set80211powersavemode),
5864         DEF_CMD("powersave",    1,      set80211powersave),
5865         DEF_CMD("-powersave",   0,      set80211powersave),
5866         DEF_CMD_ARG("powersavesleep",   set80211powersavesleep),
5867         DEF_CMD_ARG("wepmode",          set80211wepmode),
5868         DEF_CMD("wep",          1,      set80211wep),
5869         DEF_CMD("-wep",         0,      set80211wep),
5870         DEF_CMD_ARG("deftxkey",         set80211weptxkey),
5871         DEF_CMD_ARG("weptxkey",         set80211weptxkey),
5872         DEF_CMD_ARG("wepkey",           set80211wepkey),
5873         DEF_CMD_ARG("nwkey",            set80211nwkey),         /* NetBSD */
5874         DEF_CMD("-nwkey",       0,      set80211wep),           /* NetBSD */
5875         DEF_CMD_ARG("rtsthreshold",     set80211rtsthreshold),
5876         DEF_CMD_ARG("protmode",         set80211protmode),
5877         DEF_CMD_ARG("txpower",          set80211txpower),
5878         DEF_CMD_ARG("roaming",          set80211roaming),
5879         DEF_CMD("wme",          1,      set80211wme),
5880         DEF_CMD("-wme",         0,      set80211wme),
5881         DEF_CMD("wmm",          1,      set80211wme),
5882         DEF_CMD("-wmm",         0,      set80211wme),
5883         DEF_CMD("hidessid",     1,      set80211hidessid),
5884         DEF_CMD("-hidessid",    0,      set80211hidessid),
5885         DEF_CMD("apbridge",     1,      set80211apbridge),
5886         DEF_CMD("-apbridge",    0,      set80211apbridge),
5887         DEF_CMD_ARG("chanlist",         set80211chanlist),
5888         DEF_CMD_ARG("bssid",            set80211bssid),
5889         DEF_CMD_ARG("ap",               set80211bssid),
5890         DEF_CMD("scan", 0,              set80211scan),
5891         DEF_CMD_ARG("list",             set80211list),
5892         DEF_CMD_ARG2("cwmin",           set80211cwmin),
5893         DEF_CMD_ARG2("cwmax",           set80211cwmax),
5894         DEF_CMD_ARG2("aifs",            set80211aifs),
5895         DEF_CMD_ARG2("txoplimit",       set80211txoplimit),
5896         DEF_CMD_ARG("acm",              set80211acm),
5897         DEF_CMD_ARG("-acm",             set80211noacm),
5898         DEF_CMD_ARG("ack",              set80211ackpolicy),
5899         DEF_CMD_ARG("-ack",             set80211noackpolicy),
5900         DEF_CMD_ARG2("bss:cwmin",       set80211bsscwmin),
5901         DEF_CMD_ARG2("bss:cwmax",       set80211bsscwmax),
5902         DEF_CMD_ARG2("bss:aifs",        set80211bssaifs),
5903         DEF_CMD_ARG2("bss:txoplimit",   set80211bsstxoplimit),
5904         DEF_CMD_ARG("dtimperiod",       set80211dtimperiod),
5905         DEF_CMD_ARG("bintval",          set80211bintval),
5906         DEF_CMD("mac:open",     IEEE80211_MACCMD_POLICY_OPEN,   set80211maccmd),
5907         DEF_CMD("mac:allow",    IEEE80211_MACCMD_POLICY_ALLOW,  set80211maccmd),
5908         DEF_CMD("mac:deny",     IEEE80211_MACCMD_POLICY_DENY,   set80211maccmd),
5909         DEF_CMD("mac:radius",   IEEE80211_MACCMD_POLICY_RADIUS, set80211maccmd),
5910         DEF_CMD("mac:flush",    IEEE80211_MACCMD_FLUSH,         set80211maccmd),
5911         DEF_CMD("mac:detach",   IEEE80211_MACCMD_DETACH,        set80211maccmd),
5912         DEF_CMD_ARG("mac:add",          set80211addmac),
5913         DEF_CMD_ARG("mac:del",          set80211delmac),
5914         DEF_CMD_ARG("mac:kick",         set80211kickmac),
5915         DEF_CMD("pureg",        1,      set80211pureg),
5916         DEF_CMD("-pureg",       0,      set80211pureg),
5917         DEF_CMD("ff",           1,      set80211fastframes),
5918         DEF_CMD("-ff",          0,      set80211fastframes),
5919         DEF_CMD("dturbo",       1,      set80211dturbo),
5920         DEF_CMD("-dturbo",      0,      set80211dturbo),
5921         DEF_CMD("bgscan",       1,      set80211bgscan),
5922         DEF_CMD("-bgscan",      0,      set80211bgscan),
5923         DEF_CMD_ARG("bgscanidle",       set80211bgscanidle),
5924         DEF_CMD_ARG("bgscanintvl",      set80211bgscanintvl),
5925         DEF_CMD_ARG("scanvalid",        set80211scanvalid),
5926         DEF_CMD("quiet",        1,      set80211quiet),
5927         DEF_CMD("-quiet",       0,      set80211quiet),
5928         DEF_CMD_ARG("quiet_count",      set80211quietcount),
5929         DEF_CMD_ARG("quiet_period",     set80211quietperiod),
5930         DEF_CMD_ARG("quiet_duration",   set80211quietduration),
5931         DEF_CMD_ARG("quiet_offset",     set80211quietoffset),
5932         DEF_CMD_ARG("roam:rssi",        set80211roamrssi),
5933         DEF_CMD_ARG("roam:rate",        set80211roamrate),
5934         DEF_CMD_ARG("mcastrate",        set80211mcastrate),
5935         DEF_CMD_ARG("ucastrate",        set80211ucastrate),
5936         DEF_CMD_ARG("mgtrate",          set80211mgtrate),
5937         DEF_CMD_ARG("mgmtrate",         set80211mgtrate),
5938         DEF_CMD_ARG("maxretry",         set80211maxretry),
5939         DEF_CMD_ARG("fragthreshold",    set80211fragthreshold),
5940         DEF_CMD("burst",        1,      set80211burst),
5941         DEF_CMD("-burst",       0,      set80211burst),
5942         DEF_CMD_ARG("bmiss",            set80211bmissthreshold),
5943         DEF_CMD_ARG("bmissthreshold",   set80211bmissthreshold),
5944         DEF_CMD("shortgi",      1,      set80211shortgi),
5945         DEF_CMD("-shortgi",     0,      set80211shortgi),
5946         DEF_CMD("ampdurx",      2,      set80211ampdu),
5947         DEF_CMD("-ampdurx",     -2,     set80211ampdu),
5948         DEF_CMD("ampdutx",      1,      set80211ampdu),
5949         DEF_CMD("-ampdutx",     -1,     set80211ampdu),
5950         DEF_CMD("ampdu",        3,      set80211ampdu),         /* NB: tx+rx */
5951         DEF_CMD("-ampdu",       -3,     set80211ampdu),
5952         DEF_CMD_ARG("ampdulimit",       set80211ampdulimit),
5953         DEF_CMD_ARG("ampdudensity",     set80211ampdudensity),
5954         DEF_CMD("amsdurx",      2,      set80211amsdu),
5955         DEF_CMD("-amsdurx",     -2,     set80211amsdu),
5956         DEF_CMD("amsdutx",      1,      set80211amsdu),
5957         DEF_CMD("-amsdutx",     -1,     set80211amsdu),
5958         DEF_CMD("amsdu",        3,      set80211amsdu),         /* NB: tx+rx */
5959         DEF_CMD("-amsdu",       -3,     set80211amsdu),
5960         DEF_CMD_ARG("amsdulimit",       set80211amsdulimit),
5961         DEF_CMD("stbcrx",       2,      set80211stbc),
5962         DEF_CMD("-stbcrx",      -2,     set80211stbc),
5963         DEF_CMD("stbctx",       1,      set80211stbc),
5964         DEF_CMD("-stbctx",      -1,     set80211stbc),
5965         DEF_CMD("stbc",         3,      set80211stbc),          /* NB: tx+rx */
5966         DEF_CMD("-stbc",        -3,     set80211stbc),
5967         DEF_CMD("ldpcrx",       2,      set80211ldpc),
5968         DEF_CMD("-ldpcrx",      -2,     set80211ldpc),
5969         DEF_CMD("ldpctx",       1,      set80211ldpc),
5970         DEF_CMD("-ldpctx",      -1,     set80211ldpc),
5971         DEF_CMD("ldpc",         3,      set80211ldpc),          /* NB: tx+rx */
5972         DEF_CMD("-ldpc",        -3,     set80211ldpc),
5973         DEF_CMD("uapsd",        1,      set80211uapsd),
5974         DEF_CMD("-uapsd",       0,      set80211uapsd),
5975         DEF_CMD("puren",        1,      set80211puren),
5976         DEF_CMD("-puren",       0,      set80211puren),
5977         DEF_CMD("doth",         1,      set80211doth),
5978         DEF_CMD("-doth",        0,      set80211doth),
5979         DEF_CMD("dfs",          1,      set80211dfs),
5980         DEF_CMD("-dfs",         0,      set80211dfs),
5981         DEF_CMD("htcompat",     1,      set80211htcompat),
5982         DEF_CMD("-htcompat",    0,      set80211htcompat),
5983         DEF_CMD("dwds",         1,      set80211dwds),
5984         DEF_CMD("-dwds",        0,      set80211dwds),
5985         DEF_CMD("inact",        1,      set80211inact),
5986         DEF_CMD("-inact",       0,      set80211inact),
5987         DEF_CMD("tsn",          1,      set80211tsn),
5988         DEF_CMD("-tsn",         0,      set80211tsn),
5989         DEF_CMD_ARG("regdomain",        set80211regdomain),
5990         DEF_CMD_ARG("country",          set80211country),
5991         DEF_CMD("indoor",       'I',    set80211location),
5992         DEF_CMD("-indoor",      'O',    set80211location),
5993         DEF_CMD("outdoor",      'O',    set80211location),
5994         DEF_CMD("-outdoor",     'I',    set80211location),
5995         DEF_CMD("anywhere",     ' ',    set80211location),
5996         DEF_CMD("ecm",          1,      set80211ecm),
5997         DEF_CMD("-ecm",         0,      set80211ecm),
5998         DEF_CMD("dotd",         1,      set80211dotd),
5999         DEF_CMD("-dotd",        0,      set80211dotd),
6000         DEF_CMD_ARG("htprotmode",       set80211htprotmode),
6001         DEF_CMD("ht20",         1,      set80211htconf),
6002         DEF_CMD("-ht20",        0,      set80211htconf),
6003         DEF_CMD("ht40",         3,      set80211htconf),        /* NB: 20+40 */
6004         DEF_CMD("-ht40",        0,      set80211htconf),
6005         DEF_CMD("ht",           3,      set80211htconf),        /* NB: 20+40 */
6006         DEF_CMD("-ht",          0,      set80211htconf),
6007         DEF_CMD("vht",          IEEE80211_FVHT_VHT,             set80211vhtconf),
6008         DEF_CMD("-vht",         0,                              set80211vhtconf),
6009         DEF_CMD("vht40",        IEEE80211_FVHT_USEVHT40,        set80211vhtconf),
6010         DEF_CMD("-vht40",       -IEEE80211_FVHT_USEVHT40,       set80211vhtconf),
6011         DEF_CMD("vht80",        IEEE80211_FVHT_USEVHT80,        set80211vhtconf),
6012         DEF_CMD("-vht80",       -IEEE80211_FVHT_USEVHT80,       set80211vhtconf),
6013         DEF_CMD("vht160",       IEEE80211_FVHT_USEVHT160,       set80211vhtconf),
6014         DEF_CMD("-vht160",      -IEEE80211_FVHT_USEVHT160,      set80211vhtconf),
6015         DEF_CMD("vht80p80",     IEEE80211_FVHT_USEVHT80P80,     set80211vhtconf),
6016         DEF_CMD("-vht80p80",    -IEEE80211_FVHT_USEVHT80P80,    set80211vhtconf),
6017         DEF_CMD("rifs",         1,      set80211rifs),
6018         DEF_CMD("-rifs",        0,      set80211rifs),
6019         DEF_CMD("smps",         IEEE80211_HTCAP_SMPS_ENA,       set80211smps),
6020         DEF_CMD("smpsdyn",      IEEE80211_HTCAP_SMPS_DYNAMIC,   set80211smps),
6021         DEF_CMD("-smps",        IEEE80211_HTCAP_SMPS_OFF,       set80211smps),
6022         /* XXX for testing */
6023         DEF_CMD_ARG("chanswitch",       set80211chanswitch),
6024
6025         DEF_CMD_ARG("tdmaslot",         set80211tdmaslot),
6026         DEF_CMD_ARG("tdmaslotcnt",      set80211tdmaslotcnt),
6027         DEF_CMD_ARG("tdmaslotlen",      set80211tdmaslotlen),
6028         DEF_CMD_ARG("tdmabintval",      set80211tdmabintval),
6029
6030         DEF_CMD_ARG("meshttl",          set80211meshttl),
6031         DEF_CMD("meshforward",  1,      set80211meshforward),
6032         DEF_CMD("-meshforward", 0,      set80211meshforward),
6033         DEF_CMD("meshgate",     1,      set80211meshgate),
6034         DEF_CMD("-meshgate",    0,      set80211meshgate),
6035         DEF_CMD("meshpeering",  1,      set80211meshpeering),
6036         DEF_CMD("-meshpeering", 0,      set80211meshpeering),
6037         DEF_CMD_ARG("meshmetric",       set80211meshmetric),
6038         DEF_CMD_ARG("meshpath",         set80211meshpath),
6039         DEF_CMD("meshrt:flush", IEEE80211_MESH_RTCMD_FLUSH,     set80211meshrtcmd),
6040         DEF_CMD_ARG("meshrt:add",       set80211addmeshrt),
6041         DEF_CMD_ARG("meshrt:del",       set80211delmeshrt),
6042         DEF_CMD_ARG("hwmprootmode",     set80211hwmprootmode),
6043         DEF_CMD_ARG("hwmpmaxhops",      set80211hwmpmaxhops),
6044
6045         /* vap cloning support */
6046         DEF_CLONE_CMD_ARG("wlanaddr",   set80211clone_wlanaddr),
6047         DEF_CLONE_CMD_ARG("wlanbssid",  set80211clone_wlanbssid),
6048         DEF_CLONE_CMD_ARG("wlandev",    set80211clone_wlandev),
6049         DEF_CLONE_CMD_ARG("wlanmode",   set80211clone_wlanmode),
6050         DEF_CLONE_CMD("beacons", 1,     set80211clone_beacons),
6051         DEF_CLONE_CMD("-beacons", 0,    set80211clone_beacons),
6052         DEF_CLONE_CMD("bssid",  1,      set80211clone_bssid),
6053         DEF_CLONE_CMD("-bssid", 0,      set80211clone_bssid),
6054         DEF_CLONE_CMD("wdslegacy", 1,   set80211clone_wdslegacy),
6055         DEF_CLONE_CMD("-wdslegacy", 0,  set80211clone_wdslegacy),
6056 };
6057 static struct afswtch af_ieee80211 = {
6058         .af_name        = "af_ieee80211",
6059         .af_af          = AF_UNSPEC,
6060         .af_other_status = ieee80211_status,
6061 };
6062
6063 static __constructor void
6064 ieee80211_ctor(void)
6065 {
6066         int i;
6067
6068         for (i = 0; i < nitems(ieee80211_cmds);  i++)
6069                 cmd_register(&ieee80211_cmds[i]);
6070         af_register(&af_ieee80211);
6071         clone_setdefcallback_prefix("wlan", wlan_create);
6072 }