[libiwinfo] introduce country() and countrylist() (ISO3166 to driver code mapping)
[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