xref: /dragonfly/sbin/ifconfig/ifieee80211.c (revision 1bf4b486)
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 
626