[svn-upgrade] new version madwifi (0.9.4+r4133.20100922)
[debian/madwifi.git] / tools / wlanconfig.c
1 /*-
2  * Copyright (c) 2005 Sam Leffler, Errno Consulting
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  * 3. Neither the names of the above-listed copyright holders nor the names
16  *    of any contributors may be used to endorse or promote products derived
17  *    from this software without specific prior written permission.
18  *
19  * Alternatively, this software may be distributed under the terms of the
20  * GNU General Public License ("GPL") version 2 as published by the Free
21  * Software Foundation.
22  *
23  * NO WARRANTY
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
27  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
28  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
29  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
32  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
34  * THE POSSIBILITY OF SUCH DAMAGES.
35  *
36  * $Id: wlanconfig.c 4126 2010-03-23 22:50:36Z proski $
37  */
38
39 /*
40  * wlanconfig athX create wlandev wifiX
41  *      wlanmode station | adhoc | ibss | ap | monitor [bssid | -bssid]
42  * wlanconfig athX destroy
43  */
44 #include <sys/types.h>
45 #include <sys/file.h>
46 #include <sys/ioctl.h>
47 #include <sys/socket.h>
48
49 #include <unistd.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <stdint.h>
54 #include <ctype.h>
55 #include <getopt.h>
56 #include <err.h>
57
58 #include <include/compat.h>
59
60 #include "wireless_copy.h"
61 #include "net80211/ieee80211.h"
62 #include "net80211/ieee80211_crypto.h"
63 #include "net80211/ieee80211_ioctl.h"
64
65 /*
66  * These are taken from ieee80211_node.h
67  */
68 #define IEEE80211_NODE_TURBOP   0x0001          /* Turbo prime enable */
69 #define IEEE80211_NODE_COMP     0x0002          /* Compression enable */
70 #define IEEE80211_NODE_FF       0x0004          /* Fast Frame capable */
71 #define IEEE80211_NODE_XR       0x0008          /* Atheros WME enable */
72 #define IEEE80211_NODE_AR       0x0010          /* AR capable */
73 #define IEEE80211_NODE_BOOST    0x0080 
74
75 #define streq(a,b)      (strncasecmp(a,b,sizeof(b)-1) == 0)
76
77 static int if_split_name(const char *, char **, unsigned int *);
78 static int proc_if_get_name(char *, char *, size_t);
79 static int if_find_unit(const char *);
80 static void vap_create(struct ifreq *);
81 static void vap_destroy(const char *);
82 static void list_stations(const char *);
83 static void list_scan(const char *);
84 static void list_channels(const char *, int);
85 static void list_keys(const char *);
86 static void list_capabilities(const char *);
87 static void list_wme(const char *);
88 static void ieee80211_status(const char *);
89
90 static void usage(void);
91 static int getopmode(const char *);
92 static int getflag(const char *);
93 static int get80211param(const char *, int, void *, size_t);
94 static int get80211priv(const char *, int, void *, size_t);
95 static const char *getstamode(u_int8_t);
96
97 size_t strlcat(char *, const char *, size_t);
98
99 int verbose = 0;
100
101 int
102 main(int argc, char *argv[])
103 {
104         const char *ifname, *cmd;
105         unsigned char bnounit = 0;
106         char *if_base = NULL;
107         unsigned int unit_res = -1;
108         int res = 0;
109
110         if (argc < 2 ||
111           strncmp(argv[1],"-h",2) == 0 || strncmp(argv[1],"--h",3) == 0)
112                 usage();
113
114         ifname = argv[1];
115
116         if (argc == 2) {
117                 ieee80211_status(ifname);
118                 return 0;
119         }
120
121         cmd = argv[2];
122         if (streq(cmd, "create")) {
123                 struct ieee80211_clone_params cp;
124                 struct ifreq ifr;
125
126                 memset(&ifr, 0, sizeof(ifr));
127
128                 memset(&cp, 0, sizeof(cp));
129                 strncpy(cp.icp_name, ifname, IFNAMSIZ);
130                 /* NB: station mode is the default */
131                 cp.icp_opmode = IEEE80211_M_STA;
132                 /* NB: default is to request a unique bssid/mac */
133                 cp.icp_flags = IEEE80211_CLONE_BSSID;
134
135                 while (argc > 3) {
136                         if (strcmp(argv[3], "wlanmode") == 0) {
137                                 if (argc < 5)
138                                         usage();
139                                 cp.icp_opmode = (u_int16_t) getopmode(argv[4]);
140                                 argc--;
141                                 argv++;
142                         } else if (strcmp(argv[3], "wlandev") == 0) {
143                                 if (argc < 5)
144                                         usage();
145                                 strncpy(ifr.ifr_name, argv[4], IFNAMSIZ);
146                                 argc--;
147                                 argv++;
148                         } else if (strcmp(argv[3], "nounit" ) == 0) {
149                                 bnounit = 1;
150                         } else {
151                                 int flag = getflag(argv[3]);
152                                 if (flag < 0)
153                                         cp.icp_flags &= ~(-flag);
154                                 else
155                                         cp.icp_flags |= flag;
156                         }
157                         argc--;
158                         argv++;
159                 }
160                 if (ifr.ifr_name[0] == '\0')
161                         errx(1, "no device specified with wlandev");
162
163                 res = if_split_name(cp.icp_name, &if_base, &unit_res);
164                 if (res < 0) {
165                         err(1, "if_split_name() - malloc");
166                 } else if ((res == 0) && (bnounit == 0)) {
167                         /* user gave a string only and using a unit */
168                         unit_res = if_find_unit(if_base);
169
170                         if (unit_res < 0) {
171                                 err(1, "if_find_unit - failed");
172                         } else {
173                                 snprintf(cp.icp_name + strlen(if_base),
174                                         IFNAMSIZ - strlen(if_base), "%d", unit_res);
175                         }
176                 }
177
178                 free(if_base);
179                 if_base = NULL;
180
181                 ifr.ifr_data = (void *) &cp;
182                 vap_create(&ifr);
183                 printf("%s\n", ifr.ifr_name);
184         } else if (streq(cmd, "destroy")) {
185                 vap_destroy(ifname);
186         } else if (streq(cmd, "list")) {
187                 if (argc > 3) {
188                         const char *arg = argv[3];
189
190                         if (streq(arg, "sta"))
191                                 list_stations(ifname);
192                         else if (streq(arg, "scan") || streq(arg, "ap"))
193                                 list_scan(ifname);
194                         else if (streq(arg, "chan") || streq(arg, "freq"))
195                                 list_channels(ifname, 1);
196                         else if (streq(arg, "active"))
197                                 list_channels(ifname, 0);
198                         else if (streq(arg, "keys"))
199                                 list_keys(ifname);
200                         else if (streq(arg, "caps"))
201                                 list_capabilities(ifname);
202                         else if (streq(arg, "wme"))
203                                 list_wme(ifname);
204                         else
205                                 err(1, "unknown 'list' option: %s", arg);
206                 } else                          /* NB: for compatibility */
207                         list_stations(ifname);
208         } else
209                 usage();
210
211         return 0;
212 }
213
214 // if_split_name - takes a name and splits it into the longest non-numeric only string
215 //                 including the first character plus the value of the longest numeric 
216 //                 string including the last character
217 //      returns  : < 0 on error
218 //                 0 on finding only a string
219 //                 1 on finding a numeric/unit at the end of the string
220 //
221 //                 NOTE Allocates memory.
222 static int if_split_name(const char *if_name, char **if_base_name, unsigned int *unit) {
223         int status = -1;
224         const char *p = NULL;
225         const char *l = NULL;
226
227         /*
228          * Look for the unit from end to start
229          */
230         l = if_name + strlen(if_name) - 1;
231         for (p = l; p >= if_name; --p) {
232                 if (!isdigit(*p))
233                         break;
234         }
235         
236         if (p < l) {
237                 if (unit != NULL)
238                         *unit = atoi(p + 1);
239                 status = 1;
240         } else
241                 status = 0;
242
243         /*
244          * Wherever the unit began, one index before it is the ending of the string
245          */
246         if (if_base_name != NULL) {
247                 *if_base_name = malloc(p - if_name + 2);
248                 if (*if_base_name != NULL) {
249                         memset(*if_base_name, 0, p - if_name + 2);
250                         strncpy(*if_base_name, if_name, p - if_name + 1);
251                 } else
252                         status = -1;
253         }
254
255         return status;
256 }
257
258 // *if_name must point to a memory location of at least IFNAMSIZ bytes
259 static int proc_if_get_name(char *if_name, char *line, size_t line_sz) {
260         char    *p      = NULL;
261         char    *s      = NULL;
262         int     status  = -1;
263         
264         memset(if_name, 0, IFNAMSIZ);
265         
266         for (p = line; p - line < line_sz; ++p) {
267                 if ((!isspace(*p)) && (s == NULL)) {
268                         // First character
269                         s = p;
270                 } else if (isspace(*p) && (s != NULL)) {
271                         // Sanity Check: no spaces in interface name
272                         break;
273                 } else if (*p == ':') {
274                         // Copy everything up to the ':' - string is already zeroed
275                         strncpy(if_name, s, p - s);
276                         status = 0;
277                         break;
278                 }
279         }
280
281         return (status);
282 }
283
284 static int if_find_unit(const char *if_name) {
285         int     unit    = -1;
286         
287         FILE    *fh_proc_if     = NULL;
288         char    lin_buf[512];
289         char    if_cmp[IFNAMSIZ];
290
291         if ((fh_proc_if = fopen("/proc/net/dev", "r")) < 0) {
292                 err(1, "fopen(/proc/net/dev)");
293         } else {
294                 int i;
295                 // Eat two header lines
296                 for (i = 0; i < 2; ++i) {
297                         fgets(lin_buf, sizeof(lin_buf), fh_proc_if);
298                 }
299                 while (fgets(lin_buf, sizeof(lin_buf), fh_proc_if) != NULL) {
300                         char    *p      = NULL;
301                         int     tmp     = -1;
302                         
303                         if (proc_if_get_name(if_cmp, lin_buf, sizeof(lin_buf)) < 0) {
304                                 err(1, "proc_if_get_name - malformed data from /proc/net/dev");
305                         } else {
306                                 p = strstr(if_cmp, if_name);
307                                 if (p != NULL)
308                                         tmp = atoi(p + strlen(if_name));
309                                 if (unit < tmp)
310                                         unit = tmp;
311                         }
312                 }
313                 fclose(fh_proc_if);
314         }
315
316         return (unit + 1);
317 }
318
319 static void
320 vap_create(struct ifreq *ifr)
321 {
322         int s;
323         s = socket(AF_INET, SOCK_DGRAM, 0);
324         if (s < 0)
325                 err(1, "socket(SOCK_DGRAM)");
326
327         if (ioctl(s, SIOC80211IFCREATE, ifr) < 0)
328                 err(1, "ioctl");
329
330         close(s);
331 }
332
333 static void
334 vap_destroy(const char *ifname)
335 {
336         struct ifreq ifr;
337         int s;
338
339         s = socket(AF_INET, SOCK_DGRAM, 0);
340         if (s < 0)
341                 err(1, "socket(SOCK_DGRAM)");
342         memset(&ifr, 0, sizeof(ifr));
343         strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
344         if (ioctl(s, SIOC80211IFDESTROY, &ifr) < 0)
345                 err(1, "ioctl");
346         close(s);
347 }
348
349 static void
350 usage(void)
351 {
352         fprintf(stderr, "usage: wlanconfig athX create [nounit] wlandev wifiY\n");
353         fprintf(stderr, "            wlanmode [sta|adhoc|ap|monitor|wds|ahdemo] [bssid | -bssid] [nosbeacon]\n");
354         fprintf(stderr, "usage: wlanconfig athX destroy\n");
355         fprintf(stderr, "usage: wlanconfig athX list [active|ap|caps|chan|freq|keys|scan|sta|wme]\n");
356         exit(-1);
357 }
358
359 static int
360 getopmode(const char *s)
361 {
362         if (streq(s, "sta") || streq(s, "managed"))
363                 return IEEE80211_M_STA;
364         if (streq(s, "ibss") || streq(s, "adhoc") || streq(s, "ad-hoc"))
365                 return IEEE80211_M_IBSS;
366         if (streq(s, "mon"))
367                 return IEEE80211_M_MONITOR;
368         if (streq(s, "ap") || streq(s, "hostap") || streq(s, "master"))
369                 return IEEE80211_M_HOSTAP;
370         if (streq(s, "wds"))
371                 return IEEE80211_M_WDS;
372         if (streq(s, "ahdemo"))
373                 return IEEE80211_M_AHDEMO;
374         
375         errx(1, "unknown operating mode %s", s);
376         /*NOTREACHED*/
377         return -1;
378 }
379
380 static int
381 getflag(const char  *s)
382 {
383         const char *cp;
384         int flag = 0;
385
386         cp = (s[0] == '-' ? s+1 : s);
387         if (strcmp(cp, "bssid") == 0)
388                 flag = IEEE80211_CLONE_BSSID;
389         if (strcmp(cp, "nosbeacon") == 0)
390                 flag |= IEEE80211_NO_STABEACONS;
391         if (flag == 0)
392                 errx(1, "unknown create option %s", s);
393         return (s[0] == '-' ? -flag : flag);
394 }
395
396 /*
397  * Convert MHz frequency to IEEE channel number.
398  */
399 static u_int
400 ieee80211_mhz2ieee(u_int freq)
401 {
402         if (freq == 2484)
403                 return 14;
404         if (freq < 2484)
405                 return (freq - 2407) / 5;
406         if (freq < 5000)
407                 return 15 + ((freq - 2512) / 20);
408         return (freq - 5000) / 5;
409 }
410
411 /*
412  * Convert RSSI to dBm.
413  */
414 static u_int
415 rssi2dbm(u_int rssi)
416 {
417         return rssi - 95;
418 }
419
420 static int
421 getmaxrate(uint8_t rates[15], uint8_t nrates)
422 {
423         int i, maxrate = -1;
424
425         for (i = 0; i < nrates; i++) {
426                 int rate = rates[i] & IEEE80211_RATE_VAL;
427                 if (rate > maxrate)
428                         maxrate = rate;
429         }
430         return maxrate / 2;
431 }
432
433 static const char *
434 getcaps(int capinfo)
435 {
436         static char capstring[32];
437         char *cp = capstring;
438
439         if (capinfo & IEEE80211_CAPINFO_ESS)
440                 *cp++ = 'E';
441         if (capinfo & IEEE80211_CAPINFO_IBSS)
442                 *cp++ = 'I';
443         if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
444                 *cp++ = 'c';
445         if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
446                 *cp++ = 'C';
447         if (capinfo & IEEE80211_CAPINFO_PRIVACY)
448                 *cp++ = 'P';
449         if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
450                 *cp++ = 'S';
451         if (capinfo & IEEE80211_CAPINFO_PBCC)
452                 *cp++ = 'B';
453         if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
454                 *cp++ = 'A';
455         if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
456                 *cp++ = 's';
457         if (capinfo & IEEE80211_CAPINFO_RSN)
458                 *cp++ = 'R';
459         if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
460                 *cp++ = 'D';
461         *cp = '\0';
462         return capstring;
463 }
464
465 static const char *
466 getathcaps(int capinfo)
467 {
468         static char capstring[32];
469         char *cp = capstring;
470
471         if (capinfo & IEEE80211_NODE_TURBOP)
472                 *cp++ = 'D';
473         if (capinfo & IEEE80211_NODE_COMP)
474                 *cp++ = 'C';
475         if (capinfo & IEEE80211_NODE_FF)
476                 *cp++ = 'F';
477         if (capinfo & IEEE80211_NODE_XR)
478                 *cp++ = 'X';
479         if (capinfo & IEEE80211_NODE_AR)
480                 *cp++ = 'A';
481         if (capinfo & IEEE80211_NODE_BOOST)
482                 *cp++ = 'T';
483         *cp = '\0';
484         return capstring;
485 }
486
487 static const char *
488 getstamode(u_int8_t opmode)
489 {
490         if (opmode ==  IEEE80211_STA_OPMODE_XR)
491                 return "XR";
492
493         return "Normal";
494 }
495
496 static void
497 printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
498 {
499         printf("%s", tag);
500         if (verbose) {
501                 maxlen -= strlen(tag)+2;
502                 if (2*ielen > maxlen)
503                         maxlen--;
504                 printf("<");
505                 for (; ielen > 0; ie++, ielen--) {
506                         if (maxlen-- <= 0)
507                                 break;
508                         printf("%02x", *ie);
509                 }
510                 if (ielen != 0)
511                         printf("-");
512                 printf(">");
513         }
514 }
515
516 /*
517  * Copy the ssid string contents into buf, truncating to fit.  If the
518  * ssid is entirely printable then just copy intact.  Otherwise convert
519  * to hexadecimal.  If the result is truncated then replace the last
520  * three characters with "...".
521  */
522 static int
523 copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
524 {
525         const u_int8_t *p; 
526         int maxlen;
527         int i;
528
529         if (essid_len > bufsize)
530                 maxlen = bufsize;
531         else
532                 maxlen = essid_len;
533         /* determine printable or not */
534         for (i = 0, p = essid; i < maxlen; i++, p++) {
535                 if (*p < ' ' || *p > 0x7e)
536                         break;
537         }
538         if (i != maxlen) {              /* not printable, print as hex */
539                 if (bufsize < 3)
540                         return 0;
541 #if 0
542                 strlcpy(buf, "0x", bufsize);
543 #else
544                 strncpy(buf, "0x", bufsize);
545 #endif
546                 bufsize -= 2;
547                 p = essid;
548                 for (i = 0; i < maxlen && bufsize >= 2; i++) {
549                         sprintf(&buf[2 + 2 * i], "%02x", *p++);
550                         bufsize -= 2;
551                 }
552                 maxlen = 2 + 2 * i;
553         } else {                        /* printable, truncate as needed */
554                 memcpy(buf, essid, maxlen);
555         }
556         if (maxlen != essid_len)
557                 memcpy(buf+maxlen - 3, "...", 3);
558         return maxlen;
559 }
560
561 /* unaligned little endian access */     
562 #define LE_READ_4(p)                                    \
563         ((u_int32_t)                                    \
564          ((((const u_int8_t *)(p))[0]      ) |          \
565           (((const u_int8_t *)(p))[1] <<  8) |          \
566           (((const u_int8_t *)(p))[2] << 16) |          \
567           (((const u_int8_t *)(p))[3] << 24)))
568
569 static int __inline
570 iswpaoui(const u_int8_t *frm)
571 {
572         return frm[1] > 3 && LE_READ_4(frm + 2) == ((WPA_OUI_TYPE << 24) | WPA_OUI);
573 }
574
575 static int __inline
576 iswmeoui(const u_int8_t *frm)
577 {
578         return frm[1] > 3 && LE_READ_4(frm + 2) == ((WME_OUI_TYPE << 24) | WME_OUI);
579 }
580
581 static int __inline
582 isatherosoui(const u_int8_t *frm)
583 {
584         return frm[1] > 3 && LE_READ_4(frm + 2) == ((ATH_OUI_TYPE << 24) | ATH_OUI);
585 }
586
587 static void
588 printies(const u_int8_t *vp, int ielen, int maxcols)
589 {
590         while (ielen > 0) {
591                 switch (vp[0]) {
592                 case IEEE80211_ELEMID_VENDOR:
593                         if (iswpaoui(vp))
594                                 printie(" WPA", vp, 2 + vp[1], maxcols);
595                         else if (iswmeoui(vp))
596                                 printie(" WME", vp, 2 + vp[1], maxcols);
597                         else if (isatherosoui(vp))
598                                 printie(" ATH", vp, 2 + vp[1], maxcols);
599                         else
600                                 printie(" VEN", vp, 2 + vp[1], maxcols);
601                         break;
602                 case IEEE80211_ELEMID_RSN:
603                         printie(" RSN", vp, 2 + vp[1], maxcols);
604                         break;
605                 default:
606                         printie(" ???", vp, 2 + vp[1], maxcols);
607                         break;
608                 }
609                 ielen -= 2 + vp[1];
610                 vp += 2 + vp[1];
611         }
612 }
613
614 static const char *
615 ieee80211_ntoa(const uint8_t mac[IEEE80211_ADDR_LEN])
616 {
617         static char a[18];
618         int i;
619
620         i = snprintf(a, sizeof(a), "%02x:%02x:%02x:%02x:%02x:%02x",
621                 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
622         return (i < 17 ? NULL : a);
623 }
624
625 static void
626 list_stations(const char *ifname)
627 {
628         uint8_t buf[24*1024];
629         struct iwreq iwr;
630         uint8_t *cp;
631         int s, len;
632
633         s = socket(AF_INET, SOCK_DGRAM, 0);
634         if (s < 0)
635                 err(1, "socket(SOCK_DGRAM)");
636
637         (void) memset(&iwr, 0, sizeof(iwr));
638         (void) strncpy(iwr.ifr_name, ifname, sizeof(iwr.ifr_name));
639         iwr.u.data.pointer = (void *) buf;
640         iwr.u.data.length = sizeof(buf);
641         if (ioctl(s, IEEE80211_IOCTL_STA_INFO, &iwr) < 0)
642                 errx(1, "unable to get station information");
643         len = iwr.u.data.length;
644         if (len < sizeof(struct ieee80211req_sta_info))
645                 return;
646         close(s);
647
648         printf("%-17.17s %4s %4s %4s %4s %4s %4s %6s %6s %4s %5s %3s %8s %8s\n",
649                 "ADDR",
650                 "AID",
651                 "CHAN",
652                 "RATE",
653                 "RSSI",
654                 "DBM",
655                 "IDLE",
656                 "TXSEQ",
657                 "RXSEQ",
658                 "CAPS",
659                 "ACAPS",
660                 "ERP",
661                 "STATE",
662                 "MODE");
663         cp = buf;
664         do {
665                 struct ieee80211req_sta_info *si;
666                 uint8_t *vp;
667
668                 si = (struct ieee80211req_sta_info *) cp;
669                 vp = (u_int8_t *)(si+1);
670                 printf("%s %4u %4d %3dM %4d %4d %4d %6d %6d %-4.4s %-5.5s %3x %8x %8s",
671                         ieee80211_ntoa(si->isi_macaddr),
672                         IEEE80211_AID(si->isi_associd),
673                         ieee80211_mhz2ieee(si->isi_freq),
674                         (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL) / 2,
675                         si->isi_rssi,
676                         rssi2dbm(si->isi_rssi),
677                         si->isi_inact,
678                         si->isi_txseqs[0],
679                         si->isi_rxseqs[0],
680                         getcaps(si->isi_capinfo),
681                         getathcaps(si->isi_athflags),
682                         si->isi_erp,
683                         si->isi_state,
684                         getstamode(si->isi_opmode));
685                 printies(vp, si->isi_ie_len, 24);
686                 printf("\n");
687                 if (si->isi_uapsd) {
688                         printf("                   UAPSD QoSInfo: 0x%02x, ",
689                                 si->isi_uapsd);
690                         printf("(VO,VI,BE,BK) = (%d,%d,%d,%d), MaxSpLimit = %s\n",
691                                    WME_UAPSD_AC_ENABLED(WME_AC_VO, si->isi_uapsd) ? 1 : 0,
692                                    WME_UAPSD_AC_ENABLED(WME_AC_VI, si->isi_uapsd) ? 1 : 0,
693                                    WME_UAPSD_AC_ENABLED(WME_AC_BE, si->isi_uapsd) ? 1 : 0,
694                                    WME_UAPSD_AC_ENABLED(WME_AC_BK, si->isi_uapsd) ? 1 : 0,
695                                    WME_UAPSD_MAXSP(si->isi_uapsd) == 1 ? "2" :
696                                    WME_UAPSD_MAXSP(si->isi_uapsd) == 2 ? "4" :
697                                    WME_UAPSD_MAXSP(si->isi_uapsd) == 3 ? "6" : "NoLimit");
698                 }
699                 cp += si->isi_len;
700                 len -= si->isi_len;
701         } while (len >= sizeof(struct ieee80211req_sta_info));
702 }
703
704 /* unaligned little endian access */     
705 #define LE_READ_4(p)                                    \
706         ((u_int32_t)                                    \
707          ((((const u_int8_t *)(p))[0]      ) |          \
708           (((const u_int8_t *)(p))[1] <<  8) |          \
709           (((const u_int8_t *)(p))[2] << 16) |          \
710           (((const u_int8_t *)(p))[3] << 24)))
711
712 static void
713 list_scan(const char *ifname)
714 {
715         uint8_t buf[24 * 1024];
716         char ssid[14];
717         uint8_t *cp;
718         int len;
719
720         len = get80211priv(ifname, IEEE80211_IOCTL_SCAN_RESULTS,
721                 buf, sizeof(buf));
722         if (len == -1)
723                 errx(1, "unable to get scan results");
724         if (len < sizeof(struct ieee80211req_scan_result))
725                 return;
726
727         printf("%-14.14s  %-17.17s  %4s %4s  %-5s %3s %4s\n",
728                 "SSID",
729                 "BSSID",
730                 "CHAN",
731                 "RATE",
732                 "S:N",
733                 "INT",
734                 "CAPS");
735         cp = buf;
736         do {
737                 struct ieee80211req_scan_result *sr;
738                 uint8_t *vp;
739
740                 sr = (struct ieee80211req_scan_result *) cp;
741                 vp = (u_int8_t *)(sr+1);
742                 printf("%-14.*s  %s  %3d  %3dM %2d:%-2d  %3d %-4.4s",
743                         copy_essid(ssid, sizeof(ssid), vp, sr->isr_ssid_len),
744                         ssid,
745                         ieee80211_ntoa(sr->isr_bssid),
746                         ieee80211_mhz2ieee(sr->isr_freq),
747                         getmaxrate(sr->isr_rates, sr->isr_nrates),
748                         (int8_t) sr->isr_rssi, sr->isr_noise,
749                         sr->isr_intval,
750                         getcaps(sr->isr_capinfo));
751                 printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);
752                 printf("\n");
753                 cp += sr->isr_len, len -= sr->isr_len;
754         } while (len >= sizeof(struct ieee80211req_scan_result));
755 }
756
757 static void
758 print_chaninfo(const struct ieee80211_channel *c)
759 {
760 #define IEEE80211_IS_CHAN_PASSIVE(_c) \
761         (((_c)->ic_flags & IEEE80211_CHAN_PASSIVE))
762         char buf[14];
763
764         buf[0] = '\0';
765         if (IEEE80211_IS_CHAN_FHSS(c))
766                 strlcat(buf, " FHSS", sizeof(buf));
767         if (IEEE80211_IS_CHAN_A(c))
768                 strlcat(buf, " 11a", sizeof(buf));
769         /* XXX 11g schizophrenia */
770         if (IEEE80211_IS_CHAN_G(c) ||
771             IEEE80211_IS_CHAN_PUREG(c))
772                 strlcat(buf, " 11g", sizeof(buf));
773         else if (IEEE80211_IS_CHAN_B(c))
774                 strlcat(buf, " 11b", sizeof(buf));
775         if (IEEE80211_IS_CHAN_STURBO(c))
776                 strlcat(buf, " Static", sizeof(buf));
777         if (IEEE80211_IS_CHAN_DTURBO(c))
778                 strlcat(buf, " Dynamic", sizeof(buf));
779         printf("Channel %3u : %u%c Mhz%-14.14s",
780                 c->ic_ieee, c->ic_freq,
781                 IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ', buf);
782 #undef IEEE80211_IS_CHAN_PASSIVE
783 }
784
785 static void
786 list_channels(const char *ifname, int allchans)
787 {
788         struct ieee80211req_chaninfo chans;
789         struct ieee80211req_chaninfo achans;
790         const struct ieee80211_channel *c;
791         int i, half;
792
793         if (get80211priv(ifname, IEEE80211_IOCTL_GETCHANINFO, &chans, sizeof(chans)) < 0)
794                 errx(1, "unable to get channel information");
795         if (!allchans) {
796                 uint8_t active[32];
797
798                 if (get80211priv(ifname, IEEE80211_IOCTL_GETCHANLIST, &active, sizeof(active)) < 0)
799                         errx(1, "unable to get active channel list");
800                 memset(&achans, 0, sizeof(achans));
801                 for (i = 0; i < chans.ic_nchans; i++) {
802                         c = &chans.ic_chans[i];
803                         if (isset(active, ieee80211_mhz2ieee(c->ic_freq)) || allchans)
804                                 achans.ic_chans[achans.ic_nchans++] = *c;
805                 }
806         } else
807                 achans = chans;
808         half = achans.ic_nchans / 2;
809         if (achans.ic_nchans % 2)
810                 half++;
811         for (i = 0; i < achans.ic_nchans / 2; i++) {
812                 print_chaninfo(&achans.ic_chans[i]);
813                 print_chaninfo(&achans.ic_chans[half + i]);
814                 printf("\n");
815         }
816         if (achans.ic_nchans % 2) {
817                 print_chaninfo(&achans.ic_chans[i]);
818                 printf("\n");
819         }
820 }
821
822 static void
823 list_keys(const char *ifname)
824 {
825         char cmd[256];
826         puts("[list_keys not implemented (yet). Spawning iwlist...]");
827         strcpy(cmd,"iwlist ");
828         strcat(cmd,ifname);
829         strcat(cmd," key");
830         system(cmd);
831 }
832
833 #define IEEE80211_C_BITS \
834 "\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\7FF\10TURBOP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \
835 "\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \
836 "\31WPA2\32BURST\33WME"
837
838 /*
839  * Print a value a la the %b format of the kernel's printf
840  */
841 void
842 printb(const char *s, unsigned v, const char *bits)
843 {
844         int i, any = 0;
845         char c;
846
847         if (bits && *bits == 8)
848                 printf("%s=%o", s, v);
849         else
850                 printf("%s=%x", s, v);
851         bits++;
852         if (bits) {
853                 putchar('<');
854                 while ((i = *bits++) != '\0') {
855                         if (v & (1 << (i-1))) {
856                                 if (any)
857                                         putchar(',');
858                                 any = 1;
859                                 for (; (c = *bits) > 32; bits++)
860                                         putchar(c);
861                         } else
862                                 for (; *bits > 32; bits++);
863                 }
864                 putchar('>');
865         }
866 }
867
868 static void
869 list_capabilities(const char *ifname)
870 {
871         u_int32_t caps;
872
873         if (get80211param(ifname, IEEE80211_PARAM_DRIVER_CAPS, &caps,
874             sizeof(caps)) < 0)
875                 errx(1, "unable to get driver capabilities");
876         printb(ifname, caps, IEEE80211_C_BITS);
877         putchar('\n');
878 }
879
880 static void
881 list_wme(const char *ifname)
882 {
883 #define GETPARAM() \
884         (get80211priv(ifname, IEEE80211_IOCTL_GETWMMPARAMS, param, sizeof(param)) != -1)
885         static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
886         int param[3];
887         int ac;
888
889         param[2] = 0;           /* channel params */
890         for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
891 again:
892                 if (param[2] != 0)
893                         printf("\t%s", "     ");
894                 else
895                         printf("\t%s", acnames[ac]);
896
897                 param[1] = ac;
898
899                 /* show WME BSS parameters */
900                 param[0] = IEEE80211_WMMPARAMS_CWMIN;
901                 if (GETPARAM())
902                         printf(" cwmin %2u", param[0]);
903                 param[0] = IEEE80211_WMMPARAMS_CWMAX;
904                 if (GETPARAM())
905                         printf(" cwmax %2u", param[0]);
906                 param[0] = IEEE80211_WMMPARAMS_AIFS;
907                 if (GETPARAM())
908                         printf(" aifs %2u", param[0]);
909                 param[0] = IEEE80211_WMMPARAMS_TXOPLIMIT;
910                 if (GETPARAM())
911                         printf(" txopLimit %3u", param[0]);
912                 param[0] = IEEE80211_WMMPARAMS_ACM;
913                 if (GETPARAM()) {
914                         if (param[0])
915                                 printf(" acm");
916                         else if (verbose)
917                                 printf(" -acm");
918                 }
919                 /* !BSS only */
920                 if (param[2] == 0) {
921                         param[0] = IEEE80211_WMMPARAMS_NOACKPOLICY;
922                         if (GETPARAM()) {
923                                 if (param[0])
924                                         printf(" -ack");
925                                 else if (verbose)
926                                         printf(" ack");
927                         }
928                 }
929                 printf("\n");
930                 if (param[2] == 0) {
931                         param[2] = 1;           /* bss params */
932                         goto again;
933                 } else
934                         param[2] = 0;
935         }
936 }
937
938 static void
939 ieee80211_status(const char *ifname)
940 {
941         /* XXX fill in */
942         char cmd[256];
943         puts("[status not implemented (yet). Spawning iwconfig...]");
944         strcpy(cmd,"iwconfig ");
945         strcat(cmd,ifname);
946         system(cmd);
947 }
948
949 static int
950 getsocket(void)
951 {
952         static int s = -1;
953
954         if (s < 0) {
955                 s = socket(AF_INET, SOCK_DGRAM, 0);
956                 if (s < 0)
957                         err(1, "socket(SOCK_DGRAM)");
958         }
959         return s;
960 }
961
962 static int
963 get80211param(const char *ifname, int param, void *data, size_t len)
964 {
965         struct iwreq iwr;
966
967         memset(&iwr, 0, sizeof(iwr));
968         strncpy(iwr.ifr_name, ifname, IFNAMSIZ);
969         iwr.u.mode = param;
970
971         if (ioctl(getsocket(), IEEE80211_IOCTL_GETPARAM, &iwr) < 0) {
972                 perror("ioctl[IEEE80211_IOCTL_GETPARAM]");
973                 return -1;
974         }
975         if (len < IFNAMSIZ) {
976                 /*
977                  * Argument data fits inline; put it there.
978                  */
979                 memcpy(data, iwr.u.name, len);
980         }
981         return iwr.u.data.length;
982 }
983
984 #define IOCTL_ERR(x) [x - SIOCIWFIRSTPRIV] "ioctl[" #x "]"
985 static int
986 do80211priv(struct iwreq *iwr, const char *ifname, int op, void *data, size_t len)
987 {
988 #define N(a)    (sizeof(a)/sizeof(a[0]))
989
990         memset(iwr, 0, sizeof(struct iwreq));
991         strncpy(iwr->ifr_name, ifname, IFNAMSIZ);
992         if (len < IFNAMSIZ) {
993                 /*
994                  * Argument data fits inline; put it there.
995                  */
996                 memcpy(iwr->u.name, data, len);
997         } else {
998                 /*
999                  * Argument data too big for inline transfer; setup a
1000                  * parameter block instead; the kernel will transfer
1001                  * the data for the driver.
1002                  */
1003                 iwr->u.data.pointer = data;
1004                 iwr->u.data.length = len;
1005         }
1006
1007         if (ioctl(getsocket(), op, iwr) < 0) {
1008                 static const char *opnames[] = {
1009                         IOCTL_ERR(IEEE80211_IOCTL_SETPARAM),
1010                         IOCTL_ERR(IEEE80211_IOCTL_GETPARAM),
1011                         IOCTL_ERR(IEEE80211_IOCTL_SETMODE),
1012                         IOCTL_ERR(IEEE80211_IOCTL_GETMODE),
1013                         IOCTL_ERR(IEEE80211_IOCTL_SETWMMPARAMS),
1014                         IOCTL_ERR(IEEE80211_IOCTL_GETWMMPARAMS),
1015                         IOCTL_ERR(IEEE80211_IOCTL_SETCHANLIST),
1016                         IOCTL_ERR(IEEE80211_IOCTL_GETCHANLIST),
1017                         IOCTL_ERR(IEEE80211_IOCTL_CHANSWITCH),
1018                         IOCTL_ERR(IEEE80211_IOCTL_GETCHANINFO),
1019                         IOCTL_ERR(IEEE80211_IOCTL_SETOPTIE),
1020                         IOCTL_ERR(IEEE80211_IOCTL_GETOPTIE),
1021                         IOCTL_ERR(IEEE80211_IOCTL_SETMLME),
1022                         IOCTL_ERR(IEEE80211_IOCTL_SETKEY),
1023                         IOCTL_ERR(IEEE80211_IOCTL_DELKEY),
1024                         IOCTL_ERR(IEEE80211_IOCTL_ADDMAC),
1025                         IOCTL_ERR(IEEE80211_IOCTL_DELMAC),
1026                         IOCTL_ERR(IEEE80211_IOCTL_WDSADDMAC),
1027                         IOCTL_ERR(IEEE80211_IOCTL_WDSDELMAC),
1028                 };
1029                 op -= SIOCIWFIRSTPRIV;
1030                 if (0 <= op && op < N(opnames))
1031                         perror(opnames[op]);
1032                 else
1033                         perror("ioctl[unknown???]");
1034                 return -1;
1035         }
1036         return 0;
1037 #undef N
1038 }
1039
1040 static int
1041 get80211priv(const char *ifname, int op, void *data, size_t len)
1042 {
1043         struct iwreq iwr;
1044
1045         if (do80211priv(&iwr, ifname, op, data, len) < 0)
1046                 return -1;
1047         if (len < IFNAMSIZ)
1048                 memcpy(data, iwr.u.name, len);
1049         return iwr.u.data.length;
1050 }
1051
1052 /*
1053  * Appends src to string dst of size siz (unlike strncat, siz is the
1054  * full size of dst, not space left).  At most siz-1 characters
1055  * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
1056  * Returns strlen(src) + MIN(siz, strlen(initial dst)).
1057  * If retval >= siz, truncation occurred.
1058  */
1059 size_t
1060 strlcat(char *dst, const char *src, size_t siz)
1061 {
1062         char *d = dst;
1063         const char *s = src;
1064         size_t n = siz;
1065         size_t dlen;
1066
1067         /* Find the end of dst and adjust bytes left but don't go past end */
1068         while (n-- != 0 && *d != '\0')
1069                 d++;
1070         dlen = d - dst;
1071         n = siz - dlen;
1072
1073         if (n == 0)
1074                 return(dlen + strlen(s));
1075         while (*s != '\0') {
1076                 if (n != 1) {
1077                         *d++ = *s;
1078                         n--;
1079                 }
1080                 s++;
1081         }
1082         *d = '\0';
1083
1084         return(dlen + (s - src));       /* count does not include NUL */
1085 }
1086