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