xref: /freebsd/sbin/ifconfig/ifieee80211.c (revision 2546665afcaf0d53dc2c7058fee96354b3680f5a)
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/route.h>
78 #include <net80211/ieee80211.h>
79 #include <net80211/ieee80211_crypto.h>
80 #include <net80211/ieee80211_ioctl.h>
81 
82 #include <ctype.h>
83 #include <err.h>
84 #include <errno.h>
85 #include <fcntl.h>
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <string.h>
89 #include <unistd.h>
90 
91 #include "ifconfig.h"
92 
93 static void set80211(int s, int type, int val, int len, u_int8_t *data);
94 static const char *get_string(const char *val, const char *sep,
95     u_int8_t *buf, int *lenp);
96 static void print_string(const u_int8_t *buf, int len);
97 
98 void
99 set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
100 {
101 	int		ssid;
102 	int		len;
103 	u_int8_t	data[33];
104 
105 	ssid = 0;
106 	len = strlen(val);
107 	if (len > 2 && isdigit(val[0]) && val[1] == ':') {
108 		ssid = atoi(val)-1;
109 		val += 2;
110 	}
111 
112 	bzero(data, sizeof(data));
113 	len = sizeof(data);
114 	get_string(val, NULL, data, &len);
115 
116 	set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
117 }
118 
119 void
120 set80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
121 {
122 	int			len;
123 	u_int8_t		data[33];
124 
125 	bzero(data, sizeof(data));
126 	len = sizeof(data);
127 	get_string(val, NULL, data, &len);
128 
129 	set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
130 }
131 
132 void
133 set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
134 {
135 	if (strcmp(val, "-") == 0)
136 		set80211(s, IEEE80211_IOC_CHANNEL, IEEE80211_CHAN_ANY, 0, NULL);
137 	else
138 		set80211(s, IEEE80211_IOC_CHANNEL, atoi(val), 0, NULL);
139 }
140 
141 void
142 set80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
143 {
144 	int	mode;
145 
146 	if (strcasecmp(val, "none") == 0) {
147 		mode = IEEE80211_AUTH_NONE;
148 	} else if (strcasecmp(val, "open") == 0) {
149 		mode = IEEE80211_AUTH_OPEN;
150 	} else if (strcasecmp(val, "shared") == 0) {
151 		mode = IEEE80211_AUTH_SHARED;
152 	} else {
153 		err(1, "unknown authmode");
154 	}
155 
156 	set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
157 }
158 
159 void
160 set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
161 {
162 	int	mode;
163 
164 	if (strcasecmp(val, "off") == 0) {
165 		mode = IEEE80211_POWERSAVE_OFF;
166 	} else if (strcasecmp(val, "on") == 0) {
167 		mode = IEEE80211_POWERSAVE_ON;
168 	} else if (strcasecmp(val, "cam") == 0) {
169 		mode = IEEE80211_POWERSAVE_CAM;
170 	} else if (strcasecmp(val, "psp") == 0) {
171 		mode = IEEE80211_POWERSAVE_PSP;
172 	} else if (strcasecmp(val, "psp-cam") == 0) {
173 		mode = IEEE80211_POWERSAVE_PSP_CAM;
174 	} else {
175 		err(1, "unknown powersavemode");
176 	}
177 
178 	set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
179 }
180 
181 void
182 set80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
183 {
184 	if (d == 0)
185 		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
186 		    0, NULL);
187 	else
188 		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
189 		    0, NULL);
190 }
191 
192 void
193 set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
194 {
195 	set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
196 }
197 
198 void
199 set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
200 {
201 	int	mode;
202 
203 	if (strcasecmp(val, "off") == 0) {
204 		mode = IEEE80211_WEP_OFF;
205 	} else if (strcasecmp(val, "on") == 0) {
206 		mode = IEEE80211_WEP_ON;
207 	} else if (strcasecmp(val, "mixed") == 0) {
208 		mode = IEEE80211_WEP_MIXED;
209 	} else {
210 		err(1, "unknown wep mode");
211 	}
212 
213 	set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
214 }
215 
216 void
217 set80211wep(const char *val, int d, int s, const struct afswtch *rafp)
218 {
219 	set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
220 }
221 
222 void
223 set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
224 {
225 	set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
226 }
227 
228 void
229 set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
230 {
231 	int		key = 0;
232 	int		len;
233 	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
234 
235 	if (isdigit(val[0]) && val[1] == ':') {
236 		key = atoi(val)-1;
237 		val += 2;
238 	}
239 
240 	bzero(data, sizeof(data));
241 	len = sizeof(data);
242 	get_string(val, NULL, data, &len);
243 
244 	set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
245 }
246 
247 /*
248  * This function is purly a NetBSD compatability interface.  The NetBSD
249  * iterface is too inflexable, but it's there so we'll support it since
250  * it's not all that hard.
251  */
252 void
253 set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
254 {
255 	int		txkey;
256 	int		i, len;
257 	u_int8_t	data[IEEE80211_KEYBUF_SIZE];
258 
259 	set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
260 
261 	if (isdigit(val[0]) && val[1] == ':') {
262 		txkey = val[0]-'0'-1;
263 		val += 2;
264 
265 		for (i = 0; i < 4; i++) {
266 			bzero(data, sizeof(data));
267 			len = sizeof(data);
268 			val = get_string(val, ",", data, &len);
269 
270 			set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
271 		}
272 	} else {
273 		bzero(data, sizeof(data));
274 		len = sizeof(data);
275 		get_string(val, NULL, data, &len);
276 		txkey = 0;
277 
278 		set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
279 
280 		bzero(data, sizeof(data));
281 		for (i = 1; i < 4; i++)
282 			set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
283 	}
284 
285 	set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
286 }
287 
288 void
289 set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
290 {
291 	set80211(s, IEEE80211_IOC_RTSTHRESHOLD, atoi(val), 0, NULL);
292 }
293 
294 void
295 set80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
296 {
297 	int	mode;
298 
299 	if (strcasecmp(val, "off") == 0) {
300 		mode = IEEE80211_PROTMODE_OFF;
301 	} else if (strcasecmp(val, "cts") == 0) {
302 		mode = IEEE80211_PROTMODE_CTS;
303 	} else if (strcasecmp(val, "rtscts") == 0) {
304 		mode = IEEE80211_PROTMODE_RTSCTS;
305 	} else {
306 		err(1, "unknown protection mode");
307 	}
308 
309 	set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
310 }
311 
312 void
313 set80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
314 {
315 	set80211(s, IEEE80211_IOC_TXPOWER, atoi(val), 0, NULL);
316 }
317 
318 void
319 ieee80211_status (int s, struct rt_addrinfo *info __unused)
320 {
321 	int			i;
322 	int			num;
323 	struct ieee80211req	ireq;
324 	u_int8_t		data[32];
325 	char			spacer;
326 
327 	(void) memset(&ireq, 0, sizeof(ireq));
328 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
329 	ireq.i_data = &data;
330 
331 	ireq.i_type = IEEE80211_IOC_SSID;
332 	ireq.i_val = -1;
333 	if (ioctl(s, SIOCG80211, &ireq) < 0) {
334 		/* If we can't get the SSID, the this isn't an 802.11 device. */
335 		return;
336 	}
337 	printf("\tssid ");
338 	print_string(data, ireq.i_len);
339 	num = 0;
340 	ireq.i_type = IEEE80211_IOC_NUMSSIDS;
341 	if (ioctl(s, SIOCG80211, &ireq) >= 0) {
342 		num = ireq.i_val;
343 	}
344 	ireq.i_type = IEEE80211_IOC_SSID;
345 	for (ireq.i_val = 0; ireq.i_val < num; ireq.i_val++) {
346 		if (ioctl(s, SIOCG80211, &ireq) >= 0 && ireq.i_len > 0) {
347 			printf(" %d:", ireq.i_val + 1);
348 			print_string(data, ireq.i_len);
349 		}
350 	}
351 	printf("\n");
352 
353 	ireq.i_type = IEEE80211_IOC_STATIONNAME;
354 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
355 		printf("\tstationname ");
356 		print_string(data, ireq.i_len);
357 		printf("\n");
358 	}
359 
360 	ireq.i_type = IEEE80211_IOC_CHANNEL;
361 	if (ioctl(s, SIOCG80211, &ireq) < 0) {
362 		goto end;
363 	}
364 	printf("\tchannel %d", ireq.i_val);
365 
366 	ireq.i_type = IEEE80211_IOC_AUTHMODE;
367 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
368 		printf(" authmode");
369 		switch (ireq.i_val) {
370 			case IEEE80211_AUTH_NONE:
371 				printf(" NONE");
372 				break;
373 			case IEEE80211_AUTH_OPEN:
374 				printf(" OPEN");
375 				break;
376 			case IEEE80211_AUTH_SHARED:
377 				printf(" SHARED");
378 				break;
379 			default:
380 				printf(" UNKNOWN");
381 				break;
382 		}
383 	}
384 
385 	ireq.i_type = IEEE80211_IOC_POWERSAVE;
386 	if (ioctl(s, SIOCG80211, &ireq) != -1 &&
387 	    ireq.i_val != IEEE80211_POWERSAVE_NOSUP ) {
388 		printf(" powersavemode");
389 		switch (ireq.i_val) {
390 			case IEEE80211_POWERSAVE_OFF:
391 				printf(" OFF");
392 				break;
393 			case IEEE80211_POWERSAVE_CAM:
394 				printf(" CAM");
395 				break;
396 			case IEEE80211_POWERSAVE_PSP:
397 				printf(" PSP");
398 				break;
399 			case IEEE80211_POWERSAVE_PSP_CAM:
400 				printf(" PSP-CAM");
401 				break;
402 		}
403 
404 		ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP;
405 		if (ioctl(s, SIOCG80211, &ireq) != -1) {
406 			if (ireq.i_val)
407 				printf(" powersavesleep %d", ireq.i_val);
408 		}
409 	}
410 
411 	printf("\n");
412 
413 	spacer = '\t';
414 	ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD;
415 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
416 		printf("%crtsthreshold %d", spacer, ireq.i_val);
417 		spacer = ' ';
418 	}
419 
420 	ireq.i_type = IEEE80211_IOC_PROTMODE;
421 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
422 		printf("%cprotmode", spacer);
423 		switch (ireq.i_val) {
424 			case IEEE80211_PROTMODE_OFF:
425 				printf(" OFF");
426 				break;
427 			case IEEE80211_PROTMODE_CTS:
428 				printf(" CTS");
429 				break;
430 			case IEEE80211_PROTMODE_RTSCTS:
431 				printf(" RTSCTS");
432 				break;
433 			default:
434 				printf(" UNKNOWN");
435 				break;
436 		}
437 		spacer = ' ';
438 	}
439 
440 	ireq.i_type = IEEE80211_IOC_TXPOWER;
441 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
442 		printf("%ctxpower %d", spacer, ireq.i_val);
443 		spacer = ' ';
444 	}
445 
446 	if (spacer != '\t')
447 		printf("\n");
448 
449 	ireq.i_type = IEEE80211_IOC_WEP;
450 	if (ioctl(s, SIOCG80211, &ireq) != -1 &&
451 	    ireq.i_val != IEEE80211_WEP_NOSUP) {
452 		printf("\twepmode");
453 		switch (ireq.i_val) {
454 			case IEEE80211_WEP_OFF:
455 				printf(" OFF");
456 				break;
457 			case IEEE80211_WEP_ON:
458 				printf(" ON");
459 				break;
460 			case IEEE80211_WEP_MIXED:
461 				printf(" MIXED");
462 				break;
463 			default:
464 				printf(" UNKNOWN");
465 				break;
466 		}
467 
468 		/*
469 		 * If we get here then we've got WEP support so we need
470 		 * to print WEP status.
471 		 */
472 
473 		ireq.i_type = IEEE80211_IOC_WEPTXKEY;
474 		if (ioctl(s, SIOCG80211, &ireq) < 0) {
475 			warn("WEP support, but no tx key!");
476 			goto end;
477 		}
478 		printf(" weptxkey %d", ireq.i_val+1);
479 
480 		ireq.i_type = IEEE80211_IOC_NUMWEPKEYS;
481 		if (ioctl(s, SIOCG80211, &ireq) < 0) {
482 			warn("WEP support, but no NUMWEPKEYS support!");
483 			goto end;
484 		}
485 		num = ireq.i_val;
486 
487 		printf("\n");
488 
489 		ireq.i_type = IEEE80211_IOC_WEPKEY;
490 		spacer = '\t';
491 		for (i = 0; i < num; i++) {
492 			ireq.i_val = i;
493 			if (ioctl(s, SIOCG80211, &ireq) < 0) {
494 				warn("WEP support, but can get keys!");
495 				goto end;
496 			}
497 			if (ireq.i_len == 0 ||
498 			    ireq.i_len > IEEE80211_KEYBUF_SIZE)
499 				continue;
500 			printf("%cwepkey %d:%s", spacer, i+1,
501 			    ireq.i_len <= 5 ? "40-bit" :
502 			    ireq.i_len <= 13 ? "104-bit" : "128-bit");
503 			if (spacer == '\t')
504 				spacer = ' ';
505 		}
506 		if (spacer == ' ')
507 			printf("\n");
508 	}
509 
510 end:
511 	return;
512 }
513 
514 static void
515 set80211(int s, int type, int val, int len, u_int8_t *data)
516 {
517 	struct ieee80211req	ireq;
518 
519 	(void) memset(&ireq, 0, sizeof(ireq));
520 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
521 	ireq.i_type = type;
522 	ireq.i_val = val;
523 	ireq.i_len = len;
524 	ireq.i_data = data;
525 	if (ioctl(s, SIOCS80211, &ireq) < 0)
526 		err(1, "SIOCS80211");
527 }
528 
529 static const char *
530 get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
531 {
532 	int len;
533 	int hexstr;
534 	u_int8_t *p;
535 
536 	len = *lenp;
537 	p = buf;
538 	hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
539 	if (hexstr)
540 		val += 2;
541 	for (;;) {
542 		if (*val == '\0')
543 			break;
544 		if (sep != NULL && strchr(sep, *val) != NULL) {
545 			val++;
546 			break;
547 		}
548 		if (hexstr) {
549 			if (!isxdigit((u_char)val[0])) {
550 				warnx("bad hexadecimal digits");
551 				return NULL;
552 			}
553 			if (!isxdigit((u_char)val[1])) {
554 				warnx("odd count hexadecimal digits");
555 				return NULL;
556 			}
557 		}
558 		if (p >= buf + len) {
559 			if (hexstr)
560 				warnx("hexadecimal digits too long");
561 			else
562 				warnx("string too long");
563 			return NULL;
564 		}
565 		if (hexstr) {
566 #define	tohex(x)	(isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
567 			*p++ = (tohex((u_char)val[0]) << 4) |
568 			    tohex((u_char)val[1]);
569 #undef tohex
570 			val += 2;
571 		} else
572 			*p++ = *val++;
573 	}
574 	len = p - buf;
575 	/* The string "-" is treated as the empty string. */
576 	if (!hexstr && len == 1 && buf[0] == '-')
577 		len = 0;
578 	if (len < *lenp)
579 		memset(p, 0, *lenp - len);
580 	*lenp = len;
581 	return val;
582 }
583 
584 static void
585 print_string(const u_int8_t *buf, int len)
586 {
587 	int i;
588 	int hasspc;
589 
590 	i = 0;
591 	hasspc = 0;
592 	for (; i < len; i++) {
593 		if (!isprint(buf[i]) && buf[i] != '\0')
594 			break;
595 		if (isspace(buf[i]))
596 			hasspc++;
597 	}
598 	if (i == len) {
599 		if (hasspc || len == 0 || buf[0] == '\0')
600 			printf("\"%.*s\"", len, buf);
601 		else
602 			printf("%.*s", len, buf);
603 	} else {
604 		printf("0x");
605 		for (i = 0; i < len; i++)
606 			printf("%02x", buf[i]);
607 	}
608 }
609 
610