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