xref: /netbsd-src/sbin/ifconfig/ieee80211.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /*	$NetBSD: ieee80211.c,v 1.3 2005/09/15 23:35:15 dyoung Exp $	*/
2 
3 /*
4  * Copyright (c) 1983, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __RCSID("$NetBSD: ieee80211.c,v 1.3 2005/09/15 23:35:15 dyoung Exp $");
35 #endif /* not lint */
36 
37 #include <sys/param.h>
38 #include <sys/ioctl.h>
39 #include <sys/socket.h>
40 
41 #include <net/if.h>
42 #include <net/if_ether.h>
43 #include <net80211/ieee80211.h>
44 #include <net80211/ieee80211_ioctl.h>
45 
46 #include <ctype.h>
47 #include <err.h>
48 #include <netdb.h>
49 #include <string.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 
53 #include "extern.h"
54 #include "ieee80211.h"
55 
56 void
57 setifnwid(const char *val, int d)
58 {
59 	struct ieee80211_nwid nwid;
60 	int len;
61 
62 	len = sizeof(nwid.i_nwid);
63 	if (get_string(val, NULL, nwid.i_nwid, &len) == NULL)
64 		return;
65 	nwid.i_len = len;
66 	(void)strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
67 	ifr.ifr_data = (void *)&nwid;
68 	if (ioctl(s, SIOCS80211NWID, &ifr) == -1)
69 		warn("SIOCS80211NWID");
70 }
71 
72 void
73 setifbssid(const char *val, int d)
74 {
75 	struct ieee80211_bssid bssid;
76 	struct ether_addr *ea;
77 
78 	if (d != 0) {
79 		/* no BSSID is especially desired */
80 		memset(&bssid.i_bssid, 0, sizeof(bssid.i_bssid));
81 	} else {
82 		ea = ether_aton(val);
83 		if (ea == NULL) {
84 			warnx("malformed BSSID: %s", val);
85 			return;
86 		}
87 		memcpy(&bssid.i_bssid, ea->ether_addr_octet,
88 		    sizeof(bssid.i_bssid));
89 	}
90 	(void)strncpy(bssid.i_name, name, sizeof(bssid.i_name));
91 	if (ioctl(s, SIOCS80211BSSID, &bssid) == -1)
92 		warn("SIOCS80211BSSID");
93 }
94 
95 void
96 setifchan(const char *val, int d)
97 {
98 	struct ieee80211chanreq channel;
99 	int chan;
100 
101 	if (d != 0)
102 		chan = IEEE80211_CHAN_ANY;
103 	else {
104 		chan = atoi(val);
105 		if (chan < 0 || chan > 0xffff) {
106 			warnx("invalid channel: %s", val);
107 			return;
108 		}
109 	}
110 
111 	(void)strncpy(channel.i_name, name, sizeof(channel.i_name));
112 	channel.i_channel = (u_int16_t) chan;
113 	if (ioctl(s, SIOCS80211CHANNEL, &channel) == -1)
114 		warn("SIOCS80211CHANNEL");
115 }
116 
117 void
118 setifnwkey(const char *val, int d)
119 {
120 	struct ieee80211_nwkey nwkey;
121 	int i;
122 	u_int8_t keybuf[IEEE80211_WEP_NKID][16];
123 
124 	nwkey.i_wepon = IEEE80211_NWKEY_WEP;
125 	nwkey.i_defkid = 1;
126 	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
127 		nwkey.i_key[i].i_keylen = sizeof(keybuf[i]);
128 		nwkey.i_key[i].i_keydat = keybuf[i];
129 	}
130 	if (d != 0) {
131 		/* disable WEP encryption */
132 		nwkey.i_wepon = 0;
133 		i = 0;
134 	} else if (strcasecmp("persist", val) == 0) {
135 		/* use all values from persistent memory */
136 		nwkey.i_wepon |= IEEE80211_NWKEY_PERSIST;
137 		nwkey.i_defkid = 0;
138 		for (i = 0; i < IEEE80211_WEP_NKID; i++)
139 			nwkey.i_key[i].i_keylen = -1;
140 	} else if (strncasecmp("persist:", val, 8) == 0) {
141 		val += 8;
142 		/* program keys in persistent memory */
143 		nwkey.i_wepon |= IEEE80211_NWKEY_PERSIST;
144 		goto set_nwkey;
145 	} else {
146   set_nwkey:
147 		if (isdigit((unsigned char)val[0]) && val[1] == ':') {
148 			/* specifying a full set of four keys */
149 			nwkey.i_defkid = val[0] - '0';
150 			val += 2;
151 			for (i = 0; i < IEEE80211_WEP_NKID; i++) {
152 				val = get_string(val, ",", keybuf[i],
153 				    &nwkey.i_key[i].i_keylen);
154 				if (val == NULL)
155 					return;
156 			}
157 			if (*val != '\0') {
158 				warnx("SIOCS80211NWKEY: too many keys.");
159 				return;
160 			}
161 		} else {
162 			val = get_string(val, NULL, keybuf[0],
163 			    &nwkey.i_key[0].i_keylen);
164 			if (val == NULL)
165 				return;
166 			i = 1;
167 		}
168 	}
169 	for (; i < IEEE80211_WEP_NKID; i++)
170 		nwkey.i_key[i].i_keylen = 0;
171 	(void)strncpy(nwkey.i_name, name, sizeof(nwkey.i_name));
172 	if (ioctl(s, SIOCS80211NWKEY, &nwkey) == -1)
173 		warn("SIOCS80211NWKEY");
174 }
175 
176 void
177 setifpowersave(const char *val, int d)
178 {
179 	struct ieee80211_power power;
180 
181 	(void)strncpy(power.i_name, name, sizeof(power.i_name));
182 	if (ioctl(s, SIOCG80211POWER, &power) == -1) {
183 		warn("SIOCG80211POWER");
184 		return;
185 	}
186 
187 	power.i_enabled = d;
188 	if (ioctl(s, SIOCS80211POWER, &power) == -1)
189 		warn("SIOCS80211POWER");
190 }
191 
192 void
193 setifpowersavesleep(const char *val, int d)
194 {
195 	struct ieee80211_power power;
196 
197 	(void)strncpy(power.i_name, name, sizeof(power.i_name));
198 	if (ioctl(s, SIOCG80211POWER, &power) == -1) {
199 		warn("SIOCG80211POWER");
200 		return;
201 	}
202 
203 	power.i_maxsleep = atoi(val);
204 	if (ioctl(s, SIOCS80211POWER, &power) == -1)
205 		warn("SIOCS80211POWER");
206 }
207 
208 void
209 ieee80211_statistics(void)
210 {
211 	struct ieee80211_stats stats;
212 
213 	memset(&ifr, 0, sizeof(ifr));
214 	ifr.ifr_buflen = sizeof(stats);
215 	ifr.ifr_buf = (caddr_t)&stats;
216 	(void)strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
217 	if (ioctl(s, (zflag) ? SIOCG80211ZSTATS : SIOCG80211STATS,
218 	    (caddr_t)&ifr) == -1)
219 		return;
220 #define	STAT_PRINT(_member, _desc)	\
221 	printf("\t" _desc ": %" PRIu32 "\n", stats._member)
222 
223 	STAT_PRINT(is_rx_badversion, "rx frame with bad version");
224 	STAT_PRINT(is_rx_tooshort, "rx frame too short");
225 	STAT_PRINT(is_rx_wrongbss, "rx from wrong bssid");
226 	STAT_PRINT(is_rx_dup, "rx discard 'cuz dup");
227 	STAT_PRINT(is_rx_wrongdir, "rx w/ wrong direction");
228 	STAT_PRINT(is_rx_mcastecho, "rx discard 'cuz mcast echo");
229 	STAT_PRINT(is_rx_notassoc, "rx discard 'cuz sta !assoc");
230 	STAT_PRINT(is_rx_noprivacy, "rx w/ wep but privacy off");
231 	STAT_PRINT(is_rx_unencrypted, "rx w/o wep and privacy on");
232 	STAT_PRINT(is_rx_wepfail, "rx wep processing failed");
233 	STAT_PRINT(is_rx_decap, "rx decapsulation failed");
234 	STAT_PRINT(is_rx_mgtdiscard, "rx discard mgt frames");
235 	STAT_PRINT(is_rx_ctl, "rx discard ctrl frames");
236 	STAT_PRINT(is_rx_beacon, "rx beacon frames");
237 	STAT_PRINT(is_rx_rstoobig, "rx rate set truncated");
238 	STAT_PRINT(is_rx_elem_missing, "rx required element missin");
239 	STAT_PRINT(is_rx_elem_toobig, "rx element too big");
240 	STAT_PRINT(is_rx_elem_toosmall, "rx element too small");
241 	STAT_PRINT(is_rx_elem_unknown, "rx element unknown");
242 	STAT_PRINT(is_rx_badchan, "rx frame w/ invalid chan");
243 	STAT_PRINT(is_rx_chanmismatch, "rx frame chan mismatch");
244 	STAT_PRINT(is_rx_nodealloc, "rx frame dropped");
245 	STAT_PRINT(is_rx_ssidmismatch, "rx frame ssid mismatch ");
246 	STAT_PRINT(is_rx_auth_unsupported, "rx w/ unsupported auth alg");
247 	STAT_PRINT(is_rx_auth_fail, "rx sta auth failure");
248 	STAT_PRINT(is_rx_auth_countermeasures, "rx auth discard 'cuz CM");
249 	STAT_PRINT(is_rx_assoc_bss, "rx assoc from wrong bssid");
250 	STAT_PRINT(is_rx_assoc_notauth, "rx assoc w/o auth");
251 	STAT_PRINT(is_rx_assoc_capmismatch, "rx assoc w/ cap mismatch");
252 	STAT_PRINT(is_rx_assoc_norate, "rx assoc w/ no rate match");
253 	STAT_PRINT(is_rx_assoc_badwpaie, "rx assoc w/ bad WPA IE");
254 	STAT_PRINT(is_rx_deauth, "rx deauthentication");
255 	STAT_PRINT(is_rx_disassoc, "rx disassociation");
256 	STAT_PRINT(is_rx_badsubtype, "rx frame w/ unknown subtyp");
257 	STAT_PRINT(is_rx_nobuf, "rx failed for lack of buf");
258 	STAT_PRINT(is_rx_decryptcrc, "rx decrypt failed on crc");
259 	STAT_PRINT(is_rx_ahdemo_mgt, "rx discard ahdemo mgt fram");
260 	STAT_PRINT(is_rx_bad_auth, "rx bad auth request");
261 	STAT_PRINT(is_rx_unauth, "rx on unauthorized port");
262 	STAT_PRINT(is_rx_badkeyid, "rx w/ incorrect keyid");
263 	STAT_PRINT(is_rx_ccmpreplay, "rx seq# violation (CCMP)");
264 	STAT_PRINT(is_rx_ccmpformat, "rx format bad (CCMP)");
265 	STAT_PRINT(is_rx_ccmpmic, "rx MIC check failed (CCMP)");
266 	STAT_PRINT(is_rx_tkipreplay, "rx seq# violation (TKIP)");
267 	STAT_PRINT(is_rx_tkipformat, "rx format bad (TKIP)");
268 	STAT_PRINT(is_rx_tkipmic, "rx MIC check failed (TKIP)");
269 	STAT_PRINT(is_rx_tkipicv, "rx ICV check failed (TKIP)");
270 	STAT_PRINT(is_rx_badcipher, "rx failed 'cuz key type");
271 	STAT_PRINT(is_rx_nocipherctx, "rx failed 'cuz key !setup");
272 	STAT_PRINT(is_rx_acl, "rx discard 'cuz acl policy");
273 
274 	STAT_PRINT(is_tx_nobuf, "tx failed for lack of buf");
275 	STAT_PRINT(is_tx_nonode, "tx failed for no node");
276 	STAT_PRINT(is_tx_unknownmgt, "tx of unknown mgt frame");
277 	STAT_PRINT(is_tx_badcipher, "tx failed 'cuz key type");
278 	STAT_PRINT(is_tx_nodefkey, "tx failed 'cuz no defkey");
279 	STAT_PRINT(is_tx_noheadroom, "tx failed 'cuz no space");
280 
281 	STAT_PRINT(is_scan_active, "active scans started");
282 	STAT_PRINT(is_scan_passive, "passive scans started");
283 	STAT_PRINT(is_node_timeout, "nodes timed out inactivity");
284 	STAT_PRINT(is_crypto_nomem, "no memory for crypto ctx");
285 	STAT_PRINT(is_crypto_tkip, "tkip crypto done in s/w");
286 	STAT_PRINT(is_crypto_tkipenmic, "tkip en-MIC done in s/w");
287 	STAT_PRINT(is_crypto_tkipdemic, "tkip de-MIC done in s/w");
288 	STAT_PRINT(is_crypto_tkipcm, "tkip counter measures");
289 	STAT_PRINT(is_crypto_ccmp, "ccmp crypto done in s/w");
290 	STAT_PRINT(is_crypto_wep, "wep crypto done in s/w");
291 	STAT_PRINT(is_crypto_setkey_cipher, "cipher rejected key");
292 	STAT_PRINT(is_crypto_setkey_nokey, "no key index for setkey");
293 	STAT_PRINT(is_crypto_delkey, "driver key delete failed");
294 	STAT_PRINT(is_crypto_badcipher, "unknown cipher");
295 	STAT_PRINT(is_crypto_nocipher, "cipher not available");
296 	STAT_PRINT(is_crypto_attachfail, "cipher attach failed");
297 	STAT_PRINT(is_crypto_swfallback, "cipher fallback to s/w");
298 	STAT_PRINT(is_crypto_keyfail, "driver key alloc failed");
299 	STAT_PRINT(is_crypto_enmicfail, "en-MIC failed");
300 	STAT_PRINT(is_ibss_capmismatch, "merge failed-cap mismatch");
301 	STAT_PRINT(is_ibss_norate, "merge failed-rate mismatch");
302 	STAT_PRINT(is_ps_unassoc, "ps-poll for unassoc. sta");
303 	STAT_PRINT(is_ps_badaid, "ps-poll w/ incorrect aid");
304 	STAT_PRINT(is_ps_qempty, "ps-poll w/ nothing to send");
305 }
306 
307 void
308 ieee80211_status(void)
309 {
310 	int i, nwkey_verbose;
311 	struct ieee80211_nwid nwid;
312 	struct ieee80211_nwkey nwkey;
313 	struct ieee80211_power power;
314 	u_int8_t keybuf[IEEE80211_WEP_NKID][16];
315 	struct ieee80211_bssid bssid;
316 	struct ieee80211chanreq channel;
317 	struct ether_addr ea;
318 	static const u_int8_t zero_macaddr[IEEE80211_ADDR_LEN];
319 
320 	memset(&ifr, 0, sizeof(ifr));
321 	ifr.ifr_data = (void *)&nwid;
322 	(void)strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
323 	if (ioctl(s, SIOCG80211NWID, &ifr) == -1)
324 		return;
325 	if (nwid.i_len > IEEE80211_NWID_LEN) {
326 		warnx("SIOCG80211NWID: wrong length of nwid (%d)", nwid.i_len);
327 		return;
328 	}
329 	printf("\tssid ");
330 	print_string(nwid.i_nwid, nwid.i_len);
331 	memset(&nwkey, 0, sizeof(nwkey));
332 	(void)strncpy(nwkey.i_name, name, sizeof(nwkey.i_name));
333 	/* show nwkey only when WEP is enabled */
334 	if (ioctl(s, SIOCG80211NWKEY, &nwkey) == -1 ||
335 	    nwkey.i_wepon == 0) {
336 		printf("\n");
337 		goto skip_wep;
338 	}
339 
340 	printf(" nwkey ");
341 	/* try to retrieve WEP keys */
342 	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
343 		nwkey.i_key[i].i_keydat = keybuf[i];
344 		nwkey.i_key[i].i_keylen = sizeof(keybuf[i]);
345 	}
346 	if (ioctl(s, SIOCG80211NWKEY, &nwkey) == -1) {
347 		printf("*****");
348 	} else {
349 		nwkey_verbose = 0;
350 		/* check to see non default key or multiple keys defined */
351 		if (nwkey.i_defkid != 1) {
352 			nwkey_verbose = 1;
353 		} else {
354 			for (i = 1; i < IEEE80211_WEP_NKID; i++) {
355 				if (nwkey.i_key[i].i_keylen != 0) {
356 					nwkey_verbose = 1;
357 					break;
358 				}
359 			}
360 		}
361 		/* check extra ambiguity with keywords */
362 		if (!nwkey_verbose) {
363 			if (nwkey.i_key[0].i_keylen >= 2 &&
364 			    isdigit(nwkey.i_key[0].i_keydat[0]) &&
365 			    nwkey.i_key[0].i_keydat[1] == ':')
366 				nwkey_verbose = 1;
367 			else if (nwkey.i_key[0].i_keylen >= 7 &&
368 			    strncasecmp("persist", nwkey.i_key[0].i_keydat, 7)
369 			    == 0)
370 				nwkey_verbose = 1;
371 		}
372 		if (nwkey_verbose)
373 			printf("%d:", nwkey.i_defkid);
374 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
375 			if (i > 0)
376 				printf(",");
377 			if (nwkey.i_key[i].i_keylen < 0)
378 				printf("persist");
379 			else
380 				print_string(nwkey.i_key[i].i_keydat,
381 				    nwkey.i_key[i].i_keylen);
382 			if (!nwkey_verbose)
383 				break;
384 		}
385 	}
386 	printf("\n");
387 
388  skip_wep:
389 	(void)strncpy(power.i_name, name, sizeof(power.i_name));
390 	if (ioctl(s, SIOCG80211POWER, &power) == -1)
391 		goto skip_power;
392 	printf("\tpowersave ");
393 	if (power.i_enabled)
394 		printf("on (%dms sleep)", power.i_maxsleep);
395 	else
396 		printf("off");
397 	printf("\n");
398 
399  skip_power:
400 	(void)strncpy(bssid.i_name, name, sizeof(bssid.i_name));
401 	if (ioctl(s, SIOCG80211BSSID, &bssid) == -1)
402 		return;
403 	(void)strncpy(channel.i_name, name, sizeof(channel.i_name));
404 	if (ioctl(s, SIOCG80211CHANNEL, &channel) == -1)
405 		return;
406 	if (memcmp(bssid.i_bssid, zero_macaddr, IEEE80211_ADDR_LEN) == 0) {
407 		if (channel.i_channel != (u_int16_t)-1)
408 			printf("\tchan %d\n", channel.i_channel);
409 	} else {
410 		memcpy(ea.ether_addr_octet, bssid.i_bssid,
411 		    sizeof(ea.ether_addr_octet));
412 		printf("\tbssid %s", ether_ntoa(&ea));
413 		if (channel.i_channel != IEEE80211_CHAN_ANY)
414 			printf(" chan %d", channel.i_channel);
415 		printf("\n");
416 	}
417 }
418