b0c1f15e8e72ea379e0ef329f206226784f1982a
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 )
32 ioctl_socket
= socket(AF_INET
, SOCK_DGRAM
, 0);
34 strncpy(wrq
->ifr_name
, ifname
, IFNAMSIZ
);
35 return ioctl(ioctl_socket
, cmd
, wrq
);
38 static double wext_freq2float(const struct iw_freq
*in
)
41 double res
= (double) in
->m
;
42 for(i
= 0; i
< in
->e
; i
++) res
*= 10;
46 static int wext_extract_event(struct stream_descr
*stream
, struct iw_event
*iwe
, int wev
)
48 const struct iw_ioctl_description
*descr
= NULL
;
50 unsigned int event_len
= 1;
52 unsigned cmd_index
; /* *MUST* be unsigned */
54 /* Check for end of stream */
55 if((stream
->current
+ IW_EV_LCP_PK_LEN
) > stream
->end
)
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
);
62 /* Check invalid events */
63 if(iwe
->len
<= IW_EV_LCP_PK_LEN
)
66 /* Get the type and length of that event */
67 if(iwe
->cmd
<= SIOCIWLAST
)
69 cmd_index
= iwe
->cmd
- SIOCIWFIRST
;
70 if(cmd_index
< standard_ioctl_num
)
71 descr
= &(standard_ioctl_descr
[cmd_index
]);
75 cmd_index
= iwe
->cmd
- IWEVFIRST
;
76 if(cmd_index
< standard_event_num
)
77 descr
= &(standard_event_descr
[cmd_index
]);
81 event_type
= descr
->header_type
;
83 /* Unknown events -> event_type=0 => IW_EV_LCP_PK_LEN */
84 event_len
= event_type_size
[event_type
];
86 /* Fixup for earlier version of WE */
87 if((wev
<= 18) && (event_type
== IW_HEADER_TYPE_POINT
))
88 event_len
+= IW_EV_POINT_OFF
;
90 /* Check if we know about this event */
91 if(event_len
<= IW_EV_LCP_PK_LEN
)
93 /* Skip to next event */
94 stream
->current
+= iwe
->len
;
98 event_len
-= IW_EV_LCP_PK_LEN
;
100 /* Set pointer on data */
101 if(stream
->value
!= NULL
)
102 pointer
= stream
->value
; /* Next value in event */
104 pointer
= stream
->current
+ IW_EV_LCP_PK_LEN
; /* First value in event */
106 /* Copy the rest of the event (at least, fixed part) */
107 if((pointer
+ event_len
) > stream
->end
)
109 /* Go to next event */
110 stream
->current
+= iwe
->len
;
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
);
119 memcpy((char *) iwe
+ IW_EV_LCP_LEN
, pointer
, event_len
);
121 /* Skip event in the stream */
122 pointer
+= event_len
;
124 /* Special processing for iw_point events */
125 if(event_type
== IW_HEADER_TYPE_POINT
)
127 /* Check the length of the payload */
128 unsigned int extra_len
= iwe
->len
- (event_len
+ IW_EV_LCP_PK_LEN
);
131 /* Set pointer on variable part (warning : non aligned) */
132 iwe
->u
.data
.pointer
= pointer
;
134 /* Check that we have a descriptor for the command */
136 /* Can't check payload -> unsafe... */
137 iwe
->u
.data
.pointer
= NULL
; /* Discard paylod */
140 /* Those checks are actually pretty hard to trigger,
141 * because of the checks done in the kernel... */
143 unsigned int token_len
= iwe
->u
.data
.length
* descr
->token_size
;
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))
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
)
155 /* Ok, let's redo everything */
156 pointer
-= event_len
;
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
;
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 */
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 */
177 /* Same for underflows... */
178 if(iwe
->u
.data
.length
< descr
->min_tokens
)
179 iwe
->u
.data
.pointer
= NULL
; /* Discard paylod */
184 iwe
->u
.data
.pointer
= NULL
;
186 /* Go to next event */
187 stream
->current
+= iwe
->len
;
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
))) ))
200 pointer
-= event_len
;
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
;
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
;
213 /* Go to next event */
214 stream
->value
= NULL
;
215 stream
->current
+= iwe
->len
;
222 static inline void wext_fill_wpa(unsigned char *iebuf
, int buflen
, struct iwinfo_scanlist_entry
*e
)
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
;
232 int wpa1
= 0, wpa2
= 0;
235 struct iwinfo_crypto_entry
*ce
= &e
->crypto
;
242 case 0x30: /* WPA2 */
243 /* Check if we have enough data */
250 case 0xdd: /* WPA or else */
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)))
265 /* Pick version number (little endian) */
266 ver
= iebuf
[offset
] | (iebuf
[offset
+ 1] << 8);
275 if( wpa1
&& (ce
->wpa_version
== 2) )
277 else if( wpa2
&& (ce
->wpa_version
== 1) )
279 else if( wpa1
&& !ce
->wpa_version
)
281 else if( wpa2
&& !ce
->wpa_version
)
284 if(ielen
< (offset
+ 4))
286 ce
->group_ciphers
|= (1<<2); /* TKIP */
287 ce
->pair_ciphers
|= (1<<2); /* TKIP */
288 ce
->auth_suites
|= (1<<2); /* PSK */
292 if(memcmp(&iebuf
[offset
], wpa_oui
, 3) != 0)
293 ce
->group_ciphers
|= (1<<7); /* Proprietary */
295 ce
->group_ciphers
|= (1<<iebuf
[offset
+3]);
299 if(ielen
< (offset
+ 2))
301 ce
->pair_ciphers
|= (1<<2); /* TKIP */
302 ce
->auth_suites
|= (1<<2); /* PSK */
306 /* Otherwise, we have some number of pairwise ciphers. */
307 cnt
= iebuf
[offset
] | (iebuf
[offset
+ 1] << 8);
310 if(ielen
< (offset
+ 4*cnt
))
314 for(i
= 0; i
< cnt
; i
++)
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]);
321 // ce->pair_ciphers[ce->pair_cipher_num++] = 255; /* Unknown */
326 /* Check if we are done */
327 if(ielen
< (offset
+ 2))
330 /* Now, we have authentication suites. */
331 cnt
= iebuf
[offset
] | (iebuf
[offset
+ 1] << 8);
335 if(ielen
< (offset
+ 4*cnt
))
338 for(i
= 0; i
< cnt
; i
++)
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]);
345 // ce->auth_suites[ce->auth_suite_num++] = 255; /* Unknown */
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
)
358 /* Now, let's decode the event */
362 memcpy(e
->mac
, &event
->u
.ap_addr
.sa_data
, 6);
366 if( event
->u
.freq
.m
>= 1000 )
368 freq
= wext_freq2float(&(event
->u
.freq
));
370 for(i
= 0; i
< iw_range
->num_frequency
; i
++)
372 if( wext_freq2float(&iw_range
->freq
[i
]) == freq
)
374 e
->channel
= iw_range
->freq
[i
].i
;
381 e
->channel
= event
->u
.freq
.m
;
387 switch(event
->u
.mode
)
390 sprintf((char *) e
->mode
, "Ad-Hoc");
395 sprintf((char *) e
->mode
, "Master");
399 sprintf((char *) e
->mode
, "Unknown");
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
);
411 e
->crypto
.enabled
= !(event
->u
.data
.flags
& IW_ENCODE_DISABLED
);
415 e
->signal
= event
->u
.qual
.level
;
416 e
->quality
= event
->u
.qual
.qual
;
417 e
->quality_max
= iw_range
->max_qual
.qual
;
421 if(state
->val_index
== 0)
423 lua_pushstring(L
, "bitrates");
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
);
432 /* Check for termination */
433 if(stream
->value
== NULL
)
436 state
->val_index
= 0;
444 while(i
<= (event
->u
.data
.length
- 2))
446 switch(((unsigned char *)event
->u
.data
.pointer
)[i
])
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
);
456 i
+= ((unsigned char *)event
->u
.data
.pointer
)[i
+1] + 2;
464 int wext_get_scanlist(const char *ifname
, char *buf
, int *len
)
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
;
472 struct timeval tv
; /* Select timeout */
473 int timeout
= 15000000; /* 15s */
476 struct iwinfo_scanlist_entry e
;
478 wrq
.u
.data
.pointer
= (caddr_t
) &range
;
479 wrq
.u
.data
.length
= sizeof(struct iw_range
);
480 wrq
.u
.data
.flags
= 0;
482 if( wext_ioctl(ifname
, SIOCGIWRANGE
, &wrq
) >= 0 )
484 /* Init timeout value -> 250ms between set and first get */
488 /* Clean up set args */
489 memset(&scanopt
, 0, sizeof(scanopt
));
491 wrq
.u
.data
.pointer
= NULL
;
492 wrq
.u
.data
.flags
= 0;
493 wrq
.u
.data
.length
= 0;
495 /* Initiate Scanning */
496 if( wext_ioctl(ifname
, SIOCSIWSCAN
, &wrq
) >= 0 )
498 timeout
-= tv
.tv_usec
;
503 fd_set rfds
; /* File descriptors for select */
504 int last_fd
; /* Last fd */
507 /* Guess what ? We must re-generate rfds each time */
510 /* In here, add the rtnetlink fd in the list */
512 /* Wait until something happens */
513 ret
= select(last_fd
+ 1, &rfds
, NULL
, NULL
, &tv
);
515 /* Check if there was an error */
518 if(errno
== EAGAIN
|| errno
== EINTR
)
524 /* Check if there was a timeout */
527 unsigned char *newbuf
;
530 /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */
531 newbuf
= realloc(buffer
, buflen
);
542 /* Try to read the results */
543 wrq
.u
.data
.pointer
= buffer
;
544 wrq
.u
.data
.flags
= 0;
545 wrq
.u
.data
.length
= buflen
;
547 if( wext_ioctl(ifname
, SIOCGIWSCAN
, &wrq
) )
549 /* Check if buffer was too small (WE-17 only) */
550 if((errno
== E2BIG
) && (range
.we_version_compiled
> 16))
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 */
560 /* Check if the driver gave us any hints. */
561 if(wrq
.u
.data
.length
> buflen
)
562 buflen
= wrq
.u
.data
.length
;
570 /* Check if results not available yet */
573 /* Restart timer for only 100ms*/
576 timeout
-= tv
.tv_usec
;
579 continue; /* Try again later */
587 /* We have the results, go to process them */
593 if( wrq
.u
.data
.length
)
596 struct stream_descr stream
;
600 memset(&stream
, 0, sizeof(stream
));
601 stream
.current
= (char *)buffer
;
602 stream
.end
= (char *)buffer
+ wrq
.u
.data
.length
;
606 /* Extract an event and print it */
607 ret
= wext_extract_event(&stream
, &iwe
, range
.we_version_compiled
);
611 if( (iwe
.cmd
== SIOCGIWAP
) || (ret
== 0) )
617 else if( (entrylen
+ sizeof(struct iwinfo_scanlist_entry
)) <= IWINFO_BUFSIZE
)
619 /* if encryption is off, clear the crypto strunct */
620 if( !e
.crypto
.enabled
)
621 memset(&e
.crypto
, 0, sizeof(struct iwinfo_crypto_entry
));
623 memcpy(&buf
[entrylen
], &e
, sizeof(struct iwinfo_scanlist_entry
));
624 entrylen
+= sizeof(struct iwinfo_scanlist_entry
);
628 /* we exceed the callers buffer size, abort here ... */
632 memset(&e
, 0, sizeof(struct iwinfo_scanlist_entry
));
635 wext_fill_entry(&stream
, &iwe
, &range
, has_range
, &e
);