Merging upstream version 0.3.6.
[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 struct iwreq wrq;
324 struct iw_range range;
325 struct iw_event event;
326 struct stream_descr stream;
327 struct timeval tv;
328 fd_set rfds; /* File descriptors for select */
329 int selfd;
330 int ret;
331 int bufferlen = IW_SCAN_MAX_DATA;
332 int timeout = 15000000;
333
334 strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
335
336 /* Getting range */
337 iw_get_range_info(iwsockfd, ifname, &range);
338
339 /* check scanning support */
340 if (range.we_version_compiled < 14)
341 return NULL;
342
343 /* Initiate Scanning */
344 wrq.u.data.pointer = malloc(sizeof(char)*IW_SCAN_MAX_DATA);
345 wrq.u.data.length = IW_SCAN_MAX_DATA;
346 wrq.u.data.flags = 0;
347
348 if (ioctl(iwsockfd, SIOCSIWSCAN, &wrq) < 0) {
349 if (errno!=EPERM)
350 return NULL;
351 }
352
353 /* Init timeout value -> 250ms */
354 tv.tv_sec = 0;
355 tv.tv_usec = 250000;
356
357 /* Scanning APs */
358 while(1) {
359 if (ioctl(iwsockfd, SIOCGIWSCAN, &wrq) < 0) {
360 if (errno == EAGAIN) { /* not yet ready */
361 FD_ZERO(&rfds);
362 selfd = -1;
363 ret = select(selfd + 1, &rfds, NULL, NULL, &tv);
364 if (ret==0) {
365 tv.tv_usec = 100000;
366 timeout -= tv.tv_usec;
367 if (timeout>0)
368 continue;
369 } else if (ret<0) {
370 if (errno == EAGAIN || errno == EINTR)
371 continue;
372 }
373
374 break;
375 } else if ((errno == E2BIG) && (range.we_version_compiled > 16)) {
376 if(wrq.u.data.length > bufferlen)
377 bufferlen = wrq.u.data.length;
378 else
379 bufferlen *= 2;
380
381 wrq.u.data.pointer = realloc(wrq.u.data.pointer, bufferlen);
382 continue;
383 } else {
384 break;
385 }
386 }
387
388 if (wrq.u.data.length <= 0)
389 break;
390
391 /* Initializing event */
392 iw_init_event_stream(&stream, wrq.u.data.pointer, wrq.u.data.length);
393 do {
394 ret = iw_extract_event_stream(&stream, &event, range.we_version_compiled);
395 if (ret > 0) {
396 /* found a new AP */
397 if (event.cmd==SIOCGIWAP) {
398 newap = g_new0(APLIST, 1);
399 newap->info = NULL;
400 newap->next = ap;
401 ap = newap;
402 }
403
404 /* Scanning Event */
405 ap->info = wireless_parse_scanning_event(&event, ap->info);
406 }
407 } while (ret > 0);
408
409 break;
410 }
411
412 return ap;
413 }