Root/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
26static 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
32static 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
40static 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
216static inline void wext_fill_wpa(unsigned char *iebuf, int ielen, struct iwinfo_scanlist_entry *e)
217{
218    static unsigned char ms_oui[3] = { 0x00, 0x50, 0xf2 };
219
220    while (ielen >= 2 && ielen >= iebuf[1])
221    {
222        switch (iebuf[0])
223        {
224        case 48: /* RSN */
225            iwinfo_parse_rsn(&e->crypto, iebuf + 2, iebuf[1],
226                             IWINFO_CIPHER_CCMP, IWINFO_KMGMT_8021x);
227            break;
228
229        case 221: /* Vendor */
230            if (iebuf[1] >= 4 && !memcmp(iebuf + 2, ms_oui, 3) && iebuf[5] == 1)
231                iwinfo_parse_rsn(&e->crypto, iebuf + 6, iebuf[1] - 4,
232                                 IWINFO_CIPHER_TKIP, IWINFO_KMGMT_PSK);
233            break;
234        }
235
236        ielen -= iebuf[1] + 2;
237        iebuf += iebuf[1] + 2;
238    }
239}
240
241
242static inline void wext_fill_entry(struct stream_descr *stream, struct iw_event *event,
243    struct iw_range *iw_range, int has_range, struct iwinfo_scanlist_entry *e)
244{
245    int i;
246    double freq;
247
248    /* Now, let's decode the event */
249    switch(event->cmd)
250    {
251        case SIOCGIWAP:
252            memcpy(e->mac, &event->u.ap_addr.sa_data, 6);
253            break;
254
255        case SIOCGIWFREQ:
256            if( event->u.freq.m >= 1000 )
257            {
258                freq = wext_freq2float(&(event->u.freq));
259
260                for(i = 0; i < iw_range->num_frequency; i++)
261                {
262                    if( wext_freq2float(&iw_range->freq[i]) == freq )
263                    {
264                        e->channel = iw_range->freq[i].i;
265                        break;
266                    }
267                }
268            }
269            else
270            {
271                e->channel = event->u.freq.m;
272            }
273
274            break;
275
276        case SIOCGIWMODE:
277            switch(event->u.mode)
278            {
279                case 1:
280                    e->mode = IWINFO_OPMODE_ADHOC;
281                    break;
282
283                case 2:
284                case 3:
285                    e->mode = IWINFO_OPMODE_MASTER;
286                    break;
287
288                default:
289                    e->mode = IWINFO_OPMODE_UNKNOWN;
290                    break;
291            }
292
293            break;
294
295        case SIOCGIWESSID:
296            if( event->u.essid.pointer && event->u.essid.length && event->u.essid.flags )
297                memcpy(e->ssid, event->u.essid.pointer, event->u.essid.length);
298
299            break;
300
301        case SIOCGIWENCODE:
302            e->crypto.enabled = !(event->u.data.flags & IW_ENCODE_DISABLED);
303            break;
304
305        case IWEVQUAL:
306            e->signal = event->u.qual.level;
307            e->quality = event->u.qual.qual;
308            e->quality_max = iw_range->max_qual.qual;
309            break;
310#if 0
311        case SIOCGIWRATE:
312            if(state->val_index == 0)
313            {
314                lua_pushstring(L, "bitrates");
315                lua_newtable(L);
316            }
317            //iw_print_bitrate(buffer, sizeof(buffer), event->u.bitrate.value);
318            snprintf(buffer, sizeof(buffer), "%d", event->u.bitrate.value);
319            lua_pushinteger(L, state->val_index + 1);
320            lua_pushstring(L, buffer);
321            lua_settable(L, -3);
322
323            /* Check for termination */
324            if(stream->value == NULL)
325            {
326                lua_settable(L, -3);
327                state->val_index = 0;
328            } else
329                state->val_index++;
330            break;
331#endif
332         case IWEVGENIE:
333            wext_fill_wpa(event->u.data.pointer, event->u.data.length, e);
334            break;
335    }
336}
337
338
339int wext_get_scanlist(const char *ifname, char *buf, int *len)
340{
341    struct iwreq wrq;
342    struct iw_scan_req scanopt; /* Options for 'set' */
343    unsigned char *buffer = NULL; /* Results */
344    int buflen = IW_SCAN_MAX_DATA; /* Min for compat WE<17 */
345    struct iw_range range;
346    int has_range = 1;
347    struct timeval tv; /* Select timeout */
348    int timeout = 15000000; /* 15s */
349
350    int entrylen = 0;
351    struct iwinfo_scanlist_entry e;
352
353    wrq.u.data.pointer = (caddr_t) &range;
354    wrq.u.data.length = sizeof(struct iw_range);
355    wrq.u.data.flags = 0;
356
357    if( wext_ioctl(ifname, SIOCGIWRANGE, &wrq) >= 0 )
358    {
359        /* Init timeout value -> 250ms between set and first get */
360        tv.tv_sec = 0;
361        tv.tv_usec = 250000;
362
363        /* Clean up set args */
364        memset(&scanopt, 0, sizeof(scanopt));
365
366        wrq.u.data.pointer = NULL;
367        wrq.u.data.flags = 0;
368        wrq.u.data.length = 0;
369
370        /* Initiate Scanning */
371        if( wext_ioctl(ifname, SIOCSIWSCAN, &wrq) >= 0 )
372        {
373            timeout -= tv.tv_usec;
374
375            /* Forever */
376            while(1)
377            {
378                fd_set rfds; /* File descriptors for select */
379                int last_fd; /* Last fd */
380                int ret;
381
382                /* Guess what ? We must re-generate rfds each time */
383                FD_ZERO(&rfds);
384                last_fd = -1;
385                /* In here, add the rtnetlink fd in the list */
386
387                /* Wait until something happens */
388                ret = select(last_fd + 1, &rfds, NULL, NULL, &tv);
389
390                /* Check if there was an error */
391                if(ret < 0)
392                {
393                    if(errno == EAGAIN || errno == EINTR)
394                        continue;
395
396                    return -1;
397                }
398
399                /* Check if there was a timeout */
400                if(ret == 0)
401                {
402                    unsigned char *newbuf;
403
404        realloc:
405                    /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */
406                    newbuf = realloc(buffer, buflen);
407                    if(newbuf == NULL)
408                    {
409                        if(buffer)
410                            free(buffer);
411
412                        return -1;
413                    }
414
415                    buffer = newbuf;
416
417                    /* Try to read the results */
418                    wrq.u.data.pointer = buffer;
419                    wrq.u.data.flags = 0;
420                    wrq.u.data.length = buflen;
421
422                    if( wext_ioctl(ifname, SIOCGIWSCAN, &wrq) )
423                    {
424                        /* Check if buffer was too small (WE-17 only) */
425                        if((errno == E2BIG) && (range.we_version_compiled > 16))
426                        {
427                            /* Some driver may return very large scan results, either
428                             * because there are many cells, or because they have many
429                             * large elements in cells (like IWEVCUSTOM). Most will
430                             * only need the regular sized buffer. We now use a dynamic
431                             * allocation of the buffer to satisfy everybody. Of course,
432                             * as we don't know in advance the size of the array, we try
433                             * various increasing sizes. Jean II */
434
435                            /* Check if the driver gave us any hints. */
436                            if(wrq.u.data.length > buflen)
437                                buflen = wrq.u.data.length;
438                            else
439                                buflen *= 2;
440
441                            /* Try again */
442                            goto realloc;
443                        }
444
445                        /* Check if results not available yet */
446                        if(errno == EAGAIN)
447                        {
448                            /* Restart timer for only 100ms*/
449                            tv.tv_sec = 0;
450                            tv.tv_usec = 100000;
451                            timeout -= tv.tv_usec;
452
453                            if(timeout > 0)
454                                continue; /* Try again later */
455                        }
456
457                        /* Bad error */
458                        free(buffer);
459                        return -1;
460
461                    } else {
462                        /* We have the results, go to process them */
463                        break;
464                    }
465                }
466            }
467
468            if( wrq.u.data.length )
469            {
470                struct iw_event iwe;
471                struct stream_descr stream;
472                int ret;
473                int first = 1;
474
475                memset(&stream, 0, sizeof(stream));
476                stream.current = (char *)buffer;
477                stream.end = (char *)buffer + wrq.u.data.length;
478
479                do
480                {
481                    /* Extract an event and print it */
482                    ret = wext_extract_event(&stream, &iwe, range.we_version_compiled);
483
484                    if(ret >= 0)
485                    {
486                        if( (iwe.cmd == SIOCGIWAP) || (ret == 0) )
487                        {
488                            if( first )
489                            {
490                                first = 0;
491                            }
492                            else if( (entrylen + sizeof(struct iwinfo_scanlist_entry)) <= IWINFO_BUFSIZE )
493                            {
494                                /* if encryption is off, clear the crypto strunct */
495                                if( !e.crypto.enabled )
496                                    memset(&e.crypto, 0, sizeof(struct iwinfo_crypto_entry));
497
498                                memcpy(&buf[entrylen], &e, sizeof(struct iwinfo_scanlist_entry));
499                                entrylen += sizeof(struct iwinfo_scanlist_entry);
500                            }
501                            else
502                            {
503                                /* we exceed the callers buffer size, abort here ... */
504                                break;
505                            }
506
507                            memset(&e, 0, sizeof(struct iwinfo_scanlist_entry));
508                        }
509
510                        wext_fill_entry(&stream, &iwe, &range, has_range, &e);
511                    }
512
513                } while(ret > 0);
514
515                free(buffer);
516                *len = entrylen;
517                return 0;
518            }
519
520            *len = 0;
521            free(buffer);
522            return 0;
523        }
524    }
525
526    return -1;
527}
528

Archive Download this file



interactive