ieee80211.c revision 1.6 1 /* $NetBSD: ieee80211.c,v 1.6 2006/06/16 23:48:35 elad 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.6 2006/06/16 23:48:35 elad 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 <net/if_media.h>
44 #include <net80211/ieee80211.h>
45 #include <net80211/ieee80211_ioctl.h>
46
47 #include <ctype.h>
48 #include <err.h>
49 #include <netdb.h>
50 #include <string.h>
51 #include <stdlib.h>
52 #include <stdio.h>
53
54 #include "extern.h"
55 #include "ieee80211.h"
56
57 static void set80211(int, int, int, uint8_t *);
58
59 static void
60 set80211(int type, int val, int len, u_int8_t *data)
61 {
62 struct ieee80211req ireq;
63
64 (void) memset(&ireq, 0, sizeof(ireq));
65 estrlcpy(ireq.i_name, name, sizeof(ireq.i_name));
66 ireq.i_type = type;
67 ireq.i_val = val;
68 ireq.i_len = len;
69 ireq.i_data = data;
70 if (ioctl(s, SIOCS80211, &ireq) < 0)
71 err(1, "SIOCS80211");
72 }
73
74 void
75 sethidessid(const char *val, int d)
76 {
77 set80211(IEEE80211_IOC_HIDESSID, d, 0, NULL);
78 }
79
80 void
81 setapbridge(const char *val, int d)
82 {
83 set80211(IEEE80211_IOC_APBRIDGE, d, 0, NULL);
84 }
85
86 static enum ieee80211_opmode
87 get80211opmode(void)
88 {
89 struct ifmediareq ifmr;
90
91 (void) memset(&ifmr, 0, sizeof(ifmr));
92 estrlcpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
93 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
94 if (ifmr.ifm_current & IFM_IEEE80211_ADHOC)
95 return IEEE80211_M_IBSS; /* XXX ahdemo */
96 if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
97 return IEEE80211_M_HOSTAP;
98 if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
99 return IEEE80211_M_MONITOR;
100 }
101
102 return IEEE80211_M_STA;
103 }
104
105 void
106 setifnwid(const char *val, int d)
107 {
108 struct ieee80211_nwid nwid;
109 int len;
110
111 len = sizeof(nwid.i_nwid);
112 if (get_string(val, NULL, nwid.i_nwid, &len) == NULL)
113 return;
114 nwid.i_len = len;
115 estrlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
116 ifr.ifr_data = (void *)&nwid;
117 if (ioctl(s, SIOCS80211NWID, &ifr) == -1)
118 warn("SIOCS80211NWID");
119 }
120
121 void
122 setifbssid(const char *val, int d)
123 {
124 struct ieee80211_bssid bssid;
125 struct ether_addr *ea;
126
127 if (d != 0) {
128 /* no BSSID is especially desired */
129 memset(&bssid.i_bssid, 0, sizeof(bssid.i_bssid));
130 } else {
131 ea = ether_aton(val);
132 if (ea == NULL) {
133 warnx("malformed BSSID: %s", val);
134 return;
135 }
136 memcpy(&bssid.i_bssid, ea->ether_addr_octet,
137 sizeof(bssid.i_bssid));
138 }
139 estrlcpy(bssid.i_name, name, sizeof(bssid.i_name));
140 if (ioctl(s, SIOCS80211BSSID, &bssid) == -1)
141 warn("SIOCS80211BSSID");
142 }
143
144 void
145 setifchan(const char *val, int d)
146 {
147 struct ieee80211chanreq channel;
148 int chan;
149
150 if (d != 0)
151 chan = IEEE80211_CHAN_ANY;
152 else {
153 chan = atoi(val);
154 if (chan < 0 || chan > 0xffff) {
155 warnx("invalid channel: %s", val);
156 return;
157 }
158 }
159
160 estrlcpy(channel.i_name, name, sizeof(channel.i_name));
161 channel.i_channel = (u_int16_t) chan;
162 if (ioctl(s, SIOCS80211CHANNEL, &channel) == -1)
163 warn("SIOCS80211CHANNEL");
164 }
165
166 void
167 setifnwkey(const char *val, int d)
168 {
169 struct ieee80211_nwkey nwkey;
170 int i;
171 u_int8_t keybuf[IEEE80211_WEP_NKID][16];
172
173 nwkey.i_wepon = IEEE80211_NWKEY_WEP;
174 nwkey.i_defkid = 1;
175 for (i = 0; i < IEEE80211_WEP_NKID; i++) {
176 nwkey.i_key[i].i_keylen = sizeof(keybuf[i]);
177 nwkey.i_key[i].i_keydat = keybuf[i];
178 }
179 if (d != 0) {
180 /* disable WEP encryption */
181 nwkey.i_wepon = 0;
182 i = 0;
183 } else if (strcasecmp("persist", val) == 0) {
184 /* use all values from persistent memory */
185 nwkey.i_wepon |= IEEE80211_NWKEY_PERSIST;
186 nwkey.i_defkid = 0;
187 for (i = 0; i < IEEE80211_WEP_NKID; i++)
188 nwkey.i_key[i].i_keylen = -1;
189 } else if (strncasecmp("persist:", val, 8) == 0) {
190 val += 8;
191 /* program keys in persistent memory */
192 nwkey.i_wepon |= IEEE80211_NWKEY_PERSIST;
193 goto set_nwkey;
194 } else {
195 set_nwkey:
196 if (isdigit((unsigned char)val[0]) && val[1] == ':') {
197 /* specifying a full set of four keys */
198 nwkey.i_defkid = val[0] - '0';
199 val += 2;
200 for (i = 0; i < IEEE80211_WEP_NKID; i++) {
201 val = get_string(val, ",", keybuf[i],
202 &nwkey.i_key[i].i_keylen);
203 if (val == NULL)
204 return;
205 }
206 if (*val != '\0') {
207 warnx("SIOCS80211NWKEY: too many keys.");
208 return;
209 }
210 } else {
211 val = get_string(val, NULL, keybuf[0],
212 &nwkey.i_key[0].i_keylen);
213 if (val == NULL)
214 return;
215 i = 1;
216 }
217 }
218 for (; i < IEEE80211_WEP_NKID; i++)
219 nwkey.i_key[i].i_keylen = 0;
220 estrlcpy(nwkey.i_name, name, sizeof(nwkey.i_name));
221 if (ioctl(s, SIOCS80211NWKEY, &nwkey) == -1)
222 warn("SIOCS80211NWKEY");
223 }
224
225 void
226 setifpowersave(const char *val, int d)
227 {
228 struct ieee80211_power power;
229
230 estrlcpy(power.i_name, name, sizeof(power.i_name));
231 if (ioctl(s, SIOCG80211POWER, &power) == -1) {
232 warn("SIOCG80211POWER");
233 return;
234 }
235
236 power.i_enabled = d;
237 if (ioctl(s, SIOCS80211POWER, &power) == -1)
238 warn("SIOCS80211POWER");
239 }
240
241 void
242 setifpowersavesleep(const char *val, int d)
243 {
244 struct ieee80211_power power;
245
246 estrlcpy(power.i_name, name, sizeof(power.i_name));
247 if (ioctl(s, SIOCG80211POWER, &power) == -1) {
248 warn("SIOCG80211POWER");
249 return;
250 }
251
252 power.i_maxsleep = atoi(val);
253 if (ioctl(s, SIOCS80211POWER, &power) == -1)
254 warn("SIOCS80211POWER");
255 }
256
257 void
258 ieee80211_statistics(void)
259 {
260 struct ieee80211_stats stats;
261
262 memset(&ifr, 0, sizeof(ifr));
263 ifr.ifr_buflen = sizeof(stats);
264 ifr.ifr_buf = (caddr_t)&stats;
265 estrlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
266 if (ioctl(s, (zflag) ? SIOCG80211ZSTATS : SIOCG80211STATS,
267 (caddr_t)&ifr) == -1)
268 return;
269 #define STAT_PRINT(_member, _desc) \
270 printf("\t" _desc ": %" PRIu32 "\n", stats._member)
271
272 STAT_PRINT(is_rx_badversion, "rx frame with bad version");
273 STAT_PRINT(is_rx_tooshort, "rx frame too short");
274 STAT_PRINT(is_rx_wrongbss, "rx from wrong bssid");
275 STAT_PRINT(is_rx_dup, "rx discard 'cuz dup");
276 STAT_PRINT(is_rx_wrongdir, "rx w/ wrong direction");
277 STAT_PRINT(is_rx_mcastecho, "rx discard 'cuz mcast echo");
278 STAT_PRINT(is_rx_notassoc, "rx discard 'cuz sta !assoc");
279 STAT_PRINT(is_rx_noprivacy, "rx w/ wep but privacy off");
280 STAT_PRINT(is_rx_unencrypted, "rx w/o wep and privacy on");
281 STAT_PRINT(is_rx_wepfail, "rx wep processing failed");
282 STAT_PRINT(is_rx_decap, "rx decapsulation failed");
283 STAT_PRINT(is_rx_mgtdiscard, "rx discard mgt frames");
284 STAT_PRINT(is_rx_ctl, "rx discard ctrl frames");
285 STAT_PRINT(is_rx_beacon, "rx beacon frames");
286 STAT_PRINT(is_rx_rstoobig, "rx rate set truncated");
287 STAT_PRINT(is_rx_elem_missing, "rx required element missin");
288 STAT_PRINT(is_rx_elem_toobig, "rx element too big");
289 STAT_PRINT(is_rx_elem_toosmall, "rx element too small");
290 STAT_PRINT(is_rx_elem_unknown, "rx element unknown");
291 STAT_PRINT(is_rx_badchan, "rx frame w/ invalid chan");
292 STAT_PRINT(is_rx_chanmismatch, "rx frame chan mismatch");
293 STAT_PRINT(is_rx_nodealloc, "rx frame dropped");
294 STAT_PRINT(is_rx_ssidmismatch, "rx frame ssid mismatch ");
295 STAT_PRINT(is_rx_auth_unsupported, "rx w/ unsupported auth alg");
296 STAT_PRINT(is_rx_auth_fail, "rx sta auth failure");
297 STAT_PRINT(is_rx_auth_countermeasures, "rx auth discard 'cuz CM");
298 STAT_PRINT(is_rx_assoc_bss, "rx assoc from wrong bssid");
299 STAT_PRINT(is_rx_assoc_notauth, "rx assoc w/o auth");
300 STAT_PRINT(is_rx_assoc_capmismatch, "rx assoc w/ cap mismatch");
301 STAT_PRINT(is_rx_assoc_norate, "rx assoc w/ no rate match");
302 STAT_PRINT(is_rx_assoc_badwpaie, "rx assoc w/ bad WPA IE");
303 STAT_PRINT(is_rx_deauth, "rx deauthentication");
304 STAT_PRINT(is_rx_disassoc, "rx disassociation");
305 STAT_PRINT(is_rx_badsubtype, "rx frame w/ unknown subtyp");
306 STAT_PRINT(is_rx_nobuf, "rx failed for lack of buf");
307 STAT_PRINT(is_rx_decryptcrc, "rx decrypt failed on crc");
308 STAT_PRINT(is_rx_ahdemo_mgt, "rx discard ahdemo mgt fram");
309 STAT_PRINT(is_rx_bad_auth, "rx bad auth request");
310 STAT_PRINT(is_rx_unauth, "rx on unauthorized port");
311 STAT_PRINT(is_rx_badkeyid, "rx w/ incorrect keyid");
312 STAT_PRINT(is_rx_ccmpreplay, "rx seq# violation (CCMP)");
313 STAT_PRINT(is_rx_ccmpformat, "rx format bad (CCMP)");
314 STAT_PRINT(is_rx_ccmpmic, "rx MIC check failed (CCMP)");
315 STAT_PRINT(is_rx_tkipreplay, "rx seq# violation (TKIP)");
316 STAT_PRINT(is_rx_tkipformat, "rx format bad (TKIP)");
317 STAT_PRINT(is_rx_tkipmic, "rx MIC check failed (TKIP)");
318 STAT_PRINT(is_rx_tkipicv, "rx ICV check failed (TKIP)");
319 STAT_PRINT(is_rx_badcipher, "rx failed 'cuz key type");
320 STAT_PRINT(is_rx_nocipherctx, "rx failed 'cuz key !setup");
321 STAT_PRINT(is_rx_acl, "rx discard 'cuz acl policy");
322
323 STAT_PRINT(is_tx_nobuf, "tx failed for lack of buf");
324 STAT_PRINT(is_tx_nonode, "tx failed for no node");
325 STAT_PRINT(is_tx_unknownmgt, "tx of unknown mgt frame");
326 STAT_PRINT(is_tx_badcipher, "tx failed 'cuz key type");
327 STAT_PRINT(is_tx_nodefkey, "tx failed 'cuz no defkey");
328 STAT_PRINT(is_tx_noheadroom, "tx failed 'cuz no space");
329
330 STAT_PRINT(is_scan_active, "active scans started");
331 STAT_PRINT(is_scan_passive, "passive scans started");
332 STAT_PRINT(is_node_timeout, "nodes timed out inactivity");
333 STAT_PRINT(is_crypto_nomem, "no memory for crypto ctx");
334 STAT_PRINT(is_crypto_tkip, "tkip crypto done in s/w");
335 STAT_PRINT(is_crypto_tkipenmic, "tkip en-MIC done in s/w");
336 STAT_PRINT(is_crypto_tkipdemic, "tkip de-MIC done in s/w");
337 STAT_PRINT(is_crypto_tkipcm, "tkip counter measures");
338 STAT_PRINT(is_crypto_ccmp, "ccmp crypto done in s/w");
339 STAT_PRINT(is_crypto_wep, "wep crypto done in s/w");
340 STAT_PRINT(is_crypto_setkey_cipher, "cipher rejected key");
341 STAT_PRINT(is_crypto_setkey_nokey, "no key index for setkey");
342 STAT_PRINT(is_crypto_delkey, "driver key delete failed");
343 STAT_PRINT(is_crypto_badcipher, "unknown cipher");
344 STAT_PRINT(is_crypto_nocipher, "cipher not available");
345 STAT_PRINT(is_crypto_attachfail, "cipher attach failed");
346 STAT_PRINT(is_crypto_swfallback, "cipher fallback to s/w");
347 STAT_PRINT(is_crypto_keyfail, "driver key alloc failed");
348 STAT_PRINT(is_crypto_enmicfail, "en-MIC failed");
349 STAT_PRINT(is_ibss_capmismatch, "merge failed-cap mismatch");
350 STAT_PRINT(is_ibss_norate, "merge failed-rate mismatch");
351 STAT_PRINT(is_ps_unassoc, "ps-poll for unassoc. sta");
352 STAT_PRINT(is_ps_badaid, "ps-poll w/ incorrect aid");
353 STAT_PRINT(is_ps_qempty, "ps-poll w/ nothing to send");
354 }
355
356 void
357 ieee80211_status(void)
358 {
359 int i, nwkey_verbose;
360 struct ieee80211_nwid nwid;
361 struct ieee80211_nwkey nwkey;
362 struct ieee80211_power power;
363 u_int8_t keybuf[IEEE80211_WEP_NKID][16];
364 struct ieee80211_bssid bssid;
365 struct ieee80211chanreq channel;
366 struct ieee80211req ireq;
367 struct ether_addr ea;
368 static const u_int8_t zero_macaddr[IEEE80211_ADDR_LEN];
369 enum ieee80211_opmode opmode = get80211opmode();
370 extern int vflag;
371
372 memset(&ifr, 0, sizeof(ifr));
373 ifr.ifr_data = (void *)&nwid;
374 estrlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
375 if (ioctl(s, SIOCG80211NWID, &ifr) == -1)
376 return;
377 if (nwid.i_len > IEEE80211_NWID_LEN) {
378 warnx("SIOCG80211NWID: wrong length of nwid (%d)", nwid.i_len);
379 return;
380 }
381 printf("\tssid ");
382 print_string(nwid.i_nwid, nwid.i_len);
383
384 if (opmode == IEEE80211_M_HOSTAP) {
385 estrlcpy(ireq.i_name, name, sizeof(ireq.i_name));
386 ireq.i_type = IEEE80211_IOC_HIDESSID;
387 if (ioctl(s, SIOCG80211, &ireq) != -1) {
388 if (ireq.i_val)
389 printf(" [hidden]");
390 else if (vflag)
391 printf(" [shown]");
392 }
393
394 ireq.i_type = IEEE80211_IOC_APBRIDGE;
395 if (ioctl(s, SIOCG80211, &ireq) != -1) {
396 if (ireq.i_val)
397 printf(" apbridge");
398 else if (vflag)
399 printf(" -apbridge");
400 }
401 }
402
403 memset(&nwkey, 0, sizeof(nwkey));
404 estrlcpy(nwkey.i_name, name, sizeof(nwkey.i_name));
405 /* show nwkey only when WEP is enabled */
406 if (ioctl(s, SIOCG80211NWKEY, &nwkey) == -1 ||
407 nwkey.i_wepon == 0) {
408 printf("\n");
409 goto skip_wep;
410 }
411
412 printf(" nwkey ");
413 /* try to retrieve WEP keys */
414 for (i = 0; i < IEEE80211_WEP_NKID; i++) {
415 nwkey.i_key[i].i_keydat = keybuf[i];
416 nwkey.i_key[i].i_keylen = sizeof(keybuf[i]);
417 }
418 if (ioctl(s, SIOCG80211NWKEY, &nwkey) == -1) {
419 printf("*****");
420 } else {
421 nwkey_verbose = 0;
422 /* check to see non default key or multiple keys defined */
423 if (nwkey.i_defkid != 1) {
424 nwkey_verbose = 1;
425 } else {
426 for (i = 1; i < IEEE80211_WEP_NKID; i++) {
427 if (nwkey.i_key[i].i_keylen != 0) {
428 nwkey_verbose = 1;
429 break;
430 }
431 }
432 }
433 /* check extra ambiguity with keywords */
434 if (!nwkey_verbose) {
435 if (nwkey.i_key[0].i_keylen >= 2 &&
436 isdigit(nwkey.i_key[0].i_keydat[0]) &&
437 nwkey.i_key[0].i_keydat[1] == ':')
438 nwkey_verbose = 1;
439 else if (nwkey.i_key[0].i_keylen >= 7 &&
440 strncasecmp("persist",
441 (const char *)nwkey.i_key[0].i_keydat, 7) == 0)
442 nwkey_verbose = 1;
443 }
444 if (nwkey_verbose)
445 printf("%d:", nwkey.i_defkid);
446 for (i = 0; i < IEEE80211_WEP_NKID; i++) {
447 if (i > 0)
448 printf(",");
449 if (nwkey.i_key[i].i_keylen < 0)
450 printf("persist");
451 else
452 print_string(nwkey.i_key[i].i_keydat,
453 nwkey.i_key[i].i_keylen);
454 if (!nwkey_verbose)
455 break;
456 }
457 }
458 printf("\n");
459
460 skip_wep:
461 estrlcpy(power.i_name, name, sizeof(power.i_name));
462 if (ioctl(s, SIOCG80211POWER, &power) == -1)
463 goto skip_power;
464 printf("\tpowersave ");
465 if (power.i_enabled)
466 printf("on (%dms sleep)", power.i_maxsleep);
467 else
468 printf("off");
469 printf("\n");
470
471 skip_power:
472 estrlcpy(bssid.i_name, name, sizeof(bssid.i_name));
473 if (ioctl(s, SIOCG80211BSSID, &bssid) == -1)
474 return;
475 estrlcpy(channel.i_name, name, sizeof(channel.i_name));
476 if (ioctl(s, SIOCG80211CHANNEL, &channel) == -1)
477 return;
478 if (memcmp(bssid.i_bssid, zero_macaddr, IEEE80211_ADDR_LEN) == 0) {
479 if (channel.i_channel != (u_int16_t)-1)
480 printf("\tchan %d\n", channel.i_channel);
481 } else {
482 memcpy(ea.ether_addr_octet, bssid.i_bssid,
483 sizeof(ea.ether_addr_octet));
484 printf("\tbssid %s", ether_ntoa(&ea));
485 if (channel.i_channel != IEEE80211_CHAN_ANY)
486 printf(" chan %d", channel.i_channel);
487 printf("\n");
488 }
489 }
490