Adding upstream version 0.3.5.2+svn20080509.
[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 } else {
206 info = oldinfo;
207 }
208
209 switch (event->cmd) {
210 case SIOCGIWESSID: /* ESSID */
211 if (!event->u.essid.flags||event->u.essid.length==0) {
212 info->essid = NULL;
213 } else {
214 info->essid = g_strndup(event->u.essid.pointer, event->u.essid.length);
215 }
216 break;
217 case IWEVQUAL: /* Signal Quality */
218 info->quality = (int)rint((log (event->u.qual.qual) / log (92)) * 100.0);
219 break;
220 case SIOCGIWENCODE: /* Encryption */
221 if (!event->u.data.pointer)
222 event->u.data.flags |= IW_ENCODE_NOKEY;
223
224 if (!(event->u.data.flags & IW_ENCODE_DISABLED)) {
225 info->haskey = TRUE;
226 /* assume WEP */
227 info->en_method = NS_WIRELESS_AUTH_WEP;
228 } else {
229 info->haskey = FALSE;
230 info->en_method = NS_WIRELESS_AUTH_OFF;
231 }
232 break;
233 case IWEVGENIE: /* Extra information */
234 {
235 int offset = 0;
236 int ielen = event->u.data.length;
237 unsigned char *iebuf;
238
239 while(offset <= (ielen - 2)) {
240 iebuf = (event->u.data.pointer + offset);
241 /* check IE type */
242 switch(iebuf[offset]) {
243 case 0xdd: /* WPA or else */
244 case 0x30: /* IEEE 802.11i/WPA2 */
245 wireless_gen_ie(info, iebuf, ielen);
246 break;
247 }
248 offset += iebuf[offset+1] + 2;
249 }
250 }
251 break;
252 }
253
254 return info;
255 }
256
257 /* when we have some workaround problems,
258 * we need this function to rescanning access-point.
259 * */
260 gboolean wireless_refresh(int iwsockfd, const char *ifname)
261 {
262 struct iwreq wrq;
263 struct iw_range range;
264 struct timeval tv;
265 fd_set rfds; /* File descriptors for select */
266 int selfd;
267 char buffer[IW_SCAN_MAX_DATA];
268
269 /* setting interfaces name */
270 strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
271
272 /* Getting range */
273 iw_get_range_info(iwsockfd, ifname, &range);
274
275 /* check scanning support */
276 if (range.we_version_compiled < 14)
277 return FALSE;
278
279 /* Initiate Scanning */
280 wrq.u.data.pointer = buffer;
281 wrq.u.data.length = IW_SCAN_MAX_DATA;
282 wrq.u.data.flags = 0;
283
284 if (ioctl(iwsockfd, SIOCSIWSCAN, &wrq) < 0) {
285 if (errno!=EPERM)
286 return FALSE;
287 }
288
289 /* Init timeout value -> 250ms */
290 tv.tv_sec = 0;
291 tv.tv_usec = 250000;
292
293 /* Scanning APs */
294 while(1) {
295 if (ioctl(iwsockfd, SIOCGIWSCAN, &wrq) < 0) {
296 if (errno == EAGAIN) { /* not yet ready */
297 FD_ZERO(&rfds);
298 selfd = -1;
299
300 if (select(selfd + 1, &rfds, NULL, NULL, &tv)==0)
301 continue; /* timeout */
302 } else {
303 break;
304 }
305 }
306
307 if (wrq.u.data.length <= 0)
308 break;
309 }
310
311 return TRUE;
312 }
313
314 APLIST *wireless_scanning(int iwsockfd, const char *ifname)
315 {
316 APLIST *ap = NULL;
317 APLIST *newap;
318 struct iwreq wrq;
319 struct iw_range range;
320 struct iw_event event;
321 struct stream_descr stream;
322 struct timeval tv;
323 fd_set rfds; /* File descriptors for select */
324 int selfd;
325 int ret;
326 char buffer[IW_SCAN_MAX_DATA];
327
328 strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
329
330 /* Getting range */
331 iw_get_range_info(iwsockfd, ifname, &range);
332
333 /* check scanning support */
334 if (range.we_version_compiled < 14)
335 return NULL;
336
337 /* Initiate Scanning */
338 wrq.u.data.pointer = buffer;
339 wrq.u.data.length = IW_SCAN_MAX_DATA;
340 wrq.u.data.flags = 0;
341
342 if (ioctl(iwsockfd, SIOCSIWSCAN, &wrq) < 0) {
343 if (errno!=EPERM)
344 return NULL;
345 }
346
347 /* Init timeout value -> 250ms */
348 tv.tv_sec = 0;
349 tv.tv_usec = 250000;
350
351 /* Scanning APs */
352 while(1) {
353 if (ioctl(iwsockfd, SIOCGIWSCAN, &wrq) < 0) {
354 if (errno == EAGAIN) { /* not yet ready */
355 FD_ZERO(&rfds);
356 selfd = -1;
357
358 if (select(selfd + 1, &rfds, NULL, NULL, &tv)==0)
359 continue; /* timeout */
360 } else {
361 break;
362 }
363 }
364
365 if (wrq.u.data.length <= 0)
366 break;
367
368 /* Initializing event */
369 iw_init_event_stream(&stream, buffer, wrq.u.data.length);
370 do {
371 ret = iw_extract_event_stream(&stream, &event, range.we_version_compiled);
372 if (ret > 0) {
373 /* found a new AP */
374 if (event.cmd==SIOCGIWAP) {
375 newap = g_new0(APLIST, 1);
376 newap->info = NULL;
377 newap->next = ap;
378 ap = newap;
379 }
380
381 /* Scanning Event */
382 ap->info = wireless_parse_scanning_event(&event, ap->info);
383 }
384 } while (ret > 0);
385 }
386
387 return ap;
388 }