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.h" 24 25 26 static int wext_ioctl(const char *ifname, int cmd, struct iwreq *wrq) 27 { 28 strncpy(wrq->ifr_name, ifname, IFNAMSIZ - 1); 29 return iwinfo_ioctl(cmd, wrq); 30 } 31 32 static 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 40 static 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 216 static 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 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) 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 339 int 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) ⦥ 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
This page was automatically generated by LXR 0.3.1. • OpenWrt