Merging upstream version 0.5.9.
[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(void *aplist, GObject *dummy)
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_sawap_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 = event->u.data.pointer;
243
244 while(offset <= (ielen - 2)) {
245 /* check IE type */
246 switch(iebuf[offset]) {
247 case 0xdd: /* WPA or else */
248 case 0x30: /* IEEE 802.11i/WPA2 */
249 wireless_gen_ie(info, iebuf, ielen);
250 break;
251 }
252 offset += iebuf[offset+1] + 2;
253 }
254 }
255 break;
256 }
257
258 return info;
259 }
260
261 /* when we have some workaround problems,
262 * we need this function to rescanning access-point.
263 * */
264 gboolean wireless_refresh(int iwsockfd, const char *ifname)
265 {
266 struct iwreq wrq;
267 struct iw_range range;
268 struct timeval tv;
269 fd_set rfds; /* File descriptors for select */
270 int selfd;
271 char buffer[IW_SCAN_MAX_DATA];
272
273 /* setting interfaces name */
274 strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
275
276 /* Getting range */
277 iw_get_range_info(iwsockfd, ifname, &range);
278
279 /* check scanning support */
280 if (range.we_version_compiled < 14)
281 return FALSE;
282
283 /* Initiate Scanning */
284 wrq.u.data.pointer = buffer;
285 wrq.u.data.length = IW_SCAN_MAX_DATA;
286 wrq.u.data.flags = 0;
287
288 if (ioctl(iwsockfd, SIOCSIWSCAN, &wrq) < 0) {
289 if (errno!=EPERM)
290 return FALSE;
291 }
292
293 /* Init timeout value -> 250ms */
294 tv.tv_sec = 0;
295 tv.tv_usec = 250000;
296
297 /* Scanning APs */
298 while(1) {
299 if (ioctl(iwsockfd, SIOCGIWSCAN, &wrq) < 0) {
300 if (errno == EAGAIN) { /* not yet ready */
301 FD_ZERO(&rfds);
302 selfd = -1;
303
304 if (select(selfd + 1, &rfds, NULL, NULL, &tv)==0)
305 continue; /* timeout */
306 } else {
307 break;
308 }
309 }
310
311 if (wrq.u.data.length <= 0)
312 break;
313 }
314
315 return TRUE;
316 }
317
318 APLIST *wireless_scanning(int iwsockfd, const char *ifname)
319 {
320 APLIST *ap = NULL;
321 APLIST *newap;
322
323 struct iwreq wrq;
324 int scanflags = 0; /* Flags for scan */
325 unsigned char * buffer = NULL; /* Results */
326 int buflen = IW_SCAN_MAX_DATA; /* Min for compat WE < 17 */
327 struct iw_range range;
328 int has_range;
329 struct timeval tv; /* select timeout */
330 int timeout = 15000000; /* 15s */
331
332 /* Get range stuff */
333 has_range = (iw_get_range_info(iwsockfd, ifname, &range) >= 0);
334
335 /* Check if the interface could support scanning. */
336 if ((!has_range) || (range.we_version_compiled < 14)) {
337 fprintf(stderr, "%-8.16s Interface doesn't support scanning.\n\n",
338 ifname);
339 return NULL;
340 }
341
342 /* Init timeout value -> 250ms between set and first get */
343 tv.tv_sec = 0;
344 tv.tv_usec = 250000;
345
346 wrq.u.data.pointer = NULL;
347 wrq.u.data.flags = 0;
348 wrq.u.data.length = 0;
349
350 /* Initiate Scanning */
351 if (iw_set_ext(iwsockfd, ifname, SIOCSIWSCAN, &wrq) < 0) {
352 if ((errno != EPERM) || (scanflags != 0)) {
353 fprintf(stderr, "%-8.16s Interface doesn't support "
354 "scanning : %s\n\n", ifname, strerror(errno));
355 return NULL;
356 }
357 tv.tv_usec = 0;
358 }
359 timeout -= tv.tv_usec;
360
361 /* Forever */
362 while (1) {
363 fd_set rfds; /* File descriptors for select */
364 int last_fd; /* Last fd */
365 int ret;
366
367 /* Guess what ? We must re-generate rfds each time */
368 FD_ZERO(&rfds);
369 last_fd = -1;
370
371 /* In here, add the rtnetlink fd in the list */
372
373 /* Wait until something happens */
374 ret = select(last_fd + 1, &rfds, NULL, NULL, &tv);
375
376 /* Check if there was an error */
377 if (ret < 0) {
378 if (errno == EAGAIN || errno == EINTR)
379 continue;
380 fprintf(stderr, "Unhandled signal - exiting...\n");
381 return NULL;
382 }
383
384 /* Check if there was a timeout */
385 if (ret == 0) {
386 unsigned char *newbuf;
387
388 realloc:
389 /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */
390 newbuf = realloc(buffer, buflen);
391 if (newbuf == NULL) {
392 if (buffer)
393 free(buffer);
394 fprintf(stderr, "%s: Allocation failed\n", __FUNCTION__);
395 return NULL;
396 }
397 buffer = newbuf;
398
399 /* Try to read the results */
400 wrq.u.data.pointer = buffer;
401 wrq.u.data.flags = 0;
402 wrq.u.data.length = buflen;
403 if (iw_get_ext(iwsockfd, ifname, SIOCGIWSCAN, &wrq) < 0) {
404 /* Check if buffer was too small (WE-17 only) */
405 if ((errno == E2BIG) &&
406 (range.we_version_compiled > 16)) {
407 /* Check if the driver gave us any hints. */
408 if (wrq.u.data.length > buflen)
409 buflen = wrq.u.data.length;
410 else
411 buflen *= 2;
412 /* Try again */
413 goto realloc;
414 }
415
416 /* Check if results not available yet */
417 if(errno == EAGAIN) {
418 /* Restart timer for only 100ms*/
419 tv.tv_sec = 0;
420 tv.tv_usec = 100000;
421 timeout -= tv.tv_usec;
422 if (timeout > 0)
423 continue; /* Try again later */
424 }
425
426 /* Bad error */
427 free(buffer);
428 fprintf(stderr,
429 "%-8.16s Failed to read scan data : %s\n\n",
430 ifname, strerror(errno));
431 return NULL;
432 }
433 else
434 /* We have the results, go to process them */
435 break;
436 }
437
438 /* In here, check if event and event type
439 * if scan event, read results. All errors bad & no reset timeout */
440 }
441
442 if(wrq.u.data.length) {
443 struct iw_event iwe;
444 struct stream_descr stream;
445 int ret;
446
447 iw_init_event_stream(&stream, (char *) buffer, wrq.u.data.length);
448 do {
449 /* Extract an event and print it */
450 ret = iw_extract_event_stream(&stream, &iwe, range.we_version_compiled);
451 if (iwe.cmd==SIOCGIWAP) {
452 newap = malloc(sizeof(APLIST));
453 newap->info = NULL;
454 newap->next = ap;
455 ap = newap;
456 }
457 ap->info = wireless_parse_scanning_event(&iwe, ap->info);
458 }
459 while (ret > 0);
460 printf("\n");
461 }
462 else
463 printf("%-8.16s No scan results\n\n", ifname);
464
465 free(buffer);
466 return ap;
467 }