root/luci/trunk/libs/iwinfo/src/iwinfo_wext_scan.c @ 5304

Revision 5304, 15.4 KB (checked in by jow, 4 years ago)

libs/iwinfo: implement wifi scans

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