ifieee80211.c revision 127831
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: head/sbin/ifconfig/ifieee80211.c 127831 2004-04-04 07:28:58Z phk $
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
93static void set80211(int s, int type, int val, int len, u_int8_t *data);
94static const char *get_string(const char *val, const char *sep,
95    u_int8_t *buf, int *lenp);
96static void print_string(const u_int8_t *buf, int len);
97
98void
99set80211ssid(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
119void
120set80211stationname(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
132void
133set80211channel(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
141void
142set80211authmode(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
159void
160set80211powersavemode(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
181void
182set80211powersave(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
192void
193set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
194{
195	set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
196}
197
198void
199set80211wepmode(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
216void
217set80211wep(const char *val, int d, int s, const struct afswtch *rafp)
218{
219	set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
220}
221
222void
223set80211weptxkey(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
228void
229set80211wepkey(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 */
252void
253set80211nwkey(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
288void
289set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
290{
291	set80211(s, IEEE80211_IOC_RTSTHRESHOLD, atoi(val), 0, NULL);
292}
293
294void
295set80211protmode(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
312void
313set80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
314{
315	set80211(s, IEEE80211_IOC_TXPOWER, atoi(val), 0, NULL);
316}
317
318void
319ieee80211_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
510end:
511	return;
512}
513
514static void
515set80211(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
529static const char *
530get_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
584static void
585print_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