Merge from vendor branch CVS:
[dragonfly.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: src/sbin/ifconfig/ifieee80211.c,v 1.1.2.3 2002/02/07 15:12:37 ambrisko Exp $
28  * $DragonFly: src/sbin/ifconfig/ifieee80211.c,v 1.8 2005/03/06 05:01:59 dillon Exp $
29  */
30
31 /*-
32  * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
33  * All rights reserved.
34  *
35  * This code is derived from software contributed to The NetBSD Foundation
36  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
37  * NASA Ames Research Center.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in the
46  *    documentation and/or other materials provided with the distribution.
47  * 3. All advertising materials mentioning features or use of this software
48  *    must display the following acknowledgement:
49  *      This product includes software developed by the NetBSD
50  *      Foundation, Inc. and its contributors.
51  * 4. Neither the name of The NetBSD Foundation nor the names of its
52  *    contributors may be used to endorse or promote products derived
53  *    from this software without specific prior written permission.
54  *
55  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
56  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
57  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
58  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
59  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
60  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
61  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
62  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
63  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
64  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
65  * POSSIBILITY OF SUCH DAMAGE.
66  */
67
68 #include <sys/param.h>
69 #include <sys/ioctl.h>
70 #include <sys/socket.h>
71 #include <sys/sysctl.h>
72 #include <sys/time.h>
73
74 #include <net/ethernet.h>
75 #include <net/if.h>
76 #include <net/if_dl.h>
77 #include <net/if_types.h>
78 #include <net/route.h>
79 #include <netproto/802_11/ieee80211.h>
80 #include <netproto/802_11/ieee80211_crypto.h>
81 #include <netproto/802_11/ieee80211_ioctl.h>
82
83 #include <ctype.h>
84 #include <err.h>
85 #include <errno.h>
86 #include <fcntl.h>
87 #include <stdio.h>
88 #include <stdlib.h>
89 #include <string.h>
90 #include <unistd.h>
91
92 #include "ifconfig.h"
93
94 static void set80211(int s, int type, int val, int len, u_int8_t *data);
95 static const char *get_string(const char *val, const char *sep,
96     u_int8_t *buf, int *lenp);
97 static void print_string(const u_int8_t *buf, int len);
98
99 void
100 set80211ssid(const char *val, int d __unused, int s,
101              const struct afswtch *rafp __unused)
102 {
103         int             ssid;
104         int             len;
105         u_int8_t        data[33];
106
107         ssid = 0;
108         len = strlen(val);
109         if (len > 2 && isdigit(val[0]) && val[1] == ':') {
110                 ssid = atoi(val)-1;
111                 val += 2;
112         }
113
114         bzero(data, sizeof(data));
115         len = sizeof(data);
116         get_string(val, NULL, data, &len);
117
118         set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
119 }
120
121 void
122 set80211stationname(const char *val, int d __unused, int s,
123                     const struct afswtch *rafp __unused)
124 {
125         int                     len;
126         u_int8_t                data[33];
127
128         bzero(data, sizeof(data));
129         len = sizeof(data);
130         get_string(val, NULL, data, &len);
131
132         set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
133 }
134
135 void
136 set80211channel(const char *val, int d __unused, int s,
137                 const struct afswtch *rafp __unused)
138 {
139         if (strcmp(val, "-") == 0)
140                 set80211(s, IEEE80211_IOC_CHANNEL, IEEE80211_CHAN_ANY, 0, NULL);
141         else
142                 set80211(s, IEEE80211_IOC_CHANNEL, atoi(val), 0, NULL);
143 }
144
145 void
146 set80211authmode(const char *val, int d __unused, int s,
147                  const struct afswtch *rafp __unused)
148 {
149         int     mode;
150
151         if (strcasecmp(val, "none") == 0) {
152                 mode = IEEE80211_AUTH_NONE;
153         } else if (strcasecmp(val, "open") == 0) {
154                 mode = IEEE80211_AUTH_OPEN;
155         } else if (strcasecmp(val, "shared") == 0) {
156                 mode = IEEE80211_AUTH_SHARED;
157         } else {
158                 err(1, "unknown authmode");
159         }
160
161         set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
162 }
163
164 void
165 set80211powersavemode(const char *val, int d __unused, int s,
166                       const struct afswtch *rafp __unused)
167 {
168         int     mode;
169
170         if (strcasecmp(val, "off") == 0) {
171                 mode = IEEE80211_POWERSAVE_OFF;
172         } else if (strcasecmp(val, "on") == 0) {
173                 mode = IEEE80211_POWERSAVE_ON;
174         } else if (strcasecmp(val, "cam") == 0) {
175                 mode = IEEE80211_POWERSAVE_CAM;
176         } else if (strcasecmp(val, "psp") == 0) {
177                 mode = IEEE80211_POWERSAVE_PSP;
178         } else if (strcasecmp(val, "psp-cam") == 0) {
179                 mode = IEEE80211_POWERSAVE_PSP_CAM;
180         } else {
181                 err(1, "unknown powersavemode");
182         }
183
184         set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
185 }
186
187 void
188 set80211powersave(const char *val __unused, int d, int s,
189                   const struct afswtch *rafp __unused)
190 {
191         if (d == 0)
192                 set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
193                     0, NULL);
194         else
195                 set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
196                     0, NULL);
197 }
198
199 void
200 set80211powersavesleep(const char *val, int d __unused, int s,
201                        const struct afswtch *rafp __unused)
202 {
203         set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
204 }
205
206 void
207 set80211wepmode(const char *val, int d __unused, int s,
208                 const struct afswtch *rafp __unused)
209 {
210         int     mode;
211
212         if (strcasecmp(val, "off") == 0) {
213                 mode = IEEE80211_WEP_OFF;
214         } else if (strcasecmp(val, "on") == 0) {
215                 mode = IEEE80211_WEP_ON;
216         } else if (strcasecmp(val, "mixed") == 0) {
217                 mode = IEEE80211_WEP_MIXED;
218         } else {
219                 err(1, "unknown wep mode");
220         }
221
222         set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
223 }
224
225 void
226 set80211wep(const char *val __unused, int d, int s,
227             const struct afswtch *rafp __unused)
228 {
229         set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
230 }
231
232 void
233 set80211weptxkey(const char *val, int d __unused, int s,
234                  const struct afswtch *rafp __unused)
235 {
236         set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
237 }
238
239 void
240 set80211wepkey(const char *val, int d __unused, int s,
241                const struct afswtch *rafp __unused)
242 {
243         int             key = 0;
244         int             len;
245         u_int8_t        data[IEEE80211_KEYBUF_SIZE];
246
247         if (isdigit(val[0]) && val[1] == ':') {
248                 key = atoi(val)-1;
249                 val += 2;
250         }
251
252         bzero(data, sizeof(data));
253         len = sizeof(data);
254         get_string(val, NULL, data, &len);
255
256         set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
257 }
258
259 /*
260  * This function is purly a NetBSD compatability interface.  The NetBSD
261  * iterface is too inflexable, but it's there so we'll support it since
262  * it's not all that hard.
263  */
264 void
265 set80211nwkey(const char *val, int d __unused, int s,
266               const struct afswtch *rafp __unused)
267 {
268         int             txkey;
269         int             i, len;
270         u_int8_t        data[IEEE80211_KEYBUF_SIZE];
271
272         set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
273
274         if (isdigit(val[0]) && val[1] == ':') {
275                 txkey = val[0]-'0'-1;
276                 val += 2;
277
278                 for (i = 0; i < 4; i++) {
279                         bzero(data, sizeof(data));
280                         len = sizeof(data);
281                         val = get_string(val, ",", data, &len);
282
283                         set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
284                 }
285         } else {
286                 bzero(data, sizeof(data));
287                 len = sizeof(data);
288                 get_string(val, NULL, data, &len);
289                 txkey = 0;
290
291                 set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
292
293                 bzero(data, sizeof(data));
294                 for (i = 1; i < 4; i++)
295                         set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
296         }
297
298         set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
299 }
300
301 void
302 set80211rtsthreshold(const char *val, int d __unused, int s,
303         const struct afswtch *rafp __unused)
304 {
305         set80211(s, IEEE80211_IOC_RTSTHRESHOLD, atoi(val), 0, NULL);
306 }
307
308 void
309 set80211protmode(const char *val, int d __unused, int s,
310         const struct afswtch *rafp __unused)
311 {
312         int     mode;
313
314         if (strcasecmp(val, "off") == 0) {
315                 mode = IEEE80211_PROTMODE_OFF;
316         } else if (strcasecmp(val, "cts") == 0) {
317                 mode = IEEE80211_PROTMODE_CTS;
318         } else if (strcasecmp(val, "rtscts") == 0) {
319                 mode = IEEE80211_PROTMODE_RTSCTS;
320         } else {
321                 err(1, "unknown protection mode");
322         }
323
324         set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
325 }
326
327 void
328 set80211txpower(const char *val, int d __unused, int s,
329         const struct afswtch *rafp __unused)
330 {
331         set80211(s, IEEE80211_IOC_TXPOWER, atoi(val), 0, NULL);
332 }
333
334 void
335 ieee80211_status (int s, struct rt_addrinfo *info __unused)
336 {
337         int                     i;
338         int                     num;
339         struct ieee80211req     ireq;
340         u_int8_t                data[32];
341         char                    spacer;
342
343         (void) memset(&ireq, 0, sizeof(ireq));
344         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
345         ireq.i_data = &data;
346
347         ireq.i_type = IEEE80211_IOC_SSID;
348         ireq.i_val = -1;
349         if (ioctl(s, SIOCG80211, &ireq) < 0) {
350                 /* If we can't get the SSID, the this isn't an 802.11 device. */
351                 return;
352         }
353         printf("\tssid ");
354         print_string(data, ireq.i_len);
355         num = 0;
356         ireq.i_type = IEEE80211_IOC_NUMSSIDS;
357         if (ioctl(s, SIOCG80211, &ireq) >= 0) {
358                 num = ireq.i_val;
359         }
360         ireq.i_type = IEEE80211_IOC_SSID;
361         for (ireq.i_val = 0; ireq.i_val < num; ireq.i_val++) {
362                 if (ioctl(s, SIOCG80211, &ireq) >= 0 && ireq.i_len > 0) {
363                         printf(" %d:", ireq.i_val + 1);
364                         print_string(data, ireq.i_len);
365                 }
366         }
367         printf("\n");
368
369         ireq.i_type = IEEE80211_IOC_STATIONNAME;
370         if (ioctl(s, SIOCG80211, &ireq) != -1) {
371                 printf("\tstationname ");
372                 print_string(data, ireq.i_len);
373                 printf("\n");
374         }
375
376         ireq.i_type = IEEE80211_IOC_CHANNEL;
377         if (ioctl(s, SIOCG80211, &ireq) < 0) {
378                 goto end;
379         }
380         printf("\tchannel %d", ireq.i_val);
381
382         ireq.i_type = IEEE80211_IOC_AUTHMODE;
383         if (ioctl(s, SIOCG80211, &ireq) != -1) {
384                 printf(" authmode");
385                 switch (ireq.i_val) {
386                         case IEEE80211_AUTH_NONE:
387                                 printf(" NONE");
388                                 break;
389                         case IEEE80211_AUTH_OPEN:
390                                 printf(" OPEN");
391                                 break;
392                         case IEEE80211_AUTH_SHARED:
393                                 printf(" SHARED");
394                                 break;
395                         default:
396                                 printf(" UNKNOWN");
397                                 break;
398                 }
399         }
400
401         ireq.i_type = IEEE80211_IOC_POWERSAVE;
402         if (ioctl(s, SIOCG80211, &ireq) != -1 &&
403             ireq.i_val != IEEE80211_POWERSAVE_NOSUP ) {
404                 printf(" powersavemode");
405                 switch (ireq.i_val) {
406                         case IEEE80211_POWERSAVE_OFF:
407                                 printf(" OFF");
408                                 break;
409                         case IEEE80211_POWERSAVE_CAM:
410                                 printf(" CAM");
411                                 break;
412                         case IEEE80211_POWERSAVE_PSP:
413                                 printf(" PSP");
414                                 break;
415                         case IEEE80211_POWERSAVE_PSP_CAM:
416                                 printf(" PSP-CAM");
417                                 break;
418                 }
419
420                 ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP;
421                 if (ioctl(s, SIOCG80211, &ireq) != -1) {
422                         if (ireq.i_val)
423                                 printf(" powersavesleep %d", ireq.i_val);
424                 }
425         }
426
427         printf("\n");
428
429         spacer = '\t';
430         ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD;
431         if (ioctl(s, SIOCG80211, &ireq) != -1) {
432                 printf("%crtsthreshold %d", spacer, ireq.i_val);
433                 spacer = ' ';
434         }
435
436         ireq.i_type = IEEE80211_IOC_PROTMODE;
437         if (ioctl(s, SIOCG80211, &ireq) != -1) {
438                 printf("%cprotmode", spacer);
439                 switch (ireq.i_val) {
440                         case IEEE80211_PROTMODE_OFF:
441                                 printf(" OFF");
442                                 break;
443                         case IEEE80211_PROTMODE_CTS:
444                                 printf(" CTS");
445                                 break;
446                         case IEEE80211_PROTMODE_RTSCTS:
447                                 printf(" RTSCTS");
448                                 break;
449                         default:
450                                 printf(" UNKNOWN");
451                                 break;
452                 }
453                 spacer = ' ';
454         }
455
456         ireq.i_type = IEEE80211_IOC_TXPOWER;
457         if (ioctl(s, SIOCG80211, &ireq) != -1) {
458                 printf("%ctxpower %d", spacer, ireq.i_val);
459                 spacer = ' ';
460         }
461
462         if (spacer != '\t')
463                 printf("\n");
464
465         ireq.i_type = IEEE80211_IOC_WEP;
466         if (ioctl(s, SIOCG80211, &ireq) != -1 &&
467             ireq.i_val != IEEE80211_WEP_NOSUP) {
468                 printf("\twepmode");
469                 switch (ireq.i_val) {
470                         case IEEE80211_WEP_OFF:
471                                 printf(" OFF");
472                                 break;
473                         case IEEE80211_WEP_ON:
474                                 printf(" ON");
475                                 break;
476                         case IEEE80211_WEP_MIXED:
477                                 printf(" MIXED");
478                                 break;
479                         default:
480                                 printf(" UNKNOWN");
481                                 break;
482                 }
483
484                 /*
485                  * If we get here then we've got WEP support so we need
486                  * to print WEP status.
487                  */
488
489                 ireq.i_type = IEEE80211_IOC_WEPTXKEY;
490                 if (ioctl(s, SIOCG80211, &ireq) < 0) {
491                         warn("WEP support, but no tx key!");
492                         goto end;
493                 }
494                 printf(" weptxkey %d", ireq.i_val+1);
495
496                 ireq.i_type = IEEE80211_IOC_NUMWEPKEYS;
497                 if (ioctl(s, SIOCG80211, &ireq) < 0) {
498                         warn("WEP support, but no NUMWEPKEYS support!");
499                         goto end;
500                 }
501                 num = ireq.i_val;
502
503                 printf("\n");
504
505                 ireq.i_type = IEEE80211_IOC_WEPKEY;
506                 spacer = '\t';
507                 for (i = 0; i < num; i++) {
508                         ireq.i_val = i;
509                         if (ioctl(s, SIOCG80211, &ireq) < 0) {
510                                 warn("WEP support, but can get keys!");
511                                 goto end;
512                         }
513                         if (ireq.i_len == 0 ||
514                             ireq.i_len > IEEE80211_KEYBUF_SIZE)
515                                 continue;
516                         printf("%cwepkey %d:%s", spacer, i+1,
517                             ireq.i_len <= 5 ? "40-bit" :
518                             ireq.i_len <= 13 ? "104-bit" : "128-bit");
519                         if (spacer == '\t')
520                                 spacer = ' ';
521                 }
522                 if (spacer == ' ')
523                         printf("\n");
524         }
525
526 end:
527         return;
528 }
529
530 static void
531 set80211(int s, int type, int val, int len, u_int8_t *data)
532 {
533         struct ieee80211req     ireq;
534
535         (void) memset(&ireq, 0, sizeof(ireq));
536         (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
537         ireq.i_type = type;
538         ireq.i_val = val;
539         ireq.i_len = len;
540         ireq.i_data = data;
541         if (ioctl(s, SIOCS80211, &ireq) < 0)
542                 err(1, "SIOCS80211");
543 }
544
545 static const char *
546 get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
547 {
548         int len;
549         int hexstr;
550         u_int8_t *p;
551
552         len = *lenp;
553         p = buf;
554         hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
555         if (hexstr)
556                 val += 2;
557         for (;;) {
558                 if (*val == '\0')
559                         break;
560                 if (sep != NULL && strchr(sep, *val) != NULL) {
561                         val++;
562                         break;
563                 }
564                 if (hexstr) {
565                         if (!isxdigit((u_char)val[0])) {
566                                 warnx("bad hexadecimal digits");
567                                 return NULL;
568                         }
569                         if (!isxdigit((u_char)val[1])) {
570                                 warnx("odd count hexadecimal digits");
571                                 return NULL;
572                         }
573                 }
574                 if (p >= buf + len) {
575                         if (hexstr)
576                                 warnx("hexadecimal digits too long");
577                         else
578                                 warnx("string too long");
579                         return NULL;
580                 }
581                 if (hexstr) {
582 #define tohex(x)        (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
583                         *p++ = (tohex((u_char)val[0]) << 4) |
584                             tohex((u_char)val[1]);
585 #undef tohex
586                         val += 2;
587                 } else
588                         *p++ = *val++;
589         }
590         len = p - buf;
591         /* The string "-" is treated as the empty string. */
592         if (!hexstr && len == 1 && buf[0] == '-')
593                 len = 0;
594         if (len < *lenp)
595                 memset(p, 0, *lenp - len);
596         *lenp = len;
597         return val;
598 }
599
600 static void
601 print_string(const u_int8_t *buf, int len)
602 {
603         int i;
604         int hasspc;
605
606         i = 0;
607         hasspc = 0;
608         for (; i < len; i++) {
609                 if (!isprint(buf[i]) && buf[i] != '\0')
610                         break;
611                 if (isspace(buf[i]))
612                         hasspc++;
613         }
614         if (i == len) {
615                 if (hasspc || len == 0 || buf[0] == '\0')
616                         printf("\"%.*s\"", len, buf);
617                 else
618                         printf("%.*s", len, buf);
619         } else {
620                 printf("0x");
621                 for (i = 0; i < len; i++)
622                         printf("%02x", buf[i]);
623         }
624 }
625