]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sbin/ifconfig/ifieee80211.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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.h>
81 #include <net80211/ieee80211_crypto.h>
82 #include <net80211/ieee80211_ioctl.h>
83
84 #include <ctype.h>
85 #include <err.h>
86 #include <errno.h>
87 #include <fcntl.h>
88 #include <inttypes.h>
89 #include <stdio.h>
90 #include <stdlib.h>
91 #include <string.h>
92 #include <unistd.h>
93 #include <stdarg.h>
94 #include <stddef.h>             /* NB: for offsetof */
95
96 #include "ifconfig.h"
97
98 #define MAXCOL  78
99 static  int col;
100 static  char spacer;
101
102 static void LINE_INIT(char c);
103 static void LINE_BREAK(void);
104 static void LINE_CHECK(const char *fmt, ...);
105
106 /* XXX need max array size */
107 static const int htrates[16] = {
108         13,             /* IFM_IEEE80211_MCS0 */
109         26,             /* IFM_IEEE80211_MCS1 */
110         39,             /* IFM_IEEE80211_MCS2 */
111         52,             /* IFM_IEEE80211_MCS3 */
112         78,             /* IFM_IEEE80211_MCS4 */
113         104,            /* IFM_IEEE80211_MCS5 */
114         117,            /* IFM_IEEE80211_MCS6 */
115         130,            /* IFM_IEEE80211_MCS7 */
116         26,             /* IFM_IEEE80211_MCS8 */
117         52,             /* IFM_IEEE80211_MCS9 */
118         78,             /* IFM_IEEE80211_MCS10 */
119         104,            /* IFM_IEEE80211_MCS11 */
120         156,            /* IFM_IEEE80211_MCS12 */
121         208,            /* IFM_IEEE80211_MCS13 */
122         234,            /* IFM_IEEE80211_MCS14 */
123         260,            /* IFM_IEEE80211_MCS15 */
124 };
125
126 static int get80211(int s, int type, void *data, int len);
127 static int get80211len(int s, int type, void *data, int len, int *plen);
128 static int get80211val(int s, int type, int *val);
129 static void set80211(int s, int type, int val, int len, void *data);
130 static const char *get_string(const char *val, const char *sep,
131     u_int8_t *buf, int *lenp);
132 static void print_string(const u_int8_t *buf, int len);
133
134 static struct ieee80211req_chaninfo chaninfo;
135 static struct ifmediareq *ifmr;
136 static struct ieee80211_channel curchan;
137 static int gotcurchan = 0;
138 static int htconf = 0;
139 static  int gothtconf = 0;
140
141 static void
142 gethtconf(int s)
143 {
144         if (gothtconf)
145                 return;
146         if (get80211val(s, IEEE80211_IOC_HTCONF, &htconf) < 0)
147                 warn("unable to get HT configuration information");
148         gothtconf = 1;
149 }
150
151 /*
152  * Collect channel info from the kernel.  We use this (mostly)
153  * to handle mapping between frequency and IEEE channel number.
154  */
155 static void
156 getchaninfo(int s)
157 {
158         if (chaninfo.ic_nchans != 0)
159                 return;
160         if (get80211(s, IEEE80211_IOC_CHANINFO, &chaninfo, sizeof(chaninfo)) < 0)
161                 errx(1, "unable to get channel information");
162
163         ifmr = ifmedia_getstate(s);
164         gethtconf(s);
165 }
166
167 /*
168  * Given the channel at index i with attributes from,
169  * check if there is a channel with attributes to in
170  * the channel table.  With suitable attributes this
171  * allows the caller to look for promotion; e.g. from
172  * 11b > 11g.
173  */
174 static int
175 canpromote(int i, int from, int to)
176 {
177         const struct ieee80211_channel *fc = &chaninfo.ic_chans[i];
178         int j;
179
180         if ((fc->ic_flags & from) != from)
181                 return i;
182         /* NB: quick check exploiting ordering of chans w/ same frequency */
183         if (i+1 < chaninfo.ic_nchans &&
184             chaninfo.ic_chans[i+1].ic_freq == fc->ic_freq &&
185             (chaninfo.ic_chans[i+1].ic_flags & to) == to)
186                 return i+1;
187         /* brute force search in case channel list is not ordered */
188         for (j = 0; j < chaninfo.ic_nchans; j++) {
189                 const struct ieee80211_channel *tc = &chaninfo.ic_chans[j];
190                 if (j != i &&
191                     tc->ic_freq == fc->ic_freq && (tc->ic_flags & to) == to)
192                 return j;
193         }
194         return i;
195 }
196
197 /*
198  * Handle channel promotion.  When a channel is specified with
199  * only a frequency we want to promote it to the ``best'' channel
200  * available.  The channel list has separate entries for 11b, 11g,
201  * 11a, and 11n[ga] channels so specifying a frequency w/o any
202  * attributes requires we upgrade, e.g. from 11b -> 11g.  This
203  * gets complicated when the channel is specified on the same
204  * command line with a media request that constrains the available
205  * channe list (e.g. mode 11a); we want to honor that to avoid
206  * confusing behaviour.
207  */
208 static int
209 promote(int i)
210 {
211         /*
212          * Query the current mode of the interface in case it's
213          * constrained (e.g. to 11a).  We must do this carefully
214          * as there may be a pending ifmedia request in which case
215          * asking the kernel will give us the wrong answer.  This
216          * is an unfortunate side-effect of the way ifconfig is
217          * structure for modularity (yech).
218          *
219          * NB: ifmr is actually setup in getchaninfo (above); we
220          *     assume it's called coincident with to this call so
221          *     we have a ``current setting''; otherwise we must pass
222          *     the socket descriptor down to here so we can make
223          *     the ifmedia_getstate call ourselves.
224          */
225         int chanmode = ifmr != NULL ? IFM_MODE(ifmr->ifm_current) : IFM_AUTO;
226
227         /* when ambiguous promote to ``best'' */
228         /* NB: we abitrarily pick HT40+ over HT40- */
229         if (chanmode != IFM_IEEE80211_11B)
230                 i = canpromote(i, IEEE80211_CHAN_B, IEEE80211_CHAN_G);
231         if (chanmode != IFM_IEEE80211_11G && (htconf & 1)) {
232                 i = canpromote(i, IEEE80211_CHAN_G,
233                         IEEE80211_CHAN_G | IEEE80211_CHAN_HT20);
234                 if (htconf & 2) {
235                         i = canpromote(i, IEEE80211_CHAN_G,
236                                 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D);
237                         i = canpromote(i, IEEE80211_CHAN_G,
238                                 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U);
239                 }
240         }
241         if (chanmode != IFM_IEEE80211_11A && (htconf & 1)) {
242                 i = canpromote(i, IEEE80211_CHAN_A,
243                         IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
244                 if (htconf & 2) {
245                         i = canpromote(i, IEEE80211_CHAN_A,
246                                 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
247                         i = canpromote(i, IEEE80211_CHAN_A,
248                                 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
249                 }
250         }
251         return i;
252 }
253
254 static void
255 mapfreq(struct ieee80211_channel *chan, int freq, int flags)
256 {
257         int i;
258
259         for (i = 0; i < chaninfo.ic_nchans; i++) {
260                 const struct ieee80211_channel *c = &chaninfo.ic_chans[i];
261
262                 if (c->ic_freq == freq && (c->ic_flags & flags) == flags) {
263                         if (flags == 0) {
264                                 /* when ambiguous promote to ``best'' */
265                                 c = &chaninfo.ic_chans[promote(i)];
266                         }
267                         *chan = *c;
268                         return;
269                 }
270         }
271         errx(1, "unknown/undefined frequency %u/0x%x", freq, flags);
272 }
273
274 static void
275 mapchan(struct ieee80211_channel *chan, int ieee, int flags)
276 {
277         int i;
278
279         for (i = 0; i < chaninfo.ic_nchans; i++) {
280                 const struct ieee80211_channel *c = &chaninfo.ic_chans[i];
281
282                 if (c->ic_ieee == ieee && (c->ic_flags & flags) == flags) {
283                         if (flags == 0) {
284                                 /* when ambiguous promote to ``best'' */
285                                 c = &chaninfo.ic_chans[promote(i)];
286                         }
287                         *chan = *c;
288                         return;
289                 }
290         }
291         errx(1, "unknown/undefined channel number %d flags 0x%x", ieee, flags);
292 }
293
294 static const struct ieee80211_channel *
295 getcurchan(int s)
296 {
297         if (gotcurchan)
298                 return &curchan;
299         if (get80211(s, IEEE80211_IOC_CURCHAN, &curchan, sizeof(curchan)) < 0) {
300                 int val;
301                 /* fall back to legacy ioctl */
302                 if (get80211val(s, IEEE80211_IOC_CHANNEL, &val) < 0)
303                         errx(-1, "cannot figure out current channel");
304                 getchaninfo(s);
305                 mapchan(&curchan, val, 0);
306         }
307         gotcurchan = 1;
308         return &curchan;
309 }
310
311 static int
312 ieee80211_mhz2ieee(int freq, int flags)
313 {
314         struct ieee80211_channel chan;
315         mapfreq(&chan, freq, flags);
316         return chan.ic_ieee;
317 }
318
319 static int
320 isanyarg(const char *arg)
321 {
322         return (strncmp(arg, "-", 1) == 0 ||
323             strncasecmp(arg, "any", 3) == 0 || strncasecmp(arg, "off", 3) == 0);
324 }
325
326 static void
327 set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
328 {
329         int             ssid;
330         int             len;
331         u_int8_t        data[IEEE80211_NWID_LEN];
332
333         ssid = 0;
334         len = strlen(val);
335         if (len > 2 && isdigit(val[0]) && val[1] == ':') {
336                 ssid = atoi(val)-1;
337                 val += 2;
338         }
339
340         bzero(data, sizeof(data));
341         len = sizeof(data);
342         if (get_string(val, NULL, data, &len) == NULL)
343                 exit(1);
344
345         set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
346 }
347
348 static void
349 set80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
350 {
351         int                     len;
352         u_int8_t                data[33];
353
354         bzero(data, sizeof(data));
355         len = sizeof(data);
356         get_string(val, NULL, data, &len);
357
358         set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
359 }
360
361 /*
362  * Parse a channel specification for attributes/flags.
363  * The syntax is:
364  *      freq/xx         channel width (5,10,20,40,40+,40-)
365  *      freq:mode       channel mode (a,b,g,h,n,t,s,d)
366  *
367  * These can be combined in either order; e.g. 2437:ng/40.
368  * Modes are case insensitive.
369  *
370  * The result is not validated here; it's assumed to be
371  * checked against the channel table fetched from the kernel.
372  */ 
373 static int
374 getchannelflags(const char *val, int freq)
375 {
376 #define _CHAN_HT        0x80000000
377         const char *cp;
378         int flags;
379
380         flags = 0;
381
382         cp = strchr(val, ':');
383         if (cp != NULL) {
384                 for (cp++; isalpha((int) *cp); cp++) {
385                         /* accept mixed case */
386                         int c = *cp;
387                         if (isupper(c))
388                                 c = tolower(c);
389                         switch (c) {
390                         case 'a':               /* 802.11a */
391                                 flags |= IEEE80211_CHAN_A;
392                                 break;
393                         case 'b':               /* 802.11b */
394                                 flags |= IEEE80211_CHAN_B;
395                                 break;
396                         case 'g':               /* 802.11g */
397                                 flags |= IEEE80211_CHAN_G;
398                                 break;
399                         case 'h':               /* ht = 802.11n */
400                         case 'n':               /* 802.11n */
401                                 flags |= _CHAN_HT;      /* NB: private */
402                                 break;
403                         case 'd':               /* dt = Atheros Dynamic Turbo */
404                                 flags |= IEEE80211_CHAN_TURBO;
405                                 break;
406                         case 't':               /* ht, dt, st, t */
407                                 /* dt and unadorned t specify Dynamic Turbo */
408                                 if ((flags & (IEEE80211_CHAN_STURBO|_CHAN_HT)) == 0)
409                                         flags |= IEEE80211_CHAN_TURBO;
410                                 break;
411                         case 's':               /* st = Atheros Static Turbo */
412                                 flags |= IEEE80211_CHAN_STURBO;
413                                 break;
414                         default:
415                                 errx(-1, "%s: Invalid channel attribute %c\n",
416                                     val, *cp);
417                         }
418                 }
419         }
420         cp = strchr(val, '/');
421         if (cp != NULL) {
422                 char *ep;
423                 u_long cw = strtoul(cp+1, &ep, 10);
424
425                 switch (cw) {
426                 case 5:
427                         flags |= IEEE80211_CHAN_QUARTER;
428                         break;
429                 case 10:
430                         flags |= IEEE80211_CHAN_HALF;
431                         break;
432                 case 20:
433                         /* NB: this may be removed below */
434                         flags |= IEEE80211_CHAN_HT20;
435                         break;
436                 case 40:
437                         if (ep != NULL && *ep == '+')
438                                 flags |= IEEE80211_CHAN_HT40U;
439                         else if (ep != NULL && *ep == '-')
440                                 flags |= IEEE80211_CHAN_HT40D;
441                         break;
442                 default:
443                         errx(-1, "%s: Invalid channel width\n", val);
444                 }
445         }
446         /*
447          * Cleanup specifications.
448          */ 
449         if ((flags & _CHAN_HT) == 0) {
450                 /*
451                  * If user specified freq/20 or freq/40 quietly remove
452                  * HT cw attributes depending on channel use.  To give
453                  * an explicit 20/40 width for an HT channel you must
454                  * indicate it is an HT channel since all HT channels
455                  * are also usable for legacy operation; e.g. freq:n/40.
456                  */
457                 flags &= ~IEEE80211_CHAN_HT;
458         } else {
459                 /*
460                  * Remove private indicator that this is an HT channel
461                  * and if no explicit channel width has been given
462                  * provide the default settings.
463                  */
464                 flags &= ~_CHAN_HT;
465                 if ((flags & IEEE80211_CHAN_HT) == 0) {
466                         struct ieee80211_channel chan;
467                         /*
468                          * Consult the channel list to see if we can use
469                          * HT40+ or HT40- (if both the map routines choose).
470                          */
471                         if (freq > 255)
472                                 mapfreq(&chan, freq, 0);
473                         else
474                                 mapchan(&chan, freq, 0);
475                         flags |= (chan.ic_flags & IEEE80211_CHAN_HT);
476                 }
477         }
478         return flags;
479 #undef _CHAN_HT
480 }
481
482 static void
483 set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
484 {
485         struct ieee80211_channel chan;
486
487         memset(&chan, 0, sizeof(chan));
488         if (!isanyarg(val)) {
489                 int v, flags;
490
491                 getchaninfo(s);
492                 v = atoi(val);
493                 flags = getchannelflags(val, v);
494                 if (v > 255) {          /* treat as frequency */
495                         mapfreq(&chan, v, flags);
496                 } else {
497                         mapchan(&chan, v, flags);
498                 }
499         } else {
500                 chan.ic_freq = IEEE80211_CHAN_ANY;
501         }
502         set80211(s, IEEE80211_IOC_CURCHAN, 0, sizeof(chan), &chan);
503 }
504
505 static void
506 set80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
507 {
508         int     mode;
509
510         if (strcasecmp(val, "none") == 0) {
511                 mode = IEEE80211_AUTH_NONE;
512         } else if (strcasecmp(val, "open") == 0) {
513                 mode = IEEE80211_AUTH_OPEN;
514         } else if (strcasecmp(val, "shared") == 0) {
515                 mode = IEEE80211_AUTH_SHARED;
516         } else if (strcasecmp(val, "8021x") == 0) {
517                 mode = IEEE80211_AUTH_8021X;
518         } else if (strcasecmp(val, "wpa") == 0) {
519                 mode = IEEE80211_AUTH_WPA;
520         } else {
521                 errx(1, "unknown authmode");
522         }
523
524         set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
525 }
526
527 static void
528 set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
529 {
530         int     mode;
531
532         if (strcasecmp(val, "off") == 0) {
533                 mode = IEEE80211_POWERSAVE_OFF;
534         } else if (strcasecmp(val, "on") == 0) {
535                 mode = IEEE80211_POWERSAVE_ON;
536         } else if (strcasecmp(val, "cam") == 0) {
537                 mode = IEEE80211_POWERSAVE_CAM;
538         } else if (strcasecmp(val, "psp") == 0) {
539                 mode = IEEE80211_POWERSAVE_PSP;
540         } else if (strcasecmp(val, "psp-cam") == 0) {
541                 mode = IEEE80211_POWERSAVE_PSP_CAM;
542         } else {
543                 errx(1, "unknown powersavemode");
544         }
545
546         set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
547 }
548
549 static void
550 set80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
551 {
552         if (d == 0)
553                 set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
554                     0, NULL);
555         else
556                 set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
557                     0, NULL);
558 }
559
560 static void
561 set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
562 {
563         set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
564 }
565
566 static void
567 set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
568 {
569         int     mode;
570
571         if (strcasecmp(val, "off") == 0) {
572                 mode = IEEE80211_WEP_OFF;
573         } else if (strcasecmp(val, "on") == 0) {
574                 mode = IEEE80211_WEP_ON;
575         } else if (strcasecmp(val, "mixed") == 0) {
576                 mode = IEEE80211_WEP_MIXED;
577         } else {
578                 errx(1, "unknown wep mode");
579         }
580
581         set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
582 }
583
584 static void
585 set80211wep(const char *val, int d, int s, const struct afswtch *rafp)
586 {
587         set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
588 }
589
590 static int
591 isundefarg(const char *arg)
592 {
593         return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0);
594 }
595
596 static void
597 set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
598 {
599         if (isundefarg(val))
600                 set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
601         else
602                 set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
603 }
604
605 static void
606 set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
607 {
608         int             key = 0;
609         int             len;
610         u_int8_t        data[IEEE80211_KEYBUF_SIZE];
611
612         if (isdigit(val[0]) && val[1] == ':') {
613                 key = atoi(val)-1;
614                 val += 2;
615         }
616
617         bzero(data, sizeof(data));
618         len = sizeof(data);
619         get_string(val, NULL, data, &len);
620
621         set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
622 }
623
624 /*
625  * This function is purely a NetBSD compatability interface.  The NetBSD
626  * interface is too inflexible, but it's there so we'll support it since
627  * it's not all that hard.
628  */
629 static void
630 set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
631 {
632         int             txkey;
633         int             i, len;
634         u_int8_t        data[IEEE80211_KEYBUF_SIZE];
635
636         set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
637
638         if (isdigit(val[0]) && val[1] == ':') {
639                 txkey = val[0]-'0'-1;
640                 val += 2;
641
642                 for (i = 0; i < 4; i++) {
643                         bzero(data, sizeof(data));
644                         len = sizeof(data);
645                         val = get_string(val, ",", data, &len);
646                         if (val == NULL)
647                                 exit(1);
648
649                         set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
650                 }
651         } else {
652                 bzero(data, sizeof(data));
653                 len = sizeof(data);
654                 get_string(val, NULL, data, &len);
655                 txkey = 0;
656
657                 set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
658
659                 bzero(data, sizeof(data));
660                 for (i = 1; i < 4; i++)
661                         set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
662         }
663
664         set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
665 }
666
667 static void
668 set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
669 {
670         set80211(s, IEEE80211_IOC_RTSTHRESHOLD,
671                 isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL);
672 }
673
674 static void
675 set80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
676 {
677         int     mode;
678
679         if (strcasecmp(val, "off") == 0) {
680                 mode = IEEE80211_PROTMODE_OFF;
681         } else if (strcasecmp(val, "cts") == 0) {
682                 mode = IEEE80211_PROTMODE_CTS;
683         } else if (strncasecmp(val, "rtscts", 3) == 0) {
684                 mode = IEEE80211_PROTMODE_RTSCTS;
685         } else {
686                 errx(1, "unknown protection mode");
687         }
688
689         set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
690 }
691
692 static void
693 set80211htprotmode(const char *val, int d, int s, const struct afswtch *rafp)
694 {
695         int     mode;
696
697         if (strcasecmp(val, "off") == 0) {
698                 mode = IEEE80211_PROTMODE_OFF;
699         } else if (strncasecmp(val, "rts", 3) == 0) {
700                 mode = IEEE80211_PROTMODE_RTSCTS;
701         } else {
702                 errx(1, "unknown protection mode");
703         }
704
705         set80211(s, IEEE80211_IOC_HTPROTMODE, mode, 0, NULL);
706 }
707
708 static void
709 set80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
710 {
711         double v = atof(val);
712         int txpow;
713
714         txpow = (int) (2*v);
715         if (txpow != 2*v)
716                 errx(-1, "invalid tx power (must be .5 dBm units)");
717         set80211(s, IEEE80211_IOC_TXPOWER, txpow, 0, NULL);
718 }
719
720 #define IEEE80211_ROAMING_DEVICE        0
721 #define IEEE80211_ROAMING_AUTO          1
722 #define IEEE80211_ROAMING_MANUAL        2
723
724 static void
725 set80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
726 {
727         int mode;
728
729         if (strcasecmp(val, "device") == 0) {
730                 mode = IEEE80211_ROAMING_DEVICE;
731         } else if (strcasecmp(val, "auto") == 0) {
732                 mode = IEEE80211_ROAMING_AUTO;
733         } else if (strcasecmp(val, "manual") == 0) {
734                 mode = IEEE80211_ROAMING_MANUAL;
735         } else {
736                 errx(1, "unknown roaming mode");
737         }
738         set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
739 }
740
741 static void
742 set80211wme(const char *val, int d, int s, const struct afswtch *rafp)
743 {
744         set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
745 }
746
747 static void
748 set80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
749 {
750         set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
751 }
752
753 static void
754 set80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
755 {
756         set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
757 }
758
759 static void
760 set80211fastframes(const char *val, int d, int s, const struct afswtch *rafp)
761 {
762         set80211(s, IEEE80211_IOC_FF, d, 0, NULL);
763 }
764
765 static void
766 set80211dturbo(const char *val, int d, int s, const struct afswtch *rafp)
767 {
768         set80211(s, IEEE80211_IOC_TURBOP, d, 0, NULL);
769 }
770
771 static void
772 set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
773 {
774         struct ieee80211req_chanlist chanlist;
775 #define MAXCHAN (sizeof(chanlist.ic_channels)*NBBY)
776         char *temp, *cp, *tp;
777
778         temp = malloc(strlen(val) + 1);
779         if (temp == NULL)
780                 errx(1, "malloc failed");
781         strcpy(temp, val);
782         memset(&chanlist, 0, sizeof(chanlist));
783         cp = temp;
784         for (;;) {
785                 int first, last, f, c;
786
787                 tp = strchr(cp, ',');
788                 if (tp != NULL)
789                         *tp++ = '\0';
790                 switch (sscanf(cp, "%u-%u", &first, &last)) {
791                 case 1:
792                         if (first > MAXCHAN)
793                                 errx(-1, "channel %u out of range, max %zu",
794                                         first, MAXCHAN);
795                         setbit(chanlist.ic_channels, first);
796                         break;
797                 case 2:
798                         if (first > MAXCHAN)
799                                 errx(-1, "channel %u out of range, max %zu",
800                                         first, MAXCHAN);
801                         if (last > MAXCHAN)
802                                 errx(-1, "channel %u out of range, max %zu",
803                                         last, MAXCHAN);
804                         if (first > last)
805                                 errx(-1, "void channel range, %u > %u",
806                                         first, last);
807                         for (f = first; f <= last; f++)
808                                 setbit(chanlist.ic_channels, f);
809                         break;
810                 }
811                 if (tp == NULL)
812                         break;
813                 c = *tp;
814                 while (isspace(c))
815                         tp++;
816                 if (!isdigit(c))
817                         break;
818                 cp = tp;
819         }
820         set80211(s, IEEE80211_IOC_CHANLIST, 0, sizeof(chanlist), &chanlist);
821 #undef MAXCHAN
822 }
823
824 static void
825 set80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
826 {
827
828         if (!isanyarg(val)) {
829                 char *temp;
830                 struct sockaddr_dl sdl;
831
832                 temp = malloc(strlen(val) + 2); /* ':' and '\0' */
833                 if (temp == NULL)
834                         errx(1, "malloc failed");
835                 temp[0] = ':';
836                 strcpy(temp + 1, val);
837                 sdl.sdl_len = sizeof(sdl);
838                 link_addr(temp, &sdl);
839                 free(temp);
840                 if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
841                         errx(1, "malformed link-level address");
842                 set80211(s, IEEE80211_IOC_BSSID, 0,
843                         IEEE80211_ADDR_LEN, LLADDR(&sdl));
844         } else {
845                 uint8_t zerobssid[IEEE80211_ADDR_LEN];
846                 memset(zerobssid, 0, sizeof(zerobssid));
847                 set80211(s, IEEE80211_IOC_BSSID, 0,
848                         IEEE80211_ADDR_LEN, zerobssid);
849         }
850 }
851
852 static int
853 getac(const char *ac)
854 {
855         if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0)
856                 return WME_AC_BE;
857         if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0)
858                 return WME_AC_BK;
859         if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0)
860                 return WME_AC_VI;
861         if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0)
862                 return WME_AC_VO;
863         errx(1, "unknown wme access class %s", ac);
864 }
865
866 static
867 DECL_CMD_FUNC2(set80211cwmin, ac, val)
868 {
869         set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
870 }
871
872 static
873 DECL_CMD_FUNC2(set80211cwmax, ac, val)
874 {
875         set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
876 }
877
878 static
879 DECL_CMD_FUNC2(set80211aifs, ac, val)
880 {
881         set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
882 }
883
884 static
885 DECL_CMD_FUNC2(set80211txoplimit, ac, val)
886 {
887         set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
888 }
889
890 static
891 DECL_CMD_FUNC(set80211acm, ac, d)
892 {
893         set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL);
894 }
895 static
896 DECL_CMD_FUNC(set80211noacm, ac, d)
897 {
898         set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL);
899 }
900
901 static
902 DECL_CMD_FUNC(set80211ackpolicy, ac, d)
903 {
904         set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL);
905 }
906 static
907 DECL_CMD_FUNC(set80211noackpolicy, ac, d)
908 {
909         set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL);
910 }
911
912 static
913 DECL_CMD_FUNC2(set80211bsscwmin, ac, val)
914 {
915         set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
916                 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
917 }
918
919 static
920 DECL_CMD_FUNC2(set80211bsscwmax, ac, val)
921 {
922         set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
923                 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
924 }
925
926 static
927 DECL_CMD_FUNC2(set80211bssaifs, ac, val)
928 {
929         set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
930                 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
931 }
932
933 static
934 DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
935 {
936         set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
937                 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
938 }
939
940 static
941 DECL_CMD_FUNC(set80211dtimperiod, val, d)
942 {
943         set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
944 }
945
946 static
947 DECL_CMD_FUNC(set80211bintval, val, d)
948 {
949         set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
950 }
951
952 static void
953 set80211macmac(int s, int op, const char *val)
954 {
955         char *temp;
956         struct sockaddr_dl sdl;
957
958         temp = malloc(strlen(val) + 2); /* ':' and '\0' */
959         if (temp == NULL)
960                 errx(1, "malloc failed");
961         temp[0] = ':';
962         strcpy(temp + 1, val);
963         sdl.sdl_len = sizeof(sdl);
964         link_addr(temp, &sdl);
965         free(temp);
966         if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
967                 errx(1, "malformed link-level address");
968         set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
969 }
970
971 static
972 DECL_CMD_FUNC(set80211addmac, val, d)
973 {
974         set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
975 }
976
977 static
978 DECL_CMD_FUNC(set80211delmac, val, d)
979 {
980         set80211macmac(s, IEEE80211_IOC_DELMAC, val);
981 }
982
983 static
984 DECL_CMD_FUNC(set80211kickmac, val, d)
985 {
986         char *temp;
987         struct sockaddr_dl sdl;
988         struct ieee80211req_mlme mlme;
989
990         temp = malloc(strlen(val) + 2); /* ':' and '\0' */
991         if (temp == NULL)
992                 errx(1, "malloc failed");
993         temp[0] = ':';
994         strcpy(temp + 1, val);
995         sdl.sdl_len = sizeof(sdl);
996         link_addr(temp, &sdl);
997         free(temp);
998         if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
999                 errx(1, "malformed link-level address");
1000         memset(&mlme, 0, sizeof(mlme));
1001         mlme.im_op = IEEE80211_MLME_DEAUTH;
1002         mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE;
1003         memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN);
1004         set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), &mlme);
1005 }
1006
1007 static
1008 DECL_CMD_FUNC(set80211maccmd, val, d)
1009 {
1010         set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
1011 }
1012
1013 static void
1014 set80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
1015 {
1016         set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
1017 }
1018
1019 static void
1020 set80211bgscan(const char *val, int d, int s, const struct afswtch *rafp)
1021 {
1022         set80211(s, IEEE80211_IOC_BGSCAN, d, 0, NULL);
1023 }
1024
1025 static
1026 DECL_CMD_FUNC(set80211bgscanidle, val, d)
1027 {
1028         set80211(s, IEEE80211_IOC_BGSCAN_IDLE, atoi(val), 0, NULL);
1029 }
1030
1031 static
1032 DECL_CMD_FUNC(set80211bgscanintvl, val, d)
1033 {
1034         set80211(s, IEEE80211_IOC_BGSCAN_INTERVAL, atoi(val), 0, NULL);
1035 }
1036
1037 static
1038 DECL_CMD_FUNC(set80211scanvalid, val, d)
1039 {
1040         set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL);
1041 }
1042
1043 static
1044 DECL_CMD_FUNC(set80211roamrssi11a, val, d)
1045 {
1046         set80211(s, IEEE80211_IOC_ROAM_RSSI_11A, atoi(val), 0, NULL);
1047 }
1048
1049 static
1050 DECL_CMD_FUNC(set80211roamrssi11b, val, d)
1051 {
1052         set80211(s, IEEE80211_IOC_ROAM_RSSI_11B, atoi(val), 0, NULL);
1053 }
1054
1055 static
1056 DECL_CMD_FUNC(set80211roamrssi11g, val, d)
1057 {
1058         set80211(s, IEEE80211_IOC_ROAM_RSSI_11G, atoi(val), 0, NULL);
1059 }
1060
1061 static
1062 DECL_CMD_FUNC(set80211roamrate11a, val, d)
1063 {
1064         set80211(s, IEEE80211_IOC_ROAM_RATE_11A, 2*atoi(val), 0, NULL);
1065 }
1066
1067 static
1068 DECL_CMD_FUNC(set80211roamrate11b, val, d)
1069 {
1070         set80211(s, IEEE80211_IOC_ROAM_RATE_11B, 2*atoi(val), 0, NULL);
1071 }
1072
1073 static
1074 DECL_CMD_FUNC(set80211roamrate11g, val, d)
1075 {
1076         set80211(s, IEEE80211_IOC_ROAM_RATE_11G, 2*atoi(val), 0, NULL);
1077 }
1078
1079 static
1080 DECL_CMD_FUNC(set80211mcastrate, val, d)
1081 {
1082         set80211(s, IEEE80211_IOC_MCAST_RATE, 2*atoi(val), 0, NULL);
1083 }
1084
1085 static
1086 DECL_CMD_FUNC(set80211fragthreshold, val, d)
1087 {
1088         set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
1089                 isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
1090 }
1091
1092 static
1093 DECL_CMD_FUNC(set80211bmissthreshold, val, d)
1094 {
1095         set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
1096                 isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
1097 }
1098
1099 static void
1100 set80211burst(const char *val, int d, int s, const struct afswtch *rafp)
1101 {
1102         set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
1103 }
1104
1105 static void
1106 set80211doth(const char *val, int d, int s, const struct afswtch *rafp)
1107 {
1108         set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL);
1109 }
1110
1111 static void
1112 set80211shortgi(const char *val, int d, int s, const struct afswtch *rafp)
1113 {
1114         set80211(s, IEEE80211_IOC_SHORTGI,
1115                 d ? (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) : 0,
1116                 0, NULL);
1117 }
1118
1119 static void
1120 set80211ampdu(const char *val, int d, int s, const struct afswtch *rafp)
1121 {
1122         int ampdu;
1123
1124         if (get80211val(s, IEEE80211_IOC_AMPDU, &ampdu) < 0)
1125                 errx(-1, "cannot get AMPDU setting");
1126         if (d < 0) {
1127                 d = -d;
1128                 ampdu &= ~d;
1129         } else
1130                 ampdu |= d;
1131         set80211(s, IEEE80211_IOC_AMPDU, ampdu, 0, NULL);
1132 }
1133
1134 static
1135 DECL_CMD_FUNC(set80211ampdulimit, val, d)
1136 {
1137         int v;
1138
1139         switch (atoi(val)) {
1140         case 8:
1141         case 8*1024:
1142                 v = IEEE80211_HTCAP_MAXRXAMPDU_8K;
1143                 break;
1144         case 16:
1145         case 16*1024:
1146                 v = IEEE80211_HTCAP_MAXRXAMPDU_16K;
1147                 break;
1148         case 32:
1149         case 32*1024:
1150                 v = IEEE80211_HTCAP_MAXRXAMPDU_32K;
1151                 break;
1152         case 64:
1153         case 64*1024:
1154                 v = IEEE80211_HTCAP_MAXRXAMPDU_64K;
1155                 break;
1156         default:
1157                 errx(-1, "invalid A-MPDU limit %s", val);
1158         }
1159         set80211(s, IEEE80211_IOC_AMPDU_LIMIT, v, 0, NULL);
1160 }
1161
1162 static
1163 DECL_CMD_FUNC(set80211ampdudensity, val, d)
1164 {
1165         int v;
1166
1167         if (isanyarg(val))
1168                 v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1169         else switch ((int)(atof(val)*4)) {
1170         case 0:
1171                 v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1172                 break;
1173         case 1:
1174                 v = IEEE80211_HTCAP_MPDUDENSITY_025;
1175                 break;
1176         case 2:
1177                 v = IEEE80211_HTCAP_MPDUDENSITY_05;
1178                 break;
1179         case 4:
1180                 v = IEEE80211_HTCAP_MPDUDENSITY_1;
1181                 break;
1182         case 8:
1183                 v = IEEE80211_HTCAP_MPDUDENSITY_2;
1184                 break;
1185         case 16:
1186                 v = IEEE80211_HTCAP_MPDUDENSITY_4;
1187                 break;
1188         case 32:
1189                 v = IEEE80211_HTCAP_MPDUDENSITY_8;
1190                 break;
1191         case 64:
1192                 v = IEEE80211_HTCAP_MPDUDENSITY_16;
1193                 break;
1194         default:
1195                 errx(-1, "invalid A-MPDU density %s", val);
1196         }
1197         set80211(s, IEEE80211_IOC_AMPDU_DENSITY, v, 0, NULL);
1198 }
1199
1200 static void
1201 set80211amsdu(const char *val, int d, int s, const struct afswtch *rafp)
1202 {
1203         int amsdu;
1204
1205         if (get80211val(s, IEEE80211_IOC_AMSDU, &amsdu) < 0)
1206                 errx(-1, "cannot get AMSDU setting");
1207         if (d < 0) {
1208                 d = -d;
1209                 amsdu &= ~d;
1210         } else
1211                 amsdu |= d;
1212         set80211(s, IEEE80211_IOC_AMSDU, amsdu, 0, NULL);
1213 }
1214
1215 static
1216 DECL_CMD_FUNC(set80211amsdulimit, val, d)
1217 {
1218         set80211(s, IEEE80211_IOC_AMSDU_LIMIT, atoi(val), 0, NULL);
1219 }
1220
1221 static void
1222 set80211puren(const char *val, int d, int s, const struct afswtch *rafp)
1223 {
1224         set80211(s, IEEE80211_IOC_PUREN, d, 0, NULL);
1225 }
1226
1227 static void
1228 set80211htcompat(const char *val, int d, int s, const struct afswtch *rafp)
1229 {
1230         set80211(s, IEEE80211_IOC_HTCOMPAT, d, 0, NULL);
1231 }
1232
1233 static void
1234 set80211htconf(const char *val, int d, int s, const struct afswtch *rafp)
1235 {
1236         set80211(s, IEEE80211_IOC_HTCONF, d, 0, NULL);
1237         htconf = d;
1238 }
1239
1240 static void
1241 set80211inact(const char *val, int d, int s, const struct afswtch *rafp)
1242 {
1243         set80211(s, IEEE80211_IOC_INACTIVITY, d, 0, NULL);
1244 }
1245
1246 static void
1247 LINE_INIT(char c)
1248 {
1249         spacer = c;
1250         if (c == '\t')
1251                 col = 8;
1252         else
1253                 col = 1;
1254 }
1255
1256 static void
1257 LINE_BREAK(void)
1258 {
1259         if (spacer != '\t') {
1260                 printf("\n");
1261                 spacer = '\t';
1262         }
1263         col = 8;                /* 8-col tab */
1264 }
1265
1266 static void
1267 LINE_CHECK(const char *fmt, ...)
1268 {
1269         char buf[80];
1270         va_list ap;
1271         int n;
1272
1273         va_start(ap, fmt);
1274         n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
1275         va_end(ap);
1276         col += 1+n;
1277         if (col > MAXCOL) {
1278                 LINE_BREAK();
1279                 col += n;
1280         }
1281         buf[0] = spacer;
1282         printf("%s", buf);
1283         spacer = ' ';
1284 }
1285
1286 static int
1287 getmaxrate(const uint8_t rates[15], uint8_t nrates)
1288 {
1289         int i, maxrate = -1;
1290
1291         for (i = 0; i < nrates; i++) {
1292                 int rate = rates[i] & IEEE80211_RATE_VAL;
1293                 if (rate > maxrate)
1294                         maxrate = rate;
1295         }
1296         return maxrate / 2;
1297 }
1298
1299 static const char *
1300 getcaps(int capinfo)
1301 {
1302         static char capstring[32];
1303         char *cp = capstring;
1304
1305         if (capinfo & IEEE80211_CAPINFO_ESS)
1306                 *cp++ = 'E';
1307         if (capinfo & IEEE80211_CAPINFO_IBSS)
1308                 *cp++ = 'I';
1309         if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
1310                 *cp++ = 'c';
1311         if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
1312                 *cp++ = 'C';
1313         if (capinfo & IEEE80211_CAPINFO_PRIVACY)
1314                 *cp++ = 'P';
1315         if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
1316                 *cp++ = 'S';
1317         if (capinfo & IEEE80211_CAPINFO_PBCC)
1318                 *cp++ = 'B';
1319         if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
1320                 *cp++ = 'A';
1321         if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
1322                 *cp++ = 's';
1323         if (capinfo & IEEE80211_CAPINFO_RSN)
1324                 *cp++ = 'R';
1325         if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
1326                 *cp++ = 'D';
1327         *cp = '\0';
1328         return capstring;
1329 }
1330
1331 static const char *
1332 getflags(int flags)
1333 {
1334 /* XXX need these publicly defined or similar */
1335 #define IEEE80211_NODE_AUTH     0x0001          /* authorized for data */
1336 #define IEEE80211_NODE_QOS      0x0002          /* QoS enabled */
1337 #define IEEE80211_NODE_ERP      0x0004          /* ERP enabled */
1338 #define IEEE80211_NODE_PWR_MGT  0x0010          /* power save mode enabled */
1339 #define IEEE80211_NODE_HT       0x0040          /* HT enabled */
1340 #define IEEE80211_NODE_HTCOMPAT 0x0080          /* HT setup w/ vendor OUI's */
1341 #define IEEE80211_NODE_WPS      0x0100          /* WPS association */
1342 #define IEEE80211_NODE_TSN      0x0200          /* TSN association */
1343
1344         static char flagstring[32];
1345         char *cp = flagstring;
1346
1347         if (flags & IEEE80211_NODE_AUTH)
1348                 *cp++ = 'A';
1349         if (flags & IEEE80211_NODE_QOS)
1350                 *cp++ = 'Q';
1351         if (flags & IEEE80211_NODE_ERP)
1352                 *cp++ = 'E';
1353         if (flags & IEEE80211_NODE_PWR_MGT)
1354                 *cp++ = 'P';
1355         if (flags & IEEE80211_NODE_HT) {
1356                 *cp++ = 'H';
1357                 if (flags & IEEE80211_NODE_HTCOMPAT)
1358                         *cp++ = '+';
1359         }
1360         if (flags & IEEE80211_NODE_WPS)
1361                 *cp++ = 'W';
1362         if (flags & IEEE80211_NODE_TSN)
1363                 *cp++ = 'T';
1364         *cp = '\0';
1365         return flagstring;
1366 #undef IEEE80211_NODE_TSN
1367 #undef IEEE80211_NODE_WPS
1368 #undef IEEE80211_NODE_HTCOMPAT
1369 #undef IEEE80211_NODE_HT
1370 #undef IEEE80211_NODE_AUTH
1371 #undef IEEE80211_NODE_QOS
1372 #undef IEEE80211_NODE_ERP
1373 #undef IEEE80211_NODE_PWR_MGT
1374 }
1375
1376 static void
1377 printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
1378 {
1379         printf("%s", tag);
1380         if (verbose) {
1381                 maxlen -= strlen(tag)+2;
1382                 if (2*ielen > maxlen)
1383                         maxlen--;
1384                 printf("<");
1385                 for (; ielen > 0; ie++, ielen--) {
1386                         if (maxlen-- <= 0)
1387                                 break;
1388                         printf("%02x", *ie);
1389                 }
1390                 if (ielen != 0)
1391                         printf("-");
1392                 printf(">");
1393         }
1394 }
1395
1396 #define LE_READ_2(p)                                    \
1397         ((u_int16_t)                                    \
1398          ((((const u_int8_t *)(p))[0]      ) |          \
1399           (((const u_int8_t *)(p))[1] <<  8)))
1400 #define LE_READ_4(p)                                    \
1401         ((u_int32_t)                                    \
1402          ((((const u_int8_t *)(p))[0]      ) |          \
1403           (((const u_int8_t *)(p))[1] <<  8) |          \
1404           (((const u_int8_t *)(p))[2] << 16) |          \
1405           (((const u_int8_t *)(p))[3] << 24)))
1406
1407 /*
1408  * NB: The decoding routines assume a properly formatted ie
1409  *     which should be safe as the kernel only retains them
1410  *     if they parse ok.
1411  */
1412
1413 static void
1414 printwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1415 {
1416 #define MS(_v, _f)      (((_v) & _f) >> _f##_S)
1417         static const char *acnames[] = { "BE", "BK", "VO", "VI" };
1418         const struct ieee80211_wme_param *wme =
1419             (const struct ieee80211_wme_param *) ie;
1420         int i;
1421
1422         printf("%s", tag);
1423         if (!verbose)
1424                 return;
1425         printf("<qosinfo 0x%x", wme->param_qosInfo);
1426         ie += offsetof(struct ieee80211_wme_param, params_acParams);
1427         for (i = 0; i < WME_NUM_AC; i++) {
1428                 const struct ieee80211_wme_acparams *ac =
1429                     &wme->params_acParams[i];
1430
1431                 printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]"
1432                         , acnames[i]
1433                         , MS(ac->acp_aci_aifsn, WME_PARAM_ACM) ? "acm " : ""
1434                         , MS(ac->acp_aci_aifsn, WME_PARAM_AIFSN)
1435                         , MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMIN)
1436                         , MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMAX)
1437                         , LE_READ_2(&ac->acp_txop)
1438                 );
1439         }
1440         printf(">");
1441 #undef MS
1442 }
1443
1444 static void
1445 printwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1446 {
1447         printf("%s", tag);
1448         if (verbose) {
1449                 const struct ieee80211_wme_info *wme =
1450                     (const struct ieee80211_wme_info *) ie;
1451                 printf("<version 0x%x info 0x%x>",
1452                     wme->wme_version, wme->wme_info);
1453         }
1454 }
1455
1456 static void
1457 printhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1458 {
1459         printf("%s", tag);
1460         if (verbose) {
1461                 const struct ieee80211_ie_htcap *htcap =
1462                     (const struct ieee80211_ie_htcap *) ie;
1463                 const char *sep;
1464                 int i, j;
1465
1466                 printf("<cap 0x%x param 0x%x",
1467                     LE_READ_2(&htcap->hc_cap), htcap->hc_param);
1468                 printf(" mcsset[");
1469                 sep = "";
1470                 for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
1471                         if (isset(htcap->hc_mcsset, i)) {
1472                                 for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
1473                                         if (isclr(htcap->hc_mcsset, j))
1474                                                 break;
1475                                 j--;
1476                                 if (i == j)
1477                                         printf("%s%u", sep, i);
1478                                 else
1479                                         printf("%s%u-%u", sep, i, j);
1480                                 i += j-i;
1481                                 sep = ",";
1482                         }
1483                 printf("] extcap 0x%x txbf 0x%x antenna 0x%x>",
1484                     LE_READ_2(&htcap->hc_extcap),
1485                     LE_READ_4(&htcap->hc_txbf),
1486                     htcap->hc_antenna);
1487         }
1488 }
1489
1490 static void
1491 printhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1492 {
1493         printf("%s", tag);
1494         if (verbose) {
1495                 const struct ieee80211_ie_htinfo *htinfo =
1496                     (const struct ieee80211_ie_htinfo *) ie;
1497                 const char *sep;
1498                 int i, j;
1499
1500                 printf("<ctl %u, %x,%x,%x,%x", htinfo->hi_ctrlchannel,
1501                     htinfo->hi_byte1, htinfo->hi_byte2, htinfo->hi_byte3,
1502                     LE_READ_2(&htinfo->hi_byte45));
1503                 printf(" basicmcs[");
1504                 sep = "";
1505                 for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
1506                         if (isset(htinfo->hi_basicmcsset, i)) {
1507                                 for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
1508                                         if (isclr(htinfo->hi_basicmcsset, j))
1509                                                 break;
1510                                 j--;
1511                                 if (i == j)
1512                                         printf("%s%u", sep, i);
1513                                 else
1514                                         printf("%s%u-%u", sep, i, j);
1515                                 i += j-i;
1516                                 sep = ",";
1517                         }
1518                 printf("]>");
1519         }
1520 }
1521
1522 static void
1523 printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1524 {
1525
1526         printf("%s", tag);
1527         if (verbose) {
1528                 const struct ieee80211_ath_ie *ath =
1529                         (const struct ieee80211_ath_ie *)ie;
1530
1531                 printf("<");
1532                 if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME)
1533                         printf("DTURBO,");
1534                 if (ath->ath_capability & ATHEROS_CAP_COMPRESSION)
1535                         printf("COMP,");
1536                 if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME)
1537                         printf("FF,");
1538                 if (ath->ath_capability & ATHEROS_CAP_XR)
1539                         printf("XR,");
1540                 if (ath->ath_capability & ATHEROS_CAP_AR)
1541                         printf("AR,");
1542                 if (ath->ath_capability & ATHEROS_CAP_BURST)
1543                         printf("BURST,");
1544                 if (ath->ath_capability & ATHEROS_CAP_WME)
1545                         printf("WME,");
1546                 if (ath->ath_capability & ATHEROS_CAP_BOOST)
1547                         printf("BOOST,");
1548                 printf("0x%x>", LE_READ_2(ath->ath_defkeyix));
1549         }
1550 }
1551
1552 static const char *
1553 wpa_cipher(const u_int8_t *sel)
1554 {
1555 #define WPA_SEL(x)      (((x)<<24)|WPA_OUI)
1556         u_int32_t w = LE_READ_4(sel);
1557
1558         switch (w) {
1559         case WPA_SEL(WPA_CSE_NULL):
1560                 return "NONE";
1561         case WPA_SEL(WPA_CSE_WEP40):
1562                 return "WEP40";
1563         case WPA_SEL(WPA_CSE_WEP104):
1564                 return "WEP104";
1565         case WPA_SEL(WPA_CSE_TKIP):
1566                 return "TKIP";
1567         case WPA_SEL(WPA_CSE_CCMP):
1568                 return "AES-CCMP";
1569         }
1570         return "?";             /* NB: so 1<< is discarded */
1571 #undef WPA_SEL
1572 }
1573
1574 static const char *
1575 wpa_keymgmt(const u_int8_t *sel)
1576 {
1577 #define WPA_SEL(x)      (((x)<<24)|WPA_OUI)
1578         u_int32_t w = LE_READ_4(sel);
1579
1580         switch (w) {
1581         case WPA_SEL(WPA_ASE_8021X_UNSPEC):
1582                 return "8021X-UNSPEC";
1583         case WPA_SEL(WPA_ASE_8021X_PSK):
1584                 return "8021X-PSK";
1585         case WPA_SEL(WPA_ASE_NONE):
1586                 return "NONE";
1587         }
1588         return "?";
1589 #undef WPA_SEL
1590 }
1591
1592 static void
1593 printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1594 {
1595         u_int8_t len = ie[1];
1596
1597         printf("%s", tag);
1598         if (verbose) {
1599                 const char *sep;
1600                 int n;
1601
1602                 ie += 6, len -= 4;              /* NB: len is payload only */
1603
1604                 printf("<v%u", LE_READ_2(ie));
1605                 ie += 2, len -= 2;
1606
1607                 printf(" mc:%s", wpa_cipher(ie));
1608                 ie += 4, len -= 4;
1609
1610                 /* unicast ciphers */
1611                 n = LE_READ_2(ie);
1612                 ie += 2, len -= 2;
1613                 sep = " uc:";
1614                 for (; n > 0; n--) {
1615                         printf("%s%s", sep, wpa_cipher(ie));
1616                         ie += 4, len -= 4;
1617                         sep = "+";
1618                 }
1619
1620                 /* key management algorithms */
1621                 n = LE_READ_2(ie);
1622                 ie += 2, len -= 2;
1623                 sep = " km:";
1624                 for (; n > 0; n--) {
1625                         printf("%s%s", sep, wpa_keymgmt(ie));
1626                         ie += 4, len -= 4;
1627                         sep = "+";
1628                 }
1629
1630                 if (len > 2)            /* optional capabilities */
1631                         printf(", caps 0x%x", LE_READ_2(ie));
1632                 printf(">");
1633         }
1634 }
1635
1636 static const char *
1637 rsn_cipher(const u_int8_t *sel)
1638 {
1639 #define RSN_SEL(x)      (((x)<<24)|RSN_OUI)
1640         u_int32_t w = LE_READ_4(sel);
1641
1642         switch (w) {
1643         case RSN_SEL(RSN_CSE_NULL):
1644                 return "NONE";
1645         case RSN_SEL(RSN_CSE_WEP40):
1646                 return "WEP40";
1647         case RSN_SEL(RSN_CSE_WEP104):
1648                 return "WEP104";
1649         case RSN_SEL(RSN_CSE_TKIP):
1650                 return "TKIP";
1651         case RSN_SEL(RSN_CSE_CCMP):
1652                 return "AES-CCMP";
1653         case RSN_SEL(RSN_CSE_WRAP):
1654                 return "AES-OCB";
1655         }
1656         return "?";
1657 #undef WPA_SEL
1658 }
1659
1660 static const char *
1661 rsn_keymgmt(const u_int8_t *sel)
1662 {
1663 #define RSN_SEL(x)      (((x)<<24)|RSN_OUI)
1664         u_int32_t w = LE_READ_4(sel);
1665
1666         switch (w) {
1667         case RSN_SEL(RSN_ASE_8021X_UNSPEC):
1668                 return "8021X-UNSPEC";
1669         case RSN_SEL(RSN_ASE_8021X_PSK):
1670                 return "8021X-PSK";
1671         case RSN_SEL(RSN_ASE_NONE):
1672                 return "NONE";
1673         }
1674         return "?";
1675 #undef RSN_SEL
1676 }
1677
1678 static void
1679 printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1680 {
1681         printf("%s", tag);
1682         if (verbose) {
1683                 const char *sep;
1684                 int n;
1685
1686                 ie += 2, ielen -= 2;
1687
1688                 printf("<v%u", LE_READ_2(ie));
1689                 ie += 2, ielen -= 2;
1690
1691                 printf(" mc:%s", rsn_cipher(ie));
1692                 ie += 4, ielen -= 4;
1693
1694                 /* unicast ciphers */
1695                 n = LE_READ_2(ie);
1696                 ie += 2, ielen -= 2;
1697                 sep = " uc:";
1698                 for (; n > 0; n--) {
1699                         printf("%s%s", sep, rsn_cipher(ie));
1700                         ie += 4, ielen -= 4;
1701                         sep = "+";
1702                 }
1703
1704                 /* key management algorithms */
1705                 n = LE_READ_2(ie);
1706                 ie += 2, ielen -= 2;
1707                 sep = " km:";
1708                 for (; n > 0; n--) {
1709                         printf("%s%s", sep, rsn_keymgmt(ie));
1710                         ie += 4, ielen -= 4;
1711                         sep = "+";
1712                 }
1713
1714                 if (ielen > 2)          /* optional capabilities */
1715                         printf(", caps 0x%x", LE_READ_2(ie));
1716                 /* XXXPMKID */
1717                 printf(">");
1718         }
1719 }
1720
1721 /* XXX move to a public include file */
1722 #define IEEE80211_WPS_DEV_PASS_ID       0x1012
1723 #define IEEE80211_WPS_SELECTED_REG      0x1041
1724 #define IEEE80211_WPS_SETUP_STATE       0x1044
1725 #define IEEE80211_WPS_UUID_E            0x1047
1726 #define IEEE80211_WPS_VERSION           0x104a
1727
1728 #define BE_READ_2(p)                                    \
1729         ((u_int16_t)                                    \
1730          ((((const u_int8_t *)(p))[1]      ) |          \
1731           (((const u_int8_t *)(p))[0] <<  8)))
1732
1733 static void
1734 printwpsie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1735 {
1736 #define N(a)    (sizeof(a) / sizeof(a[0]))
1737         u_int8_t len = ie[1];
1738
1739         printf("%s", tag);
1740         if (verbose) {
1741                 static const char *dev_pass_id[] = {
1742                         "D",    /* Default (PIN) */
1743                         "U",    /* User-specified */
1744                         "M",    /* Machine-specified */
1745                         "K",    /* Rekey */
1746                         "P",    /* PushButton */
1747                         "R"     /* Registrar-specified */
1748                 };
1749                 int n;
1750
1751                 ie +=6, len -= 4;               /* NB: len is payload only */
1752
1753                 /* WPS IE in Beacon and Probe Resp frames have different fields */
1754                 printf("<");
1755                 while (len) {
1756                         uint16_t tlv_type = BE_READ_2(ie);
1757                         uint16_t tlv_len  = BE_READ_2(ie + 2);
1758
1759                         ie += 4, len -= 4;
1760
1761                         switch (tlv_type) {
1762                         case IEEE80211_WPS_VERSION:
1763                                 printf("v:%d.%d", *ie >> 4, *ie & 0xf);
1764                                 break;
1765                         case IEEE80211_WPS_SETUP_STATE:
1766                                 /* Only 1 and 2 are valid */
1767                                 if (*ie == 0 || *ie >= 3)
1768                                         printf(" state:B");
1769                                 else
1770                                         printf(" st:%s", *ie == 1 ? "N" : "C");
1771                                 break;
1772                         case IEEE80211_WPS_SELECTED_REG:
1773                                 printf(" sel:%s", *ie ? "T" : "F");
1774                                 break;
1775                         case IEEE80211_WPS_DEV_PASS_ID:
1776                                 n = LE_READ_2(ie);
1777                                 if (n < N(dev_pass_id))
1778                                         printf(" dpi:%s", dev_pass_id[n]);
1779                                 break;
1780                         case IEEE80211_WPS_UUID_E:
1781                                 printf(" uuid-e:");
1782                                 for (n = 0; n < (tlv_len - 1); n++)
1783                                         printf("%02x-", ie[n]);
1784                                 printf("%02x", ie[n]);
1785                                 break;
1786                         }
1787                         ie += tlv_len, len -= tlv_len;
1788                 }
1789                 printf(">");
1790         }
1791 #undef N
1792 }
1793
1794 /*
1795  * Copy the ssid string contents into buf, truncating to fit.  If the
1796  * ssid is entirely printable then just copy intact.  Otherwise convert
1797  * to hexadecimal.  If the result is truncated then replace the last
1798  * three characters with "...".
1799  */
1800 static int
1801 copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
1802 {
1803         const u_int8_t *p; 
1804         size_t maxlen;
1805         int i;
1806
1807         if (essid_len > bufsize)
1808                 maxlen = bufsize;
1809         else
1810                 maxlen = essid_len;
1811         /* determine printable or not */
1812         for (i = 0, p = essid; i < maxlen; i++, p++) {
1813                 if (*p < ' ' || *p > 0x7e)
1814                         break;
1815         }
1816         if (i != maxlen) {              /* not printable, print as hex */
1817                 if (bufsize < 3)
1818                         return 0;
1819                 strlcpy(buf, "0x", bufsize);
1820                 bufsize -= 2;
1821                 p = essid;
1822                 for (i = 0; i < maxlen && bufsize >= 2; i++) {
1823                         sprintf(&buf[2+2*i], "%02x", p[i]);
1824                         bufsize -= 2;
1825                 }
1826                 if (i != essid_len)
1827                         memcpy(&buf[2+2*i-3], "...", 3);
1828         } else {                        /* printable, truncate as needed */
1829                 memcpy(buf, essid, maxlen);
1830                 if (maxlen != essid_len)
1831                         memcpy(&buf[maxlen-3], "...", 3);
1832         }
1833         return maxlen;
1834 }
1835
1836 static void
1837 printssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1838 {
1839         char ssid[2*IEEE80211_NWID_LEN+1];
1840
1841         printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid);
1842 }
1843
1844 static void
1845 printrates(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1846 {
1847         const char *sep;
1848         int i;
1849
1850         printf("%s", tag);
1851         sep = "<";
1852         for (i = 2; i < ielen; i++) {
1853                 printf("%s%s%d", sep,
1854                     ie[i] & IEEE80211_RATE_BASIC ? "B" : "",
1855                     ie[i] & IEEE80211_RATE_VAL);
1856                 sep = ",";
1857         }
1858         printf(">");
1859 }
1860
1861 static void
1862 printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1863 {
1864         const struct ieee80211_country_ie *cie =
1865            (const struct ieee80211_country_ie *) ie;
1866         int i, nbands, schan, nchan;
1867
1868         printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]);
1869         nbands = (cie->len - 3) / sizeof(cie->band[0]);
1870         for (i = 0; i < nbands; i++) {
1871                 schan = cie->band[i].schan;
1872                 nchan = cie->band[i].nchan;
1873                 if (nchan != 1)
1874                         printf(" %u-%u,%u", schan, schan + nchan-1,
1875                             cie->band[i].maxtxpwr);
1876                 else
1877                         printf(" %u,%u", schan, cie->band[i].maxtxpwr);
1878         }
1879         printf(">");
1880 }
1881
1882 /* unaligned little endian access */     
1883 #define LE_READ_4(p)                                    \
1884         ((u_int32_t)                                    \
1885          ((((const u_int8_t *)(p))[0]      ) |          \
1886           (((const u_int8_t *)(p))[1] <<  8) |          \
1887           (((const u_int8_t *)(p))[2] << 16) |          \
1888           (((const u_int8_t *)(p))[3] << 24)))
1889
1890 static int __inline
1891 iswpaoui(const u_int8_t *frm)
1892 {
1893         return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
1894 }
1895
1896 static int __inline
1897 iswmeinfo(const u_int8_t *frm)
1898 {
1899         return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
1900                 frm[6] == WME_INFO_OUI_SUBTYPE;
1901 }
1902
1903 static int __inline
1904 iswmeparam(const u_int8_t *frm)
1905 {
1906         return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
1907                 frm[6] == WME_PARAM_OUI_SUBTYPE;
1908 }
1909
1910 static int __inline
1911 isatherosoui(const u_int8_t *frm)
1912 {
1913         return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
1914 }
1915
1916 static __inline int
1917 iswpsoui(const uint8_t *frm)
1918 {
1919         return frm[1] > 3 && LE_READ_4(frm+2) == ((WPS_OUI_TYPE<<24)|WPA_OUI);
1920 }
1921
1922 static const char *
1923 iename(int elemid)
1924 {
1925         switch (elemid) {
1926         case IEEE80211_ELEMID_FHPARMS:  return " FHPARMS";
1927         case IEEE80211_ELEMID_CFPARMS:  return " CFPARMS";
1928         case IEEE80211_ELEMID_TIM:      return " TIM";
1929         case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS";
1930         case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE";
1931         case IEEE80211_ELEMID_PWRCNSTR: return " PWRCNSTR";
1932         case IEEE80211_ELEMID_PWRCAP:   return " PWRCAP";
1933         case IEEE80211_ELEMID_TPCREQ:   return " TPCREQ";
1934         case IEEE80211_ELEMID_TPCREP:   return " TPCREP";
1935         case IEEE80211_ELEMID_SUPPCHAN: return " SUPPCHAN";
1936         case IEEE80211_ELEMID_CHANSWITCHANN:return " CSA";
1937         case IEEE80211_ELEMID_MEASREQ:  return " MEASREQ";
1938         case IEEE80211_ELEMID_MEASREP:  return " MEASREP";
1939         case IEEE80211_ELEMID_QUIET:    return " QUIET";
1940         case IEEE80211_ELEMID_IBSSDFS:  return " IBSSDFS";
1941         case IEEE80211_ELEMID_TPC:      return " TPC";
1942         case IEEE80211_ELEMID_CCKM:     return " CCKM";
1943         }
1944         return " ???";
1945 }
1946
1947 static void
1948 printies(const u_int8_t *vp, int ielen, int maxcols)
1949 {
1950         while (ielen > 0) {
1951                 switch (vp[0]) {
1952                 case IEEE80211_ELEMID_SSID:
1953                         if (verbose)
1954                                 printssid(" SSID", vp, 2+vp[1], maxcols);
1955                         break;
1956                 case IEEE80211_ELEMID_RATES:
1957                 case IEEE80211_ELEMID_XRATES:
1958                         if (verbose)
1959                                 printrates(vp[0] == IEEE80211_ELEMID_RATES ?
1960                                     " RATES" : " XRATES", vp, 2+vp[1], maxcols);
1961                         break;
1962                 case IEEE80211_ELEMID_DSPARMS:
1963                         if (verbose)
1964                                 printf(" DSPARMS<%u>", vp[2]);
1965                         break;
1966                 case IEEE80211_ELEMID_COUNTRY:
1967                         if (verbose)
1968                                 printcountry(" COUNTRY", vp, 2+vp[1], maxcols);
1969                         break;
1970                 case IEEE80211_ELEMID_ERP:
1971                         if (verbose)
1972                                 printf(" ERP<0x%x>", vp[2]);
1973                         break;
1974                 case IEEE80211_ELEMID_VENDOR:
1975                         if (iswpaoui(vp))
1976                                 printwpaie(" WPA", vp, 2+vp[1], maxcols);
1977                         else if (iswmeinfo(vp))
1978                                 printwmeinfo(" WME", vp, 2+vp[1], maxcols);
1979                         else if (iswmeparam(vp))
1980                                 printwmeparam(" WME", vp, 2+vp[1], maxcols);
1981                         else if (isatherosoui(vp))
1982                                 printathie(" ATH", vp, 2+vp[1], maxcols);
1983                         else if (iswpsoui(vp))
1984                                 printwpsie(" WPS", vp, 2+vp[1], maxcols);
1985                         else if (verbose)
1986                                 printie(" VEN", vp, 2+vp[1], maxcols);
1987                         break;
1988                 case IEEE80211_ELEMID_RSN:
1989                         printrsnie(" RSN", vp, 2+vp[1], maxcols);
1990                         break;
1991                 case IEEE80211_ELEMID_HTCAP:
1992                         printhtcap(" HTCAP", vp, 2+vp[1], maxcols);
1993                         break;
1994                 case IEEE80211_ELEMID_HTINFO:
1995                         if (verbose)
1996                                 printhtinfo(" HTINFO", vp, 2+vp[1], maxcols);
1997                         break;
1998                 default:
1999                         if (verbose)
2000                                 printie(iename(vp[0]), vp, 2+vp[1], maxcols);
2001                         break;
2002                 }
2003                 ielen -= 2+vp[1];
2004                 vp += 2+vp[1];
2005         }
2006 }
2007
2008 static void
2009 list_scan(int s)
2010 {
2011         uint8_t buf[24*1024];
2012         char ssid[IEEE80211_NWID_LEN+1];
2013         const uint8_t *cp;
2014         int len, ssidmax;
2015
2016         if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0)
2017                 errx(1, "unable to get scan results");
2018         if (len < sizeof(struct ieee80211req_scan_result))
2019                 return;
2020
2021         getchaninfo(s);
2022
2023         ssidmax = verbose ? IEEE80211_NWID_LEN : 14;
2024         printf("%-*.*s  %-17.17s  %4s %4s  %-7s  %3s %4s\n"
2025                 , ssidmax, ssidmax, "SSID"
2026                 , "BSSID"
2027                 , "CHAN"
2028                 , "RATE"
2029                 , " S:N"
2030                 , "INT"
2031                 , "CAPS"
2032         );
2033         cp = buf;
2034         do {
2035                 const struct ieee80211req_scan_result *sr;
2036                 const uint8_t *vp;
2037
2038                 sr = (const struct ieee80211req_scan_result *) cp;
2039                 vp = cp + sr->isr_ie_off;
2040                 printf("%-*.*s  %s  %3d  %3dM %3d:%-3d  %3d %-4.4s"
2041                         , ssidmax
2042                           , copy_essid(ssid, ssidmax, vp, sr->isr_ssid_len)
2043                           , ssid
2044                         , ether_ntoa((const struct ether_addr *) sr->isr_bssid)
2045                         , ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
2046                         , getmaxrate(sr->isr_rates, sr->isr_nrates)
2047                         , (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise
2048                         , sr->isr_intval
2049                         , getcaps(sr->isr_capinfo)
2050                 );
2051                 printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);
2052                 printf("\n");
2053                 cp += sr->isr_len, len -= sr->isr_len;
2054         } while (len >= sizeof(struct ieee80211req_scan_result));
2055 }
2056
2057 #include <net80211/ieee80211_freebsd.h>
2058
2059 static void
2060 scan_and_wait(int s)
2061 {
2062         struct ieee80211req ireq;
2063         int sroute;
2064
2065         sroute = socket(PF_ROUTE, SOCK_RAW, 0);
2066         if (sroute < 0) {
2067                 perror("socket(PF_ROUTE,SOCK_RAW)");
2068                 return;
2069         }
2070         (void) memset(&ireq, 0, sizeof(ireq));
2071         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2072         ireq.i_type = IEEE80211_IOC_SCAN_REQ;
2073         /* NB: only root can trigger a scan so ignore errors */
2074         if (ioctl(s, SIOCS80211, &ireq) >= 0) {
2075                 char buf[2048];
2076                 struct if_announcemsghdr *ifan;
2077                 struct rt_msghdr *rtm;
2078
2079                 do {
2080                         if (read(sroute, buf, sizeof(buf)) < 0) {
2081                                 perror("read(PF_ROUTE)");
2082                                 break;
2083                         }
2084                         rtm = (struct rt_msghdr *) buf;
2085                         if (rtm->rtm_version != RTM_VERSION)
2086                                 break;
2087                         ifan = (struct if_announcemsghdr *) rtm;
2088                 } while (rtm->rtm_type != RTM_IEEE80211 ||
2089                     ifan->ifan_what != RTM_IEEE80211_SCAN);
2090         }
2091         close(sroute);
2092 }
2093
2094 static
2095 DECL_CMD_FUNC(set80211scan, val, d)
2096 {
2097         scan_and_wait(s);
2098         list_scan(s);
2099 }
2100
2101 static enum ieee80211_opmode get80211opmode(int s);
2102
2103 static int
2104 gettxseq(const struct ieee80211req_sta_info *si)
2105 {
2106 #define IEEE80211_NODE_QOS      0x0002          /* QoS enabled */
2107
2108         int i, txseq;
2109
2110         if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
2111                 return si->isi_txseqs[0];
2112         /* XXX not right but usually what folks want */
2113         txseq = 0;
2114         for (i = 0; i < IEEE80211_TID_SIZE; i++)
2115                 if (si->isi_txseqs[i] > txseq)
2116                         txseq = si->isi_txseqs[i];
2117         return txseq;
2118 #undef IEEE80211_NODE_QOS
2119 }
2120
2121 static int
2122 getrxseq(const struct ieee80211req_sta_info *si)
2123 {
2124 #define IEEE80211_NODE_QOS      0x0002          /* QoS enabled */
2125
2126         int i, rxseq;
2127
2128         if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
2129                 return si->isi_rxseqs[0];
2130         /* XXX not right but usually what folks want */
2131         rxseq = 0;
2132         for (i = 0; i < IEEE80211_TID_SIZE; i++)
2133                 if (si->isi_rxseqs[i] > rxseq)
2134                         rxseq = si->isi_rxseqs[i];
2135         return rxseq;
2136 #undef IEEE80211_NODE_QOS
2137 }
2138
2139 static int
2140 gettxrate(const struct ieee80211req_sta_info *si)
2141 {
2142         int txrate = si->isi_txrate;
2143
2144         if (txrate & 0x80) {
2145                 txrate = htrates[txrate & 0xf];
2146                 /* NB: could bump this more based on short gi */
2147                 return si->isi_flags & IEEE80211_CHAN_HT40 ?
2148                     txrate : txrate / 2;
2149         } else
2150                 return (si->isi_rates[txrate] & IEEE80211_RATE_VAL) / 2;
2151 }
2152
2153 static void
2154 list_stations(int s)
2155 {
2156         union {
2157                 struct ieee80211req_sta_req req;
2158                 uint8_t buf[24*1024];
2159         } u;
2160         enum ieee80211_opmode opmode = get80211opmode(s);
2161         const uint8_t *cp;
2162         int len;
2163
2164         /* broadcast address =>'s get all stations */
2165         (void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
2166         if (opmode == IEEE80211_M_STA) {
2167                 /*
2168                  * Get information about the associated AP.
2169                  */
2170                 (void) get80211(s, IEEE80211_IOC_BSSID,
2171                     u.req.is_u.macaddr, IEEE80211_ADDR_LEN);
2172         }
2173         if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0)
2174                 errx(1, "unable to get station information");
2175         if (len < sizeof(struct ieee80211req_sta_info))
2176                 return;
2177
2178         getchaninfo(s);
2179
2180         printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %4s\n"
2181                 , "ADDR"
2182                 , "AID"
2183                 , "CHAN"
2184                 , "RATE"
2185                 , "RSSI"
2186                 , "IDLE"
2187                 , "TXSEQ"
2188                 , "RXSEQ"
2189                 , "CAPS"
2190                 , "FLAG"
2191         );
2192         cp = (const uint8_t *) u.req.info;
2193         do {
2194                 const struct ieee80211req_sta_info *si;
2195
2196                 si = (const struct ieee80211req_sta_info *) cp;
2197                 if (si->isi_len < sizeof(*si))
2198                         break;
2199                 printf("%s %4u %4d %3dM %3.1f %4d %6d %6d %-4.4s %-4.4s"
2200                         , ether_ntoa((const struct ether_addr*) si->isi_macaddr)
2201                         , IEEE80211_AID(si->isi_associd)
2202                         , ieee80211_mhz2ieee(si->isi_freq, si->isi_flags)
2203                         , gettxrate(si)
2204                         , si->isi_rssi/2.
2205                         , si->isi_inact
2206                         , gettxseq(si)
2207                         , getrxseq(si)
2208                         , getcaps(si->isi_capinfo)
2209                         , getflags(si->isi_state)
2210                 );
2211                 printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
2212                 printf("\n");
2213                 cp += si->isi_len, len -= si->isi_len;
2214         } while (len >= sizeof(struct ieee80211req_sta_info));
2215 }
2216
2217 static const char *
2218 get_chaninfo(const struct ieee80211_channel *c, int precise,
2219         char buf[], size_t bsize)
2220 {
2221         buf[0] = '\0';
2222         if (IEEE80211_IS_CHAN_FHSS(c))
2223                 strlcat(buf, " FHSS", bsize);
2224         if (IEEE80211_IS_CHAN_A(c)) {
2225                 if (IEEE80211_IS_CHAN_HALF(c))
2226                         strlcat(buf, " 11a/10Mhz", bsize);
2227                 else if (IEEE80211_IS_CHAN_QUARTER(c))
2228                         strlcat(buf, " 11a/5Mhz", bsize);
2229                 else
2230                         strlcat(buf, " 11a", bsize);
2231         }
2232         if (IEEE80211_IS_CHAN_ANYG(c)) {
2233                 if (IEEE80211_IS_CHAN_HALF(c))
2234                         strlcat(buf, " 11g/10Mhz", bsize);
2235                 else if (IEEE80211_IS_CHAN_QUARTER(c))
2236                         strlcat(buf, " 11g/5Mhz", bsize);
2237                 else
2238                         strlcat(buf, " 11g", bsize);
2239         } else if (IEEE80211_IS_CHAN_B(c))
2240                 strlcat(buf, " 11b", bsize);
2241         if (IEEE80211_IS_CHAN_TURBO(c))
2242                 strlcat(buf, " Turbo", bsize);
2243         if (precise) {
2244                 if (IEEE80211_IS_CHAN_HT20(c))
2245                         strlcat(buf, " ht/20", bsize);
2246                 else if (IEEE80211_IS_CHAN_HT40D(c))
2247                         strlcat(buf, " ht/40-", bsize);
2248                 else if (IEEE80211_IS_CHAN_HT40U(c))
2249                         strlcat(buf, " ht/40+", bsize);
2250         } else {
2251                 if (IEEE80211_IS_CHAN_HT(c))
2252                         strlcat(buf, " ht", bsize);
2253         }
2254         return buf;
2255 }
2256
2257 static void
2258 print_chaninfo(const struct ieee80211_channel *c, int verb)
2259 {
2260         char buf[14];
2261
2262         printf("Channel %3u : %u%c Mhz%-14.14s",
2263                 ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
2264                 IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
2265                 get_chaninfo(c, verb, buf, sizeof(buf)));
2266 }
2267
2268 static void
2269 print_channels(int s, const struct ieee80211req_chaninfo *chans,
2270         int allchans, int verb)
2271 {
2272         struct ieee80211req_chaninfo achans;
2273         uint8_t reported[IEEE80211_CHAN_BYTES];
2274         const struct ieee80211_channel *c;
2275         int i, half;
2276
2277         memset(&achans, 0, sizeof(achans));
2278         memset(reported, 0, sizeof(reported));
2279         if (!allchans) {
2280                 struct ieee80211req_chanlist active;
2281
2282                 if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0)
2283                         errx(1, "unable to get active channel list");
2284                 memset(&achans, 0, sizeof(achans));
2285                 for (i = 0; i < chans->ic_nchans; i++) {
2286                         c = &chans->ic_chans[i];
2287                         if (!isset(active.ic_channels, c->ic_ieee))
2288                                 continue;
2289                         /*
2290                          * Suppress compatible duplicates unless
2291                          * verbose.  The kernel gives us it's
2292                          * complete channel list which has separate
2293                          * entries for 11g/11b and 11a/turbo.
2294                          */
2295                         if (isset(reported, c->ic_ieee) && !verb) {
2296                                 /* XXX we assume duplicates are adjacent */
2297                                 achans.ic_chans[achans.ic_nchans-1] = *c;
2298                         } else {
2299                                 achans.ic_chans[achans.ic_nchans++] = *c;
2300                                 setbit(reported, c->ic_ieee);
2301                         }
2302                 }
2303         } else {
2304                 for (i = 0; i < chans->ic_nchans; i++) {
2305                         c = &chans->ic_chans[i];
2306                         /* suppress duplicates as above */
2307                         if (isset(reported, c->ic_ieee) && !verb) {
2308                                 /* XXX we assume duplicates are adjacent */
2309                                 achans.ic_chans[achans.ic_nchans-1] = *c;
2310                         } else {
2311                                 achans.ic_chans[achans.ic_nchans++] = *c;
2312                                 setbit(reported, c->ic_ieee);
2313                         }
2314                 }
2315         }
2316         half = achans.ic_nchans / 2;
2317         if (achans.ic_nchans % 2)
2318                 half++;
2319
2320         for (i = 0; i < achans.ic_nchans / 2; i++) {
2321                 print_chaninfo(&achans.ic_chans[i], verb);
2322                 print_chaninfo(&achans.ic_chans[half+i], verb);
2323                 printf("\n");
2324         }
2325         if (achans.ic_nchans % 2) {
2326                 print_chaninfo(&achans.ic_chans[i], verb);
2327                 printf("\n");
2328         }
2329 }
2330
2331 static void
2332 list_channels(int s, int allchans)
2333 {
2334         getchaninfo(s);
2335         print_channels(s, &chaninfo, allchans, verbose);
2336 }
2337
2338 static void
2339 print_txpow(const struct ieee80211_channel *c)
2340 {
2341         printf("Channel %3u : %u Mhz %3.1f reg %2d  ",
2342             c->ic_ieee, c->ic_freq,
2343             c->ic_maxpower/2., c->ic_maxregpower);
2344 }
2345
2346 static void
2347 print_txpow_verbose(const struct ieee80211_channel *c)
2348 {
2349         print_chaninfo(c, 1);
2350         printf("min %4.1f dBm  max %3.1f dBm  reg %2d dBm",
2351             c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
2352         /* indicate where regulatory cap limits power use */
2353         if (c->ic_maxpower > 2*c->ic_maxregpower)
2354                 printf(" <");
2355 }
2356
2357 static void
2358 list_txpow(int s)
2359 {
2360         struct ieee80211req_chaninfo achans;
2361         uint8_t reported[IEEE80211_CHAN_BYTES];
2362         struct ieee80211_channel *c, *prev;
2363         int i, half;
2364
2365         getchaninfo(s);
2366         memset(&achans, 0, sizeof(achans));
2367         memset(reported, 0, sizeof(reported));
2368         for (i = 0; i < chaninfo.ic_nchans; i++) {
2369                 c = &chaninfo.ic_chans[i];
2370                 /* suppress duplicates as above */
2371                 if (isset(reported, c->ic_ieee) && !verbose) {
2372                         /* XXX we assume duplicates are adjacent */
2373                         prev = &achans.ic_chans[achans.ic_nchans-1];
2374                         /* display highest power on channel */
2375                         if (c->ic_maxpower > prev->ic_maxpower)
2376                                 *prev = *c;
2377                 } else {
2378                         achans.ic_chans[achans.ic_nchans++] = *c;
2379                         setbit(reported, c->ic_ieee);
2380                 }
2381         }
2382         if (!verbose) {
2383                 half = achans.ic_nchans / 2;
2384                 if (achans.ic_nchans % 2)
2385                         half++;
2386
2387                 for (i = 0; i < achans.ic_nchans / 2; i++) {
2388                         print_txpow(&achans.ic_chans[i]);
2389                         print_txpow(&achans.ic_chans[half+i]);
2390                         printf("\n");
2391                 }
2392                 if (achans.ic_nchans % 2) {
2393                         print_txpow(&achans.ic_chans[i]);
2394                         printf("\n");
2395                 }
2396         } else {
2397                 for (i = 0; i < achans.ic_nchans; i++) {
2398                         print_txpow_verbose(&achans.ic_chans[i]);
2399                         printf("\n");
2400                 }
2401         }
2402 }
2403
2404 static void
2405 list_keys(int s)
2406 {
2407 }
2408
2409 #define IEEE80211_C_BITS \
2410 "\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\7FF\10TURBOP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \
2411 "\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \
2412 "\31WPA2\32BURST\33WME\34WDS\36BGSCAN\37TXFRAG"
2413
2414 static void
2415 list_capabilities(int s)
2416 {
2417         struct ieee80211req ireq;
2418         u_int32_t caps;
2419
2420         (void) memset(&ireq, 0, sizeof(ireq));
2421         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2422         ireq.i_type = IEEE80211_IOC_DRIVER_CAPS;
2423         if (ioctl(s, SIOCG80211, &ireq) < 0)
2424                 errx(1, "unable to get driver capabilities");
2425         caps = (((u_int16_t) ireq.i_val) << 16) | ((u_int16_t) ireq.i_len);
2426         printb(name, caps, IEEE80211_C_BITS);
2427         putchar('\n');
2428 }
2429
2430 static int
2431 get80211wme(int s, int param, int ac, int *val)
2432 {
2433         struct ieee80211req ireq;
2434
2435         (void) memset(&ireq, 0, sizeof(ireq));
2436         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2437         ireq.i_type = param;
2438         ireq.i_len = ac;
2439         if (ioctl(s, SIOCG80211, &ireq) < 0) {
2440                 warn("cannot get WME parameter %d, ac %d%s",
2441                     param, ac & IEEE80211_WMEPARAM_VAL,
2442                     ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : "");
2443                 return -1;
2444         }
2445         *val = ireq.i_val;
2446         return 0;
2447 }
2448
2449 static void
2450 list_wme(int s)
2451 {
2452         static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
2453         int ac, val;
2454
2455         for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
2456 again:
2457                 if (ac & IEEE80211_WMEPARAM_BSS)
2458                         printf("\t%s", "     ");
2459                 else
2460                         printf("\t%s", acnames[ac]);
2461
2462                 /* show WME BSS parameters */
2463                 if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1)
2464                         printf(" cwmin %2u", val);
2465                 if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1)
2466                         printf(" cwmax %2u", val);
2467                 if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1)
2468                         printf(" aifs %2u", val);
2469                 if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1)
2470                         printf(" txopLimit %3u", val);
2471                 if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) {
2472                         if (val)
2473                                 printf(" acm");
2474                         else if (verbose)
2475                                 printf(" -acm");
2476                 }
2477                 /* !BSS only */
2478                 if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
2479                         if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) {
2480                                 if (!val)
2481                                         printf(" -ack");
2482                                 else if (verbose)
2483                                         printf(" ack");
2484                         }
2485                 }
2486                 printf("\n");
2487                 if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
2488                         ac |= IEEE80211_WMEPARAM_BSS;
2489                         goto again;
2490                 } else
2491                         ac &= ~IEEE80211_WMEPARAM_BSS;
2492         }
2493 }
2494
2495 static void
2496 printpolicy(int policy)
2497 {
2498         switch (policy) {
2499         case IEEE80211_MACCMD_POLICY_OPEN:
2500                 printf("policy: open\n");
2501                 break;
2502         case IEEE80211_MACCMD_POLICY_ALLOW:
2503                 printf("policy: allow\n");
2504                 break;
2505         case IEEE80211_MACCMD_POLICY_DENY:
2506                 printf("policy: deny\n");
2507                 break;
2508         default:
2509                 printf("policy: unknown (%u)\n", policy);
2510                 break;
2511         }
2512 }
2513
2514 static void
2515 list_mac(int s)
2516 {
2517         struct ieee80211req ireq;
2518         struct ieee80211req_maclist *acllist;
2519         int i, nacls, policy, len;
2520         uint8_t *data;
2521         char c;
2522
2523         (void) memset(&ireq, 0, sizeof(ireq));
2524         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
2525         ireq.i_type = IEEE80211_IOC_MACCMD;
2526         ireq.i_val = IEEE80211_MACCMD_POLICY;
2527         if (ioctl(s, SIOCG80211, &ireq) < 0) {
2528                 if (errno == EINVAL) {
2529                         printf("No acl policy loaded\n");
2530                         return;
2531                 }
2532                 err(1, "unable to get mac policy");
2533         }
2534         policy = ireq.i_val;
2535         if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
2536                 c = '*';
2537         } else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
2538                 c = '+';
2539         } else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
2540                 c = '-';
2541         } else {
2542                 printf("policy: unknown (%u)\n", policy);
2543                 c = '?';
2544         }
2545         if (verbose || c == '?')
2546                 printpolicy(policy);
2547
2548         ireq.i_val = IEEE80211_MACCMD_LIST;
2549         ireq.i_len = 0;
2550         if (ioctl(s, SIOCG80211, &ireq) < 0)
2551                 err(1, "unable to get mac acl list size");
2552         if (ireq.i_len == 0) {          /* NB: no acls */
2553                 if (!(verbose || c == '?'))
2554                         printpolicy(policy);
2555                 return;
2556         }
2557         len = ireq.i_len;
2558
2559         data = malloc(len);
2560         if (data == NULL)
2561                 err(1, "out of memory for acl list");
2562
2563         ireq.i_data = data;
2564         if (ioctl(s, SIOCG80211, &ireq) < 0)
2565                 err(1, "unable to get mac acl list");
2566         nacls = len / sizeof(*acllist);
2567         acllist = (struct ieee80211req_maclist *) data;
2568         for (i = 0; i < nacls; i++)
2569                 printf("%c%s\n", c, ether_ntoa(
2570                         (const struct ether_addr *) acllist[i].ml_macaddr));
2571         free(data);
2572 }
2573
2574 static
2575 DECL_CMD_FUNC(set80211list, arg, d)
2576 {
2577 #define iseq(a,b)       (strncasecmp(a,b,sizeof(b)-1) == 0)
2578
2579         LINE_INIT('\t');
2580
2581         if (iseq(arg, "sta"))
2582                 list_stations(s);
2583         else if (iseq(arg, "scan") || iseq(arg, "ap"))
2584                 list_scan(s);
2585         else if (iseq(arg, "chan") || iseq(arg, "freq"))
2586                 list_channels(s, 1);
2587         else if (iseq(arg, "active"))
2588                 list_channels(s, 0);
2589         else if (iseq(arg, "keys"))
2590                 list_keys(s);
2591         else if (iseq(arg, "caps"))
2592                 list_capabilities(s);
2593         else if (iseq(arg, "wme"))
2594                 list_wme(s);
2595         else if (iseq(arg, "mac"))
2596                 list_mac(s);
2597         else if (iseq(arg, "txpow"))
2598                 list_txpow(s);
2599         else
2600                 errx(1, "Don't know how to list %s for %s", arg, name);
2601 #undef iseq
2602 }
2603
2604 static enum ieee80211_opmode
2605 get80211opmode(int s)
2606 {
2607         struct ifmediareq ifmr;
2608
2609         (void) memset(&ifmr, 0, sizeof(ifmr));
2610         (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
2611
2612         if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
2613                 if (ifmr.ifm_current & IFM_IEEE80211_ADHOC)
2614                         return IEEE80211_M_IBSS;        /* XXX ahdemo */
2615                 if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
2616                         return IEEE80211_M_HOSTAP;
2617                 if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
2618                         return IEEE80211_M_MONITOR;
2619         }
2620         return IEEE80211_M_STA;
2621 }
2622
2623 #if 0
2624 static void
2625 printcipher(int s, struct ieee80211req *ireq, int keylenop)
2626 {
2627         switch (ireq->i_val) {
2628         case IEEE80211_CIPHER_WEP:
2629                 ireq->i_type = keylenop;
2630                 if (ioctl(s, SIOCG80211, ireq) != -1)
2631                         printf("WEP-%s", 
2632                             ireq->i_len <= 5 ? "40" :
2633                             ireq->i_len <= 13 ? "104" : "128");
2634                 else
2635                         printf("WEP");
2636                 break;
2637         case IEEE80211_CIPHER_TKIP:
2638                 printf("TKIP");
2639                 break;
2640         case IEEE80211_CIPHER_AES_OCB:
2641                 printf("AES-OCB");
2642                 break;
2643         case IEEE80211_CIPHER_AES_CCM:
2644                 printf("AES-CCM");
2645                 break;
2646         case IEEE80211_CIPHER_CKIP:
2647                 printf("CKIP");
2648                 break;
2649         case IEEE80211_CIPHER_NONE:
2650                 printf("NONE");
2651                 break;
2652         default:
2653                 printf("UNKNOWN (0x%x)", ireq->i_val);
2654                 break;
2655         }
2656 }
2657 #endif
2658
2659 static void
2660 printkey(const struct ieee80211req_key *ik)
2661 {
2662         static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
2663         int keylen = ik->ik_keylen;
2664         int printcontents;
2665
2666         printcontents = printkeys &&
2667                 (memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
2668         if (printcontents)
2669                 LINE_BREAK();
2670         switch (ik->ik_type) {
2671         case IEEE80211_CIPHER_WEP:
2672                 /* compatibility */
2673                 LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
2674                     keylen <= 5 ? "40-bit" :
2675                     keylen <= 13 ? "104-bit" : "128-bit");
2676                 break;
2677         case IEEE80211_CIPHER_TKIP:
2678                 if (keylen > 128/8)
2679                         keylen -= 128/8;        /* ignore MIC for now */
2680                 LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2681                 break;
2682         case IEEE80211_CIPHER_AES_OCB:
2683                 LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2684                 break;
2685         case IEEE80211_CIPHER_AES_CCM:
2686                 LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2687                 break;
2688         case IEEE80211_CIPHER_CKIP:
2689                 LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2690                 break;
2691         case IEEE80211_CIPHER_NONE:
2692                 LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2693                 break;
2694         default:
2695                 LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
2696                         ik->ik_type, ik->ik_keyix+1, 8*keylen);
2697                 break;
2698         }
2699         if (printcontents) {
2700                 int i;
2701
2702                 printf(" <");
2703                 for (i = 0; i < keylen; i++)
2704                         printf("%02x", ik->ik_keydata[i]);
2705                 printf(">");
2706                 if (ik->ik_type != IEEE80211_CIPHER_WEP &&
2707                     (ik->ik_keyrsc != 0 || verbose))
2708                         printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
2709                 if (ik->ik_type != IEEE80211_CIPHER_WEP &&
2710                     (ik->ik_keytsc != 0 || verbose))
2711                         printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
2712                 if (ik->ik_flags != 0 && verbose) {
2713                         const char *sep = " ";
2714
2715                         if (ik->ik_flags & IEEE80211_KEY_XMIT)
2716                                 printf("%stx", sep), sep = "+";
2717                         if (ik->ik_flags & IEEE80211_KEY_RECV)
2718                                 printf("%srx", sep), sep = "+";
2719                         if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
2720                                 printf("%sdef", sep), sep = "+";
2721                 }
2722                 LINE_BREAK();
2723         }
2724 }
2725
2726 static void
2727 printrate(const char *tag, int v, int defrate, int defmcs)
2728 {
2729         if (v == 11)
2730                 LINE_CHECK("%s 5.5", tag);
2731         else if (v & 0x80) {
2732                 if (v != defmcs)
2733                         LINE_CHECK("%s %d", tag, v &~ 0x80);
2734         } else {
2735                 if (v != defrate)
2736                         LINE_CHECK("%s %d", tag, v/2);
2737         }
2738 }
2739
2740 static int
2741 getssid(int s, int ix, void *data, size_t len, int *plen)
2742 {
2743         struct ieee80211req ireq;
2744
2745         (void) memset(&ireq, 0, sizeof(ireq));
2746         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2747         ireq.i_type = IEEE80211_IOC_SSID;
2748         ireq.i_val = ix;
2749         ireq.i_data = data;
2750         ireq.i_len = len;
2751         if (ioctl(s, SIOCG80211, &ireq) < 0)
2752                 return -1;
2753         *plen = ireq.i_len;
2754         return 0;
2755 }
2756
2757 static void
2758 printrssi(const char *tag, int rssi)
2759 {
2760         if (rssi & 1)
2761                 LINE_CHECK("%s %u.5", tag, rssi/2);
2762         else
2763                 LINE_CHECK("%s %u", tag, rssi/2);
2764 }
2765
2766 static void
2767 ieee80211_status(int s)
2768 {
2769         static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
2770         enum ieee80211_opmode opmode = get80211opmode(s);
2771         int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode;
2772         uint8_t data[32];
2773         const struct ieee80211_channel *c;
2774
2775         if (getssid(s, -1, data, sizeof(data), &len) < 0) {
2776                 /* If we can't get the SSID, this isn't an 802.11 device. */
2777                 return;
2778         }
2779
2780         /*
2781          * Invalidate cached state so printing status for multiple
2782          * if's doesn't reuse the first interfaces' cached state.
2783          */
2784         gotcurchan = 0;
2785         gothtconf = 0;
2786
2787         if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
2788                 num = 0;
2789         printf("\tssid ");
2790         if (num > 1) {
2791                 for (i = 0; i < num; i++) {
2792                         if (getssid(s, i, data, sizeof(data), &len) >= 0 && len > 0) {
2793                                 printf(" %d:", i + 1);
2794                                 print_string(data, len);
2795                         }
2796                 }
2797         } else
2798                 print_string(data, len);
2799
2800         c = getcurchan(s);
2801         if (c->ic_freq != IEEE80211_CHAN_ANY) {
2802                 char buf[14];
2803                 printf(" channel %d (%u Mhz%s)", c->ic_ieee, c->ic_freq,
2804                         get_chaninfo(c, 1, buf, sizeof(buf)));
2805         } else if (verbose)
2806                 printf(" channel UNDEF");
2807
2808         if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 &&
2809             (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
2810                 printf(" bssid %s", ether_ntoa((struct ether_addr *)data));
2811
2812         if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) {
2813                 printf("\n\tstationname ");
2814                 print_string(data, len);
2815         }
2816
2817         spacer = ' ';           /* force first break */
2818         LINE_BREAK();
2819
2820         wpa = 0;
2821         if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) {
2822                 switch (val) {
2823                 case IEEE80211_AUTH_NONE:
2824                         LINE_CHECK("authmode NONE");
2825                         break;
2826                 case IEEE80211_AUTH_OPEN:
2827                         LINE_CHECK("authmode OPEN");
2828                         break;
2829                 case IEEE80211_AUTH_SHARED:
2830                         LINE_CHECK("authmode SHARED");
2831                         break;
2832                 case IEEE80211_AUTH_8021X:
2833                         LINE_CHECK("authmode 802.1x");
2834                         break;
2835                 case IEEE80211_AUTH_WPA:
2836                         if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0)
2837                                 wpa = 1;        /* default to WPA1 */
2838                         switch (wpa) {
2839                         case 2:
2840                                 LINE_CHECK("authmode WPA2/802.11i");
2841                                 break;
2842                         case 3:
2843                                 LINE_CHECK("authmode WPA1+WPA2/802.11i");
2844                                 break;
2845                         default:
2846                                 LINE_CHECK("authmode WPA");
2847                                 break;
2848                         }
2849                         break;
2850                 case IEEE80211_AUTH_AUTO:
2851                         LINE_CHECK("authmode AUTO");
2852                         break;
2853                 default:
2854                         LINE_CHECK("authmode UNKNOWN (0x%x)", val);
2855                         break;
2856                 }
2857         }
2858
2859         if (wpa || verbose) {
2860                 if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) {
2861                         if (val)
2862                                 LINE_CHECK("countermeasures");
2863                         else if (verbose)
2864                                 LINE_CHECK("-countermeasures");
2865                 }
2866         }
2867
2868         if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 &&
2869             wepmode != IEEE80211_WEP_NOSUP) {
2870                 int firstkey;
2871
2872                 switch (wepmode) {
2873                 case IEEE80211_WEP_OFF:
2874                         LINE_CHECK("privacy OFF");
2875                         break;
2876                 case IEEE80211_WEP_ON:
2877                         LINE_CHECK("privacy ON");
2878                         break;
2879                 case IEEE80211_WEP_MIXED:
2880                         LINE_CHECK("privacy MIXED");
2881                         break;
2882                 default:
2883                         LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
2884                         break;
2885                 }
2886
2887                 /*
2888                  * If we get here then we've got WEP support so we need
2889                  * to print WEP status.
2890                  */
2891
2892                 if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) {
2893                         warn("WEP support, but no tx key!");
2894                         goto end;
2895                 }
2896                 if (val != -1)
2897                         LINE_CHECK("deftxkey %d", val+1);
2898                 else if (wepmode != IEEE80211_WEP_OFF || verbose)
2899                         LINE_CHECK("deftxkey UNDEF");
2900
2901                 if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) {
2902                         warn("WEP support, but no NUMWEPKEYS support!");
2903                         goto end;
2904                 }
2905
2906                 firstkey = 1;
2907                 for (i = 0; i < num; i++) {
2908                         struct ieee80211req_key ik;
2909
2910                         memset(&ik, 0, sizeof(ik));
2911                         ik.ik_keyix = i;
2912                         if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) {
2913                                 warn("WEP support, but can get keys!");
2914                                 goto end;
2915                         }
2916                         if (ik.ik_keylen != 0) {
2917                                 if (verbose)
2918                                         LINE_BREAK();
2919                                 printkey(&ik);
2920                                 firstkey = 0;
2921                         }
2922                 }
2923 end:
2924                 ;
2925         }
2926
2927         if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 &&
2928             val != IEEE80211_POWERSAVE_NOSUP ) {
2929                 if (val != IEEE80211_POWERSAVE_OFF || verbose) {
2930                         switch (val) {
2931                         case IEEE80211_POWERSAVE_OFF:
2932                                 LINE_CHECK("powersavemode OFF");
2933                                 break;
2934                         case IEEE80211_POWERSAVE_CAM:
2935                                 LINE_CHECK("powersavemode CAM");
2936                                 break;
2937                         case IEEE80211_POWERSAVE_PSP:
2938                                 LINE_CHECK("powersavemode PSP");
2939                                 break;
2940                         case IEEE80211_POWERSAVE_PSP_CAM:
2941                                 LINE_CHECK("powersavemode PSP-CAM");
2942                                 break;
2943                         }
2944                         if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1)
2945                                 LINE_CHECK("powersavesleep %d", val);
2946                 }
2947         }
2948
2949         if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) {
2950                 if (val & 1)
2951                         LINE_CHECK("txpower %d.5", val/2);
2952                 else
2953                         LINE_CHECK("txpower %d", val/2);
2954         }
2955         if (verbose) {
2956                 if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1)
2957                         LINE_CHECK("txpowmax %.1f", val/2.);
2958         }
2959
2960         if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) {
2961                 if (val != IEEE80211_RTS_MAX || verbose)
2962                         LINE_CHECK("rtsthreshold %d", val);
2963         }
2964
2965         if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) {
2966                 if (val != IEEE80211_FRAG_MAX || verbose)
2967                         LINE_CHECK("fragthreshold %d", val);
2968         }
2969         if (opmode == IEEE80211_M_STA || verbose) {
2970                 if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) {
2971                         if (val != IEEE80211_HWBMISS_MAX || verbose)
2972                                 LINE_CHECK("bmiss %d", val);
2973                 }
2974         }
2975
2976         if (get80211val(s, IEEE80211_IOC_MCAST_RATE, &val) != -1)
2977                 printrate("mcastrate", val, 2*1, 0/*XXX*/);
2978
2979         bgscaninterval = -1;
2980         (void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval);
2981
2982         if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) {
2983                 if (val != bgscaninterval || verbose)
2984                         LINE_CHECK("scanvalid %u", val);
2985         }
2986
2987         bgscan = 0;
2988         if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) {
2989                 if (bgscan)
2990                         LINE_CHECK("bgscan");
2991                 else if (verbose)
2992                         LINE_CHECK("-bgscan");
2993         }
2994         if (bgscan || verbose) {
2995                 if (bgscaninterval != -1)
2996                         LINE_CHECK("bgscanintvl %u", bgscaninterval);
2997                 if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1)
2998                         LINE_CHECK("bgscanidle %u", val);
2999                 if (IEEE80211_IS_CHAN_A(c) || verbose) {
3000                         if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11A, &val) != -1)
3001                                 printrssi("roam:rssi11a", val);
3002                         if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11A, &val) != -1)
3003                                 printrate("roam:rate11a", val, -1, -1);
3004                 }
3005                 if (IEEE80211_IS_CHAN_B(c) || verbose) {
3006                         if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11B, &val) != -1)
3007                                 printrssi("roam:rssi11b", val);
3008                         if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11B, &val) != -1)
3009                                 printrate("roam:rate11b", val, -1, -1);
3010                 }
3011                 if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
3012                         if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11G, &val) != -1)
3013                                 printrssi("roam:rssi11g", val);
3014                         if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11G, &val) != -1)
3015                                 printrate("roam:rate11g", val, -1, -1);
3016                 }
3017         }
3018
3019         if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
3020                 if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) {
3021                         if (val)
3022                                 LINE_CHECK("pureg");
3023                         else if (verbose)
3024                                 LINE_CHECK("-pureg");
3025                 }
3026                 if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) {
3027                         switch (val) {
3028                         case IEEE80211_PROTMODE_OFF:
3029                                 LINE_CHECK("protmode OFF");
3030                                 break;
3031                         case IEEE80211_PROTMODE_CTS:
3032                                 LINE_CHECK("protmode CTS");
3033                                 break;
3034                         case IEEE80211_PROTMODE_RTSCTS:
3035                                 LINE_CHECK("protmode RTSCTS");
3036                                 break;
3037                         default:
3038                                 LINE_CHECK("protmode UNKNOWN (0x%x)", val);
3039                                 break;
3040                         }
3041                 }
3042         }
3043
3044         if (IEEE80211_IS_CHAN_HT(c) || verbose) {
3045                 gethtconf(s);
3046                 switch (htconf & 3) {
3047                 case 0:
3048                 case 2:
3049                         LINE_CHECK("-ht");
3050                         break;
3051                 case 1:
3052                         LINE_CHECK("ht20");
3053                         break;
3054                 case 3:
3055                         if (verbose)
3056                                 LINE_CHECK("ht");
3057                         break;
3058                 }
3059                 if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) {
3060                         if (!val)
3061                                 LINE_CHECK("-htcompat");
3062                         else if (verbose)
3063                                 LINE_CHECK("htcompat");
3064                 }
3065                 if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) {
3066                         switch (val) {
3067                         case 0:
3068                                 LINE_CHECK("-ampdu");
3069                                 break;
3070                         case 1:
3071                                 LINE_CHECK("ampdutx -ampdurx");
3072                                 break;
3073                         case 2:
3074                                 LINE_CHECK("-ampdutx ampdurx");
3075                                 break;
3076                         case 3:
3077                                 if (verbose)
3078                                         LINE_CHECK("ampdu");
3079                                 break;
3080                         }
3081                 }
3082                 if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) {
3083                         switch (val) {
3084                         case IEEE80211_HTCAP_MAXRXAMPDU_8K:
3085                                 LINE_CHECK("ampdulimit 8k");
3086                                 break;
3087                         case IEEE80211_HTCAP_MAXRXAMPDU_16K:
3088                                 LINE_CHECK("ampdulimit 16k");
3089                                 break;
3090                         case IEEE80211_HTCAP_MAXRXAMPDU_32K:
3091                                 LINE_CHECK("ampdulimit 32k");
3092                                 break;
3093                         case IEEE80211_HTCAP_MAXRXAMPDU_64K:
3094                                 LINE_CHECK("ampdulimit 64k");
3095                                 break;
3096                         }
3097                 }
3098                 if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) {
3099                         switch (val) {
3100                         case IEEE80211_HTCAP_MPDUDENSITY_NA:
3101                                 if (verbose)
3102                                         LINE_CHECK("ampdudensity -");
3103                                 break;
3104                         case IEEE80211_HTCAP_MPDUDENSITY_025:
3105                                 LINE_CHECK("ampdudensity .25");
3106                                 break;
3107                         case IEEE80211_HTCAP_MPDUDENSITY_05:
3108                                 LINE_CHECK("ampdudensity .5");
3109                                 break;
3110                         case IEEE80211_HTCAP_MPDUDENSITY_1:
3111                                 LINE_CHECK("ampdudensity 1");
3112                                 break;
3113                         case IEEE80211_HTCAP_MPDUDENSITY_2:
3114                                 LINE_CHECK("ampdudensity 2");
3115                                 break;
3116                         case IEEE80211_HTCAP_MPDUDENSITY_4:
3117                                 LINE_CHECK("ampdudensity 4");
3118                                 break;
3119                         case IEEE80211_HTCAP_MPDUDENSITY_8:
3120                                 LINE_CHECK("ampdudensity 8");
3121                                 break;
3122                         case IEEE80211_HTCAP_MPDUDENSITY_16:
3123                                 LINE_CHECK("ampdudensity 16");
3124                                 break;
3125                         }
3126                 }
3127                 if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) {
3128                         switch (val) {
3129                         case 0:
3130                                 LINE_CHECK("-amsdu");
3131                                 break;
3132                         case 1:
3133                                 LINE_CHECK("amsdutx -amsdurx");
3134                                 break;
3135                         case 2:
3136                                 LINE_CHECK("-amsdutx amsdurx");
3137                                 break;
3138                         case 3:
3139                                 if (verbose)
3140                                         LINE_CHECK("amsdu");
3141                                 break;
3142                         }
3143                 }
3144                 /* XXX amsdu limit */
3145                 /* XXX 20/40 */
3146                 if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) {
3147                         if (val)
3148                                 LINE_CHECK("shortgi");
3149                         else if (verbose)
3150                                 LINE_CHECK("-shortgi");
3151                 }
3152                 if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) {
3153                         if (val == IEEE80211_PROTMODE_OFF)
3154                                 LINE_CHECK("htprotmode OFF");
3155                         else if (val != IEEE80211_PROTMODE_RTSCTS)
3156                                 LINE_CHECK("htprotmode UNKNOWN (0x%x)", val);
3157                         else if (verbose)
3158                                 LINE_CHECK("htprotmode RTSCTS");
3159                 }
3160                 if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) {
3161                         if (val)
3162                                 LINE_CHECK("puren");
3163                         else if (verbose)
3164                                 LINE_CHECK("-puren");
3165                 }
3166         }
3167
3168         if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) {
3169                 if (wme)
3170                         LINE_CHECK("wme");
3171                 else if (verbose)
3172                         LINE_CHECK("-wme");
3173         } else
3174                 wme = 0;
3175
3176         if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) {
3177                 if (val)
3178                         LINE_CHECK("burst");
3179                 else if (verbose)
3180                         LINE_CHECK("-burst");
3181         }
3182
3183         if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) {
3184                 if (val)
3185                         LINE_CHECK("ff");
3186                 else if (verbose)
3187                         LINE_CHECK("-ff");
3188         }
3189         if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) {
3190                 if (val)
3191                         LINE_CHECK("dturbo");
3192                 else if (verbose)
3193                         LINE_CHECK("-dturbo");
3194         }
3195
3196         if (opmode == IEEE80211_M_HOSTAP) {
3197                 if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) {
3198                         if (val)
3199                                 LINE_CHECK("hidessid");
3200                         else if (verbose)
3201                                 LINE_CHECK("-hidessid");
3202                 }
3203                 if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) {
3204                         if (!val)
3205                                 LINE_CHECK("-apbridge");
3206                         else if (verbose)
3207                                 LINE_CHECK("apbridge");
3208                 }
3209                 if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1)
3210                         LINE_CHECK("dtimperiod %u", val);
3211
3212                 if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) {
3213                         if (!val)
3214                                 LINE_CHECK("-doth");
3215                         else if (verbose)
3216                                 LINE_CHECK("doth");
3217                 }
3218                 if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) {
3219                         if (!val)
3220                                 LINE_CHECK("-inact");
3221                         else if (verbose)
3222                                 LINE_CHECK("inact");
3223                 }
3224         } else {
3225                 if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) {
3226                         if (val != IEEE80211_ROAMING_AUTO || verbose) {
3227                                 switch (val) {
3228                                 case IEEE80211_ROAMING_DEVICE:
3229                                         LINE_CHECK("roaming DEVICE");
3230                                         break;
3231                                 case IEEE80211_ROAMING_AUTO:
3232                                         LINE_CHECK("roaming AUTO");
3233                                         break;
3234                                 case IEEE80211_ROAMING_MANUAL:
3235                                         LINE_CHECK("roaming MANUAL");
3236                                         break;
3237                                 default:
3238                                         LINE_CHECK("roaming UNKNOWN (0x%x)",
3239                                                 val);
3240                                         break;
3241                                 }
3242                         }
3243                 }
3244         }
3245         if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) {
3246                 /* XXX default define not visible */
3247                 if (val != 100 || verbose)
3248                         LINE_CHECK("bintval %u", val);
3249         }
3250
3251         if (wme && verbose) {
3252                 LINE_BREAK();
3253                 list_wme(s);
3254         }
3255         LINE_BREAK();
3256 }
3257
3258 static int
3259 get80211(int s, int type, void *data, int len)
3260 {
3261         struct ieee80211req ireq;
3262
3263         (void) memset(&ireq, 0, sizeof(ireq));
3264         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3265         ireq.i_type = type;
3266         ireq.i_data = data;
3267         ireq.i_len = len;
3268         return ioctl(s, SIOCG80211, &ireq);
3269 }
3270
3271 static int
3272 get80211len(int s, int type, void *data, int len, int *plen)
3273 {
3274         struct ieee80211req ireq;
3275
3276         (void) memset(&ireq, 0, sizeof(ireq));
3277         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3278         ireq.i_type = type;
3279         ireq.i_len = len;
3280         ireq.i_data = data;
3281         if (ioctl(s, SIOCG80211, &ireq) < 0)
3282                 return -1;
3283         *plen = ireq.i_len;
3284         return 0;
3285 }
3286
3287 static int
3288 get80211val(int s, int type, int *val)
3289 {
3290         struct ieee80211req ireq;
3291
3292         (void) memset(&ireq, 0, sizeof(ireq));
3293         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3294         ireq.i_type = type;
3295         if (ioctl(s, SIOCG80211, &ireq) < 0)
3296                 return -1;
3297         *val = ireq.i_val;
3298         return 0;
3299 }
3300
3301 static void
3302 set80211(int s, int type, int val, int len, void *data)
3303 {
3304         struct ieee80211req     ireq;
3305
3306         (void) memset(&ireq, 0, sizeof(ireq));
3307         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3308         ireq.i_type = type;
3309         ireq.i_val = val;
3310         ireq.i_len = len;
3311         ireq.i_data = data;
3312         if (ioctl(s, SIOCS80211, &ireq) < 0)
3313                 err(1, "SIOCS80211");
3314 }
3315
3316 static const char *
3317 get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
3318 {
3319         int len;
3320         int hexstr;
3321         u_int8_t *p;
3322
3323         len = *lenp;
3324         p = buf;
3325         hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
3326         if (hexstr)
3327                 val += 2;
3328         for (;;) {
3329                 if (*val == '\0')
3330                         break;
3331                 if (sep != NULL && strchr(sep, *val) != NULL) {
3332                         val++;
3333                         break;
3334                 }
3335                 if (hexstr) {
3336                         if (!isxdigit((u_char)val[0])) {
3337                                 warnx("bad hexadecimal digits");
3338                                 return NULL;
3339                         }
3340                         if (!isxdigit((u_char)val[1])) {
3341                                 warnx("odd count hexadecimal digits");
3342                                 return NULL;
3343                         }
3344                 }
3345                 if (p >= buf + len) {
3346                         if (hexstr)
3347                                 warnx("hexadecimal digits too long");
3348                         else
3349                                 warnx("string too long");
3350                         return NULL;
3351                 }
3352                 if (hexstr) {
3353 #define tohex(x)        (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
3354                         *p++ = (tohex((u_char)val[0]) << 4) |
3355                             tohex((u_char)val[1]);
3356 #undef tohex
3357                         val += 2;
3358                 } else
3359                         *p++ = *val++;
3360         }
3361         len = p - buf;
3362         /* The string "-" is treated as the empty string. */
3363         if (!hexstr && len == 1 && buf[0] == '-') {
3364                 len = 0;
3365                 memset(buf, 0, *lenp);
3366         } else if (len < *lenp)
3367                 memset(p, 0, *lenp - len);
3368         *lenp = len;
3369         return val;
3370 }
3371
3372 static void
3373 print_string(const u_int8_t *buf, int len)
3374 {
3375         int i;
3376         int hasspc;
3377
3378         i = 0;
3379         hasspc = 0;
3380         for (; i < len; i++) {
3381                 if (!isprint(buf[i]) && buf[i] != '\0')
3382                         break;
3383                 if (isspace(buf[i]))
3384                         hasspc++;
3385         }
3386         if (i == len) {
3387                 if (hasspc || len == 0 || buf[0] == '\0')
3388                         printf("\"%.*s\"", len, buf);
3389                 else
3390                         printf("%.*s", len, buf);
3391         } else {
3392                 printf("0x");
3393                 for (i = 0; i < len; i++)
3394                         printf("%02x", buf[i]);
3395         }
3396 }
3397
3398 static struct cmd ieee80211_cmds[] = {
3399         DEF_CMD_ARG("ssid",             set80211ssid),
3400         DEF_CMD_ARG("nwid",             set80211ssid),
3401         DEF_CMD_ARG("stationname",      set80211stationname),
3402         DEF_CMD_ARG("station",          set80211stationname),   /* BSD/OS */
3403         DEF_CMD_ARG("channel",          set80211channel),
3404         DEF_CMD_ARG("authmode",         set80211authmode),
3405         DEF_CMD_ARG("powersavemode",    set80211powersavemode),
3406         DEF_CMD("powersave",    1,      set80211powersave),
3407         DEF_CMD("-powersave",   0,      set80211powersave),
3408         DEF_CMD_ARG("powersavesleep",   set80211powersavesleep),
3409         DEF_CMD_ARG("wepmode",          set80211wepmode),
3410         DEF_CMD("wep",          1,      set80211wep),
3411         DEF_CMD("-wep",         0,      set80211wep),
3412         DEF_CMD_ARG("deftxkey",         set80211weptxkey),
3413         DEF_CMD_ARG("weptxkey",         set80211weptxkey),
3414         DEF_CMD_ARG("wepkey",           set80211wepkey),
3415         DEF_CMD_ARG("nwkey",            set80211nwkey),         /* NetBSD */
3416         DEF_CMD("-nwkey",       0,      set80211wep),           /* NetBSD */
3417         DEF_CMD_ARG("rtsthreshold",     set80211rtsthreshold),
3418         DEF_CMD_ARG("protmode",         set80211protmode),
3419         DEF_CMD_ARG("txpower",          set80211txpower),
3420         DEF_CMD_ARG("roaming",          set80211roaming),
3421         DEF_CMD("wme",          1,      set80211wme),
3422         DEF_CMD("-wme",         0,      set80211wme),
3423         DEF_CMD("hidessid",     1,      set80211hidessid),
3424         DEF_CMD("-hidessid",    0,      set80211hidessid),
3425         DEF_CMD("apbridge",     1,      set80211apbridge),
3426         DEF_CMD("-apbridge",    0,      set80211apbridge),
3427         DEF_CMD_ARG("chanlist",         set80211chanlist),
3428         DEF_CMD_ARG("bssid",            set80211bssid),
3429         DEF_CMD_ARG("ap",               set80211bssid),
3430         DEF_CMD("scan", 0,              set80211scan),
3431         DEF_CMD_ARG("list",             set80211list),
3432         DEF_CMD_ARG2("cwmin",           set80211cwmin),
3433         DEF_CMD_ARG2("cwmax",           set80211cwmax),
3434         DEF_CMD_ARG2("aifs",            set80211aifs),
3435         DEF_CMD_ARG2("txoplimit",       set80211txoplimit),
3436         DEF_CMD_ARG("acm",              set80211acm),
3437         DEF_CMD_ARG("-acm",             set80211noacm),
3438         DEF_CMD_ARG("ack",              set80211ackpolicy),
3439         DEF_CMD_ARG("-ack",             set80211noackpolicy),
3440         DEF_CMD_ARG2("bss:cwmin",       set80211bsscwmin),
3441         DEF_CMD_ARG2("bss:cwmax",       set80211bsscwmax),
3442         DEF_CMD_ARG2("bss:aifs",        set80211bssaifs),
3443         DEF_CMD_ARG2("bss:txoplimit",   set80211bsstxoplimit),
3444         DEF_CMD_ARG("dtimperiod",       set80211dtimperiod),
3445         DEF_CMD_ARG("bintval",          set80211bintval),
3446         DEF_CMD("mac:open",     IEEE80211_MACCMD_POLICY_OPEN,   set80211maccmd),
3447         DEF_CMD("mac:allow",    IEEE80211_MACCMD_POLICY_ALLOW,  set80211maccmd),
3448         DEF_CMD("mac:deny",     IEEE80211_MACCMD_POLICY_DENY,   set80211maccmd),
3449         DEF_CMD("mac:flush",    IEEE80211_MACCMD_FLUSH,         set80211maccmd),
3450         DEF_CMD("mac:detach",   IEEE80211_MACCMD_DETACH,        set80211maccmd),
3451         DEF_CMD_ARG("mac:add",          set80211addmac),
3452         DEF_CMD_ARG("mac:del",          set80211delmac),
3453         DEF_CMD_ARG("mac:kick",         set80211kickmac),
3454         DEF_CMD("pureg",        1,      set80211pureg),
3455         DEF_CMD("-pureg",       0,      set80211pureg),
3456         DEF_CMD("ff",           1,      set80211fastframes),
3457         DEF_CMD("-ff",          0,      set80211fastframes),
3458         DEF_CMD("dturbo",       1,      set80211dturbo),
3459         DEF_CMD("-dturbo",      0,      set80211dturbo),
3460         DEF_CMD("bgscan",       1,      set80211bgscan),
3461         DEF_CMD("-bgscan",      0,      set80211bgscan),
3462         DEF_CMD_ARG("bgscanidle",       set80211bgscanidle),
3463         DEF_CMD_ARG("bgscanintvl",      set80211bgscanintvl),
3464         DEF_CMD_ARG("scanvalid",        set80211scanvalid),
3465         DEF_CMD_ARG("roam:rssi11a",     set80211roamrssi11a),
3466         DEF_CMD_ARG("roam:rssi11b",     set80211roamrssi11b),
3467         DEF_CMD_ARG("roam:rssi11g",     set80211roamrssi11g),
3468         DEF_CMD_ARG("roam:rate11a",     set80211roamrate11a),
3469         DEF_CMD_ARG("roam:rate11b",     set80211roamrate11b),
3470         DEF_CMD_ARG("roam:rate11g",     set80211roamrate11g),
3471         DEF_CMD_ARG("mcastrate",        set80211mcastrate),
3472         DEF_CMD_ARG("fragthreshold",    set80211fragthreshold),
3473         DEF_CMD("burst",        1,      set80211burst),
3474         DEF_CMD("-burst",       0,      set80211burst),
3475         DEF_CMD_ARG("bmiss",            set80211bmissthreshold),
3476         DEF_CMD_ARG("bmissthreshold",   set80211bmissthreshold),
3477         DEF_CMD("shortgi",      1,      set80211shortgi),
3478         DEF_CMD("-shortgi",     0,      set80211shortgi),
3479         DEF_CMD("ampdurx",      2,      set80211ampdu),
3480         DEF_CMD("-ampdurx",     -2,     set80211ampdu),
3481         DEF_CMD("ampdutx",      1,      set80211ampdu),
3482         DEF_CMD("-ampdutx",     -1,     set80211ampdu),
3483         DEF_CMD("ampdu",        3,      set80211ampdu),         /* NB: tx+rx */
3484         DEF_CMD("-ampdu",       -3,     set80211ampdu),
3485         DEF_CMD_ARG("ampdulimit",       set80211ampdulimit),
3486         DEF_CMD_ARG("ampdudensity",     set80211ampdudensity),
3487         DEF_CMD("amsdurx",      2,      set80211amsdu),
3488         DEF_CMD("-amsdurx",     -2,     set80211amsdu),
3489         DEF_CMD("amsdutx",      1,      set80211amsdu),
3490         DEF_CMD("-amsdutx",     -1,     set80211amsdu),
3491         DEF_CMD("amsdu",        3,      set80211amsdu),         /* NB: tx+rx */
3492         DEF_CMD("-amsdu",       -3,     set80211amsdu),
3493         DEF_CMD_ARG("amsdulimit",       set80211amsdulimit),
3494         DEF_CMD("puren",        1,      set80211puren),
3495         DEF_CMD("-puren",       0,      set80211puren),
3496         DEF_CMD("doth",         1,      set80211doth),
3497         DEF_CMD("-doth",        0,      set80211doth),
3498         DEF_CMD("htcompat",     1,      set80211htcompat),
3499         DEF_CMD("-htcompat",    0,      set80211htcompat),
3500         DEF_CMD("inact",        1,      set80211inact),
3501         DEF_CMD("-inact",       0,      set80211inact),
3502         DEF_CMD_ARG("htprotmode",       set80211htprotmode),
3503         DEF_CMD("ht20",         1,      set80211htconf),
3504         DEF_CMD("-ht20",        0,      set80211htconf),
3505         DEF_CMD("ht40",         3,      set80211htconf),        /* NB: 20+40 */
3506         DEF_CMD("-ht40",        0,      set80211htconf),
3507         DEF_CMD("ht",           3,      set80211htconf),        /* NB: 20+40 */
3508         DEF_CMD("-ht",          0,      set80211htconf),
3509 };
3510 static struct afswtch af_ieee80211 = {
3511         .af_name        = "af_ieee80211",
3512         .af_af          = AF_UNSPEC,
3513         .af_other_status = ieee80211_status,
3514 };
3515
3516 static __constructor void
3517 ieee80211_ctor(void)
3518 {
3519 #define N(a)    (sizeof(a) / sizeof(a[0]))
3520         int i;
3521
3522         for (i = 0; i < N(ieee80211_cmds);  i++)
3523                 cmd_register(&ieee80211_cmds[i]);
3524         af_register(&af_ieee80211);
3525 #undef N
3526 }