l2_packet_ndis.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. /*
  2. * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO
  3. * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
  4. *
  5. * This software may be distributed under the terms of the BSD license.
  6. * See README for more details.
  7. *
  8. * This implementation requires Windows specific event loop implementation,
  9. * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with
  10. * driver_ndis.c, so only that driver interface can be used and
  11. * CONFIG_USE_NDISUIO must be defined.
  12. *
  13. * WinXP version of the code uses overlapped I/O and a single threaded design
  14. * with callback functions from I/O code. WinCE version uses a separate RX
  15. * thread that blocks on ReadFile() whenever the media status is connected.
  16. */
  17. #include "includes.h"
  18. #include <winsock2.h>
  19. #include <ntddndis.h>
  20. #ifdef _WIN32_WCE
  21. #include <winioctl.h>
  22. #include <nuiouser.h>
  23. #endif /* _WIN32_WCE */
  24. #include "common.h"
  25. #include "eloop.h"
  26. #include "l2_packet.h"
  27. #ifndef _WIN32_WCE
  28. /* from nuiouser.h */
  29. #define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK
  30. #define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
  31. CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
  32. #define IOCTL_NDISUIO_SET_ETHER_TYPE \
  33. _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
  34. FILE_READ_ACCESS | FILE_WRITE_ACCESS)
  35. #endif /* _WIN32_WCE */
  36. /* From driver_ndis.c to shared the handle to NDISUIO */
  37. HANDLE driver_ndis_get_ndisuio_handle(void);
  38. /*
  39. * NDISUIO supports filtering of only one ethertype at the time, so we must
  40. * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth
  41. * whenever wpa_supplicant is trying to pre-authenticate and then switching
  42. * back to EAPOL when pre-authentication has been completed.
  43. */
  44. struct l2_packet_data;
  45. struct l2_packet_ndisuio_global {
  46. int refcount;
  47. unsigned short first_proto;
  48. struct l2_packet_data *l2[2];
  49. #ifdef _WIN32_WCE
  50. HANDLE rx_thread;
  51. HANDLE stop_request;
  52. HANDLE ready_for_read;
  53. HANDLE rx_processed;
  54. #endif /* _WIN32_WCE */
  55. };
  56. static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL;
  57. struct l2_packet_data {
  58. char ifname[100];
  59. u8 own_addr[ETH_ALEN];
  60. void (*rx_callback)(void *ctx, const u8 *src_addr,
  61. const u8 *buf, size_t len);
  62. void *rx_callback_ctx;
  63. int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to
  64. * rx_callback and l2_packet_send() */
  65. HANDLE rx_avail;
  66. #ifndef _WIN32_WCE
  67. OVERLAPPED rx_overlapped;
  68. #endif /* _WIN32_WCE */
  69. u8 rx_buf[1514];
  70. DWORD rx_written;
  71. };
  72. int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
  73. {
  74. os_memcpy(addr, l2->own_addr, ETH_ALEN);
  75. return 0;
  76. }
  77. int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
  78. const u8 *buf, size_t len)
  79. {
  80. BOOL res;
  81. DWORD written;
  82. struct l2_ethhdr *eth;
  83. #ifndef _WIN32_WCE
  84. OVERLAPPED overlapped;
  85. #endif /* _WIN32_WCE */
  86. OVERLAPPED *o;
  87. if (l2 == NULL)
  88. return -1;
  89. #ifdef _WIN32_WCE
  90. o = NULL;
  91. #else /* _WIN32_WCE */
  92. os_memset(&overlapped, 0, sizeof(overlapped));
  93. o = &overlapped;
  94. #endif /* _WIN32_WCE */
  95. if (l2->l2_hdr) {
  96. res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len,
  97. &written, o);
  98. } else {
  99. size_t mlen = sizeof(*eth) + len;
  100. eth = os_malloc(mlen);
  101. if (eth == NULL)
  102. return -1;
  103. os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
  104. os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
  105. eth->h_proto = htons(proto);
  106. os_memcpy(eth + 1, buf, len);
  107. res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen,
  108. &written, o);
  109. os_free(eth);
  110. }
  111. if (!res) {
  112. DWORD err = GetLastError();
  113. #ifndef _WIN32_WCE
  114. if (err == ERROR_IO_PENDING) {
  115. wpa_printf(MSG_DEBUG, "L2(NDISUIO): Wait for pending "
  116. "write to complete");
  117. res = GetOverlappedResult(
  118. driver_ndis_get_ndisuio_handle(), &overlapped,
  119. &written, TRUE);
  120. if (!res) {
  121. wpa_printf(MSG_DEBUG, "L2(NDISUIO): "
  122. "GetOverlappedResult failed: %d",
  123. (int) GetLastError());
  124. return -1;
  125. }
  126. return 0;
  127. }
  128. #endif /* _WIN32_WCE */
  129. wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d",
  130. (int) GetLastError());
  131. return -1;
  132. }
  133. return 0;
  134. }
  135. static void l2_packet_callback(struct l2_packet_data *l2);
  136. #ifdef _WIN32_WCE
  137. static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2)
  138. {
  139. HANDLE handles[2];
  140. wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile");
  141. if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
  142. sizeof(l2->rx_buf), &l2->rx_written, NULL)) {
  143. DWORD err = GetLastError();
  144. wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: "
  145. "%d", (int) err);
  146. /*
  147. * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED
  148. * error whenever the connection is not up. Yield the thread to
  149. * avoid triggering a busy loop. Connection event should stop
  150. * us from looping for long, but we need to allow enough CPU
  151. * for the main thread to process the media disconnection.
  152. */
  153. Sleep(100);
  154. return;
  155. }
  156. wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet",
  157. (int) l2->rx_written);
  158. /*
  159. * Notify the main thread about the availability of a frame and wait
  160. * for the frame to be processed.
  161. */
  162. SetEvent(l2->rx_avail);
  163. handles[0] = l2_ndisuio_global->stop_request;
  164. handles[1] = l2_ndisuio_global->rx_processed;
  165. WaitForMultipleObjects(2, handles, FALSE, INFINITE);
  166. ResetEvent(l2_ndisuio_global->rx_processed);
  167. }
  168. static DWORD WINAPI l2_packet_rx_thread(LPVOID arg)
  169. {
  170. struct l2_packet_data *l2 = arg;
  171. DWORD res;
  172. HANDLE handles[2];
  173. int run = 1;
  174. wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started");
  175. handles[0] = l2_ndisuio_global->stop_request;
  176. handles[1] = l2_ndisuio_global->ready_for_read;
  177. /*
  178. * Unfortunately, NDISUIO on WinCE does not seem to support waiting
  179. * on the handle. There do not seem to be anything else that we could
  180. * wait for either. If one were to modify NDISUIO to set a named event
  181. * whenever packets are available, this event could be used here to
  182. * avoid having to poll for new packets or we could even move to use a
  183. * single threaded design.
  184. *
  185. * In addition, NDISUIO on WinCE is returning
  186. * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while
  187. * the adapter is not in connected state. For now, we are just using a
  188. * local event to allow ReadFile calls only after having received NDIS
  189. * media connect event. This event could be easily converted to handle
  190. * another event if the protocol driver is replaced with somewhat more
  191. * useful design.
  192. */
  193. while (l2_ndisuio_global && run) {
  194. res = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
  195. switch (res) {
  196. case WAIT_OBJECT_0:
  197. wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received "
  198. "request to stop RX thread");
  199. run = 0;
  200. break;
  201. case WAIT_OBJECT_0 + 1:
  202. l2_packet_rx_thread_try_read(l2);
  203. break;
  204. case WAIT_FAILED:
  205. default:
  206. wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: "
  207. "WaitForMultipleObjects failed: %d",
  208. (int) GetLastError());
  209. run = 0;
  210. break;
  211. }
  212. }
  213. wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped");
  214. return 0;
  215. }
  216. #else /* _WIN32_WCE */
  217. static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive)
  218. {
  219. os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped));
  220. l2->rx_overlapped.hEvent = l2->rx_avail;
  221. if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
  222. sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped))
  223. {
  224. DWORD err = GetLastError();
  225. if (err != ERROR_IO_PENDING) {
  226. wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: "
  227. "%d", (int) err);
  228. return -1;
  229. }
  230. /*
  231. * Once read is completed, l2_packet_rx_event() will be
  232. * called.
  233. */
  234. } else {
  235. wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data "
  236. "without wait for completion");
  237. if (!recursive)
  238. l2_packet_callback(l2);
  239. }
  240. return 0;
  241. }
  242. #endif /* _WIN32_WCE */
  243. static void l2_packet_callback(struct l2_packet_data *l2)
  244. {
  245. const u8 *rx_buf, *rx_src;
  246. size_t rx_len;
  247. struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf;
  248. wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes",
  249. (int) l2->rx_written);
  250. if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) {
  251. rx_buf = (u8 *) ethhdr;
  252. rx_len = l2->rx_written;
  253. } else {
  254. rx_buf = (u8 *) (ethhdr + 1);
  255. rx_len = l2->rx_written - sizeof(*ethhdr);
  256. }
  257. rx_src = ethhdr->h_source;
  258. l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len);
  259. #ifndef _WIN32_WCE
  260. l2_ndisuio_start_read(l2, 1);
  261. #endif /* _WIN32_WCE */
  262. }
  263. static void l2_packet_rx_event(void *eloop_data, void *user_data)
  264. {
  265. struct l2_packet_data *l2 = eloop_data;
  266. if (l2_ndisuio_global)
  267. l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1];
  268. ResetEvent(l2->rx_avail);
  269. #ifndef _WIN32_WCE
  270. if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(),
  271. &l2->rx_overlapped, &l2->rx_written, FALSE)) {
  272. wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult "
  273. "failed: %d", (int) GetLastError());
  274. return;
  275. }
  276. #endif /* _WIN32_WCE */
  277. l2_packet_callback(l2);
  278. #ifdef _WIN32_WCE
  279. SetEvent(l2_ndisuio_global->rx_processed);
  280. #endif /* _WIN32_WCE */
  281. }
  282. static int l2_ndisuio_set_ether_type(unsigned short protocol)
  283. {
  284. USHORT proto = htons(protocol);
  285. DWORD written;
  286. if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
  287. IOCTL_NDISUIO_SET_ETHER_TYPE, &proto,
  288. sizeof(proto), NULL, 0, &written, NULL)) {
  289. wpa_printf(MSG_ERROR, "L2(NDISUIO): "
  290. "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d",
  291. (int) GetLastError());
  292. return -1;
  293. }
  294. return 0;
  295. }
  296. struct l2_packet_data * l2_packet_init(
  297. const char *ifname, const u8 *own_addr, unsigned short protocol,
  298. void (*rx_callback)(void *ctx, const u8 *src_addr,
  299. const u8 *buf, size_t len),
  300. void *rx_callback_ctx, int l2_hdr)
  301. {
  302. struct l2_packet_data *l2;
  303. if (l2_ndisuio_global == NULL) {
  304. l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global));
  305. if (l2_ndisuio_global == NULL)
  306. return NULL;
  307. l2_ndisuio_global->first_proto = protocol;
  308. }
  309. if (l2_ndisuio_global->refcount >= 2) {
  310. wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two "
  311. "simultaneous connections allowed");
  312. return NULL;
  313. }
  314. l2_ndisuio_global->refcount++;
  315. l2 = os_zalloc(sizeof(struct l2_packet_data));
  316. if (l2 == NULL)
  317. return NULL;
  318. l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2;
  319. os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
  320. l2->rx_callback = rx_callback;
  321. l2->rx_callback_ctx = rx_callback_ctx;
  322. l2->l2_hdr = l2_hdr;
  323. if (own_addr)
  324. os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
  325. if (l2_ndisuio_set_ether_type(protocol) < 0) {
  326. os_free(l2);
  327. return NULL;
  328. }
  329. if (l2_ndisuio_global->refcount > 1) {
  330. wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting "
  331. "filtering ethertype to %04x", protocol);
  332. if (l2_ndisuio_global->l2[0])
  333. l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail;
  334. return l2;
  335. }
  336. l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
  337. if (l2->rx_avail == NULL) {
  338. os_free(l2);
  339. return NULL;
  340. }
  341. eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
  342. l2_packet_rx_event, l2, NULL);
  343. #ifdef _WIN32_WCE
  344. l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL);
  345. /*
  346. * This event is being set based on media connect/disconnect
  347. * notifications in driver_ndis.c.
  348. */
  349. l2_ndisuio_global->ready_for_read =
  350. CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
  351. l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL);
  352. if (l2_ndisuio_global->stop_request == NULL ||
  353. l2_ndisuio_global->ready_for_read == NULL ||
  354. l2_ndisuio_global->rx_processed == NULL) {
  355. if (l2_ndisuio_global->stop_request) {
  356. CloseHandle(l2_ndisuio_global->stop_request);
  357. l2_ndisuio_global->stop_request = NULL;
  358. }
  359. if (l2_ndisuio_global->ready_for_read) {
  360. CloseHandle(l2_ndisuio_global->ready_for_read);
  361. l2_ndisuio_global->ready_for_read = NULL;
  362. }
  363. if (l2_ndisuio_global->rx_processed) {
  364. CloseHandle(l2_ndisuio_global->rx_processed);
  365. l2_ndisuio_global->rx_processed = NULL;
  366. }
  367. eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
  368. os_free(l2);
  369. return NULL;
  370. }
  371. l2_ndisuio_global->rx_thread = CreateThread(NULL, 0,
  372. l2_packet_rx_thread, l2, 0,
  373. NULL);
  374. if (l2_ndisuio_global->rx_thread == NULL) {
  375. wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX "
  376. "thread: %d", (int) GetLastError());
  377. eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
  378. CloseHandle(l2_ndisuio_global->stop_request);
  379. l2_ndisuio_global->stop_request = NULL;
  380. os_free(l2);
  381. return NULL;
  382. }
  383. #else /* _WIN32_WCE */
  384. l2_ndisuio_start_read(l2, 0);
  385. #endif /* _WIN32_WCE */
  386. return l2;
  387. }
  388. void l2_packet_deinit(struct l2_packet_data *l2)
  389. {
  390. if (l2 == NULL)
  391. return;
  392. if (l2_ndisuio_global) {
  393. l2_ndisuio_global->refcount--;
  394. l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL;
  395. if (l2_ndisuio_global->refcount) {
  396. wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering "
  397. "ethertype to %04x",
  398. l2_ndisuio_global->first_proto);
  399. l2_ndisuio_set_ether_type(
  400. l2_ndisuio_global->first_proto);
  401. return;
  402. }
  403. #ifdef _WIN32_WCE
  404. wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to "
  405. "stop");
  406. SetEvent(l2_ndisuio_global->stop_request);
  407. /*
  408. * Cancel pending ReadFile() in the RX thread (if we were still
  409. * connected at this point).
  410. */
  411. if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
  412. IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL,
  413. NULL)) {
  414. wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ "
  415. "failed: %d", (int) GetLastError());
  416. /* RX thread will exit blocking ReadFile once NDISUIO
  417. * notices that the adapter is disconnected. */
  418. }
  419. WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE);
  420. wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited");
  421. CloseHandle(l2_ndisuio_global->rx_thread);
  422. CloseHandle(l2_ndisuio_global->stop_request);
  423. CloseHandle(l2_ndisuio_global->ready_for_read);
  424. CloseHandle(l2_ndisuio_global->rx_processed);
  425. #endif /* _WIN32_WCE */
  426. os_free(l2_ndisuio_global);
  427. l2_ndisuio_global = NULL;
  428. }
  429. #ifndef _WIN32_WCE
  430. CancelIo(driver_ndis_get_ndisuio_handle());
  431. #endif /* _WIN32_WCE */
  432. eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
  433. CloseHandle(l2->rx_avail);
  434. os_free(l2);
  435. }
  436. int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
  437. {
  438. return -1;
  439. }
  440. void l2_packet_notify_auth_start(struct l2_packet_data *l2)
  441. {
  442. }
  443. int l2_packet_set_packet_filter(struct l2_packet_data *l2,
  444. enum l2_packet_filter_type type)
  445. {
  446. return -1;
  447. }