| 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 | |
|---|
| 26 | static int ioctl_socket = -1; |
|---|
| 27 | |
|---|
| 28 | static 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 | |
|---|
| 38 | static 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 | |
|---|
| 46 | static 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 | |
|---|
| 218 | static 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 | |
|---|
| 350 | static 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 | |
|---|
| 458 | int 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) ⦥ |
|---|
| 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 | |
|---|