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