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