2
0

iwinfo_wext_scan.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  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. #include "iwinfo.h"
  22. #include "iwinfo_wext.h"
  23. static int wext_ioctl(const char *ifname, int cmd, struct iwreq *wrq)
  24. {
  25. strncpy(wrq->ifr_name, ifname, IFNAMSIZ);
  26. return iwinfo_ioctl(cmd, wrq);
  27. }
  28. static inline double wext_freq2float(const struct iw_freq *in)
  29. {
  30. int i;
  31. double res = (double) in->m;
  32. for(i = 0; i < in->e; i++) res *= 10;
  33. return res;
  34. }
  35. static inline int wext_extract_event(struct stream_descr *stream, struct iw_event *iwe, int wev)
  36. {
  37. const struct iw_ioctl_description *descr = NULL;
  38. int event_type = 0;
  39. unsigned int event_len = 1;
  40. char *pointer;
  41. unsigned cmd_index; /* *MUST* be unsigned */
  42. /* Check for end of stream */
  43. if((stream->current + IW_EV_LCP_PK_LEN) > stream->end)
  44. return 0;
  45. /* Extract the event header (to get the event id).
  46. * Note : the event may be unaligned, therefore copy... */
  47. memcpy((char *) iwe, stream->current, IW_EV_LCP_PK_LEN);
  48. /* Check invalid events */
  49. if(iwe->len <= IW_EV_LCP_PK_LEN)
  50. return -1;
  51. /* Get the type and length of that event */
  52. if(iwe->cmd <= SIOCIWLAST)
  53. {
  54. cmd_index = iwe->cmd - SIOCIWFIRST;
  55. if(cmd_index < standard_ioctl_num)
  56. descr = &(standard_ioctl_descr[cmd_index]);
  57. }
  58. else
  59. {
  60. cmd_index = iwe->cmd - IWEVFIRST;
  61. if(cmd_index < standard_event_num)
  62. descr = &(standard_event_descr[cmd_index]);
  63. }
  64. if(descr != NULL)
  65. event_type = descr->header_type;
  66. /* Unknown events -> event_type=0 => IW_EV_LCP_PK_LEN */
  67. event_len = event_type_size[event_type];
  68. /* Fixup for earlier version of WE */
  69. if((wev <= 18) && (event_type == IW_HEADER_TYPE_POINT))
  70. event_len += IW_EV_POINT_OFF;
  71. /* Check if we know about this event */
  72. if(event_len <= IW_EV_LCP_PK_LEN)
  73. {
  74. /* Skip to next event */
  75. stream->current += iwe->len;
  76. return 2;
  77. }
  78. event_len -= IW_EV_LCP_PK_LEN;
  79. /* Set pointer on data */
  80. if(stream->value != NULL)
  81. pointer = stream->value; /* Next value in event */
  82. else
  83. pointer = stream->current + IW_EV_LCP_PK_LEN; /* First value in event */
  84. /* Copy the rest of the event (at least, fixed part) */
  85. if((pointer + event_len) > stream->end)
  86. {
  87. /* Go to next event */
  88. stream->current += iwe->len;
  89. return -2;
  90. }
  91. /* Fixup for WE-19 and later : pointer no longer in the stream */
  92. /* Beware of alignement. Dest has local alignement, not packed */
  93. if( (wev > 18) && (event_type == IW_HEADER_TYPE_POINT) )
  94. memcpy((char *) iwe + IW_EV_LCP_LEN + IW_EV_POINT_OFF, pointer, event_len);
  95. else
  96. memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len);
  97. /* Skip event in the stream */
  98. pointer += event_len;
  99. /* Special processing for iw_point events */
  100. if(event_type == IW_HEADER_TYPE_POINT)
  101. {
  102. /* Check the length of the payload */
  103. unsigned int extra_len = iwe->len - (event_len + IW_EV_LCP_PK_LEN);
  104. if(extra_len > 0)
  105. {
  106. /* Set pointer on variable part (warning : non aligned) */
  107. iwe->u.data.pointer = pointer;
  108. /* Check that we have a descriptor for the command */
  109. if(descr == NULL)
  110. /* Can't check payload -> unsafe... */
  111. iwe->u.data.pointer = NULL; /* Discard paylod */
  112. else
  113. {
  114. /* Those checks are actually pretty hard to trigger,
  115. * because of the checks done in the kernel... */
  116. unsigned int token_len = iwe->u.data.length * descr->token_size;
  117. /* Ugly fixup for alignement issues.
  118. * If the kernel is 64 bits and userspace 32 bits,
  119. * we have an extra 4+4 bytes.
  120. * Fixing that in the kernel would break 64 bits userspace. */
  121. if((token_len != extra_len) && (extra_len >= 4))
  122. {
  123. uint16_t alt_dlen = *((uint16_t *) pointer);
  124. unsigned int alt_token_len = alt_dlen * descr->token_size;
  125. if((alt_token_len + 8) == extra_len)
  126. {
  127. /* Ok, let's redo everything */
  128. pointer -= event_len;
  129. pointer += 4;
  130. /* Dest has local alignement, not packed */
  131. memcpy((char *) iwe + IW_EV_LCP_LEN + IW_EV_POINT_OFF, pointer, event_len);
  132. pointer += event_len + 4;
  133. iwe->u.data.pointer = pointer;
  134. token_len = alt_token_len;
  135. }
  136. }
  137. /* Discard bogus events which advertise more tokens than
  138. * what they carry... */
  139. if(token_len > extra_len)
  140. iwe->u.data.pointer = NULL; /* Discard paylod */
  141. /* Check that the advertised token size is not going to
  142. * produce buffer overflow to our caller... */
  143. if((iwe->u.data.length > descr->max_tokens)
  144. && !(descr->flags & IW_DESCR_FLAG_NOMAX))
  145. iwe->u.data.pointer = NULL; /* Discard paylod */
  146. /* Same for underflows... */
  147. if(iwe->u.data.length < descr->min_tokens)
  148. iwe->u.data.pointer = NULL; /* Discard paylod */
  149. }
  150. }
  151. else
  152. /* No data */
  153. iwe->u.data.pointer = NULL;
  154. /* Go to next event */
  155. stream->current += iwe->len;
  156. }
  157. else
  158. {
  159. /* Ugly fixup for alignement issues.
  160. * If the kernel is 64 bits and userspace 32 bits,
  161. * we have an extra 4 bytes.
  162. * Fixing that in the kernel would break 64 bits userspace. */
  163. if((stream->value == NULL)
  164. && ((((iwe->len - IW_EV_LCP_PK_LEN) % event_len) == 4)
  165. || ((iwe->len == 12) && ((event_type == IW_HEADER_TYPE_UINT) ||
  166. (event_type == IW_HEADER_TYPE_QUAL))) ))
  167. {
  168. pointer -= event_len;
  169. pointer += 4;
  170. /* Beware of alignement. Dest has local alignement, not packed */
  171. memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len);
  172. pointer += event_len;
  173. }
  174. /* Is there more value in the event ? */
  175. if((pointer + event_len) <= (stream->current + iwe->len))
  176. /* Go to next value */
  177. stream->value = pointer;
  178. else
  179. {
  180. /* Go to next event */
  181. stream->value = NULL;
  182. stream->current += iwe->len;
  183. }
  184. }
  185. return 1;
  186. }
  187. static inline void wext_fill_wpa(unsigned char *iebuf, int ielen, struct iwinfo_scanlist_entry *e)
  188. {
  189. static unsigned char ms_oui[3] = { 0x00, 0x50, 0xf2 };
  190. while (ielen >= 2 && ielen >= iebuf[1])
  191. {
  192. switch (iebuf[0])
  193. {
  194. case 48: /* RSN */
  195. iwinfo_parse_rsn(&e->crypto, iebuf + 2, iebuf[1],
  196. IWINFO_CIPHER_CCMP, IWINFO_KMGMT_8021x);
  197. break;
  198. case 221: /* Vendor */
  199. if (iebuf[1] >= 4 && !memcmp(iebuf + 2, ms_oui, 3) && iebuf[5] == 1)
  200. iwinfo_parse_rsn(&e->crypto, iebuf + 6, iebuf[1] - 4,
  201. IWINFO_CIPHER_TKIP, IWINFO_KMGMT_PSK);
  202. break;
  203. }
  204. ielen -= iebuf[1] + 2;
  205. iebuf += iebuf[1] + 2;
  206. }
  207. }
  208. static inline void wext_fill_entry(struct stream_descr *stream, struct iw_event *event,
  209. struct iw_range *iw_range, int has_range, struct iwinfo_scanlist_entry *e)
  210. {
  211. int i;
  212. double freq;
  213. /* Now, let's decode the event */
  214. switch(event->cmd)
  215. {
  216. case SIOCGIWAP:
  217. memcpy(e->mac, &event->u.ap_addr.sa_data, 6);
  218. break;
  219. case SIOCGIWFREQ:
  220. if( event->u.freq.m >= 1000 )
  221. {
  222. freq = wext_freq2float(&(event->u.freq));
  223. for(i = 0; i < iw_range->num_frequency; i++)
  224. {
  225. if( wext_freq2float(&iw_range->freq[i]) == freq )
  226. {
  227. e->channel = iw_range->freq[i].i;
  228. break;
  229. }
  230. }
  231. }
  232. else
  233. {
  234. e->channel = event->u.freq.m;
  235. }
  236. break;
  237. case SIOCGIWMODE:
  238. switch(event->u.mode)
  239. {
  240. case 1:
  241. e->mode = IWINFO_OPMODE_ADHOC;
  242. break;
  243. case 2:
  244. case 3:
  245. e->mode = IWINFO_OPMODE_MASTER;
  246. break;
  247. default:
  248. e->mode = IWINFO_OPMODE_UNKNOWN;
  249. break;
  250. }
  251. break;
  252. case SIOCGIWESSID:
  253. if( event->u.essid.pointer && event->u.essid.length && event->u.essid.flags )
  254. memcpy(e->ssid, event->u.essid.pointer, event->u.essid.length);
  255. break;
  256. case SIOCGIWENCODE:
  257. e->crypto.enabled = !(event->u.data.flags & IW_ENCODE_DISABLED);
  258. break;
  259. case IWEVQUAL:
  260. e->signal = event->u.qual.level;
  261. e->quality = event->u.qual.qual;
  262. e->quality_max = iw_range->max_qual.qual;
  263. break;
  264. #if 0
  265. case SIOCGIWRATE:
  266. if(state->val_index == 0)
  267. {
  268. lua_pushstring(L, "bitrates");
  269. lua_newtable(L);
  270. }
  271. //iw_print_bitrate(buffer, sizeof(buffer), event->u.bitrate.value);
  272. snprintf(buffer, sizeof(buffer), "%d", event->u.bitrate.value);
  273. lua_pushinteger(L, state->val_index + 1);
  274. lua_pushstring(L, buffer);
  275. lua_settable(L, -3);
  276. /* Check for termination */
  277. if(stream->value == NULL)
  278. {
  279. lua_settable(L, -3);
  280. state->val_index = 0;
  281. } else
  282. state->val_index++;
  283. break;
  284. #endif
  285. case IWEVGENIE:
  286. wext_fill_wpa(event->u.data.pointer, event->u.data.length, e);
  287. break;
  288. }
  289. }
  290. int wext_get_scanlist(const char *ifname, char *buf, int *len)
  291. {
  292. struct iwreq wrq;
  293. struct iw_scan_req scanopt; /* Options for 'set' */
  294. unsigned char *buffer = NULL; /* Results */
  295. int buflen = IW_SCAN_MAX_DATA; /* Min for compat WE<17 */
  296. struct iw_range range;
  297. int has_range = 1;
  298. struct timeval tv; /* Select timeout */
  299. int timeout = 15000000; /* 15s */
  300. int entrylen = 0;
  301. struct iwinfo_scanlist_entry e;
  302. wrq.u.data.pointer = (caddr_t) &range;
  303. wrq.u.data.length = sizeof(struct iw_range);
  304. wrq.u.data.flags = 0;
  305. if( wext_ioctl(ifname, SIOCGIWRANGE, &wrq) >= 0 )
  306. {
  307. /* Init timeout value -> 250ms between set and first get */
  308. tv.tv_sec = 0;
  309. tv.tv_usec = 250000;
  310. /* Clean up set args */
  311. memset(&scanopt, 0, sizeof(scanopt));
  312. wrq.u.data.pointer = NULL;
  313. wrq.u.data.flags = 0;
  314. wrq.u.data.length = 0;
  315. /* Initiate Scanning */
  316. if( wext_ioctl(ifname, SIOCSIWSCAN, &wrq) >= 0 )
  317. {
  318. timeout -= tv.tv_usec;
  319. /* Forever */
  320. while(1)
  321. {
  322. fd_set rfds; /* File descriptors for select */
  323. int last_fd; /* Last fd */
  324. int ret;
  325. /* Guess what ? We must re-generate rfds each time */
  326. FD_ZERO(&rfds);
  327. last_fd = -1;
  328. /* In here, add the rtnetlink fd in the list */
  329. /* Wait until something happens */
  330. ret = select(last_fd + 1, &rfds, NULL, NULL, &tv);
  331. /* Check if there was an error */
  332. if(ret < 0)
  333. {
  334. if(errno == EAGAIN || errno == EINTR)
  335. continue;
  336. return -1;
  337. }
  338. /* Check if there was a timeout */
  339. if(ret == 0)
  340. {
  341. unsigned char *newbuf;
  342. realloc:
  343. /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */
  344. newbuf = realloc(buffer, buflen);
  345. if(newbuf == NULL)
  346. {
  347. if(buffer)
  348. free(buffer);
  349. return -1;
  350. }
  351. buffer = newbuf;
  352. /* Try to read the results */
  353. wrq.u.data.pointer = buffer;
  354. wrq.u.data.flags = 0;
  355. wrq.u.data.length = buflen;
  356. if( wext_ioctl(ifname, SIOCGIWSCAN, &wrq) )
  357. {
  358. /* Check if buffer was too small (WE-17 only) */
  359. if((errno == E2BIG) && (range.we_version_compiled > 16))
  360. {
  361. /* Some driver may return very large scan results, either
  362. * because there are many cells, or because they have many
  363. * large elements in cells (like IWEVCUSTOM). Most will
  364. * only need the regular sized buffer. We now use a dynamic
  365. * allocation of the buffer to satisfy everybody. Of course,
  366. * as we don't know in advance the size of the array, we try
  367. * various increasing sizes. Jean II */
  368. /* Check if the driver gave us any hints. */
  369. if(wrq.u.data.length > buflen)
  370. buflen = wrq.u.data.length;
  371. else
  372. buflen *= 2;
  373. /* Try again */
  374. goto realloc;
  375. }
  376. /* Check if results not available yet */
  377. if(errno == EAGAIN)
  378. {
  379. /* Restart timer for only 100ms*/
  380. tv.tv_sec = 0;
  381. tv.tv_usec = 100000;
  382. timeout -= tv.tv_usec;
  383. if(timeout > 0)
  384. continue; /* Try again later */
  385. }
  386. /* Bad error */
  387. free(buffer);
  388. return -1;
  389. } else {
  390. /* We have the results, go to process them */
  391. break;
  392. }
  393. }
  394. }
  395. if( wrq.u.data.length )
  396. {
  397. struct iw_event iwe;
  398. struct stream_descr stream;
  399. int ret;
  400. int first = 1;
  401. memset(&stream, 0, sizeof(stream));
  402. stream.current = (char *)buffer;
  403. stream.end = (char *)buffer + wrq.u.data.length;
  404. do
  405. {
  406. /* Extract an event and print it */
  407. ret = wext_extract_event(&stream, &iwe, range.we_version_compiled);
  408. if(ret >= 0)
  409. {
  410. if( (iwe.cmd == SIOCGIWAP) || (ret == 0) )
  411. {
  412. if( first )
  413. {
  414. first = 0;
  415. }
  416. else if( (entrylen + sizeof(struct iwinfo_scanlist_entry)) <= IWINFO_BUFSIZE )
  417. {
  418. /* if encryption is off, clear the crypto strunct */
  419. if( !e.crypto.enabled )
  420. memset(&e.crypto, 0, sizeof(struct iwinfo_crypto_entry));
  421. memcpy(&buf[entrylen], &e, sizeof(struct iwinfo_scanlist_entry));
  422. entrylen += sizeof(struct iwinfo_scanlist_entry);
  423. }
  424. else
  425. {
  426. /* we exceed the callers buffer size, abort here ... */
  427. break;
  428. }
  429. memset(&e, 0, sizeof(struct iwinfo_scanlist_entry));
  430. }
  431. wext_fill_entry(&stream, &iwe, &range, has_range, &e);
  432. }
  433. } while(ret > 0);
  434. free(buffer);
  435. *len = entrylen;
  436. return 0;
  437. }
  438. *len = 0;
  439. free(buffer);
  440. return 0;
  441. }
  442. }
  443. return -1;
  444. }