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