2 * iwinfo - Wireless Information Library - Linux Wireless Extension Backend
4 * Copyright (C) 2009-2010 Jo-Philipp Wich <xm@subsignal.org>
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.
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.
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/.
18 * Parts of this code are derived from the Linux wireless tools, iwlib.c,
19 * iwlist.c and iwconfig.c in particular.
23 #include "iwinfo/wext_scan.h"
26 static int wext_ioctl(const char *ifname, int cmd, struct iwreq *wrq)
28 strncpy(wrq->ifr_name, ifname, IFNAMSIZ);
29 return iwinfo_ioctl(cmd, wrq);
32 static inline double wext_freq2float(const struct iw_freq *in)
35 double res = (double) in->m;
36 for(i = 0; i < in->e; i++) res *= 10;
40 static inline int wext_extract_event(struct stream_descr *stream, struct iw_event *iwe, int wev)
42 const struct iw_ioctl_description *descr = NULL;
44 unsigned int event_len = 1;
46 unsigned cmd_index; /* *MUST* be unsigned */
48 /* Check for end of stream */
49 if((stream->current + IW_EV_LCP_PK_LEN) > stream->end)
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);
56 /* Check invalid events */
57 if(iwe->len <= IW_EV_LCP_PK_LEN)
60 /* Get the type and length of that event */
61 if(iwe->cmd <= SIOCIWLAST)
63 cmd_index = iwe->cmd - SIOCIWFIRST;
64 if(cmd_index < standard_ioctl_num)
65 descr = &(standard_ioctl_descr[cmd_index]);
69 cmd_index = iwe->cmd - IWEVFIRST;
70 if(cmd_index < standard_event_num)
71 descr = &(standard_event_descr[cmd_index]);
75 event_type = descr->header_type;
77 /* Unknown events -> event_type=0 => IW_EV_LCP_PK_LEN */
78 event_len = event_type_size[event_type];
80 /* Fixup for earlier version of WE */
81 if((wev <= 18) && (event_type == IW_HEADER_TYPE_POINT))
82 event_len += IW_EV_POINT_OFF;
84 /* Check if we know about this event */
85 if(event_len <= IW_EV_LCP_PK_LEN)
87 /* Skip to next event */
88 stream->current += iwe->len;
92 event_len -= IW_EV_LCP_PK_LEN;
94 /* Set pointer on data */
95 if(stream->value != NULL)
96 pointer = stream->value; /* Next value in event */
98 pointer = stream->current + IW_EV_LCP_PK_LEN; /* First value in event */
100 /* Copy the rest of the event (at least, fixed part) */
101 if((pointer + event_len) > stream->end)
103 /* Go to next event */
104 stream->current += iwe->len;
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);
113 memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len);
115 /* Skip event in the stream */
116 pointer += event_len;
118 /* Special processing for iw_point events */
119 if(event_type == IW_HEADER_TYPE_POINT)
121 /* Check the length of the payload */
122 unsigned int extra_len = iwe->len - (event_len + IW_EV_LCP_PK_LEN);
125 /* Set pointer on variable part (warning : non aligned) */
126 iwe->u.data.pointer = pointer;
128 /* Check that we have a descriptor for the command */
130 /* Can't check payload -> unsafe... */
131 iwe->u.data.pointer = NULL; /* Discard paylod */
134 /* Those checks are actually pretty hard to trigger,
135 * because of the checks done in the kernel... */
137 unsigned int token_len = iwe->u.data.length * descr->token_size;
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))
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)
149 /* Ok, let's redo everything */
150 pointer -= event_len;
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;
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 */
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 */
171 /* Same for underflows... */
172 if(iwe->u.data.length < descr->min_tokens)
173 iwe->u.data.pointer = NULL; /* Discard paylod */
178 iwe->u.data.pointer = NULL;
180 /* Go to next event */
181 stream->current += iwe->len;
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))) ))
194 pointer -= event_len;
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;
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;
207 /* Go to next event */
208 stream->value = NULL;
209 stream->current += iwe->len;
216 static inline void wext_fill_wpa(unsigned char *iebuf, int ielen, struct iwinfo_scanlist_entry *e)
218 static unsigned char ms_oui[3] = { 0x00, 0x50, 0xf2 };
220 while (ielen >= 2 && ielen >= iebuf[1])
225 iwinfo_parse_rsn(&e->crypto, iebuf + 2, iebuf[1],
226 IWINFO_CIPHER_CCMP, IWINFO_KMGMT_8021x);
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);
236 ielen -= iebuf[1] + 2;
237 iebuf += iebuf[1] + 2;
242 static 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)
248 /* Now, let's decode the event */
252 memcpy(e->mac, &event->u.ap_addr.sa_data, 6);
256 if( event->u.freq.m >= 1000 )
258 freq = wext_freq2float(&(event->u.freq));
260 for(i = 0; i < iw_range->num_frequency; i++)
262 if( wext_freq2float(&iw_range->freq[i]) == freq )
264 e->channel = iw_range->freq[i].i;
271 e->channel = event->u.freq.m;
277 switch(event->u.mode)
280 e->mode = IWINFO_OPMODE_ADHOC;
285 e->mode = IWINFO_OPMODE_MASTER;
289 e->mode = IWINFO_OPMODE_UNKNOWN;
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);
302 e->crypto.enabled = !(event->u.data.flags & IW_ENCODE_DISABLED);
306 e->signal = event->u.qual.level;
307 e->quality = event->u.qual.qual;
308 e->quality_max = iw_range->max_qual.qual;
312 if(state->val_index == 0)
314 lua_pushstring(L, "bitrates");
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);
323 /* Check for termination */
324 if(stream->value == NULL)
327 state->val_index = 0;
333 wext_fill_wpa(event->u.data.pointer, event->u.data.length, e);
339 int wext_get_scanlist(const char *ifname, char *buf, int *len)
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;
347 struct timeval tv; /* Select timeout */
348 int timeout = 15000000; /* 15s */
351 struct iwinfo_scanlist_entry e;
353 wrq.u.data.pointer = (caddr_t) ⦥
354 wrq.u.data.length = sizeof(struct iw_range);
355 wrq.u.data.flags = 0;
357 if( wext_ioctl(ifname, SIOCGIWRANGE, &wrq) >= 0 )
359 /* Init timeout value -> 250ms between set and first get */
363 /* Clean up set args */
364 memset(&scanopt, 0, sizeof(scanopt));
366 wrq.u.data.pointer = NULL;
367 wrq.u.data.flags = 0;
368 wrq.u.data.length = 0;
370 /* Initiate Scanning */
371 if( wext_ioctl(ifname, SIOCSIWSCAN, &wrq) >= 0 )
373 timeout -= tv.tv_usec;
378 fd_set rfds; /* File descriptors for select */
379 int last_fd; /* Last fd */
382 /* Guess what ? We must re-generate rfds each time */
385 /* In here, add the rtnetlink fd in the list */
387 /* Wait until something happens */
388 ret = select(last_fd + 1, &rfds, NULL, NULL, &tv);
390 /* Check if there was an error */
393 if(errno == EAGAIN || errno == EINTR)
399 /* Check if there was a timeout */
402 unsigned char *newbuf;
405 /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */
406 newbuf = realloc(buffer, buflen);
417 /* Try to read the results */
418 wrq.u.data.pointer = buffer;
419 wrq.u.data.flags = 0;
420 wrq.u.data.length = buflen;
422 if( wext_ioctl(ifname, SIOCGIWSCAN, &wrq) )
424 /* Check if buffer was too small (WE-17 only) */
425 if((errno == E2BIG) && (range.we_version_compiled > 16))
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 */
435 /* Check if the driver gave us any hints. */
436 if(wrq.u.data.length > buflen)
437 buflen = wrq.u.data.length;
445 /* Check if results not available yet */
448 /* Restart timer for only 100ms*/
451 timeout -= tv.tv_usec;
454 continue; /* Try again later */
462 /* We have the results, go to process them */
468 if( wrq.u.data.length )
471 struct stream_descr stream;
475 memset(&stream, 0, sizeof(stream));
476 stream.current = (char *)buffer;
477 stream.end = (char *)buffer + wrq.u.data.length;
481 /* Extract an event and print it */
482 ret = wext_extract_event(&stream, &iwe, range.we_version_compiled);
486 if( (iwe.cmd == SIOCGIWAP) || (ret == 0) )
492 else if( (entrylen + sizeof(struct iwinfo_scanlist_entry)) <= IWINFO_BUFSIZE )
494 /* if encryption is off, clear the crypto strunct */
495 if( !e.crypto.enabled )
496 memset(&e.crypto, 0, sizeof(struct iwinfo_crypto_entry));
498 memcpy(&buf[entrylen], &e, sizeof(struct iwinfo_scanlist_entry));
499 entrylen += sizeof(struct iwinfo_scanlist_entry);
503 /* we exceed the callers buffer size, abort here ... */
507 memset(&e, 0, sizeof(struct iwinfo_scanlist_entry));
510 wext_fill_entry(&stream, &iwe, &range, has_range, &e);