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