]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/ifconfig/ifieee80211.c
This commit was generated by cvs2svn to compensate for changes in r172677,
[FreeBSD/FreeBSD.git] / sbin / ifconfig / ifieee80211.c
1 /*
2  * Copyright 2001 The Aerospace Corporation.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 3. The name of The Aerospace Corporation may not be used to endorse or
13  *    promote products derived from this software.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AEROSPACE CORPORATION BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29
30 /*-
31  * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
32  * All rights reserved.
33  *
34  * This code is derived from software contributed to The NetBSD Foundation
35  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
36  * NASA Ames Research Center.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  * 1. Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  * 2. Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in the
45  *    documentation and/or other materials provided with the distribution.
46  * 3. All advertising materials mentioning features or use of this software
47  *    must display the following acknowledgement:
48  *      This product includes software developed by the NetBSD
49  *      Foundation, Inc. and its contributors.
50  * 4. Neither the name of The NetBSD Foundation nor the names of its
51  *    contributors may be used to endorse or promote products derived
52  *    from this software without specific prior written permission.
53  *
54  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
55  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
56  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
58  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
59  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
60  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
61  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
62  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
63  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
64  * POSSIBILITY OF SUCH DAMAGE.
65  */
66
67 #include <sys/param.h>
68 #include <sys/ioctl.h>
69 #include <sys/socket.h>
70 #include <sys/sysctl.h>
71 #include <sys/time.h>
72
73 #include <net/ethernet.h>
74 #include <net/if.h>
75 #include <net/if_dl.h>
76 #include <net/if_types.h>
77 #include <net/if_media.h>
78 #include <net/route.h>
79
80 #include <net80211/ieee80211.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
95 #include "ifconfig.h"
96
97 static void set80211(int s, int type, int val, int len, void *data);
98 static const char *get_string(const char *val, const char *sep,
99     u_int8_t *buf, int *lenp);
100 static void print_string(const u_int8_t *buf, int len);
101
102 static struct ieee80211req_chaninfo chaninfo;
103 static struct ifmediareq *ifmr;
104
105 /*
106  * Collect channel info from the kernel.  We use this (mostly)
107  * to handle mapping between frequency and IEEE channel number.
108  */
109 static void
110 getchaninfo(int s)
111 {
112         struct ieee80211req ireq;
113
114         if (chaninfo.ic_nchans != 0)
115                 return;
116         (void) memset(&ireq, 0, sizeof(ireq));
117         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
118         ireq.i_type = IEEE80211_IOC_CHANINFO;
119         ireq.i_data = &chaninfo;
120         ireq.i_len = sizeof(chaninfo);
121         if (ioctl(s, SIOCG80211, &ireq) < 0)
122                 errx(1, "unable to get channel information");
123
124         ifmr = ifmedia_getstate(s);
125 }
126
127 /*
128  * Given the channel at index i with attributes from,
129  * check if there is a channel with attributes to in
130  * the channel table.  With suitable attributes this
131  * allows the caller to look for promotion; e.g. from
132  * 11b > 11g.
133  */
134 static int
135 canpromote(int i, int from, int to)
136 {
137         const struct ieee80211_channel *fc = &chaninfo.ic_chans[i];
138         int j;
139
140         if ((fc->ic_flags & from) != from)
141                 return i;
142         /* NB: quick check exploiting ordering of chans w/ same frequency */
143         if (i+1 < chaninfo.ic_nchans &&
144             chaninfo.ic_chans[i+1].ic_freq == fc->ic_freq &&
145             (chaninfo.ic_chans[i+1].ic_flags & to) == to)
146                 return i+1;
147         /* brute force search in case channel list is not ordered */
148         for (j = 0; j < chaninfo.ic_nchans; j++) {
149                 const struct ieee80211_channel *tc = &chaninfo.ic_chans[j];
150                 if (j != i &&
151                     tc->ic_freq == fc->ic_freq && (tc->ic_flags & to) == to)
152                 return j;
153         }
154         return i;
155 }
156
157 /*
158  * Handle channel promotion.  When a channel is specified with
159  * only a frequency we want to promote it to the ``best'' channel
160  * available.  The channel list has separate entries for 11b, 11g,
161  * 11a, and 11n[ga] channels so specifying a frequency w/o any
162  * attributes requires we upgrade, e.g. from 11b -> 11g.  This
163  * gets complicated when the channel is specified on the same
164  * command line with a media request that constrains the available
165  * channe list (e.g. mode 11a); we want to honor that to avoid
166  * confusing behaviour.
167  */
168 static int
169 promote(int i)
170 {
171         /*
172          * Query the current mode of the interface in case it's
173          * constrained (e.g. to 11a).  We must do this carefully
174          * as there may be a pending ifmedia request in which case
175          * asking the kernel will give us the wrong answer.  This
176          * is an unfortunate side-effect of the way ifconfig is
177          * structure for modularity (yech).
178          *
179          * NB: ifmr is actually setup in getchaninfo (above); we
180          *     assume it's called coincident with to this call so
181          *     we have a ``current setting''; otherwise we must pass
182          *     the socket descriptor down to here so we can make
183          *     the ifmedia_getstate call ourselves.
184          */
185         int chanmode = ifmr != NULL ? IFM_MODE(ifmr->ifm_current) : IFM_AUTO;
186
187         /* when ambiguous promote to ``best'' */
188         /* NB: we abitrarily pick HT40+ over HT40- */
189         if (chanmode != IFM_IEEE80211_11B)
190                 i = canpromote(i, IEEE80211_CHAN_B, IEEE80211_CHAN_G);
191         if (chanmode != IFM_IEEE80211_11G) {
192                 i = canpromote(i, IEEE80211_CHAN_G,
193                         IEEE80211_CHAN_G | IEEE80211_CHAN_HT20);
194                 i = canpromote(i, IEEE80211_CHAN_G,
195                         IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D);
196                 i = canpromote(i, IEEE80211_CHAN_G,
197                         IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U);
198         }
199         if (chanmode != IFM_IEEE80211_11A) {
200                 i = canpromote(i, IEEE80211_CHAN_A,
201                         IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
202                 i = canpromote(i, IEEE80211_CHAN_A,
203                         IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
204                 i = canpromote(i, IEEE80211_CHAN_A,
205                         IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
206         }
207         return i;
208 }
209
210 static void
211 mapfreq(struct ieee80211_channel *chan, int freq, int flags)
212 {
213         int i;
214
215         for (i = 0; i < chaninfo.ic_nchans; i++) {
216                 const struct ieee80211_channel *c = &chaninfo.ic_chans[i];
217
218                 if (c->ic_freq == freq && (c->ic_flags & flags) == flags) {
219                         if (flags == 0) {
220                                 /* when ambiguous promote to ``best'' */
221                                 c = &chaninfo.ic_chans[promote(i)];
222                         }
223                         *chan = *c;
224                         return;
225                 }
226         }
227         errx(1, "unknown/undefined frequency %u/0x%x", freq, flags);
228 }
229
230 static void
231 mapchan(struct ieee80211_channel *chan, int ieee, int flags)
232 {
233         int i;
234
235         for (i = 0; i < chaninfo.ic_nchans; i++) {
236                 const struct ieee80211_channel *c = &chaninfo.ic_chans[i];
237
238                 if (c->ic_ieee == ieee && (c->ic_flags & flags) == flags) {
239                         if (flags == 0) {
240                                 /* when ambiguous promote to ``best'' */
241                                 c = &chaninfo.ic_chans[promote(i)];
242                         }
243                         *chan = *c;
244                         return;
245                 }
246         }
247         errx(1, "unknown/undefined channel number %d", ieee);
248 }
249
250 static int
251 ieee80211_mhz2ieee(int freq, int flags)
252 {
253         struct ieee80211_channel chan;
254         mapfreq(&chan, freq, flags);
255         return chan.ic_ieee;
256 }
257
258 static int
259 isanyarg(const char *arg)
260 {
261         return (strcmp(arg, "-") == 0 ||
262             strcasecmp(arg, "any") == 0 || strcasecmp(arg, "off") == 0);
263 }
264
265 static void
266 set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
267 {
268         int             ssid;
269         int             len;
270         u_int8_t        data[IEEE80211_NWID_LEN];
271
272         ssid = 0;
273         len = strlen(val);
274         if (len > 2 && isdigit(val[0]) && val[1] == ':') {
275                 ssid = atoi(val)-1;
276                 val += 2;
277         }
278
279         bzero(data, sizeof(data));
280         len = sizeof(data);
281         if (get_string(val, NULL, data, &len) == NULL)
282                 exit(1);
283
284         set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
285 }
286
287 static void
288 set80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
289 {
290         int                     len;
291         u_int8_t                data[33];
292
293         bzero(data, sizeof(data));
294         len = sizeof(data);
295         get_string(val, NULL, data, &len);
296
297         set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
298 }
299
300 /*
301  * Parse a channel specification for attributes/flags.
302  * The syntax is:
303  *      freq/xx         channel width (5,10,20,40,40+,40-)
304  *      freq:mode       channel mode (a,b,g,h,n,t,s,d)
305  *
306  * These can be combined in either order; e.g. 2437:ng/40.
307  * Modes are case insensitive.
308  *
309  * The result is not validated here; it's assumed to be
310  * checked against the channel table fetched from the kernel.
311  */ 
312 static int
313 getchannelflags(const char *val)
314 {
315 #define CHAN_HT_DEFAULT IEEE80211_CHAN_HT40U
316 #define _CHAN_HT        0x80000000
317         const char *cp;
318         int flags;
319
320         flags = 0;
321
322         cp = strchr(val, ':');
323         if (cp != NULL) {
324                 for (cp++; isalpha((int) *cp); cp++) {
325                         /* accept mixed case */
326                         int c = *cp;
327                         if (isupper(c))
328                                 c = tolower(c);
329                         switch (c) {
330                         case 'a':               /* 802.11a */
331                                 flags |= IEEE80211_CHAN_A;
332                                 break;
333                         case 'b':               /* 802.11b */
334                                 flags |= IEEE80211_CHAN_B;
335                                 break;
336                         case 'g':               /* 802.11g */
337                                 flags |= IEEE80211_CHAN_G;
338                                 break;
339                         case 'h':               /* ht = 802.11n */
340                         case 'n':               /* 802.11n */
341                                 flags |= _CHAN_HT;      /* NB: private */
342                                 break;
343                         case 'd':               /* dt = Atheros Dynamic Turbo */
344                                 flags |= IEEE80211_CHAN_TURBO;
345                                 break;
346                         case 't':               /* ht, dt, st, t */
347                                 /* dt and unadorned t specify Dynamic Turbo */
348                                 if ((flags & (IEEE80211_CHAN_STURBO|_CHAN_HT)) == 0)
349                                         flags |= IEEE80211_CHAN_TURBO;
350                                 break;
351                         case 's':               /* st = Atheros Static Turbo */
352                                 flags |= IEEE80211_CHAN_STURBO;
353                                 break;
354                         default:
355                                 errx(-1, "%s: Invalid channel attribute %c",
356                                     val, *cp);
357                         }
358                 }
359         }
360         cp = strchr(val, '/');
361         if (cp != NULL) {
362                 char *ep;
363                 u_long cw = strtoul(cp+1, &ep, 10);
364
365                 switch (cw) {
366                 case 5:
367                         flags |= IEEE80211_CHAN_QUARTER;
368                         break;
369                 case 10:
370                         flags |= IEEE80211_CHAN_HALF;
371                         break;
372                 case 20:
373                         /* NB: this may be removed below */
374                         flags |= IEEE80211_CHAN_HT20;
375                         break;
376                 case 40:
377                         if (ep != NULL && *ep == '+')
378                                 flags |= IEEE80211_CHAN_HT40U;
379                         else if (ep != NULL && *ep == '-')
380                                 flags |= IEEE80211_CHAN_HT40D;
381                         else            /* NB: pick something */
382                                 flags |= CHAN_HT_DEFAULT;
383                         break;
384                 default:
385                         errx(-1, "%s: Invalid channel width", val);
386                 }
387         }
388         /*
389          * Cleanup specifications.
390          */ 
391         if ((flags & _CHAN_HT) == 0) {
392                 /*
393                  * If user specified freq/20 or freq/40 quietly remove
394                  * HT cw attributes depending on channel use.  To give
395                  * an explicit 20/40 width for an HT channel you must
396                  * indicate it is an HT channel since all HT channels
397                  * are also usable for legacy operation; e.g. freq:n/40.
398                  */
399                 flags &= ~IEEE80211_CHAN_HT;
400         } else {
401                 /*
402                  * Remove private indicator that this is an HT channel
403                  * and if no explicit channel width has been given
404                  * provide the default settings.
405                  */
406                 flags &= ~_CHAN_HT;
407                 if ((flags & IEEE80211_CHAN_HT) == 0)
408                         flags |= CHAN_HT_DEFAULT;
409         }
410         return flags;
411 #undef CHAN_HT_DEFAULT
412 #undef _CHAN_HT
413 }
414
415 static void
416 set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
417 {
418         struct ieee80211_channel chan;
419
420         memset(&chan, 0, sizeof(chan));
421         if (!isanyarg(val)) {
422                 int v = atoi(val);
423                 int flags = getchannelflags(val);
424
425                 getchaninfo(s);
426                 if (v > 255) {          /* treat as frequency */
427                         mapfreq(&chan, v, flags);
428                 } else {
429                         mapchan(&chan, v, flags);
430                 }
431         } else {
432                 chan.ic_freq = IEEE80211_CHAN_ANY;
433         }
434         set80211(s, IEEE80211_IOC_CURCHAN, 0, sizeof(chan), &chan);
435 }
436
437 static void
438 set80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
439 {
440         int     mode;
441
442         if (strcasecmp(val, "none") == 0) {
443                 mode = IEEE80211_AUTH_NONE;
444         } else if (strcasecmp(val, "open") == 0) {
445                 mode = IEEE80211_AUTH_OPEN;
446         } else if (strcasecmp(val, "shared") == 0) {
447                 mode = IEEE80211_AUTH_SHARED;
448         } else if (strcasecmp(val, "8021x") == 0) {
449                 mode = IEEE80211_AUTH_8021X;
450         } else if (strcasecmp(val, "wpa") == 0) {
451                 mode = IEEE80211_AUTH_WPA;
452         } else {
453                 errx(1, "unknown authmode");
454         }
455
456         set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
457 }
458
459 static void
460 set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
461 {
462         int     mode;
463
464         if (strcasecmp(val, "off") == 0) {
465                 mode = IEEE80211_POWERSAVE_OFF;
466         } else if (strcasecmp(val, "on") == 0) {
467                 mode = IEEE80211_POWERSAVE_ON;
468         } else if (strcasecmp(val, "cam") == 0) {
469                 mode = IEEE80211_POWERSAVE_CAM;
470         } else if (strcasecmp(val, "psp") == 0) {
471                 mode = IEEE80211_POWERSAVE_PSP;
472         } else if (strcasecmp(val, "psp-cam") == 0) {
473                 mode = IEEE80211_POWERSAVE_PSP_CAM;
474         } else {
475                 errx(1, "unknown powersavemode");
476         }
477
478         set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
479 }
480
481 static void
482 set80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
483 {
484         if (d == 0)
485                 set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
486                     0, NULL);
487         else
488                 set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
489                     0, NULL);
490 }
491
492 static void
493 set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
494 {
495         set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
496 }
497
498 static void
499 set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
500 {
501         int     mode;
502
503         if (strcasecmp(val, "off") == 0) {
504                 mode = IEEE80211_WEP_OFF;
505         } else if (strcasecmp(val, "on") == 0) {
506                 mode = IEEE80211_WEP_ON;
507         } else if (strcasecmp(val, "mixed") == 0) {
508                 mode = IEEE80211_WEP_MIXED;
509         } else {
510                 errx(1, "unknown wep mode");
511         }
512
513         set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
514 }
515
516 static void
517 set80211wep(const char *val, int d, int s, const struct afswtch *rafp)
518 {
519         set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
520 }
521
522 static int
523 isundefarg(const char *arg)
524 {
525         return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0);
526 }
527
528 static void
529 set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
530 {
531         if (isundefarg(val))
532                 set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
533         else
534                 set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
535 }
536
537 static void
538 set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
539 {
540         int             key = 0;
541         int             len;
542         u_int8_t        data[IEEE80211_KEYBUF_SIZE];
543
544         if (isdigit(val[0]) && val[1] == ':') {
545                 key = atoi(val)-1;
546                 val += 2;
547         }
548
549         bzero(data, sizeof(data));
550         len = sizeof(data);
551         get_string(val, NULL, data, &len);
552
553         set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
554 }
555
556 /*
557  * This function is purely a NetBSD compatability interface.  The NetBSD
558  * interface is too inflexible, but it's there so we'll support it since
559  * it's not all that hard.
560  */
561 static void
562 set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
563 {
564         int             txkey;
565         int             i, len;
566         u_int8_t        data[IEEE80211_KEYBUF_SIZE];
567
568         set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
569
570         if (isdigit(val[0]) && val[1] == ':') {
571                 txkey = val[0]-'0'-1;
572                 val += 2;
573
574                 for (i = 0; i < 4; i++) {
575                         bzero(data, sizeof(data));
576                         len = sizeof(data);
577                         val = get_string(val, ",", data, &len);
578                         if (val == NULL)
579                                 exit(1);
580
581                         set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
582                 }
583         } else {
584                 bzero(data, sizeof(data));
585                 len = sizeof(data);
586                 get_string(val, NULL, data, &len);
587                 txkey = 0;
588
589                 set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
590
591                 bzero(data, sizeof(data));
592                 for (i = 1; i < 4; i++)
593                         set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
594         }
595
596         set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
597 }
598
599 static void
600 set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
601 {
602         set80211(s, IEEE80211_IOC_RTSTHRESHOLD,
603                 isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL);
604 }
605
606 static void
607 set80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
608 {
609         int     mode;
610
611         if (strcasecmp(val, "off") == 0) {
612                 mode = IEEE80211_PROTMODE_OFF;
613         } else if (strcasecmp(val, "cts") == 0) {
614                 mode = IEEE80211_PROTMODE_CTS;
615         } else if (strcasecmp(val, "rtscts") == 0) {
616                 mode = IEEE80211_PROTMODE_RTSCTS;
617         } else {
618                 errx(1, "unknown protection mode");
619         }
620
621         set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
622 }
623
624 static void
625 set80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
626 {
627         set80211(s, IEEE80211_IOC_TXPOWER, atoi(val), 0, NULL);
628 }
629
630 #define IEEE80211_ROAMING_DEVICE        0
631 #define IEEE80211_ROAMING_AUTO          1
632 #define IEEE80211_ROAMING_MANUAL        2
633
634 static void
635 set80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
636 {
637         int mode;
638
639         if (strcasecmp(val, "device") == 0) {
640                 mode = IEEE80211_ROAMING_DEVICE;
641         } else if (strcasecmp(val, "auto") == 0) {
642                 mode = IEEE80211_ROAMING_AUTO;
643         } else if (strcasecmp(val, "manual") == 0) {
644                 mode = IEEE80211_ROAMING_MANUAL;
645         } else {
646                 errx(1, "unknown roaming mode");
647         }
648         set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
649 }
650
651 static void
652 set80211wme(const char *val, int d, int s, const struct afswtch *rafp)
653 {
654         set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
655 }
656
657 static void
658 set80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
659 {
660         set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
661 }
662
663 static void
664 set80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
665 {
666         set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
667 }
668
669 static void
670 set80211fastframes(const char *val, int d, int s, const struct afswtch *rafp)
671 {
672         set80211(s, IEEE80211_IOC_FF, d, 0, NULL);
673 }
674
675 static void
676 set80211dturbo(const char *val, int d, int s, const struct afswtch *rafp)
677 {
678         set80211(s, IEEE80211_IOC_TURBOP, d, 0, NULL);
679 }
680
681 static void
682 set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
683 {
684         struct ieee80211req_chanlist chanlist;
685 #define MAXCHAN (sizeof(chanlist.ic_channels)*NBBY)
686         char *temp, *cp, *tp;
687
688         temp = malloc(strlen(val) + 1);
689         if (temp == NULL)
690                 errx(1, "malloc failed");
691         strcpy(temp, val);
692         memset(&chanlist, 0, sizeof(chanlist));
693         cp = temp;
694         for (;;) {
695                 int first, last, f;
696
697                 tp = strchr(cp, ',');
698                 if (tp != NULL)
699                         *tp++ = '\0';
700                 switch (sscanf(cp, "%u-%u", &first, &last)) {
701                 case 1:
702                         if (first > MAXCHAN)
703                                 errx(-1, "channel %u out of range, max %zu",
704                                         first, MAXCHAN);
705                         setbit(chanlist.ic_channels, first);
706                         break;
707                 case 2:
708                         if (first > MAXCHAN)
709                                 errx(-1, "channel %u out of range, max %zu",
710                                         first, MAXCHAN);
711                         if (last > MAXCHAN)
712                                 errx(-1, "channel %u out of range, max %zu",
713                                         last, MAXCHAN);
714                         if (first > last)
715                                 errx(-1, "void channel range, %u > %u",
716                                         first, last);
717                         for (f = first; f <= last; f++)
718                                 setbit(chanlist.ic_channels, f);
719                         break;
720                 }
721                 if (tp == NULL)
722                         break;
723                 while (isspace(*tp))
724                         tp++;
725                 if (!isdigit(*tp))
726                         break;
727                 cp = tp;
728         }
729         set80211(s, IEEE80211_IOC_CHANLIST, 0, sizeof(chanlist), &chanlist);
730 #undef MAXCHAN
731 }
732
733 static void
734 set80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
735 {
736
737         if (!isanyarg(val)) {
738                 char *temp;
739                 struct sockaddr_dl sdl;
740
741                 temp = malloc(strlen(val) + 2); /* ':' and '\0' */
742                 if (temp == NULL)
743                         errx(1, "malloc failed");
744                 temp[0] = ':';
745                 strcpy(temp + 1, val);
746                 sdl.sdl_len = sizeof(sdl);
747                 link_addr(temp, &sdl);
748                 free(temp);
749                 if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
750                         errx(1, "malformed link-level address");
751                 set80211(s, IEEE80211_IOC_BSSID, 0,
752                         IEEE80211_ADDR_LEN, LLADDR(&sdl));
753         } else {
754                 uint8_t zerobssid[IEEE80211_ADDR_LEN];
755                 memset(zerobssid, 0, sizeof(zerobssid));
756                 set80211(s, IEEE80211_IOC_BSSID, 0,
757                         IEEE80211_ADDR_LEN, zerobssid);
758         }
759 }
760
761 static int
762 getac(const char *ac)
763 {
764         if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0)
765                 return WME_AC_BE;
766         if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0)
767                 return WME_AC_BK;
768         if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0)
769                 return WME_AC_VI;
770         if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0)
771                 return WME_AC_VO;
772         errx(1, "unknown wme access class %s", ac);
773 }
774
775 static
776 DECL_CMD_FUNC2(set80211cwmin, ac, val)
777 {
778         set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
779 }
780
781 static
782 DECL_CMD_FUNC2(set80211cwmax, ac, val)
783 {
784         set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
785 }
786
787 static
788 DECL_CMD_FUNC2(set80211aifs, ac, val)
789 {
790         set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
791 }
792
793 static
794 DECL_CMD_FUNC2(set80211txoplimit, ac, val)
795 {
796         set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
797 }
798
799 static
800 DECL_CMD_FUNC(set80211acm, ac, d)
801 {
802         set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL);
803 }
804 static
805 DECL_CMD_FUNC(set80211noacm, ac, d)
806 {
807         set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL);
808 }
809
810 static
811 DECL_CMD_FUNC(set80211ackpolicy, ac, d)
812 {
813         set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL);
814 }
815 static
816 DECL_CMD_FUNC(set80211noackpolicy, ac, d)
817 {
818         set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL);
819 }
820
821 static
822 DECL_CMD_FUNC2(set80211bsscwmin, ac, val)
823 {
824         set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
825                 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
826 }
827
828 static
829 DECL_CMD_FUNC2(set80211bsscwmax, ac, val)
830 {
831         set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
832                 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
833 }
834
835 static
836 DECL_CMD_FUNC2(set80211bssaifs, ac, val)
837 {
838         set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
839                 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
840 }
841
842 static
843 DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
844 {
845         set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
846                 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
847 }
848
849 static
850 DECL_CMD_FUNC(set80211dtimperiod, val, d)
851 {
852         set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
853 }
854
855 static
856 DECL_CMD_FUNC(set80211bintval, val, d)
857 {
858         set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
859 }
860
861 static void
862 set80211macmac(int s, int op, const char *val)
863 {
864         char *temp;
865         struct sockaddr_dl sdl;
866
867         temp = malloc(strlen(val) + 2); /* ':' and '\0' */
868         if (temp == NULL)
869                 errx(1, "malloc failed");
870         temp[0] = ':';
871         strcpy(temp + 1, val);
872         sdl.sdl_len = sizeof(sdl);
873         link_addr(temp, &sdl);
874         free(temp);
875         if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
876                 errx(1, "malformed link-level address");
877         set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
878 }
879
880 static
881 DECL_CMD_FUNC(set80211addmac, val, d)
882 {
883         set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
884 }
885
886 static
887 DECL_CMD_FUNC(set80211delmac, val, d)
888 {
889         set80211macmac(s, IEEE80211_IOC_DELMAC, val);
890 }
891
892 static
893 DECL_CMD_FUNC(set80211kickmac, val, d)
894 {
895         char *temp;
896         struct sockaddr_dl sdl;
897         struct ieee80211req_mlme mlme;
898
899         temp = malloc(strlen(val) + 2); /* ':' and '\0' */
900         if (temp == NULL)
901                 errx(1, "malloc failed");
902         temp[0] = ':';
903         strcpy(temp + 1, val);
904         sdl.sdl_len = sizeof(sdl);
905         link_addr(temp, &sdl);
906         free(temp);
907         if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
908                 errx(1, "malformed link-level address");
909         memset(&mlme, 0, sizeof(mlme));
910         mlme.im_op = IEEE80211_MLME_DEAUTH;
911         mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE;
912         memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN);
913         set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), &mlme);
914 }
915
916 static
917 DECL_CMD_FUNC(set80211maccmd, val, d)
918 {
919         set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
920 }
921
922 static void
923 set80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
924 {
925         set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
926 }
927
928 static void
929 set80211bgscan(const char *val, int d, int s, const struct afswtch *rafp)
930 {
931         set80211(s, IEEE80211_IOC_BGSCAN, d, 0, NULL);
932 }
933
934 static
935 DECL_CMD_FUNC(set80211bgscanidle, val, d)
936 {
937         set80211(s, IEEE80211_IOC_BGSCAN_IDLE, atoi(val), 0, NULL);
938 }
939
940 static
941 DECL_CMD_FUNC(set80211bgscanintvl, val, d)
942 {
943         set80211(s, IEEE80211_IOC_BGSCAN_INTERVAL, atoi(val), 0, NULL);
944 }
945
946 static
947 DECL_CMD_FUNC(set80211scanvalid, val, d)
948 {
949         set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL);
950 }
951
952 static
953 DECL_CMD_FUNC(set80211roamrssi11a, val, d)
954 {
955         set80211(s, IEEE80211_IOC_ROAM_RSSI_11A, atoi(val), 0, NULL);
956 }
957
958 static
959 DECL_CMD_FUNC(set80211roamrssi11b, val, d)
960 {
961         set80211(s, IEEE80211_IOC_ROAM_RSSI_11B, atoi(val), 0, NULL);
962 }
963
964 static
965 DECL_CMD_FUNC(set80211roamrssi11g, val, d)
966 {
967         set80211(s, IEEE80211_IOC_ROAM_RSSI_11G, atoi(val), 0, NULL);
968 }
969
970 static
971 DECL_CMD_FUNC(set80211roamrate11a, val, d)
972 {
973         set80211(s, IEEE80211_IOC_ROAM_RATE_11A, 2*atoi(val), 0, NULL);
974 }
975
976 static
977 DECL_CMD_FUNC(set80211roamrate11b, val, d)
978 {
979         set80211(s, IEEE80211_IOC_ROAM_RATE_11B, 2*atoi(val), 0, NULL);
980 }
981
982 static
983 DECL_CMD_FUNC(set80211roamrate11g, val, d)
984 {
985         set80211(s, IEEE80211_IOC_ROAM_RATE_11G, 2*atoi(val), 0, NULL);
986 }
987
988 static
989 DECL_CMD_FUNC(set80211mcastrate, val, d)
990 {
991         set80211(s, IEEE80211_IOC_MCAST_RATE, 2*atoi(val), 0, NULL);
992 }
993
994 static
995 DECL_CMD_FUNC(set80211fragthreshold, val, d)
996 {
997         set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
998                 isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
999 }
1000
1001 static
1002 DECL_CMD_FUNC(set80211bmissthreshold, val, d)
1003 {
1004         set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
1005                 isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
1006 }
1007
1008 static void
1009 set80211burst(const char *val, int d, int s, const struct afswtch *rafp)
1010 {
1011         set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
1012 }
1013
1014 static void
1015 set80211doth(const char *val, int d, int s, const struct afswtch *rafp)
1016 {
1017         set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL);
1018 }
1019
1020 static int
1021 getmaxrate(const uint8_t rates[15], uint8_t nrates)
1022 {
1023         int i, maxrate = -1;
1024
1025         for (i = 0; i < nrates; i++) {
1026                 int rate = rates[i] & IEEE80211_RATE_VAL;
1027                 if (rate > maxrate)
1028                         maxrate = rate;
1029         }
1030         return maxrate / 2;
1031 }
1032
1033 static const char *
1034 getcaps(int capinfo)
1035 {
1036         static char capstring[32];
1037         char *cp = capstring;
1038
1039         if (capinfo & IEEE80211_CAPINFO_ESS)
1040                 *cp++ = 'E';
1041         if (capinfo & IEEE80211_CAPINFO_IBSS)
1042                 *cp++ = 'I';
1043         if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
1044                 *cp++ = 'c';
1045         if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
1046                 *cp++ = 'C';
1047         if (capinfo & IEEE80211_CAPINFO_PRIVACY)
1048                 *cp++ = 'P';
1049         if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
1050                 *cp++ = 'S';
1051         if (capinfo & IEEE80211_CAPINFO_PBCC)
1052                 *cp++ = 'B';
1053         if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
1054                 *cp++ = 'A';
1055         if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
1056                 *cp++ = 's';
1057         if (capinfo & IEEE80211_CAPINFO_RSN)
1058                 *cp++ = 'R';
1059         if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
1060                 *cp++ = 'D';
1061         *cp = '\0';
1062         return capstring;
1063 }
1064
1065 static const char *
1066 getflags(int flags)
1067 {
1068 /* XXX need these publicly defined or similar */
1069 #define IEEE80211_NODE_AUTH     0x0001          /* authorized for data */
1070 #define IEEE80211_NODE_QOS      0x0002          /* QoS enabled */
1071 #define IEEE80211_NODE_ERP      0x0004          /* ERP enabled */
1072 #define IEEE80211_NODE_PWR_MGT  0x0010          /* power save mode enabled */
1073 #define IEEE80211_NODE_HT       0x0040          /* HT enabled */
1074         static char flagstring[32];
1075         char *cp = flagstring;
1076
1077         if (flags & IEEE80211_NODE_AUTH)
1078                 *cp++ = 'A';
1079         if (flags & IEEE80211_NODE_QOS)
1080                 *cp++ = 'Q';
1081         if (flags & IEEE80211_NODE_ERP)
1082                 *cp++ = 'E';
1083         if (flags & IEEE80211_NODE_PWR_MGT)
1084                 *cp++ = 'P';
1085         if (flags & IEEE80211_NODE_HT)
1086                 *cp++ = 'H';
1087         *cp = '\0';
1088         return flagstring;
1089 #undef IEEE80211_NODE_HT
1090 #undef IEEE80211_NODE_AUTH
1091 #undef IEEE80211_NODE_QOS
1092 #undef IEEE80211_NODE_ERP
1093 #undef IEEE80211_NODE_PWR_MGT
1094 }
1095
1096 static void
1097 printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
1098 {
1099         printf("%s", tag);
1100         if (verbose) {
1101                 maxlen -= strlen(tag)+2;
1102                 if (2*ielen > maxlen)
1103                         maxlen--;
1104                 printf("<");
1105                 for (; ielen > 0; ie++, ielen--) {
1106                         if (maxlen-- <= 0)
1107                                 break;
1108                         printf("%02x", *ie);
1109                 }
1110                 if (ielen != 0)
1111                         printf("-");
1112                 printf(">");
1113         }
1114 }
1115
1116 #define LE_READ_2(p)                                    \
1117         ((u_int16_t)                                    \
1118          ((((const u_int8_t *)(p))[0]      ) |          \
1119           (((const u_int8_t *)(p))[1] <<  8)))
1120 #define LE_READ_4(p)                                    \
1121         ((u_int32_t)                                    \
1122          ((((const u_int8_t *)(p))[0]      ) |          \
1123           (((const u_int8_t *)(p))[1] <<  8) |          \
1124           (((const u_int8_t *)(p))[2] << 16) |          \
1125           (((const u_int8_t *)(p))[3] << 24)))
1126
1127 /*
1128  * NB: The decoding routines assume a properly formatted ie
1129  *     which should be safe as the kernel only retains them
1130  *     if they parse ok.
1131  */
1132
1133 static void
1134 printwmeie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1135 {
1136 #define MS(_v, _f)      (((_v) & _f) >> _f##_S)
1137         static const char *acnames[] = { "BE", "BK", "VO", "VI" };
1138         int i;
1139
1140         printf("%s", tag);
1141         if (verbose) {
1142                 printf("<qosinfo 0x%x", ie[
1143                         __offsetof(struct ieee80211_wme_param, param_qosInfo)]);
1144                 ie += __offsetof(struct ieee80211_wme_param, params_acParams);
1145                 for (i = 0; i < WME_NUM_AC; i++) {
1146                         printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]"
1147                                 , acnames[i]
1148                                 , MS(ie[0], WME_PARAM_ACM) ? "acm " : ""
1149                                 , MS(ie[0], WME_PARAM_AIFSN)
1150                                 , MS(ie[1], WME_PARAM_LOGCWMIN)
1151                                 , MS(ie[1], WME_PARAM_LOGCWMAX)
1152                                 , LE_READ_2(ie+2)
1153                         );
1154                         ie += 4;
1155                 }
1156                 printf(">");
1157         }
1158 #undef MS
1159 }
1160
1161 static void
1162 printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1163 {
1164
1165         printf("%s", tag);
1166         if (verbose) {
1167                 const struct ieee80211_ath_ie *ath =
1168                         (const struct ieee80211_ath_ie *)ie;
1169
1170                 printf("<");
1171                 if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME)
1172                         printf("DTURBO,");
1173                 if (ath->ath_capability & ATHEROS_CAP_COMPRESSION)
1174                         printf("COMP,");
1175                 if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME)
1176                         printf("FF,");
1177                 if (ath->ath_capability & ATHEROS_CAP_XR)
1178                         printf("XR,");
1179                 if (ath->ath_capability & ATHEROS_CAP_AR)
1180                         printf("AR,");
1181                 if (ath->ath_capability & ATHEROS_CAP_BURST)
1182                         printf("BURST,");
1183                 if (ath->ath_capability & ATHEROS_CAP_WME)
1184                         printf("WME,");
1185                 if (ath->ath_capability & ATHEROS_CAP_BOOST)
1186                         printf("BOOST,");
1187                 printf("0x%x>", LE_READ_2(ath->ath_defkeyix));
1188         }
1189 }
1190
1191 static const char *
1192 wpa_cipher(const u_int8_t *sel)
1193 {
1194 #define WPA_SEL(x)      (((x)<<24)|WPA_OUI)
1195         u_int32_t w = LE_READ_4(sel);
1196
1197         switch (w) {
1198         case WPA_SEL(WPA_CSE_NULL):
1199                 return "NONE";
1200         case WPA_SEL(WPA_CSE_WEP40):
1201                 return "WEP40";
1202         case WPA_SEL(WPA_CSE_WEP104):
1203                 return "WEP104";
1204         case WPA_SEL(WPA_CSE_TKIP):
1205                 return "TKIP";
1206         case WPA_SEL(WPA_CSE_CCMP):
1207                 return "AES-CCMP";
1208         }
1209         return "?";             /* NB: so 1<< is discarded */
1210 #undef WPA_SEL
1211 }
1212
1213 static const char *
1214 wpa_keymgmt(const u_int8_t *sel)
1215 {
1216 #define WPA_SEL(x)      (((x)<<24)|WPA_OUI)
1217         u_int32_t w = LE_READ_4(sel);
1218
1219         switch (w) {
1220         case WPA_SEL(WPA_ASE_8021X_UNSPEC):
1221                 return "8021X-UNSPEC";
1222         case WPA_SEL(WPA_ASE_8021X_PSK):
1223                 return "8021X-PSK";
1224         case WPA_SEL(WPA_ASE_NONE):
1225                 return "NONE";
1226         }
1227         return "?";
1228 #undef WPA_SEL
1229 }
1230
1231 static void
1232 printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1233 {
1234         u_int8_t len = ie[1];
1235
1236         printf("%s", tag);
1237         if (verbose) {
1238                 const char *sep;
1239                 int n;
1240
1241                 ie += 6, len -= 4;              /* NB: len is payload only */
1242
1243                 printf("<v%u", LE_READ_2(ie));
1244                 ie += 2, len -= 2;
1245
1246                 printf(" mc:%s", wpa_cipher(ie));
1247                 ie += 4, len -= 4;
1248
1249                 /* unicast ciphers */
1250                 n = LE_READ_2(ie);
1251                 ie += 2, len -= 2;
1252                 sep = " uc:";
1253                 for (; n > 0; n--) {
1254                         printf("%s%s", sep, wpa_cipher(ie));
1255                         ie += 4, len -= 4;
1256                         sep = "+";
1257                 }
1258
1259                 /* key management algorithms */
1260                 n = LE_READ_2(ie);
1261                 ie += 2, len -= 2;
1262                 sep = " km:";
1263                 for (; n > 0; n--) {
1264                         printf("%s%s", sep, wpa_keymgmt(ie));
1265                         ie += 4, len -= 4;
1266                         sep = "+";
1267                 }
1268
1269                 if (len > 2)            /* optional capabilities */
1270                         printf(", caps 0x%x", LE_READ_2(ie));
1271                 printf(">");
1272         }
1273 }
1274
1275 static const char *
1276 rsn_cipher(const u_int8_t *sel)
1277 {
1278 #define RSN_SEL(x)      (((x)<<24)|RSN_OUI)
1279         u_int32_t w = LE_READ_4(sel);
1280
1281         switch (w) {
1282         case RSN_SEL(RSN_CSE_NULL):
1283                 return "NONE";
1284         case RSN_SEL(RSN_CSE_WEP40):
1285                 return "WEP40";
1286         case RSN_SEL(RSN_CSE_WEP104):
1287                 return "WEP104";
1288         case RSN_SEL(RSN_CSE_TKIP):
1289                 return "TKIP";
1290         case RSN_SEL(RSN_CSE_CCMP):
1291                 return "AES-CCMP";
1292         case RSN_SEL(RSN_CSE_WRAP):
1293                 return "AES-OCB";
1294         }
1295         return "?";
1296 #undef WPA_SEL
1297 }
1298
1299 static const char *
1300 rsn_keymgmt(const u_int8_t *sel)
1301 {
1302 #define RSN_SEL(x)      (((x)<<24)|RSN_OUI)
1303         u_int32_t w = LE_READ_4(sel);
1304
1305         switch (w) {
1306         case RSN_SEL(RSN_ASE_8021X_UNSPEC):
1307                 return "8021X-UNSPEC";
1308         case RSN_SEL(RSN_ASE_8021X_PSK):
1309                 return "8021X-PSK";
1310         case RSN_SEL(RSN_ASE_NONE):
1311                 return "NONE";
1312         }
1313         return "?";
1314 #undef RSN_SEL
1315 }
1316
1317 static void
1318 printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
1319 {
1320         u_int8_t len = ie[1];
1321
1322         printf("%s", tag);
1323         if (verbose) {
1324                 const char *sep;
1325                 int n;
1326
1327                 ie += 6, len -= 4;              /* NB: len is payload only */
1328
1329                 printf("<v%u", LE_READ_2(ie));
1330                 ie += 2, len -= 2;
1331
1332                 printf(" mc:%s", rsn_cipher(ie));
1333                 ie += 4, len -= 4;
1334
1335                 /* unicast ciphers */
1336                 n = LE_READ_2(ie);
1337                 ie += 2, len -= 2;
1338                 sep = " uc:";
1339                 for (; n > 0; n--) {
1340                         printf("%s%s", sep, rsn_cipher(ie));
1341                         ie += 4, len -= 4;
1342                         sep = "+";
1343                 }
1344
1345                 /* key management algorithms */
1346                 n = LE_READ_2(ie);
1347                 ie += 2, len -= 2;
1348                 sep = " km:";
1349                 for (; n > 0; n--) {
1350                         printf("%s%s", sep, rsn_keymgmt(ie));
1351                         ie += 4, len -= 4;
1352                         sep = "+";
1353                 }
1354
1355                 if (len > 2)            /* optional capabilities */
1356                         printf(", caps 0x%x", LE_READ_2(ie));
1357                 /* XXXPMKID */
1358                 printf(">");
1359         }
1360 }
1361
1362 /*
1363  * Copy the ssid string contents into buf, truncating to fit.  If the
1364  * ssid is entirely printable then just copy intact.  Otherwise convert
1365  * to hexadecimal.  If the result is truncated then replace the last
1366  * three characters with "...".
1367  */
1368 static int
1369 copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
1370 {
1371         const u_int8_t *p; 
1372         size_t maxlen;
1373         int i;
1374
1375         if (essid_len > bufsize)
1376                 maxlen = bufsize;
1377         else
1378                 maxlen = essid_len;
1379         /* determine printable or not */
1380         for (i = 0, p = essid; i < maxlen; i++, p++) {
1381                 if (*p < ' ' || *p > 0x7e)
1382                         break;
1383         }
1384         if (i != maxlen) {              /* not printable, print as hex */
1385                 if (bufsize < 3)
1386                         return 0;
1387                 strlcpy(buf, "0x", bufsize);
1388                 bufsize -= 2;
1389                 p = essid;
1390                 for (i = 0; i < maxlen && bufsize >= 2; i++) {
1391                         sprintf(&buf[2+2*i], "%02x", p[i]);
1392                         bufsize -= 2;
1393                 }
1394                 if (i != essid_len)
1395                         memcpy(&buf[2+2*i-3], "...", 3);
1396         } else {                        /* printable, truncate as needed */
1397                 memcpy(buf, essid, maxlen);
1398                 if (maxlen != essid_len)
1399                         memcpy(&buf[maxlen-3], "...", 3);
1400         }
1401         return maxlen;
1402 }
1403
1404 /* unaligned little endian access */     
1405 #define LE_READ_4(p)                                    \
1406         ((u_int32_t)                                    \
1407          ((((const u_int8_t *)(p))[0]      ) |          \
1408           (((const u_int8_t *)(p))[1] <<  8) |          \
1409           (((const u_int8_t *)(p))[2] << 16) |          \
1410           (((const u_int8_t *)(p))[3] << 24)))
1411
1412 static int __inline
1413 iswpaoui(const u_int8_t *frm)
1414 {
1415         return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
1416 }
1417
1418 static int __inline
1419 iswmeoui(const u_int8_t *frm)
1420 {
1421         return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
1422 }
1423
1424 static int __inline
1425 isatherosoui(const u_int8_t *frm)
1426 {
1427         return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
1428 }
1429
1430 static void
1431 printies(const u_int8_t *vp, int ielen, int maxcols)
1432 {
1433         while (ielen > 0) {
1434                 switch (vp[0]) {
1435                 case IEEE80211_ELEMID_VENDOR:
1436                         if (iswpaoui(vp))
1437                                 printwpaie(" WPA", vp, 2+vp[1], maxcols);
1438                         else if (iswmeoui(vp))
1439                                 printwmeie(" WME", vp, 2+vp[1], maxcols);
1440                         else if (isatherosoui(vp))
1441                                 printathie(" ATH", vp, 2+vp[1], maxcols);
1442                         else
1443                                 printie(" VEN", vp, 2+vp[1], maxcols);
1444                         break;
1445                 case IEEE80211_ELEMID_RSN:
1446                         printrsnie(" RSN", vp, 2+vp[1], maxcols);
1447                         break;
1448                 default:
1449                         printie(" ???", vp, 2+vp[1], maxcols);
1450                         break;
1451                 }
1452                 ielen -= 2+vp[1];
1453                 vp += 2+vp[1];
1454         }
1455 }
1456
1457 static void
1458 list_scan(int s)
1459 {
1460         uint8_t buf[24*1024];
1461         struct ieee80211req ireq;
1462         char ssid[IEEE80211_NWID_LEN+1];
1463         uint8_t *cp;
1464         int len, ssidmax;
1465
1466         (void) memset(&ireq, 0, sizeof(ireq));
1467         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1468         ireq.i_type = IEEE80211_IOC_SCAN_RESULTS;
1469         ireq.i_data = buf;
1470         ireq.i_len = sizeof(buf);
1471         if (ioctl(s, SIOCG80211, &ireq) < 0)
1472                 errx(1, "unable to get scan results");
1473         len = ireq.i_len;
1474         if (len < sizeof(struct ieee80211req_scan_result))
1475                 return;
1476
1477         getchaninfo(s);
1478
1479         ssidmax = verbose ? IEEE80211_NWID_LEN : 14;
1480         printf("%-*.*s  %-17.17s  %4s %4s  %-7s  %3s %4s\n"
1481                 , ssidmax, ssidmax, "SSID"
1482                 , "BSSID"
1483                 , "CHAN"
1484                 , "RATE"
1485                 , " S:N"
1486                 , "INT"
1487                 , "CAPS"
1488         );
1489         cp = buf;
1490         do {
1491                 const struct ieee80211req_scan_result *sr;
1492                 const uint8_t *vp;
1493
1494                 sr = (const struct ieee80211req_scan_result *) cp;
1495                 vp = ((const u_int8_t *)sr) + sr->isr_ie_off;
1496                 printf("%-*.*s  %s  %3d  %3dM %3d:%-3d  %3d %-4.4s"
1497                         , ssidmax
1498                           , copy_essid(ssid, ssidmax, vp, sr->isr_ssid_len)
1499                           , ssid
1500                         , ether_ntoa((const struct ether_addr *) sr->isr_bssid)
1501                         , ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
1502                         , getmaxrate(sr->isr_rates, sr->isr_nrates)
1503                         , (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise
1504                         , sr->isr_intval
1505                         , getcaps(sr->isr_capinfo)
1506                 );
1507                 printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);
1508                 printf("\n");
1509                 cp += sr->isr_len, len -= sr->isr_len;
1510         } while (len >= sizeof(struct ieee80211req_scan_result));
1511 }
1512
1513 #include <net80211/ieee80211_freebsd.h>
1514
1515 static void
1516 scan_and_wait(int s)
1517 {
1518         struct ieee80211req ireq;
1519         int sroute;
1520
1521         sroute = socket(PF_ROUTE, SOCK_RAW, 0);
1522         if (sroute < 0) {
1523                 perror("socket(PF_ROUTE,SOCK_RAW)");
1524                 return;
1525         }
1526         (void) memset(&ireq, 0, sizeof(ireq));
1527         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1528         ireq.i_type = IEEE80211_IOC_SCAN_REQ;
1529         /* NB: only root can trigger a scan so ignore errors */
1530         if (ioctl(s, SIOCS80211, &ireq) >= 0) {
1531                 char buf[2048];
1532                 struct if_announcemsghdr *ifan;
1533                 struct rt_msghdr *rtm;
1534
1535                 do {
1536                         if (read(sroute, buf, sizeof(buf)) < 0) {
1537                                 perror("read(PF_ROUTE)");
1538                                 break;
1539                         }
1540                         rtm = (struct rt_msghdr *) buf;
1541                         if (rtm->rtm_version != RTM_VERSION)
1542                                 break;
1543                         ifan = (struct if_announcemsghdr *) rtm;
1544                 } while (rtm->rtm_type != RTM_IEEE80211 ||
1545                     ifan->ifan_what != RTM_IEEE80211_SCAN);
1546         }
1547         close(sroute);
1548 }
1549
1550 static
1551 DECL_CMD_FUNC(set80211scan, val, d)
1552 {
1553         scan_and_wait(s);
1554         list_scan(s);
1555 }
1556
1557 static enum ieee80211_opmode get80211opmode(int s);
1558
1559 static void
1560 list_stations(int s)
1561 {
1562         union {
1563                 struct ieee80211req_sta_req req;
1564                 uint8_t buf[24*1024];
1565         } u;
1566         enum ieee80211_opmode opmode = get80211opmode(s);
1567         struct ieee80211req ireq;
1568         const uint8_t *cp;
1569         int len;
1570
1571         (void) memset(&ireq, 0, sizeof(ireq));
1572         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1573         /* broadcast address =>'s get all stations */
1574         (void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
1575         if (opmode == IEEE80211_M_STA) {
1576                 /*
1577                  * Get information about the associated AP.
1578                  */
1579                 ireq.i_type = IEEE80211_IOC_BSSID;
1580                 ireq.i_data = u.req.is_u.macaddr;
1581                 ireq.i_len = IEEE80211_ADDR_LEN;
1582                 (void) ioctl(s, SIOCG80211, &ireq);
1583         }
1584         ireq.i_type = IEEE80211_IOC_STA_INFO;
1585         ireq.i_data = &u;
1586         ireq.i_len = sizeof(u);
1587         if (ioctl(s, SIOCG80211, &ireq) < 0)
1588                 errx(1, "unable to get station information");
1589         len = ireq.i_len;
1590         if (len < sizeof(struct ieee80211req_sta_info))
1591                 return;
1592
1593         getchaninfo(s);
1594
1595         printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %4s\n"
1596                 , "ADDR"
1597                 , "AID"
1598                 , "CHAN"
1599                 , "RATE"
1600                 , "RSSI"
1601                 , "IDLE"
1602                 , "TXSEQ"
1603                 , "RXSEQ"
1604                 , "CAPS"
1605                 , "FLAG"
1606         );
1607         cp = (const uint8_t *) u.req.info;
1608         do {
1609                 const struct ieee80211req_sta_info *si;
1610
1611                 si = (const struct ieee80211req_sta_info *) cp;
1612                 if (si->isi_len < sizeof(*si))
1613                         break;
1614                 printf("%s %4u %4d %3dM %3.1f %4d %6d %6d %-4.4s %-4.4s"
1615                         , ether_ntoa((const struct ether_addr*) si->isi_macaddr)
1616                         , IEEE80211_AID(si->isi_associd)
1617                         , ieee80211_mhz2ieee(si->isi_freq, si->isi_flags)
1618                         , (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL)/2
1619                         , si->isi_rssi/2.
1620                         , si->isi_inact
1621                         , si->isi_txseqs[0]
1622                         , si->isi_rxseqs[0]
1623                         , getcaps(si->isi_capinfo)
1624                         , getflags(si->isi_state)
1625                 );
1626                 printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
1627                 printf("\n");
1628                 cp += si->isi_len, len -= si->isi_len;
1629         } while (len >= sizeof(struct ieee80211req_sta_info));
1630 }
1631
1632 static const char *
1633 get_chaninfo(const struct ieee80211_channel *c, int precise,
1634         char buf[], size_t bsize)
1635 {
1636         buf[0] = '\0';
1637         if (IEEE80211_IS_CHAN_FHSS(c))
1638                 strlcat(buf, " FHSS", bsize);
1639         if (IEEE80211_IS_CHAN_A(c)) {
1640                 if (IEEE80211_IS_CHAN_HALF(c))
1641                         strlcat(buf, " 11a/10Mhz", bsize);
1642                 else if (IEEE80211_IS_CHAN_QUARTER(c))
1643                         strlcat(buf, " 11a/5Mhz", bsize);
1644                 else
1645                         strlcat(buf, " 11a", bsize);
1646         }
1647         if (IEEE80211_IS_CHAN_ANYG(c)) {
1648                 if (IEEE80211_IS_CHAN_HALF(c))
1649                         strlcat(buf, " 11g/10Mhz", bsize);
1650                 else if (IEEE80211_IS_CHAN_QUARTER(c))
1651                         strlcat(buf, " 11g/5Mhz", bsize);
1652                 else
1653                         strlcat(buf, " 11g", bsize);
1654         } else if (IEEE80211_IS_CHAN_B(c))
1655                 strlcat(buf, " 11b", bsize);
1656         if (IEEE80211_IS_CHAN_TURBO(c))
1657                 strlcat(buf, " Turbo", bsize);
1658         if (precise) {
1659                 if (IEEE80211_IS_CHAN_HT20(c))
1660                         strlcat(buf, " ht/20", bsize);
1661                 else if (IEEE80211_IS_CHAN_HT40D(c))
1662                         strlcat(buf, " ht/40-", bsize);
1663                 else if (IEEE80211_IS_CHAN_HT40U(c))
1664                         strlcat(buf, " ht/40+", bsize);
1665         } else {
1666                 if (IEEE80211_IS_CHAN_HT(c))
1667                         strlcat(buf, " ht", bsize);
1668         }
1669         return buf;
1670 }
1671
1672 static void
1673 print_chaninfo(const struct ieee80211_channel *c)
1674 {
1675         char buf[14];
1676
1677         printf("Channel %3u : %u%c Mhz%-14.14s",
1678                 ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
1679                 IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
1680                 get_chaninfo(c, verbose, buf, sizeof(buf)));
1681 }
1682
1683 static void
1684 list_channels(int s, int allchans)
1685 {
1686         struct ieee80211req_chaninfo achans;
1687         uint8_t reported[IEEE80211_CHAN_BYTES];
1688         const struct ieee80211_channel *c;
1689         int i, half;
1690
1691         getchaninfo(s);
1692         memset(&achans, 0, sizeof(achans));
1693         memset(reported, 0, sizeof(reported));
1694         if (!allchans) {
1695                 struct ieee80211req_chanlist active;
1696                 struct ieee80211req ireq;
1697
1698                 (void) memset(&ireq, 0, sizeof(ireq));
1699                 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1700                 ireq.i_type = IEEE80211_IOC_CHANLIST;
1701                 ireq.i_data = &active;
1702                 ireq.i_len = sizeof(active);
1703                 if (ioctl(s, SIOCG80211, &ireq) < 0)
1704                         errx(1, "unable to get active channel list");
1705                 memset(&achans, 0, sizeof(achans));
1706                 for (i = 0; i < chaninfo.ic_nchans; i++) {
1707                         c = &chaninfo.ic_chans[i];
1708                         if (!isset(active.ic_channels, c->ic_ieee))
1709                                 continue;
1710                         /*
1711                          * Suppress compatible duplicates unless
1712                          * verbose.  The kernel gives us it's
1713                          * complete channel list which has separate
1714                          * entries for 11g/11b and 11a/turbo.
1715                          */
1716                         if (isset(reported, c->ic_ieee) && !verbose) {
1717                                 /* XXX we assume duplicates are adjacent */
1718                                 achans.ic_chans[achans.ic_nchans-1] = *c;
1719                         } else {
1720                                 achans.ic_chans[achans.ic_nchans++] = *c;
1721                                 setbit(reported, c->ic_ieee);
1722                         }
1723                 }
1724         } else {
1725                 for (i = 0; i < chaninfo.ic_nchans; i++) {
1726                         c = &chaninfo.ic_chans[i];
1727                         /* suppress duplicates as above */
1728                         if (isset(reported, c->ic_ieee) && !verbose) {
1729                                 /* XXX we assume duplicates are adjacent */
1730                                 achans.ic_chans[achans.ic_nchans-1] = *c;
1731                         } else {
1732                                 achans.ic_chans[achans.ic_nchans++] = *c;
1733                                 setbit(reported, c->ic_ieee);
1734                         }
1735                 }
1736         }
1737         half = achans.ic_nchans / 2;
1738         if (achans.ic_nchans % 2)
1739                 half++;
1740
1741         for (i = 0; i < achans.ic_nchans / 2; i++) {
1742                 print_chaninfo(&achans.ic_chans[i]);
1743                 print_chaninfo(&achans.ic_chans[half+i]);
1744                 printf("\n");
1745         }
1746         if (achans.ic_nchans % 2) {
1747                 print_chaninfo(&achans.ic_chans[i]);
1748                 printf("\n");
1749         }
1750 }
1751
1752 static void
1753 print_txpow(const struct ieee80211_channel *c)
1754 {
1755         printf("Channel %3u : %u Mhz %3.1f reg %2d  ",
1756             c->ic_ieee, c->ic_freq,
1757             c->ic_maxpower/2., c->ic_maxregpower);
1758 }
1759
1760 static void
1761 print_txpow_verbose(const struct ieee80211_channel *c)
1762 {
1763         print_chaninfo(c);
1764         printf("min %4.1f dBm  max %3.1f dBm  reg %2d dBm",
1765             c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
1766         /* indicate where regulatory cap limits power use */
1767         if (c->ic_maxpower > 2*c->ic_maxregpower)
1768                 printf(" <");
1769 }
1770
1771 static void
1772 list_txpow(int s)
1773 {
1774         struct ieee80211req_chaninfo achans;
1775         uint8_t reported[IEEE80211_CHAN_BYTES];
1776         struct ieee80211_channel *c, *prev;
1777         int i, half;
1778
1779         getchaninfo(s);
1780         memset(&achans, 0, sizeof(achans));
1781         memset(reported, 0, sizeof(reported));
1782         for (i = 0; i < chaninfo.ic_nchans; i++) {
1783                 c = &chaninfo.ic_chans[i];
1784                 /* suppress duplicates as above */
1785                 if (isset(reported, c->ic_ieee) && !verbose) {
1786                         /* XXX we assume duplicates are adjacent */
1787                         prev = &achans.ic_chans[achans.ic_nchans-1];
1788                         /* display highest power on channel */
1789                         if (c->ic_maxpower > prev->ic_maxpower)
1790                                 *prev = *c;
1791                 } else {
1792                         achans.ic_chans[achans.ic_nchans++] = *c;
1793                         setbit(reported, c->ic_ieee);
1794                 }
1795         }
1796         if (!verbose) {
1797                 half = achans.ic_nchans / 2;
1798                 if (achans.ic_nchans % 2)
1799                         half++;
1800
1801                 for (i = 0; i < achans.ic_nchans / 2; i++) {
1802                         print_txpow(&achans.ic_chans[i]);
1803                         print_txpow(&achans.ic_chans[half+i]);
1804                         printf("\n");
1805                 }
1806                 if (achans.ic_nchans % 2) {
1807                         print_txpow(&achans.ic_chans[i]);
1808                         printf("\n");
1809                 }
1810         } else {
1811                 for (i = 0; i < achans.ic_nchans; i++) {
1812                         print_txpow_verbose(&achans.ic_chans[i]);
1813                         printf("\n");
1814                 }
1815         }
1816 }
1817
1818 static void
1819 list_keys(int s)
1820 {
1821 }
1822
1823 #define IEEE80211_C_BITS \
1824 "\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\7FF\10TURBOP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \
1825 "\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \
1826 "\31WPA2\32BURST\33WME\34WDS\36BGSCAN\37TXFRAG"
1827
1828 static void
1829 list_capabilities(int s)
1830 {
1831         struct ieee80211req ireq;
1832         u_int32_t caps;
1833
1834         (void) memset(&ireq, 0, sizeof(ireq));
1835         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1836         ireq.i_type = IEEE80211_IOC_DRIVER_CAPS;
1837         if (ioctl(s, SIOCG80211, &ireq) < 0)
1838                 errx(1, "unable to get driver capabilities");
1839         caps = (((u_int16_t) ireq.i_val) << 16) | ((u_int16_t) ireq.i_len);
1840         printb(name, caps, IEEE80211_C_BITS);
1841         putchar('\n');
1842 }
1843
1844 static void
1845 list_wme(int s)
1846 {
1847         static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
1848         struct ieee80211req ireq;
1849         int ac;
1850
1851         (void) memset(&ireq, 0, sizeof(ireq));
1852         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
1853         ireq.i_len = 0;
1854         for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
1855 again:
1856                 if (ireq.i_len & IEEE80211_WMEPARAM_BSS)
1857                         printf("\t%s", "     ");
1858                 else
1859                         printf("\t%s", acnames[ac]);
1860
1861                 ireq.i_len = (ireq.i_len & IEEE80211_WMEPARAM_BSS) | ac;
1862
1863                 /* show WME BSS parameters */
1864                 ireq.i_type = IEEE80211_IOC_WME_CWMIN;
1865                 if (ioctl(s, SIOCG80211, &ireq) != -1)
1866                         printf(" cwmin %2u", ireq.i_val);
1867                 ireq.i_type = IEEE80211_IOC_WME_CWMAX;
1868                 if (ioctl(s, SIOCG80211, &ireq) != -1)
1869                         printf(" cwmax %2u", ireq.i_val);
1870                 ireq.i_type = IEEE80211_IOC_WME_AIFS;
1871                 if (ioctl(s, SIOCG80211, &ireq) != -1)
1872                         printf(" aifs %2u", ireq.i_val);
1873                 ireq.i_type = IEEE80211_IOC_WME_TXOPLIMIT;
1874                 if (ioctl(s, SIOCG80211, &ireq) != -1)
1875                         printf(" txopLimit %3u", ireq.i_val);
1876                 ireq.i_type = IEEE80211_IOC_WME_ACM;
1877                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
1878                         if (ireq.i_val)
1879                                 printf(" acm");
1880                         else if (verbose)
1881                                 printf(" -acm");
1882                 }
1883                 /* !BSS only */
1884                 if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) {
1885                         ireq.i_type = IEEE80211_IOC_WME_ACKPOLICY;
1886                         if (ioctl(s, SIOCG80211, &ireq) != -1) {
1887                                 if (!ireq.i_val)
1888                                         printf(" -ack");
1889                                 else if (verbose)
1890                                         printf(" ack");
1891                         }
1892                 }
1893                 printf("\n");
1894                 if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) {
1895                         ireq.i_len |= IEEE80211_WMEPARAM_BSS;
1896                         goto again;
1897                 } else
1898                         ireq.i_len &= ~IEEE80211_WMEPARAM_BSS;
1899         }
1900 }
1901
1902 static void
1903 list_mac(int s)
1904 {
1905         struct ieee80211req ireq;
1906         struct ieee80211req_maclist *acllist;
1907         int i, nacls, policy;
1908         char c;
1909
1910         (void) memset(&ireq, 0, sizeof(ireq));
1911         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
1912         ireq.i_type = IEEE80211_IOC_MACCMD;
1913         ireq.i_val = IEEE80211_MACCMD_POLICY;
1914         if (ioctl(s, SIOCG80211, &ireq) < 0) {
1915                 if (errno == EINVAL) {
1916                         printf("No acl policy loaded\n");
1917                         return;
1918                 }
1919                 err(1, "unable to get mac policy");
1920         }
1921         policy = ireq.i_val;
1922
1923         ireq.i_val = IEEE80211_MACCMD_LIST;
1924         ireq.i_len = 0;
1925         if (ioctl(s, SIOCG80211, &ireq) < 0)
1926                 err(1, "unable to get mac acl list size");
1927         if (ireq.i_len == 0)            /* NB: no acls */
1928                 return;
1929
1930         ireq.i_data = malloc(ireq.i_len);
1931         if (ireq.i_data == NULL)
1932                 err(1, "out of memory for acl list");
1933
1934         if (ioctl(s, SIOCG80211, &ireq) < 0)
1935                 err(1, "unable to get mac acl list");
1936         if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
1937                 if (verbose)
1938                         printf("policy: open\n");
1939                 c = '*';
1940         } else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
1941                 if (verbose)
1942                         printf("policy: allow\n");
1943                 c = '+';
1944         } else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
1945                 if (verbose)
1946                         printf("policy: deny\n");
1947                 c = '-';
1948         } else {
1949                 printf("policy: unknown (%u)\n", policy);
1950                 c = '?';
1951         }
1952         nacls = ireq.i_len / sizeof(*acllist);
1953         acllist = (struct ieee80211req_maclist *) ireq.i_data;
1954         for (i = 0; i < nacls; i++)
1955                 printf("%c%s\n", c, ether_ntoa(
1956                         (const struct ether_addr *) acllist[i].ml_macaddr));
1957 }
1958
1959 static
1960 DECL_CMD_FUNC(set80211list, arg, d)
1961 {
1962 #define iseq(a,b)       (strncasecmp(a,b,sizeof(b)-1) == 0)
1963
1964         if (iseq(arg, "sta"))
1965                 list_stations(s);
1966         else if (iseq(arg, "scan") || iseq(arg, "ap"))
1967                 list_scan(s);
1968         else if (iseq(arg, "chan") || iseq(arg, "freq"))
1969                 list_channels(s, 1);
1970         else if (iseq(arg, "active"))
1971                 list_channels(s, 0);
1972         else if (iseq(arg, "keys"))
1973                 list_keys(s);
1974         else if (iseq(arg, "caps"))
1975                 list_capabilities(s);
1976         else if (iseq(arg, "wme"))
1977                 list_wme(s);
1978         else if (iseq(arg, "mac"))
1979                 list_mac(s);
1980         else if (iseq(arg, "txpow"))
1981                 list_txpow(s);
1982         else
1983                 errx(1, "Don't know how to list %s for %s", arg, name);
1984 #undef iseq
1985 }
1986
1987 static enum ieee80211_opmode
1988 get80211opmode(int s)
1989 {
1990         struct ifmediareq ifmr;
1991
1992         (void) memset(&ifmr, 0, sizeof(ifmr));
1993         (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
1994
1995         if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
1996                 if (ifmr.ifm_current & IFM_IEEE80211_ADHOC)
1997                         return IEEE80211_M_IBSS;        /* XXX ahdemo */
1998                 if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
1999                         return IEEE80211_M_HOSTAP;
2000                 if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
2001                         return IEEE80211_M_MONITOR;
2002         }
2003         return IEEE80211_M_STA;
2004 }
2005
2006 #if 0
2007 static void
2008 printcipher(int s, struct ieee80211req *ireq, int keylenop)
2009 {
2010         switch (ireq->i_val) {
2011         case IEEE80211_CIPHER_WEP:
2012                 ireq->i_type = keylenop;
2013                 if (ioctl(s, SIOCG80211, ireq) != -1)
2014                         printf("WEP-%s", 
2015                             ireq->i_len <= 5 ? "40" :
2016                             ireq->i_len <= 13 ? "104" : "128");
2017                 else
2018                         printf("WEP");
2019                 break;
2020         case IEEE80211_CIPHER_TKIP:
2021                 printf("TKIP");
2022                 break;
2023         case IEEE80211_CIPHER_AES_OCB:
2024                 printf("AES-OCB");
2025                 break;
2026         case IEEE80211_CIPHER_AES_CCM:
2027                 printf("AES-CCM");
2028                 break;
2029         case IEEE80211_CIPHER_CKIP:
2030                 printf("CKIP");
2031                 break;
2032         case IEEE80211_CIPHER_NONE:
2033                 printf("NONE");
2034                 break;
2035         default:
2036                 printf("UNKNOWN (0x%x)", ireq->i_val);
2037                 break;
2038         }
2039 }
2040 #endif
2041
2042 #define MAXCOL  78
2043 static  int col;
2044 static  char spacer;
2045
2046 static void
2047 LINE_BREAK(void)
2048 {
2049         if (spacer != '\t') {
2050                 printf("\n");
2051                 spacer = '\t';
2052         }
2053         col = 8;        /* 8-col tab */
2054 }
2055
2056 static void
2057 LINE_CHECK(const char *fmt, ...)
2058 {
2059         char buf[80];
2060         va_list ap;
2061         int n;
2062
2063         va_start(ap, fmt);
2064         n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
2065         va_end(ap);
2066         col += 1+n;
2067         if (col > MAXCOL) {
2068                 LINE_BREAK();
2069                 col += n;
2070         }
2071         buf[0] = spacer;
2072         printf("%s", buf);
2073         spacer = ' ';
2074 }
2075
2076 static void
2077 printkey(const struct ieee80211req_key *ik)
2078 {
2079         static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
2080         int keylen = ik->ik_keylen;
2081         int printcontents;
2082
2083         printcontents = printkeys &&
2084                 (memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
2085         if (printcontents)
2086                 LINE_BREAK();
2087         switch (ik->ik_type) {
2088         case IEEE80211_CIPHER_WEP:
2089                 /* compatibility */
2090                 LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
2091                     keylen <= 5 ? "40-bit" :
2092                     keylen <= 13 ? "104-bit" : "128-bit");
2093                 break;
2094         case IEEE80211_CIPHER_TKIP:
2095                 if (keylen > 128/8)
2096                         keylen -= 128/8;        /* ignore MIC for now */
2097                 LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2098                 break;
2099         case IEEE80211_CIPHER_AES_OCB:
2100                 LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2101                 break;
2102         case IEEE80211_CIPHER_AES_CCM:
2103                 LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2104                 break;
2105         case IEEE80211_CIPHER_CKIP:
2106                 LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2107                 break;
2108         case IEEE80211_CIPHER_NONE:
2109                 LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
2110                 break;
2111         default:
2112                 LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
2113                         ik->ik_type, ik->ik_keyix+1, 8*keylen);
2114                 break;
2115         }
2116         if (printcontents) {
2117                 int i;
2118
2119                 printf(" <");
2120                 for (i = 0; i < keylen; i++)
2121                         printf("%02x", ik->ik_keydata[i]);
2122                 printf(">");
2123                 if (ik->ik_type != IEEE80211_CIPHER_WEP &&
2124                     (ik->ik_keyrsc != 0 || verbose))
2125                         printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
2126                 if (ik->ik_type != IEEE80211_CIPHER_WEP &&
2127                     (ik->ik_keytsc != 0 || verbose))
2128                         printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
2129                 if (ik->ik_flags != 0 && verbose) {
2130                         const char *sep = " ";
2131
2132                         if (ik->ik_flags & IEEE80211_KEY_XMIT)
2133                                 printf("%stx", sep), sep = "+";
2134                         if (ik->ik_flags & IEEE80211_KEY_RECV)
2135                                 printf("%srx", sep), sep = "+";
2136                         if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
2137                                 printf("%sdef", sep), sep = "+";
2138                 }
2139                 LINE_BREAK();
2140         }
2141 }
2142
2143 static void
2144 ieee80211_status(int s)
2145 {
2146         static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
2147         enum ieee80211_opmode opmode = get80211opmode(s);
2148         int i, num, wpa, wme, bgscan, bgscaninterval;
2149         struct ieee80211req ireq;
2150         u_int8_t data[32];
2151         struct ieee80211_channel chan;
2152         const struct ieee80211_channel *c;
2153
2154         (void) memset(&ireq, 0, sizeof(ireq));
2155         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2156         ireq.i_data = &data;
2157
2158         wpa = 0;                /* unknown/not set */
2159         bgscan = 0;             /* unknown/not set */
2160
2161         ireq.i_type = IEEE80211_IOC_SSID;
2162         ireq.i_val = -1;
2163         if (ioctl(s, SIOCG80211, &ireq) < 0) {
2164                 /* If we can't get the SSID, this isn't an 802.11 device. */
2165                 return;
2166         }
2167         num = 0;
2168         ireq.i_type = IEEE80211_IOC_NUMSSIDS;
2169         if (ioctl(s, SIOCG80211, &ireq) >= 0)
2170                 num = ireq.i_val;
2171         printf("\tssid ");
2172         if (num > 1) {
2173                 ireq.i_type = IEEE80211_IOC_SSID;
2174                 for (ireq.i_val = 0; ireq.i_val < num; ireq.i_val++) {
2175                         if (ioctl(s, SIOCG80211, &ireq) >= 0 && ireq.i_len > 0) {
2176                                 printf(" %d:", ireq.i_val + 1);
2177                                 print_string(data, ireq.i_len);
2178                         }
2179                 }
2180         } else
2181                 print_string(data, ireq.i_len);
2182
2183         ireq.i_data = &chan;
2184         ireq.i_len = sizeof(chan);
2185         ireq.i_type = IEEE80211_IOC_CURCHAN;
2186         if (ioctl(s, SIOCG80211, &ireq) < 0) {
2187                 /* fall back to legacy ioctl */
2188                 ireq.i_data = NULL;
2189                 ireq.i_len = 0;
2190                 ireq.i_type = IEEE80211_IOC_CHANNEL;
2191                 if (ioctl(s, SIOCG80211, &ireq) < 0)
2192                         goto end;
2193                 getchaninfo(s);
2194                 mapchan(&chan, ireq.i_val, 0);
2195         }
2196         c = &chan;
2197         if (c->ic_freq != IEEE80211_CHAN_ANY) {
2198                 char buf[14];
2199                 printf(" channel %d (%u Mhz%s)", c->ic_ieee, c->ic_freq,
2200                         get_chaninfo(c, 1, buf, sizeof(buf)));
2201         } else if (verbose)
2202                 printf(" channel UNDEF");
2203         ireq.i_data = &data;    /* reset data buffer */
2204
2205         ireq.i_type = IEEE80211_IOC_BSSID;
2206         ireq.i_len = IEEE80211_ADDR_LEN;
2207         if (ioctl(s, SIOCG80211, &ireq) >= 0 &&
2208             (memcmp(ireq.i_data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
2209                 printf(" bssid %s", ether_ntoa(ireq.i_data));
2210
2211         ireq.i_type = IEEE80211_IOC_STATIONNAME;
2212         if (ioctl(s, SIOCG80211, &ireq) != -1) {
2213                 printf("\n\tstationname ");
2214                 print_string(data, ireq.i_len);
2215         }
2216
2217         spacer = ' ';           /* force first break */
2218         LINE_BREAK();
2219
2220         ireq.i_type = IEEE80211_IOC_AUTHMODE;
2221         if (ioctl(s, SIOCG80211, &ireq) != -1) {
2222                 switch (ireq.i_val) {
2223                         case IEEE80211_AUTH_NONE:
2224                                 LINE_CHECK("authmode NONE");
2225                                 break;
2226                         case IEEE80211_AUTH_OPEN:
2227                                 LINE_CHECK("authmode OPEN");
2228                                 break;
2229                         case IEEE80211_AUTH_SHARED:
2230                                 LINE_CHECK("authmode SHARED");
2231                                 break;
2232                         case IEEE80211_AUTH_8021X:
2233                                 LINE_CHECK("authmode 802.1x");
2234                                 break;
2235                         case IEEE80211_AUTH_WPA:
2236                                 ireq.i_type = IEEE80211_IOC_WPA;
2237                                 if (ioctl(s, SIOCG80211, &ireq) != -1)
2238                                         wpa = ireq.i_val;
2239                                 if (!wpa)
2240                                         wpa = 1;        /* default to WPA1 */
2241                                 switch (wpa) {
2242                                 case 2:
2243                                         LINE_CHECK("authmode WPA2/802.11i");
2244                                         break;
2245                                 case 3:
2246                                         LINE_CHECK("authmode WPA1+WPA2/802.11i");
2247                                         break;
2248                                 default:
2249                                         LINE_CHECK("authmode WPA");
2250                                         break;
2251                                 }
2252                                 break;
2253                         case IEEE80211_AUTH_AUTO:
2254                                 LINE_CHECK("authmode AUTO");
2255                                 break;
2256                         default:
2257                                 LINE_CHECK("authmode UNKNOWN (0x%x)",
2258                                         ireq.i_val);
2259                                 break;
2260                 }
2261         }
2262
2263         ireq.i_type = IEEE80211_IOC_WEP;
2264         if (ioctl(s, SIOCG80211, &ireq) != -1 &&
2265             ireq.i_val != IEEE80211_WEP_NOSUP) {
2266                 int firstkey, wepmode;
2267
2268                 wepmode = ireq.i_val;
2269                 switch (wepmode) {
2270                         case IEEE80211_WEP_OFF:
2271                                 LINE_CHECK("privacy OFF");
2272                                 break;
2273                         case IEEE80211_WEP_ON:
2274                                 LINE_CHECK("privacy ON");
2275                                 break;
2276                         case IEEE80211_WEP_MIXED:
2277                                 LINE_CHECK("privacy MIXED");
2278                                 break;
2279                         default:
2280                                 LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
2281                                 break;
2282                 }
2283
2284                 /*
2285                  * If we get here then we've got WEP support so we need
2286                  * to print WEP status.
2287                  */
2288
2289                 ireq.i_type = IEEE80211_IOC_WEPTXKEY;
2290                 if (ioctl(s, SIOCG80211, &ireq) < 0) {
2291                         warn("WEP support, but no tx key!");
2292                         goto end;
2293                 }
2294                 if (ireq.i_val != -1)
2295                         LINE_CHECK("deftxkey %d", ireq.i_val+1);
2296                 else if (wepmode != IEEE80211_WEP_OFF || verbose)
2297                         LINE_CHECK("deftxkey UNDEF");
2298
2299                 ireq.i_type = IEEE80211_IOC_NUMWEPKEYS;
2300                 if (ioctl(s, SIOCG80211, &ireq) < 0) {
2301                         warn("WEP support, but no NUMWEPKEYS support!");
2302                         goto end;
2303                 }
2304                 num = ireq.i_val;
2305
2306                 firstkey = 1;
2307                 for (i = 0; i < num; i++) {
2308                         struct ieee80211req_key ik;
2309
2310                         memset(&ik, 0, sizeof(ik));
2311                         ik.ik_keyix = i;
2312                         ireq.i_type = IEEE80211_IOC_WPAKEY;
2313                         ireq.i_data = &ik;
2314                         ireq.i_len = sizeof(ik);
2315                         if (ioctl(s, SIOCG80211, &ireq) < 0) {
2316                                 warn("WEP support, but can get keys!");
2317                                 goto end;
2318                         }
2319                         if (ik.ik_keylen != 0) {
2320                                 if (verbose)
2321                                         LINE_BREAK();
2322                                 printkey(&ik);
2323                                 firstkey = 0;
2324                         }
2325                 }
2326                 ireq.i_data = &data;    /* reset data buffer */
2327         }
2328
2329         ireq.i_type = IEEE80211_IOC_POWERSAVE;
2330         if (ioctl(s, SIOCG80211, &ireq) != -1 &&
2331             ireq.i_val != IEEE80211_POWERSAVE_NOSUP ) {
2332                 if (ireq.i_val != IEEE80211_POWERSAVE_OFF || verbose) {
2333                         switch (ireq.i_val) {
2334                                 case IEEE80211_POWERSAVE_OFF:
2335                                         LINE_CHECK("powersavemode OFF");
2336                                         break;
2337                                 case IEEE80211_POWERSAVE_CAM:
2338                                         LINE_CHECK("powersavemode CAM");
2339                                         break;
2340                                 case IEEE80211_POWERSAVE_PSP:
2341                                         LINE_CHECK("powersavemode PSP");
2342                                         break;
2343                                 case IEEE80211_POWERSAVE_PSP_CAM:
2344                                         LINE_CHECK("powersavemode PSP-CAM");
2345                                         break;
2346                         }
2347                         ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP;
2348                         if (ioctl(s, SIOCG80211, &ireq) != -1)
2349                                 LINE_CHECK("powersavesleep %d", ireq.i_val);
2350                 }
2351         }
2352
2353         ireq.i_type = IEEE80211_IOC_TXPOWMAX;
2354         if (ioctl(s, SIOCG80211, &ireq) != -1)
2355                 LINE_CHECK("txpowmax %d", ireq.i_val);
2356
2357         if (verbose) {
2358                 ireq.i_type = IEEE80211_IOC_TXPOWER;
2359                 if (ioctl(s, SIOCG80211, &ireq) != -1)
2360                         LINE_CHECK("txpower %d", ireq.i_val);
2361         }
2362
2363         ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD;
2364         if (ioctl(s, SIOCG80211, &ireq) != -1) {
2365                 if (ireq.i_val != IEEE80211_RTS_MAX || verbose)
2366                         LINE_CHECK("rtsthreshold %d", ireq.i_val);
2367         }
2368
2369         ireq.i_type = IEEE80211_IOC_FRAGTHRESHOLD;
2370         if (ioctl(s, SIOCG80211, &ireq) != -1) {
2371                 if (ireq.i_val != IEEE80211_FRAG_MAX || verbose)
2372                         LINE_CHECK("fragthreshold %d", ireq.i_val);
2373         }
2374         ireq.i_type = IEEE80211_IOC_BMISSTHRESHOLD;
2375         if (ioctl(s, SIOCG80211, &ireq) != -1) {
2376                 if (ireq.i_val != IEEE80211_HWBMISS_MAX || verbose)
2377                         LINE_CHECK("bmiss %d", ireq.i_val);
2378         }
2379
2380         ireq.i_type = IEEE80211_IOC_MCAST_RATE;
2381         if (ioctl(s, SIOCG80211, &ireq) != -1) {
2382                 if (ireq.i_val != 2*1 || verbose) {
2383                         if (ireq.i_val == 11)
2384                                 LINE_CHECK("mcastrate 5.5");
2385                         else
2386                                 LINE_CHECK("mcastrate %d", ireq.i_val/2);
2387                 }
2388         }
2389
2390         ireq.i_type = IEEE80211_IOC_BGSCAN_INTERVAL;
2391         if (ioctl(s, SIOCG80211, &ireq) != -1)
2392                 bgscaninterval = ireq.i_val;
2393         else
2394                 bgscaninterval = -1;
2395
2396         ireq.i_type = IEEE80211_IOC_SCANVALID;
2397         if (ioctl(s, SIOCG80211, &ireq) != -1) {
2398                 if (ireq.i_val != bgscaninterval || verbose)
2399                         LINE_CHECK("scanvalid %u", ireq.i_val);
2400         }
2401
2402         ireq.i_type = IEEE80211_IOC_BGSCAN;
2403         if (ioctl(s, SIOCG80211, &ireq) != -1) {
2404                 bgscan = ireq.i_val;
2405                 if (ireq.i_val)
2406                         LINE_CHECK("bgscan");
2407                 else if (verbose)
2408                         LINE_CHECK("-bgscan");
2409         }
2410         if (bgscan || verbose) {
2411                 if (bgscaninterval != -1)
2412                         LINE_CHECK("bgscanintvl %u", bgscaninterval);
2413                 ireq.i_type = IEEE80211_IOC_BGSCAN_IDLE;
2414                 if (ioctl(s, SIOCG80211, &ireq) != -1)
2415                         LINE_CHECK("bgscanidle %u", ireq.i_val);
2416                 if (IEEE80211_IS_CHAN_A(c) || verbose) {
2417                         ireq.i_type = IEEE80211_IOC_ROAM_RSSI_11A;
2418                         if (ioctl(s, SIOCG80211, &ireq) != -1)
2419                                 LINE_CHECK("roam:rssi11a %d", ireq.i_val);
2420                         ireq.i_type = IEEE80211_IOC_ROAM_RATE_11A;
2421                         if (ioctl(s, SIOCG80211, &ireq) != -1)
2422                                 LINE_CHECK("roam:rate11a %u", ireq.i_val/2);
2423                 }
2424                 if (IEEE80211_IS_CHAN_B(c) || verbose) {
2425                         ireq.i_type = IEEE80211_IOC_ROAM_RSSI_11B;
2426                         if (ioctl(s, SIOCG80211, &ireq) != -1)
2427                                 LINE_CHECK("roam:rssi11b %d", ireq.i_val);
2428                         ireq.i_type = IEEE80211_IOC_ROAM_RATE_11B;
2429                         if (ioctl(s, SIOCG80211, &ireq) != -1)
2430                                 LINE_CHECK("roam:rate11b %u", ireq.i_val/2);
2431                 }
2432                 if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
2433                         ireq.i_type = IEEE80211_IOC_ROAM_RSSI_11G;
2434                         if (ioctl(s, SIOCG80211, &ireq) != -1)
2435                                 LINE_CHECK("roam:rssi11g %d", ireq.i_val);
2436                         ireq.i_type = IEEE80211_IOC_ROAM_RATE_11G;
2437                         if (ioctl(s, SIOCG80211, &ireq) != -1)
2438                                 LINE_CHECK("roam:rate11g %u", ireq.i_val/2);
2439                 }
2440         }
2441
2442         if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
2443                 ireq.i_type = IEEE80211_IOC_PUREG;
2444                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
2445                         if (ireq.i_val)
2446                                 LINE_CHECK("pureg");
2447                         else if (verbose)
2448                                 LINE_CHECK("-pureg");
2449                 }
2450                 ireq.i_type = IEEE80211_IOC_PROTMODE;
2451                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
2452                         switch (ireq.i_val) {
2453                                 case IEEE80211_PROTMODE_OFF:
2454                                         LINE_CHECK("protmode OFF");
2455                                         break;
2456                                 case IEEE80211_PROTMODE_CTS:
2457                                         LINE_CHECK("protmode CTS");
2458                                         break;
2459                                 case IEEE80211_PROTMODE_RTSCTS:
2460                                         LINE_CHECK("protmode RTSCTS");
2461                                         break;
2462                                 default:
2463                                         LINE_CHECK("protmode UNKNOWN (0x%x)",
2464                                                 ireq.i_val);
2465                                         break;
2466                         }
2467                 }
2468         }
2469
2470         ireq.i_type = IEEE80211_IOC_WME;
2471         if (ioctl(s, SIOCG80211, &ireq) != -1) {
2472                 wme = ireq.i_val;
2473                 if (wme)
2474                         LINE_CHECK("wme");
2475                 else if (verbose)
2476                         LINE_CHECK("-wme");
2477         } else
2478                 wme = 0;
2479
2480         ireq.i_type = IEEE80211_IOC_BURST;
2481         if (ioctl(s, SIOCG80211, &ireq) != -1) {
2482                 if (ireq.i_val)
2483                         LINE_CHECK("burst");
2484                 else if (verbose)
2485                         LINE_CHECK("-burst");
2486         }
2487
2488         ireq.i_type = IEEE80211_IOC_FF;
2489         if (ioctl(s, SIOCG80211, &ireq) != -1) {
2490                 if (ireq.i_val)
2491                         LINE_CHECK("ff");
2492                 else if (verbose)
2493                         LINE_CHECK("-ff");
2494         }
2495         ireq.i_type = IEEE80211_IOC_TURBOP;
2496         if (ioctl(s, SIOCG80211, &ireq) != -1) {
2497                 if (ireq.i_val)
2498                         LINE_CHECK("dturbo");
2499                 else if (verbose)
2500                         LINE_CHECK("-dturbo");
2501         }
2502
2503         if (opmode == IEEE80211_M_HOSTAP) {
2504                 ireq.i_type = IEEE80211_IOC_HIDESSID;
2505                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
2506                         if (ireq.i_val)
2507                                 LINE_CHECK("hidessid");
2508                         else if (verbose)
2509                                 LINE_CHECK("-hidessid");
2510                 }
2511
2512                 ireq.i_type = IEEE80211_IOC_APBRIDGE;
2513                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
2514                         if (!ireq.i_val)
2515                                 LINE_CHECK("-apbridge");
2516                         else if (verbose)
2517                                 LINE_CHECK("apbridge");
2518                 }
2519
2520                 ireq.i_type = IEEE80211_IOC_DTIM_PERIOD;
2521                 if (ioctl(s, SIOCG80211, &ireq) != -1)
2522                         LINE_CHECK("dtimperiod %u", ireq.i_val);
2523
2524                 ireq.i_type = IEEE80211_IOC_DOTH;
2525                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
2526                         if (!ireq.i_val)
2527                                 LINE_CHECK("-doth");
2528                         else if (verbose)
2529                                 LINE_CHECK("doth");
2530                 }
2531         } else {
2532                 ireq.i_type = IEEE80211_IOC_ROAMING;
2533                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
2534                         if (ireq.i_val != IEEE80211_ROAMING_AUTO || verbose) {
2535                                 switch (ireq.i_val) {
2536                                 case IEEE80211_ROAMING_DEVICE:
2537                                         LINE_CHECK("roaming DEVICE");
2538                                         break;
2539                                 case IEEE80211_ROAMING_AUTO:
2540                                         LINE_CHECK("roaming AUTO");
2541                                         break;
2542                                 case IEEE80211_ROAMING_MANUAL:
2543                                         LINE_CHECK("roaming MANUAL");
2544                                         break;
2545                                 default:
2546                                         LINE_CHECK("roaming UNKNOWN (0x%x)",
2547                                                 ireq.i_val);
2548                                         break;
2549                                 }
2550                         }
2551                 }
2552         }
2553         ireq.i_type = IEEE80211_IOC_BEACON_INTERVAL;
2554         if (ioctl(s, SIOCG80211, &ireq) != -1) {
2555                 if (ireq.i_val)
2556                         LINE_CHECK("bintval %u", ireq.i_val);
2557                 else if (verbose)
2558                         LINE_CHECK("bintval %u", ireq.i_val);
2559         }
2560
2561         if (wme && verbose) {
2562                 LINE_BREAK();
2563                 list_wme(s);
2564         }
2565
2566         if (wpa) {
2567                 ireq.i_type = IEEE80211_IOC_COUNTERMEASURES;
2568                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
2569                         if (ireq.i_val)
2570                                 LINE_CHECK("countermeasures");
2571                         else if (verbose)
2572                                 LINE_CHECK("-countermeasures");
2573                 }
2574 #if 0
2575                 /* XXX not interesting with WPA done in user space */
2576                 ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
2577                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
2578                 }
2579
2580                 ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
2581                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
2582                         LINE_CHECK("mcastcipher ");
2583                         printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
2584                         spacer = ' ';
2585                 }
2586
2587                 ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
2588                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
2589                         LINE_CHECK("ucastcipher ");
2590                         printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
2591                 }
2592
2593                 if (wpa & 2) {
2594                         ireq.i_type = IEEE80211_IOC_RSNCAPS;
2595                         if (ioctl(s, SIOCG80211, &ireq) != -1) {
2596                                 LINE_CHECK("RSN caps 0x%x", ireq.i_val);
2597                                 spacer = ' ';
2598                         }
2599                 }
2600
2601                 ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
2602                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
2603                 }
2604 #endif
2605                 LINE_BREAK();
2606         }
2607         LINE_BREAK();
2608
2609 end:
2610         return;
2611 }
2612
2613 static void
2614 set80211(int s, int type, int val, int len, void *data)
2615 {
2616         struct ieee80211req     ireq;
2617
2618         (void) memset(&ireq, 0, sizeof(ireq));
2619         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2620         ireq.i_type = type;
2621         ireq.i_val = val;
2622         ireq.i_len = len;
2623         ireq.i_data = data;
2624         if (ioctl(s, SIOCS80211, &ireq) < 0)
2625                 err(1, "SIOCS80211");
2626 }
2627
2628 static const char *
2629 get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
2630 {
2631         int len;
2632         int hexstr;
2633         u_int8_t *p;
2634
2635         len = *lenp;
2636         p = buf;
2637         hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
2638         if (hexstr)
2639                 val += 2;
2640         for (;;) {
2641                 if (*val == '\0')
2642                         break;
2643                 if (sep != NULL && strchr(sep, *val) != NULL) {
2644                         val++;
2645                         break;
2646                 }
2647                 if (hexstr) {
2648                         if (!isxdigit((u_char)val[0])) {
2649                                 warnx("bad hexadecimal digits");
2650                                 return NULL;
2651                         }
2652                         if (!isxdigit((u_char)val[1])) {
2653                                 warnx("odd count hexadecimal digits");
2654                                 return NULL;
2655                         }
2656                 }
2657                 if (p >= buf + len) {
2658                         if (hexstr)
2659                                 warnx("hexadecimal digits too long");
2660                         else
2661                                 warnx("string too long");
2662                         return NULL;
2663                 }
2664                 if (hexstr) {
2665 #define tohex(x)        (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
2666                         *p++ = (tohex((u_char)val[0]) << 4) |
2667                             tohex((u_char)val[1]);
2668 #undef tohex
2669                         val += 2;
2670                 } else
2671                         *p++ = *val++;
2672         }
2673         len = p - buf;
2674         /* The string "-" is treated as the empty string. */
2675         if (!hexstr && len == 1 && buf[0] == '-') {
2676                 len = 0;
2677                 memset(buf, 0, *lenp);
2678         } else if (len < *lenp)
2679                 memset(p, 0, *lenp - len);
2680         *lenp = len;
2681         return val;
2682 }
2683
2684 static void
2685 print_string(const u_int8_t *buf, int len)
2686 {
2687         int i;
2688         int hasspc;
2689
2690         i = 0;
2691         hasspc = 0;
2692         for (; i < len; i++) {
2693                 if (!isprint(buf[i]) && buf[i] != '\0')
2694                         break;
2695                 if (isspace(buf[i]))
2696                         hasspc++;
2697         }
2698         if (i == len) {
2699                 if (hasspc || len == 0 || buf[0] == '\0')
2700                         printf("\"%.*s\"", len, buf);
2701                 else
2702                         printf("%.*s", len, buf);
2703         } else {
2704                 printf("0x");
2705                 for (i = 0; i < len; i++)
2706                         printf("%02x", buf[i]);
2707         }
2708 }
2709
2710 static struct cmd ieee80211_cmds[] = {
2711         DEF_CMD_ARG("ssid",             set80211ssid),
2712         DEF_CMD_ARG("nwid",             set80211ssid),
2713         DEF_CMD_ARG("stationname",      set80211stationname),
2714         DEF_CMD_ARG("station",          set80211stationname),   /* BSD/OS */
2715         DEF_CMD_ARG("channel",          set80211channel),
2716         DEF_CMD_ARG("authmode",         set80211authmode),
2717         DEF_CMD_ARG("powersavemode",    set80211powersavemode),
2718         DEF_CMD("powersave",    1,      set80211powersave),
2719         DEF_CMD("-powersave",   0,      set80211powersave),
2720         DEF_CMD_ARG("powersavesleep",   set80211powersavesleep),
2721         DEF_CMD_ARG("wepmode",          set80211wepmode),
2722         DEF_CMD("wep",          1,      set80211wep),
2723         DEF_CMD("-wep",         0,      set80211wep),
2724         DEF_CMD_ARG("deftxkey",         set80211weptxkey),
2725         DEF_CMD_ARG("weptxkey",         set80211weptxkey),
2726         DEF_CMD_ARG("wepkey",           set80211wepkey),
2727         DEF_CMD_ARG("nwkey",            set80211nwkey),         /* NetBSD */
2728         DEF_CMD("-nwkey",       0,      set80211wep),           /* NetBSD */
2729         DEF_CMD_ARG("rtsthreshold",     set80211rtsthreshold),
2730         DEF_CMD_ARG("protmode",         set80211protmode),
2731         DEF_CMD_ARG("txpower",          set80211txpower),
2732         DEF_CMD_ARG("roaming",          set80211roaming),
2733         DEF_CMD("wme",          1,      set80211wme),
2734         DEF_CMD("-wme",         0,      set80211wme),
2735         DEF_CMD("hidessid",     1,      set80211hidessid),
2736         DEF_CMD("-hidessid",    0,      set80211hidessid),
2737         DEF_CMD("apbridge",     1,      set80211apbridge),
2738         DEF_CMD("-apbridge",    0,      set80211apbridge),
2739         DEF_CMD_ARG("chanlist",         set80211chanlist),
2740         DEF_CMD_ARG("bssid",            set80211bssid),
2741         DEF_CMD_ARG("ap",               set80211bssid),
2742         DEF_CMD("scan", 0,              set80211scan),
2743         DEF_CMD_ARG("list",             set80211list),
2744         DEF_CMD_ARG2("cwmin",           set80211cwmin),
2745         DEF_CMD_ARG2("cwmax",           set80211cwmax),
2746         DEF_CMD_ARG2("aifs",            set80211aifs),
2747         DEF_CMD_ARG2("txoplimit",       set80211txoplimit),
2748         DEF_CMD_ARG("acm",              set80211acm),
2749         DEF_CMD_ARG("-acm",             set80211noacm),
2750         DEF_CMD_ARG("ack",              set80211ackpolicy),
2751         DEF_CMD_ARG("-ack",             set80211noackpolicy),
2752         DEF_CMD_ARG2("bss:cwmin",       set80211bsscwmin),
2753         DEF_CMD_ARG2("bss:cwmax",       set80211bsscwmax),
2754         DEF_CMD_ARG2("bss:aifs",        set80211bssaifs),
2755         DEF_CMD_ARG2("bss:txoplimit",   set80211bsstxoplimit),
2756         DEF_CMD_ARG("dtimperiod",       set80211dtimperiod),
2757         DEF_CMD_ARG("bintval",          set80211bintval),
2758         DEF_CMD("mac:open",     IEEE80211_MACCMD_POLICY_OPEN,   set80211maccmd),
2759         DEF_CMD("mac:allow",    IEEE80211_MACCMD_POLICY_ALLOW,  set80211maccmd),
2760         DEF_CMD("mac:deny",     IEEE80211_MACCMD_POLICY_DENY,   set80211maccmd),
2761         DEF_CMD("mac:flush",    IEEE80211_MACCMD_FLUSH,         set80211maccmd),
2762         DEF_CMD("mac:detach",   IEEE80211_MACCMD_DETACH,        set80211maccmd),
2763         DEF_CMD_ARG("mac:add",          set80211addmac),
2764         DEF_CMD_ARG("mac:del",          set80211delmac),
2765         DEF_CMD_ARG("mac:kick",         set80211kickmac),
2766         DEF_CMD("pureg",        1,      set80211pureg),
2767         DEF_CMD("-pureg",       0,      set80211pureg),
2768         DEF_CMD("ff",           1,      set80211fastframes),
2769         DEF_CMD("-ff",          0,      set80211fastframes),
2770         DEF_CMD("dturbo",       1,      set80211dturbo),
2771         DEF_CMD("-dturbo",      0,      set80211dturbo),
2772         DEF_CMD("bgscan",       1,      set80211bgscan),
2773         DEF_CMD("-bgscan",      0,      set80211bgscan),
2774         DEF_CMD_ARG("bgscanidle",       set80211bgscanidle),
2775         DEF_CMD_ARG("bgscanintvl",      set80211bgscanintvl),
2776         DEF_CMD_ARG("scanvalid",        set80211scanvalid),
2777         DEF_CMD_ARG("roam:rssi11a",     set80211roamrssi11a),
2778         DEF_CMD_ARG("roam:rssi11b",     set80211roamrssi11b),
2779         DEF_CMD_ARG("roam:rssi11g",     set80211roamrssi11g),
2780         DEF_CMD_ARG("roam:rate11a",     set80211roamrate11a),
2781         DEF_CMD_ARG("roam:rate11b",     set80211roamrate11b),
2782         DEF_CMD_ARG("roam:rate11g",     set80211roamrate11g),
2783         DEF_CMD_ARG("mcastrate",        set80211mcastrate),
2784         DEF_CMD_ARG("fragthreshold",    set80211fragthreshold),
2785         DEF_CMD("burst",        1,      set80211burst),
2786         DEF_CMD("-burst",       0,      set80211burst),
2787         DEF_CMD_ARG("bmiss",            set80211bmissthreshold),
2788         DEF_CMD_ARG("bmissthreshold",   set80211bmissthreshold),
2789         DEF_CMD("doth",         1,      set80211doth),
2790         DEF_CMD("-doth",        0,      set80211doth),
2791 };
2792 static struct afswtch af_ieee80211 = {
2793         .af_name        = "af_ieee80211",
2794         .af_af          = AF_UNSPEC,
2795         .af_other_status = ieee80211_status,
2796 };
2797
2798 static __constructor void
2799 ieee80211_ctor(void)
2800 {
2801 #define N(a)    (sizeof(a) / sizeof(a[0]))
2802         int i;
2803
2804         for (i = 0; i < N(ieee80211_cmds);  i++)
2805                 cmd_register(&ieee80211_cmds[i]);
2806         af_register(&af_ieee80211);
2807 #undef N
2808 }