libiwinfo: reimplement iwinfo.lua in C and package it as "iwinfo"
[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-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 }