Merging upstream version 0.4.1+svn20090524.
[debian/lxpanel.git] / src / plugins / netstat / wireless.c
1 /**
2 * Copyright (c) 2008 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18 #include <stdio.h>
19 #include <string.h>
20 #include <glib.h>
21 #include <glib/gi18n.h>
22 #include <sys/time.h>
23 #include <iwlib.h>
24 #include "netstat.h"
25 #include "wireless.h"
26
27 /*
28 static const char * iw_ie_cypher_name[] = {
29 "none",
30 "WEP-40",
31 "TKIP",
32 "WRAP",
33 "CCMP",
34 "WEP-104",
35 };
36
37 static const char * iw_ie_key_mgmt_name[] = {
38 "none",
39 "802.1x",
40 "PSK",
41 };
42 */
43
44 void wireless_aplist_free(APLIST *aplist)
45 {
46 APLIST *ptr;
47 APLIST *delptr;
48
49 if (aplist!=NULL) {
50 ptr = aplist;
51 do {
52 g_free(ptr->info->essid);
53 g_free(ptr->info->apaddr);
54 g_free(ptr->info);
55
56 delptr = ptr;
57 ptr = ptr->next;
58 g_free(delptr);
59 } while(ptr!=NULL);
60 }
61 }
62
63 void
64 wireless_gen_ie(ap_info *info, unsigned char *buffer, int ielen)
65 {
66 int offset = 2;
67 int count;
68 int i;
69 unsigned char wpa1_oui[3] = {0x00, 0x50, 0xf2};
70 unsigned char wpa2_oui[3] = {0x00, 0x0f, 0xac};
71 unsigned char *wpa_oui;
72
73 /* check IE type */
74 switch(buffer[0]) {
75 case 0xdd: /* WPA or else */
76 wpa_oui = wpa1_oui;
77
78 if((ielen < 8)
79 || (memcmp(&buffer[offset], wpa_oui, 3) != 0)
80 || (buffer[offset + 3] != 0x01)) {
81 if (info->haskey)
82 info->en_method = NS_WIRELESS_AUTH_WEP;
83 else
84 info->en_method = NS_WIRELESS_AUTH_OFF;
85
86 info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
87 info->group = NS_IW_IE_CIPHER_NONE;
88 info->pairwise = NS_IW_IE_CIPHER_NONE;
89
90 return;
91 }
92
93 /* OUI and 0x01 */
94 offset += 4;
95 break;
96
97 case 0x30: /* IEEE 802.11i/WPA2 */
98 wpa_oui = wpa2_oui;
99 break;
100
101 default: /* Unknown */
102 if (info->haskey)
103 info->en_method = NS_WIRELESS_AUTH_WEP;
104 else
105 info->en_method = NS_WIRELESS_AUTH_OFF;
106
107 info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
108 info->group = NS_IW_IE_CIPHER_NONE;
109 info->pairwise = NS_IW_IE_CIPHER_NONE;
110 return;
111 }
112
113 /* assume TKIP */
114 info->en_method = NS_WIRELESS_AUTH_WPA;
115 info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
116 info->group = NS_IW_IE_CIPHER_TKIP;
117 info->pairwise = NS_IW_IE_CIPHER_TKIP;
118
119 /* 2 bytes for version number (little endian) */
120 offset += 2;
121
122 /* check group cipher for short IE */
123 if ((offset+4) > ielen) {
124 /* this is a short IE, we can assume TKIP/TKIP. */
125 info->group = NS_IW_IE_CIPHER_TKIP;
126 info->pairwise = NS_IW_IE_CIPHER_TKIP;
127 return;
128 }
129
130 /* 4 Bytes for group cipher information [3 bytes][1 Byte] */
131 if(memcmp(&buffer[offset], wpa_oui, 3)!=0) {
132 /* the group cipher is proprietary */
133 info->group = NS_IW_IE_CIPHER_NONE;
134 } else {
135 /* pick a byte for type of group cipher */
136 info->group = buffer[offset+3];
137 }
138 offset += 4;
139
140 /* check pairwise cipher for short IE */
141 if ((offset+2) > ielen) {
142 /* this is a short IE, we can assume TKIP. */
143 info->pairwise = NS_IW_IE_CIPHER_TKIP;
144 return;
145 }
146
147 /* 2 bytes for number of pairwise ciphers (little endian) */
148 count = buffer[offset] | (buffer[offset + 1] << 8);
149 offset += 2;
150
151 /* if we are done */
152 if ((offset+4*count) > ielen) {
153 return;
154 }
155
156 /* choose first cipher of pairwise ciphers to use,
157 * FIXME: Let user decide the cipher is the best way. */
158 for(i=0;i<count;i++) {
159 if(memcmp(&buffer[offset], wpa_oui, 3)==0) {
160 /* pick a byte for type of group cipher */
161 info->pairwise = buffer[offset+3];
162 }
163 offset += 4;
164 }
165
166 /* check authentication suites */
167 if ((offset+2) > ielen) {
168 /* this is a short IE, we can assume TKIP. */
169 info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
170 return;
171 }
172
173 /* 2 bytes for number of authentication suites (little endian) */
174 count = buffer[offset] | (buffer[offset + 1] << 8);
175 offset += 2;
176
177 /* if we are done */
178 if ((offset+4*count) > ielen) {
179 return;
180 }
181
182 /* choose first key_mgmt of authentication suites to use,
183 * FIXME: Let user decide the key_mgmt is the best way. */
184 for(i=0;i<count;i++) {
185 if(memcmp(&buffer[offset], wpa_oui, 3)==0) {
186 /* pick a byte for type of key_mgmt */
187 info->key_mgmt = buffer[offset+3];
188 }
189 offset += 4;
190 }
191 }
192
193 ap_info *
194 wireless_parse_scanning_event(struct iw_event *event, ap_info *oldinfo)
195 {
196 ap_info *info;
197
198 /* found a new AP */
199 if (event->cmd==SIOCGIWAP) {
200 char buf[128];
201 info = g_new0(ap_info, 1);
202 info->apaddr = g_strdup(iw_saether_ntop(&event->u.ap_addr, buf));
203 info->en_method = NS_WIRELESS_AUTH_OFF;
204 info->haskey = FALSE;
205 info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
206 info->group = NS_IW_IE_CIPHER_TKIP;
207 info->pairwise = NS_IW_IE_CIPHER_TKIP;
208 } else {
209 info = oldinfo;
210 }
211
212 switch (event->cmd) {
213 case SIOCGIWESSID: /* ESSID */
214 if (!event->u.essid.flags
215 || event->u.essid.length==0
216 || strlen(event->u.essid.pointer)==0) {
217 info->essid = NULL;
218 } else {
219 info->essid = g_strndup(event->u.essid.pointer, event->u.essid.length);
220 }
221 break;
222 case IWEVQUAL: /* Signal Quality */
223 info->quality = (int)rint((log (event->u.qual.qual) / log (92)) * 100.0);
224 break;
225 case SIOCGIWENCODE: /* Encryption */
226 if (!event->u.data.pointer)
227 event->u.data.flags |= IW_ENCODE_NOKEY;
228
229 if (!(event->u.data.flags & IW_ENCODE_DISABLED)) {
230 info->haskey = TRUE;
231 /* assume WEP */
232 info->en_method = NS_WIRELESS_AUTH_WEP;
233 } else {
234 info->haskey = FALSE;
235 info->en_method = NS_WIRELESS_AUTH_OFF;
236 }
237 break;
238 case IWEVGENIE: /* Extra information */
239 {
240 int offset = 0;
241 int ielen = event->u.data.length;
242 unsigned char *iebuf;
243
244 while(offset <= (ielen - 2)) {
245 iebuf = (event->u.data.pointer + offset);
246 /* check IE type */
247 switch(iebuf[offset]) {
248 case 0xdd: /* WPA or else */
249 case 0x30: /* IEEE 802.11i/WPA2 */
250 wireless_gen_ie(info, iebuf, ielen);
251 break;
252 }
253 offset += iebuf[offset+1] + 2;
254 }
255 }
256 break;
257 }
258
259 return info;
260 }
261
262 /* when we have some workaround problems,
263 * we need this function to rescanning access-point.
264 * */
265 gboolean wireless_refresh(int iwsockfd, const char *ifname)
266 {
267 struct iwreq wrq;
268 struct iw_range range;
269 struct timeval tv;
270 fd_set rfds; /* File descriptors for select */
271 int selfd;
272 char buffer[IW_SCAN_MAX_DATA];
273
274 /* setting interfaces name */
275 strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
276
277 /* Getting range */
278 iw_get_range_info(iwsockfd, ifname, &range);
279
280 /* check scanning support */
281 if (range.we_version_compiled < 14)
282 return FALSE;
283
284 /* Initiate Scanning */
285 wrq.u.data.pointer = buffer;
286 wrq.u.data.length = IW_SCAN_MAX_DATA;
287 wrq.u.data.flags = 0;
288
289 if (ioctl(iwsockfd, SIOCSIWSCAN, &wrq) < 0) {
290 if (errno!=EPERM)
291 return FALSE;
292 }
293
294 /* Init timeout value -> 250ms */
295 tv.tv_sec = 0;
296 tv.tv_usec = 250000;
297
298 /* Scanning APs */
299 while(1) {
300 if (ioctl(iwsockfd, SIOCGIWSCAN, &wrq) < 0) {
301 if (errno == EAGAIN) { /* not yet ready */
302 FD_ZERO(&rfds);
303 selfd = -1;
304
305 if (select(selfd + 1, &rfds, NULL, NULL, &tv)==0)
306 continue; /* timeout */
307 } else {
308 break;
309 }
310 }
311
312 if (wrq.u.data.length <= 0)
313 break;
314 }
315
316 return TRUE;
317 }
318
319 APLIST *wireless_scanning(int iwsockfd, const char *ifname)
320 {
321 APLIST *ap = NULL;
322 APLIST *newap;
323
324 struct iwreq wrq;
325 int scanflags = 0; /* Flags for scan */
326 unsigned char * buffer = NULL; /* Results */
327 int buflen = IW_SCAN_MAX_DATA; /* Min for compat WE < 17 */
328 struct iw_range range;
329 int has_range;
330 struct timeval tv; /* select timeout */
331 int timeout = 15000000; /* 15s */
332
333 /* Get range stuff */
334 has_range = (iw_get_range_info(iwsockfd, ifname, &range) >= 0);
335
336 /* Check if the interface could support scanning. */
337 if ((!has_range) || (range.we_version_compiled < 14)) {
338 fprintf(stderr, "%-8.16s Interface doesn't support scanning.\n\n",
339 ifname);
340 return NULL;
341 }
342
343 /* Init timeout value -> 250ms between set and first get */
344 tv.tv_sec = 0;
345 tv.tv_usec = 250000;
346
347 wrq.u.data.pointer = NULL;
348 wrq.u.data.flags = 0;
349 wrq.u.data.length = 0;
350
351 /* Initiate Scanning */
352 if (iw_set_ext(iwsockfd, ifname, SIOCSIWSCAN, &wrq) < 0) {
353 if ((errno != EPERM) || (scanflags != 0)) {
354 fprintf(stderr, "%-8.16s Interface doesn't support "
355 "scanning : %s\n\n", ifname, strerror(errno));
356 return NULL;
357 }
358 tv.tv_usec = 0;
359 }
360 timeout -= tv.tv_usec;
361
362 /* Forever */
363 while (1) {
364 fd_set rfds; /* File descriptors for select */
365 int last_fd; /* Last fd */
366 int ret;
367
368 /* Guess what ? We must re-generate rfds each time */
369 FD_ZERO(&rfds);
370 last_fd = -1;
371
372 /* In here, add the rtnetlink fd in the list */
373
374 /* Wait until something happens */
375 ret = select(last_fd + 1, &rfds, NULL, NULL, &tv);
376
377 /* Check if there was an error */
378 if (ret < 0) {
379 if (errno == EAGAIN || errno == EINTR)
380 continue;
381 fprintf(stderr, "Unhandled signal - exiting...\n");
382 return NULL;
383 }
384
385 /* Check if there was a timeout */
386 if (ret == 0) {
387 unsigned char *newbuf;
388
389 realloc:
390 /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */
391 newbuf = realloc(buffer, buflen);
392 if (newbuf == NULL) {
393 if (buffer)
394 free(buffer);
395 fprintf(stderr, "%s: Allocation failed\n", __FUNCTION__);
396 return NULL;
397 }
398 buffer = newbuf;
399
400 /* Try to read the results */
401 wrq.u.data.pointer = buffer;
402 wrq.u.data.flags = 0;
403 wrq.u.data.length = buflen;
404 if (iw_get_ext(iwsockfd, ifname, SIOCGIWSCAN, &wrq) < 0) {
405 /* Check if buffer was too small (WE-17 only) */
406 if ((errno == E2BIG) &&
407 (range.we_version_compiled > 16)) {
408 /* Check if the driver gave us any hints. */
409 if (wrq.u.data.length > buflen)
410 buflen = wrq.u.data.length;
411 else
412 buflen *= 2;
413 /* Try again */
414 goto realloc;
415 }
416
417 /* Check if results not available yet */
418 if(errno == EAGAIN) {
419 /* Restart timer for only 100ms*/
420 tv.tv_sec = 0;
421 tv.tv_usec = 100000;
422 timeout -= tv.tv_usec;
423 if (timeout > 0)
424 continue; /* Try again later */
425 }
426
427 /* Bad error */
428 free(buffer);
429 fprintf(stderr,
430 "%-8.16s Failed to read scan data : %s\n\n",
431 ifname, strerror(errno));
432 return NULL;
433 }
434 else
435 /* We have the results, go to process them */
436 break;
437 }
438
439 /* In here, check if event and event type
440 * if scan event, read results. All errors bad & no reset timeout */
441 }
442
443 if(wrq.u.data.length) {
444 struct iw_event iwe;
445 struct stream_descr stream;
446 int ret;
447
448 iw_init_event_stream(&stream, (char *) buffer, wrq.u.data.length);
449 do {
450 /* Extract an event and print it */
451 ret = iw_extract_event_stream(&stream, &iwe, range.we_version_compiled);
452 if (iwe.cmd==SIOCGIWAP) {
453 newap = malloc(sizeof(APLIST));
454 newap->info = NULL;
455 newap->next = ap;
456 ap = newap;
457 }
458 ap->info = wireless_parse_scanning_event(&iwe, ap->info);
459 }
460 while (ret > 0);
461 printf("\n");
462 }
463 else
464 printf("%-8.16s No scan results\n\n", ifname);
465
466 free(buffer);
467 return ap;
468 }