2 * iwinfo - Wireless Information Library - Linux Wireless Extension Backend
4 * Copyright (C) 2009 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 ioctl_socket = -1;
28 static int wext_ioctl(const char *ifname, int cmd, struct iwreq *wrq)
31 if( ioctl_socket == -1 )
33 ioctl_socket = socket(AF_INET, SOCK_DGRAM, 0);
34 fcntl(ioctl_socket, F_SETFD, fcntl(ioctl_socket, F_GETFD) | FD_CLOEXEC);
37 strncpy(wrq->ifr_name, ifname, IFNAMSIZ);
38 return ioctl(ioctl_socket, cmd, wrq);
41 static double wext_freq2float(const struct iw_freq *in)
44 double res = (double) in->m;
45 for(i = 0; i < in->e; i++) res *= 10;
49 static int wext_extract_event(struct stream_descr *stream, struct iw_event *iwe, int wev)
51 const struct iw_ioctl_description *descr = NULL;
53 unsigned int event_len = 1;
55 unsigned cmd_index; /* *MUST* be unsigned */
57 /* Check for end of stream */
58 if((stream->current + IW_EV_LCP_PK_LEN) > stream->end)
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);
65 /* Check invalid events */
66 if(iwe->len <= IW_EV_LCP_PK_LEN)
69 /* Get the type and length of that event */
70 if(iwe->cmd <= SIOCIWLAST)
72 cmd_index = iwe->cmd - SIOCIWFIRST;
73 if(cmd_index < standard_ioctl_num)
74 descr = &(standard_ioctl_descr[cmd_index]);
78 cmd_index = iwe->cmd - IWEVFIRST;
79 if(cmd_index < standard_event_num)
80 descr = &(standard_event_descr[cmd_index]);
84 event_type = descr->header_type;
86 /* Unknown events -> event_type=0 => IW_EV_LCP_PK_LEN */
87 event_len = event_type_size[event_type];
89 /* Fixup for earlier version of WE */
90 if((wev <= 18) && (event_type == IW_HEADER_TYPE_POINT))
91 event_len += IW_EV_POINT_OFF;
93 /* Check if we know about this event */
94 if(event_len <= IW_EV_LCP_PK_LEN)
96 /* Skip to next event */
97 stream->current += iwe->len;
101 event_len -= IW_EV_LCP_PK_LEN;
103 /* Set pointer on data */
104 if(stream->value != NULL)
105 pointer = stream->value; /* Next value in event */
107 pointer = stream->current + IW_EV_LCP_PK_LEN; /* First value in event */
109 /* Copy the rest of the event (at least, fixed part) */
110 if((pointer + event_len) > stream->end)
112 /* Go to next event */
113 stream->current += iwe->len;
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);
122 memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len);
124 /* Skip event in the stream */
125 pointer += event_len;
127 /* Special processing for iw_point events */
128 if(event_type == IW_HEADER_TYPE_POINT)
130 /* Check the length of the payload */
131 unsigned int extra_len = iwe->len - (event_len + IW_EV_LCP_PK_LEN);
134 /* Set pointer on variable part (warning : non aligned) */
135 iwe->u.data.pointer = pointer;
137 /* Check that we have a descriptor for the command */
139 /* Can't check payload -> unsafe... */
140 iwe->u.data.pointer = NULL; /* Discard paylod */
143 /* Those checks are actually pretty hard to trigger,
144 * because of the checks done in the kernel... */
146 unsigned int token_len = iwe->u.data.length * descr->token_size;
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))
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)
158 /* Ok, let's redo everything */
159 pointer -= event_len;
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;
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 */
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 */
180 /* Same for underflows... */
181 if(iwe->u.data.length < descr->min_tokens)
182 iwe->u.data.pointer = NULL; /* Discard paylod */
187 iwe->u.data.pointer = NULL;
189 /* Go to next event */
190 stream->current += iwe->len;
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))) ))
203 pointer -= event_len;
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;
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;
216 /* Go to next event */
217 stream->value = NULL;
218 stream->current += iwe->len;
225 static inline void wext_fill_wpa(unsigned char *iebuf, int buflen, struct iwinfo_scanlist_entry *e)
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;
235 int wpa1 = 0, wpa2 = 0;
238 struct iwinfo_crypto_entry *ce = &e->crypto;
245 case 0x30: /* WPA2 */
246 /* Check if we have enough data */
253 case 0xdd: /* WPA or else */
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)))
268 /* Pick version number (little endian) */
269 ver = iebuf[offset] | (iebuf[offset + 1] << 8);
278 if( wpa1 && (ce->wpa_version == 2) )
280 else if( wpa2 && (ce->wpa_version == 1) )
282 else if( wpa1 && !ce->wpa_version )
284 else if( wpa2 && !ce->wpa_version )
287 if(ielen < (offset + 4))
289 ce->group_ciphers |= (1<<2); /* TKIP */
290 ce->pair_ciphers |= (1<<2); /* TKIP */
291 ce->auth_suites |= (1<<2); /* PSK */
295 if(memcmp(&iebuf[offset], wpa_oui, 3) != 0)
296 ce->group_ciphers |= (1<<7); /* Proprietary */
298 ce->group_ciphers |= (1<<iebuf[offset+3]);
302 if(ielen < (offset + 2))
304 ce->pair_ciphers |= (1<<2); /* TKIP */
305 ce->auth_suites |= (1<<2); /* PSK */
309 /* Otherwise, we have some number of pairwise ciphers. */
310 cnt = iebuf[offset] | (iebuf[offset + 1] << 8);
313 if(ielen < (offset + 4*cnt))
317 for(i = 0; i < cnt; i++)
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]);
324 // ce->pair_ciphers[ce->pair_cipher_num++] = 255; /* Unknown */
329 /* Check if we are done */
330 if(ielen < (offset + 2))
333 /* Now, we have authentication suites. */
334 cnt = iebuf[offset] | (iebuf[offset + 1] << 8);
338 if(ielen < (offset + 4*cnt))
341 for(i = 0; i < cnt; i++)
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]);
348 // ce->auth_suites[ce->auth_suite_num++] = 255; /* Unknown */
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)
361 /* Now, let's decode the event */
365 memcpy(e->mac, &event->u.ap_addr.sa_data, 6);
369 if( event->u.freq.m >= 1000 )
371 freq = wext_freq2float(&(event->u.freq));
373 for(i = 0; i < iw_range->num_frequency; i++)
375 if( wext_freq2float(&iw_range->freq[i]) == freq )
377 e->channel = iw_range->freq[i].i;
384 e->channel = event->u.freq.m;
390 switch(event->u.mode)
393 sprintf((char *) e->mode, "Ad-Hoc");
398 sprintf((char *) e->mode, "Master");
402 sprintf((char *) e->mode, "Unknown");
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);
414 e->crypto.enabled = !(event->u.data.flags & IW_ENCODE_DISABLED);
418 e->signal = event->u.qual.level;
419 e->quality = event->u.qual.qual;
420 e->quality_max = iw_range->max_qual.qual;
424 if(state->val_index == 0)
426 lua_pushstring(L, "bitrates");
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);
435 /* Check for termination */
436 if(stream->value == NULL)
439 state->val_index = 0;
447 while(i <= (event->u.data.length - 2))
449 switch(((unsigned char *)event->u.data.pointer)[i])
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);
459 i += ((unsigned char *)event->u.data.pointer)[i+1] + 2;
467 int wext_get_scanlist(const char *ifname, char *buf, int *len)
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;
475 struct timeval tv; /* Select timeout */
476 int timeout = 15000000; /* 15s */
479 struct iwinfo_scanlist_entry e;
481 wrq.u.data.pointer = (caddr_t) ⦥
482 wrq.u.data.length = sizeof(struct iw_range);
483 wrq.u.data.flags = 0;
485 if( wext_ioctl(ifname, SIOCGIWRANGE, &wrq) >= 0 )
487 /* Init timeout value -> 250ms between set and first get */
491 /* Clean up set args */
492 memset(&scanopt, 0, sizeof(scanopt));
494 wrq.u.data.pointer = NULL;
495 wrq.u.data.flags = 0;
496 wrq.u.data.length = 0;
498 /* Initiate Scanning */
499 if( wext_ioctl(ifname, SIOCSIWSCAN, &wrq) >= 0 )
501 timeout -= tv.tv_usec;
506 fd_set rfds; /* File descriptors for select */
507 int last_fd; /* Last fd */
510 /* Guess what ? We must re-generate rfds each time */
513 /* In here, add the rtnetlink fd in the list */
515 /* Wait until something happens */
516 ret = select(last_fd + 1, &rfds, NULL, NULL, &tv);
518 /* Check if there was an error */
521 if(errno == EAGAIN || errno == EINTR)
527 /* Check if there was a timeout */
530 unsigned char *newbuf;
533 /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */
534 newbuf = realloc(buffer, buflen);
545 /* Try to read the results */
546 wrq.u.data.pointer = buffer;
547 wrq.u.data.flags = 0;
548 wrq.u.data.length = buflen;
550 if( wext_ioctl(ifname, SIOCGIWSCAN, &wrq) )
552 /* Check if buffer was too small (WE-17 only) */
553 if((errno == E2BIG) && (range.we_version_compiled > 16))
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 */
563 /* Check if the driver gave us any hints. */
564 if(wrq.u.data.length > buflen)
565 buflen = wrq.u.data.length;
573 /* Check if results not available yet */
576 /* Restart timer for only 100ms*/
579 timeout -= tv.tv_usec;
582 continue; /* Try again later */
590 /* We have the results, go to process them */
596 if( wrq.u.data.length )
599 struct stream_descr stream;
603 memset(&stream, 0, sizeof(stream));
604 stream.current = (char *)buffer;
605 stream.end = (char *)buffer + wrq.u.data.length;
609 /* Extract an event and print it */
610 ret = wext_extract_event(&stream, &iwe, range.we_version_compiled);
614 if( (iwe.cmd == SIOCGIWAP) || (ret == 0) )
620 else if( (entrylen + sizeof(struct iwinfo_scanlist_entry)) <= IWINFO_BUFSIZE )
622 /* if encryption is off, clear the crypto strunct */
623 if( !e.crypto.enabled )
624 memset(&e.crypto, 0, sizeof(struct iwinfo_crypto_entry));
626 memcpy(&buf[entrylen], &e, sizeof(struct iwinfo_scanlist_entry));
627 entrylen += sizeof(struct iwinfo_scanlist_entry);
631 /* we exceed the callers buffer size, abort here ... */
635 memset(&e, 0, sizeof(struct iwinfo_scanlist_entry));
638 wext_fill_entry(&stream, &iwe, &range, has_range, &e);
657 void wext_scan_close(void)
659 if( ioctl_socket > -1 )