b0c1f15e8e72ea379e0ef329f206226784f1982a
[project/luci.git] / contrib / package / iwinfo / src / iwinfo_wext_scan.c
1 /*
2 * iwinfo - Wireless Information Library - Linux Wireless Extension Backend
3 *
4 * Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
5 *
6 * The iwinfo library is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation.
9 *
10 * The iwinfo library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with the iwinfo library. If not, see http://www.gnu.org/licenses/.
17 *
18 * Parts of this code are derived from the Linux wireless tools, iwlib.c,
19 * iwlist.c and iwconfig.c in particular.
20 */
21
22 #include "iwinfo.h"
23 #include "iwinfo_wext_scan.h"
24
25
26 static int ioctl_socket = -1;
27
28 static int wext_ioctl(const char *ifname, int cmd, struct iwreq *wrq)
29 {
30 /* prepare socket */
31 if( ioctl_socket == -1 )
32 ioctl_socket = socket(AF_INET, SOCK_DGRAM, 0);
33
34 strncpy(wrq->ifr_name, ifname, IFNAMSIZ);
35 return ioctl(ioctl_socket, cmd, wrq);
36 }
37
38 static double wext_freq2float(const struct iw_freq *in)
39 {
40 int i;
41 double res = (double) in->m;
42 for(i = 0; i < in->e; i++) res *= 10;
43 return res;
44 }
45
46 static int wext_extract_event(struct stream_descr *stream, struct iw_event *iwe, int wev)
47 {
48 const struct iw_ioctl_description *descr = NULL;
49 int event_type = 0;
50 unsigned int event_len = 1;
51 char *pointer;
52 unsigned cmd_index; /* *MUST* be unsigned */
53
54 /* Check for end of stream */
55 if((stream->current + IW_EV_LCP_PK_LEN) > stream->end)
56 return 0;
57
58 /* Extract the event header (to get the event id).
59 * Note : the event may be unaligned, therefore copy... */
60 memcpy((char *) iwe, stream->current, IW_EV_LCP_PK_LEN);
61
62 /* Check invalid events */
63 if(iwe->len <= IW_EV_LCP_PK_LEN)
64 return -1;
65
66 /* Get the type and length of that event */
67 if(iwe->cmd <= SIOCIWLAST)
68 {
69 cmd_index = iwe->cmd - SIOCIWFIRST;
70 if(cmd_index < standard_ioctl_num)
71 descr = &(standard_ioctl_descr[cmd_index]);
72 }
73 else
74 {
75 cmd_index = iwe->cmd - IWEVFIRST;
76 if(cmd_index < standard_event_num)
77 descr = &(standard_event_descr[cmd_index]);
78 }
79
80 if(descr != NULL)
81 event_type = descr->header_type;
82
83 /* Unknown events -> event_type=0 => IW_EV_LCP_PK_LEN */
84 event_len = event_type_size[event_type];
85
86 /* Fixup for earlier version of WE */
87 if((wev <= 18) && (event_type == IW_HEADER_TYPE_POINT))
88 event_len += IW_EV_POINT_OFF;
89
90 /* Check if we know about this event */
91 if(event_len <= IW_EV_LCP_PK_LEN)
92 {
93 /* Skip to next event */
94 stream->current += iwe->len;
95 return 2;
96 }
97
98 event_len -= IW_EV_LCP_PK_LEN;
99
100 /* Set pointer on data */
101 if(stream->value != NULL)
102 pointer = stream->value; /* Next value in event */
103 else
104 pointer = stream->current + IW_EV_LCP_PK_LEN; /* First value in event */
105
106 /* Copy the rest of the event (at least, fixed part) */
107 if((pointer + event_len) > stream->end)
108 {
109 /* Go to next event */
110 stream->current += iwe->len;
111 return -2;
112 }
113
114 /* Fixup for WE-19 and later : pointer no longer in the stream */
115 /* Beware of alignement. Dest has local alignement, not packed */
116 if( (wev > 18) && (event_type == IW_HEADER_TYPE_POINT) )
117 memcpy((char *) iwe + IW_EV_LCP_LEN + IW_EV_POINT_OFF, pointer, event_len);
118 else
119 memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len);
120
121 /* Skip event in the stream */
122 pointer += event_len;
123
124 /* Special processing for iw_point events */
125 if(event_type == IW_HEADER_TYPE_POINT)
126 {
127 /* Check the length of the payload */
128 unsigned int extra_len = iwe->len - (event_len + IW_EV_LCP_PK_LEN);
129 if(extra_len > 0)
130 {
131 /* Set pointer on variable part (warning : non aligned) */
132 iwe->u.data.pointer = pointer;
133
134 /* Check that we have a descriptor for the command */
135 if(descr == NULL)
136 /* Can't check payload -> unsafe... */
137 iwe->u.data.pointer = NULL; /* Discard paylod */
138 else
139 {
140 /* Those checks are actually pretty hard to trigger,
141 * because of the checks done in the kernel... */
142
143 unsigned int token_len = iwe->u.data.length * descr->token_size;
144
145 /* Ugly fixup for alignement issues.
146 * If the kernel is 64 bits and userspace 32 bits,
147 * we have an extra 4+4 bytes.
148 * Fixing that in the kernel would break 64 bits userspace. */
149 if((token_len != extra_len) && (extra_len >= 4))
150 {
151 uint16_t alt_dlen = *((uint16_t *) pointer);
152 unsigned int alt_token_len = alt_dlen * descr->token_size;
153 if((alt_token_len + 8) == extra_len)
154 {
155 /* Ok, let's redo everything */
156 pointer -= event_len;
157 pointer += 4;
158 /* Dest has local alignement, not packed */
159 memcpy((char *) iwe + IW_EV_LCP_LEN + IW_EV_POINT_OFF, pointer, event_len);
160 pointer += event_len + 4;
161 iwe->u.data.pointer = pointer;
162 token_len = alt_token_len;
163 }
164 }
165
166 /* Discard bogus events which advertise more tokens than
167 * what they carry... */
168 if(token_len > extra_len)
169 iwe->u.data.pointer = NULL; /* Discard paylod */
170
171 /* Check that the advertised token size is not going to
172 * produce buffer overflow to our caller... */
173 if((iwe->u.data.length > descr->max_tokens)
174 && !(descr->flags & IW_DESCR_FLAG_NOMAX))
175 iwe->u.data.pointer = NULL; /* Discard paylod */
176
177 /* Same for underflows... */
178 if(iwe->u.data.length < descr->min_tokens)
179 iwe->u.data.pointer = NULL; /* Discard paylod */
180 }
181 }
182 else
183 /* No data */
184 iwe->u.data.pointer = NULL;
185
186 /* Go to next event */
187 stream->current += iwe->len;
188 }
189 else
190 {
191 /* Ugly fixup for alignement issues.
192 * If the kernel is 64 bits and userspace 32 bits,
193 * we have an extra 4 bytes.
194 * Fixing that in the kernel would break 64 bits userspace. */
195 if((stream->value == NULL)
196 && ((((iwe->len - IW_EV_LCP_PK_LEN) % event_len) == 4)
197 || ((iwe->len == 12) && ((event_type == IW_HEADER_TYPE_UINT) ||
198 (event_type == IW_HEADER_TYPE_QUAL))) ))
199 {
200 pointer -= event_len;
201 pointer += 4;
202 /* Beware of alignement. Dest has local alignement, not packed */
203 memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len);
204 pointer += event_len;
205 }
206
207 /* Is there more value in the event ? */
208 if((pointer + event_len) <= (stream->current + iwe->len))
209 /* Go to next value */
210 stream->value = pointer;
211 else
212 {
213 /* Go to next event */
214 stream->value = NULL;
215 stream->current += iwe->len;
216 }
217 }
218
219 return 1;
220 }
221
222 static inline void wext_fill_wpa(unsigned char *iebuf, int buflen, struct iwinfo_scanlist_entry *e)
223 {
224 int ielen = iebuf[1] + 2;
225 int offset = 2; /* Skip the IE id, and the length. */
226 unsigned char wpa1_oui[3] = {0x00, 0x50, 0xf2};
227 unsigned char wpa2_oui[3] = {0x00, 0x0f, 0xac};
228 unsigned char *wpa_oui;
229 int i;
230 uint16_t ver = 0;
231 uint16_t cnt = 0;
232 int wpa1 = 0, wpa2 = 0;
233 char buf[256];
234
235 struct iwinfo_crypto_entry *ce = &e->crypto;
236
237 if(ielen > buflen)
238 ielen = buflen;
239
240 switch(iebuf[0])
241 {
242 case 0x30: /* WPA2 */
243 /* Check if we have enough data */
244 if(ielen < 4)
245 return;
246
247 wpa_oui = wpa2_oui;
248 break;
249
250 case 0xdd: /* WPA or else */
251 wpa_oui = wpa1_oui;
252 /* Not all IEs that start with 0xdd are WPA.
253 * * So check that the OUI is valid. */
254 if((ielen < 8) || ((memcmp(&iebuf[offset], wpa_oui, 3) != 0)
255 && (iebuf[offset+3] == 0x01)))
256 return;
257
258 offset += 4;
259 break;
260
261 default:
262 return;
263 }
264
265 /* Pick version number (little endian) */
266 ver = iebuf[offset] | (iebuf[offset + 1] << 8);
267 offset += 2;
268
269 if(iebuf[0] == 0xdd)
270 wpa1 = 1;
271
272 if(iebuf[0] == 0x30)
273 wpa2 = 1;
274
275 if( wpa1 && (ce->wpa_version == 2) )
276 ce->wpa_version = 3;
277 else if( wpa2 && (ce->wpa_version == 1) )
278 ce->wpa_version = 3;
279 else if( wpa1 && !ce->wpa_version )
280 ce->wpa_version = 1;
281 else if( wpa2 && !ce->wpa_version )
282 ce->wpa_version = 2;
283
284 if(ielen < (offset + 4))
285 {
286 ce->group_ciphers |= (1<<2); /* TKIP */
287 ce->pair_ciphers |= (1<<2); /* TKIP */
288 ce->auth_suites |= (1<<2); /* PSK */
289 return;
290 }
291
292 if(memcmp(&iebuf[offset], wpa_oui, 3) != 0)
293 ce->group_ciphers |= (1<<7); /* Proprietary */
294 else
295 ce->group_ciphers |= (1<<iebuf[offset+3]);
296
297 offset += 4;
298
299 if(ielen < (offset + 2))
300 {
301 ce->pair_ciphers |= (1<<2); /* TKIP */
302 ce->auth_suites |= (1<<2); /* PSK */
303 return;
304 }
305
306 /* Otherwise, we have some number of pairwise ciphers. */
307 cnt = iebuf[offset] | (iebuf[offset + 1] << 8);
308 offset += 2;
309
310 if(ielen < (offset + 4*cnt))
311 return;
312
313 *buf = '\0';
314 for(i = 0; i < cnt; i++)
315 {
316 if(memcmp(&iebuf[offset], wpa_oui, 3) != 0)
317 ce->pair_ciphers |= (1<<7); /* Proprietary */
318 else if(iebuf[offset+3] <= IW_IE_CYPHER_NUM)
319 ce->pair_ciphers |= (1<<iebuf[offset+3]);
320 //else
321 // ce->pair_ciphers[ce->pair_cipher_num++] = 255; /* Unknown */
322
323 offset += 4;
324 }
325
326 /* Check if we are done */
327 if(ielen < (offset + 2))
328 return;
329
330 /* Now, we have authentication suites. */
331 cnt = iebuf[offset] | (iebuf[offset + 1] << 8);
332 offset += 2;
333 *buf = '\0';
334
335 if(ielen < (offset + 4*cnt))
336 return;
337
338 for(i = 0; i < cnt; i++)
339 {
340 if(memcmp(&iebuf[offset], wpa_oui, 3) != 0)
341 ce->auth_suites |= (1<<7); /* Proprietary */
342 else if(iebuf[offset+3] <= IW_IE_KEY_MGMT_NUM)
343 ce->auth_suites |= (1<<iebuf[offset+3]);
344 //else
345 // ce->auth_suites[ce->auth_suite_num++] = 255; /* Unknown */
346
347 offset += 4;
348 }
349 }
350
351
352 static inline void wext_fill_entry(struct stream_descr *stream, struct iw_event *event,
353 struct iw_range *iw_range, int has_range, struct iwinfo_scanlist_entry *e)
354 {
355 int i;
356 double freq;
357
358 /* Now, let's decode the event */
359 switch(event->cmd)
360 {
361 case SIOCGIWAP:
362 memcpy(e->mac, &event->u.ap_addr.sa_data, 6);
363 break;
364
365 case SIOCGIWFREQ:
366 if( event->u.freq.m >= 1000 )
367 {
368 freq = wext_freq2float(&(event->u.freq));
369
370 for(i = 0; i < iw_range->num_frequency; i++)
371 {
372 if( wext_freq2float(&iw_range->freq[i]) == freq )
373 {
374 e->channel = iw_range->freq[i].i;
375 break;
376 }
377 }
378 }
379 else
380 {
381 e->channel = event->u.freq.m;
382 }
383
384 break;
385
386 case SIOCGIWMODE:
387 switch(event->u.mode)
388 {
389 case 1:
390 sprintf((char *) e->mode, "Ad-Hoc");
391 break;
392
393 case 2:
394 case 3:
395 sprintf((char *) e->mode, "Master");
396 break;
397
398 default:
399 sprintf((char *) e->mode, "Unknown");
400 }
401
402 break;
403
404 case SIOCGIWESSID:
405 if( event->u.essid.pointer && event->u.essid.length && event->u.essid.flags )
406 memcpy(e->ssid, event->u.essid.pointer, event->u.essid.length);
407
408 break;
409
410 case SIOCGIWENCODE:
411 e->crypto.enabled = !(event->u.data.flags & IW_ENCODE_DISABLED);
412 break;
413
414 case IWEVQUAL:
415 e->signal = event->u.qual.level;
416 e->quality = event->u.qual.qual;
417 e->quality_max = iw_range->max_qual.qual;
418 break;
419 #if 0
420 case SIOCGIWRATE:
421 if(state->val_index == 0)
422 {
423 lua_pushstring(L, "bitrates");
424 lua_newtable(L);
425 }
426 //iw_print_bitrate(buffer, sizeof(buffer), event->u.bitrate.value);
427 snprintf(buffer, sizeof(buffer), "%d", event->u.bitrate.value);
428 lua_pushinteger(L, state->val_index + 1);
429 lua_pushstring(L, buffer);
430 lua_settable(L, -3);
431
432 /* Check for termination */
433 if(stream->value == NULL)
434 {
435 lua_settable(L, -3);
436 state->val_index = 0;
437 } else
438 state->val_index++;
439 break;
440 #endif
441 case IWEVGENIE:
442 i = 0;
443
444 while(i <= (event->u.data.length - 2))
445 {
446 switch(((unsigned char *)event->u.data.pointer)[i])
447 {
448 case 0xdd: /* WPA1 (and other) */
449 case 0x30: /* WPA2 */
450 wext_fill_wpa((unsigned char *)event->u.data.pointer + i,
451 event->u.data.length, e);
452
453 break;
454 }
455
456 i += ((unsigned char *)event->u.data.pointer)[i+1] + 2;
457 }
458
459 break;
460 }
461 }
462
463
464 int wext_get_scanlist(const char *ifname, char *buf, int *len)
465 {
466 struct iwreq wrq;
467 struct iw_scan_req scanopt; /* Options for 'set' */
468 unsigned char *buffer = NULL; /* Results */
469 int buflen = IW_SCAN_MAX_DATA; /* Min for compat WE<17 */
470 struct iw_range range;
471 int has_range = 1;
472 struct timeval tv; /* Select timeout */
473 int timeout = 15000000; /* 15s */
474
475 int entrylen = 0;
476 struct iwinfo_scanlist_entry e;
477
478 wrq.u.data.pointer = (caddr_t) &range;
479 wrq.u.data.length = sizeof(struct iw_range);
480 wrq.u.data.flags = 0;
481
482 if( wext_ioctl(ifname, SIOCGIWRANGE, &wrq) >= 0 )
483 {
484 /* Init timeout value -> 250ms between set and first get */
485 tv.tv_sec = 0;
486 tv.tv_usec = 250000;
487
488 /* Clean up set args */
489 memset(&scanopt, 0, sizeof(scanopt));
490
491 wrq.u.data.pointer = NULL;
492 wrq.u.data.flags = 0;
493 wrq.u.data.length = 0;
494
495 /* Initiate Scanning */
496 if( wext_ioctl(ifname, SIOCSIWSCAN, &wrq) >= 0 )
497 {
498 timeout -= tv.tv_usec;
499
500 /* Forever */
501 while(1)
502 {
503 fd_set rfds; /* File descriptors for select */
504 int last_fd; /* Last fd */
505 int ret;
506
507 /* Guess what ? We must re-generate rfds each time */
508 FD_ZERO(&rfds);
509 last_fd = -1;
510 /* In here, add the rtnetlink fd in the list */
511
512 /* Wait until something happens */
513 ret = select(last_fd + 1, &rfds, NULL, NULL, &tv);
514
515 /* Check if there was an error */
516 if(ret < 0)
517 {
518 if(errno == EAGAIN || errno == EINTR)
519 continue;
520
521 return -1;
522 }
523
524 /* Check if there was a timeout */
525 if(ret == 0)
526 {
527 unsigned char *newbuf;
528
529 realloc:
530 /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */
531 newbuf = realloc(buffer, buflen);
532 if(newbuf == NULL)
533 {
534 if(buffer)
535 free(buffer);
536
537 return -1;
538 }
539
540 buffer = newbuf;
541
542 /* Try to read the results */
543 wrq.u.data.pointer = buffer;
544 wrq.u.data.flags = 0;
545 wrq.u.data.length = buflen;
546
547 if( wext_ioctl(ifname, SIOCGIWSCAN, &wrq) )
548 {
549 /* Check if buffer was too small (WE-17 only) */
550 if((errno == E2BIG) && (range.we_version_compiled > 16))
551 {
552 /* Some driver may return very large scan results, either
553 * because there are many cells, or because they have many
554 * large elements in cells (like IWEVCUSTOM). Most will
555 * only need the regular sized buffer. We now use a dynamic
556 * allocation of the buffer to satisfy everybody. Of course,
557 * as we don't know in advance the size of the array, we try
558 * various increasing sizes. Jean II */
559
560 /* Check if the driver gave us any hints. */
561 if(wrq.u.data.length > buflen)
562 buflen = wrq.u.data.length;
563 else
564 buflen *= 2;
565
566 /* Try again */
567 goto realloc;
568 }
569
570 /* Check if results not available yet */
571 if(errno == EAGAIN)
572 {
573 /* Restart timer for only 100ms*/
574 tv.tv_sec = 0;
575 tv.tv_usec = 100000;
576 timeout -= tv.tv_usec;
577
578 if(timeout > 0)
579 continue; /* Try again later */
580 }
581
582 /* Bad error */
583 free(buffer);
584 return -1;
585
586 } else {
587 /* We have the results, go to process them */
588 break;
589 }
590 }
591 }
592
593 if( wrq.u.data.length )
594 {
595 struct iw_event iwe;
596 struct stream_descr stream;
597 int ret;
598 int first = 1;
599
600 memset(&stream, 0, sizeof(stream));
601 stream.current = (char *)buffer;
602 stream.end = (char *)buffer + wrq.u.data.length;
603
604 do
605 {
606 /* Extract an event and print it */
607 ret = wext_extract_event(&stream, &iwe, range.we_version_compiled);
608
609 if(ret >= 0)
610 {
611 if( (iwe.cmd == SIOCGIWAP) || (ret == 0) )
612 {
613 if( first )
614 {
615 first = 0;
616 }
617 else if( (entrylen + sizeof(struct iwinfo_scanlist_entry)) <= IWINFO_BUFSIZE )
618 {
619 /* if encryption is off, clear the crypto strunct */
620 if( !e.crypto.enabled )
621 memset(&e.crypto, 0, sizeof(struct iwinfo_crypto_entry));
622
623 memcpy(&buf[entrylen], &e, sizeof(struct iwinfo_scanlist_entry));
624 entrylen += sizeof(struct iwinfo_scanlist_entry);
625 }
626 else
627 {
628 /* we exceed the callers buffer size, abort here ... */
629 break;
630 }
631
632 memset(&e, 0, sizeof(struct iwinfo_scanlist_entry));
633 }
634
635 wext_fill_entry(&stream, &iwe, &range, has_range, &e);
636 }
637
638 } while(ret > 0);
639
640 free(buffer);
641 *len = entrylen;
642 return 0;
643 }
644
645 *len = 0;
646 free(buffer);
647 return 0;
648 }
649 }
650
651 return -1;
652 }
653