00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "includes.h"
00025 #include <winsock2.h>
00026 #include <ntddndis.h>
00027
00028 #ifdef _WIN32_WCE
00029 #include <winioctl.h>
00030 #include <nuiouser.h>
00031 #endif
00032
00033 #include "common.h"
00034 #include "eloop.h"
00035 #include "l2_packet.h"
00036
00037 #ifndef _WIN32_WCE
00038
00039 #define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK
00040 #define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
00041 CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
00042 #define IOCTL_NDISUIO_SET_ETHER_TYPE \
00043 _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
00044 FILE_READ_ACCESS | FILE_WRITE_ACCESS)
00045 #endif
00046
00047
00048 HANDLE driver_ndis_get_ndisuio_handle(void);
00049
00050
00051
00052
00053
00054
00055
00056
00057 struct l2_packet_data;
00058
00059 struct l2_packet_ndisuio_global {
00060 int refcount;
00061 unsigned short first_proto;
00062 struct l2_packet_data *l2[2];
00063 #ifdef _WIN32_WCE
00064 HANDLE rx_thread;
00065 HANDLE stop_request;
00066 HANDLE ready_for_read;
00067 HANDLE rx_processed;
00068 #endif
00069 };
00070
00071 static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL;
00072
00073 struct l2_packet_data {
00074 char ifname[100];
00075 u8 own_addr[ETH_ALEN];
00076 void (*rx_callback)(void *ctx, const u8 *src_addr,
00077 const u8 *buf, size_t len);
00078 void *rx_callback_ctx;
00079 int l2_hdr;
00080
00081 HANDLE rx_avail;
00082 #ifndef _WIN32_WCE
00083 OVERLAPPED rx_overlapped;
00084 #endif
00085 u8 rx_buf[1514];
00086 DWORD rx_written;
00087 };
00088
00089
00090 int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
00091 {
00092 os_memcpy(addr, l2->own_addr, ETH_ALEN);
00093 return 0;
00094 }
00095
00096
00097 int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
00098 const u8 *buf, size_t len)
00099 {
00100 BOOL res;
00101 DWORD written;
00102 struct l2_ethhdr *eth;
00103 #ifndef _WIN32_WCE
00104 OVERLAPPED overlapped;
00105 #endif
00106 OVERLAPPED *o;
00107
00108 if (l2 == NULL)
00109 return -1;
00110
00111 #ifdef _WIN32_WCE
00112 o = NULL;
00113 #else
00114 os_memset(&overlapped, 0, sizeof(overlapped));
00115 o = &overlapped;
00116 #endif
00117
00118 if (l2->l2_hdr) {
00119 res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len,
00120 &written, o);
00121 } else {
00122 size_t mlen = sizeof(*eth) + len;
00123 eth = os_malloc(mlen);
00124 if (eth == NULL)
00125 return -1;
00126
00127 os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
00128 os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
00129 eth->h_proto = htons(proto);
00130 os_memcpy(eth + 1, buf, len);
00131 res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen,
00132 &written, o);
00133 os_free(eth);
00134 }
00135
00136 if (!res) {
00137 DWORD err = GetLastError();
00138 #ifndef _WIN32_WCE
00139 if (err == ERROR_IO_PENDING) {
00140
00141
00142
00143
00144
00145 return 0;
00146 }
00147 #endif
00148 wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d",
00149 (int) GetLastError());
00150 return -1;
00151 }
00152
00153 return 0;
00154 }
00155
00156
00157 static void l2_packet_callback(struct l2_packet_data *l2);
00158
00159 #ifdef _WIN32_WCE
00160 static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2)
00161 {
00162 HANDLE handles[2];
00163
00164 wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile");
00165 if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
00166 sizeof(l2->rx_buf), &l2->rx_written, NULL)) {
00167 DWORD err = GetLastError();
00168 wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: "
00169 "%d", (int) err);
00170
00171
00172
00173
00174
00175
00176
00177 Sleep(100);
00178 return;
00179 }
00180
00181 wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet",
00182 (int) l2->rx_written);
00183
00184
00185
00186
00187
00188 SetEvent(l2->rx_avail);
00189 handles[0] = l2_ndisuio_global->stop_request;
00190 handles[1] = l2_ndisuio_global->rx_processed;
00191 WaitForMultipleObjects(2, handles, FALSE, INFINITE);
00192 ResetEvent(l2_ndisuio_global->rx_processed);
00193 }
00194
00195
00196 static DWORD WINAPI l2_packet_rx_thread(LPVOID arg)
00197 {
00198 struct l2_packet_data *l2 = arg;
00199 DWORD res;
00200 HANDLE handles[2];
00201 int run = 1;
00202
00203 wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started");
00204 handles[0] = l2_ndisuio_global->stop_request;
00205 handles[1] = l2_ndisuio_global->ready_for_read;
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224 while (l2_ndisuio_global && run) {
00225 res = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
00226 switch (res) {
00227 case WAIT_OBJECT_0:
00228 wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received "
00229 "request to stop RX thread");
00230 run = 0;
00231 break;
00232 case WAIT_OBJECT_0 + 1:
00233 l2_packet_rx_thread_try_read(l2);
00234 break;
00235 case WAIT_FAILED:
00236 default:
00237 wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: "
00238 "WaitForMultipleObjects failed: %d",
00239 (int) GetLastError());
00240 run = 0;
00241 break;
00242 }
00243 }
00244
00245 wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped");
00246
00247 return 0;
00248 }
00249 #else
00250 static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive)
00251 {
00252 os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped));
00253 l2->rx_overlapped.hEvent = l2->rx_avail;
00254 if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
00255 sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped))
00256 {
00257 DWORD err = GetLastError();
00258 if (err != ERROR_IO_PENDING) {
00259 wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: "
00260 "%d", (int) err);
00261 return -1;
00262 }
00263
00264
00265
00266
00267 } else {
00268 wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data "
00269 "without wait for completion");
00270 if (!recursive)
00271 l2_packet_callback(l2);
00272 }
00273
00274 return 0;
00275 }
00276 #endif
00277
00278
00279 static void l2_packet_callback(struct l2_packet_data *l2)
00280 {
00281 const u8 *rx_buf, *rx_src;
00282 size_t rx_len;
00283 struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf;
00284
00285 wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes",
00286 (int) l2->rx_written);
00287
00288 if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) {
00289 rx_buf = (u8 *) ethhdr;
00290 rx_len = l2->rx_written;
00291 } else {
00292 rx_buf = (u8 *) (ethhdr + 1);
00293 rx_len = l2->rx_written - sizeof(*ethhdr);
00294 }
00295 rx_src = ethhdr->h_source;
00296
00297 l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len);
00298 #ifndef _WIN32_WCE
00299 l2_ndisuio_start_read(l2, 1);
00300 #endif
00301 }
00302
00303
00304 static void l2_packet_rx_event(void *eloop_data, void *user_data)
00305 {
00306 struct l2_packet_data *l2 = eloop_data;
00307
00308 if (l2_ndisuio_global)
00309 l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1];
00310
00311 ResetEvent(l2->rx_avail);
00312
00313 #ifndef _WIN32_WCE
00314 if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(),
00315 &l2->rx_overlapped, &l2->rx_written, FALSE)) {
00316 wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult "
00317 "failed: %d", (int) GetLastError());
00318 return;
00319 }
00320 #endif
00321
00322 l2_packet_callback(l2);
00323
00324 #ifdef _WIN32_WCE
00325 SetEvent(l2_ndisuio_global->rx_processed);
00326 #endif
00327 }
00328
00329
00330 static int l2_ndisuio_set_ether_type(unsigned short protocol)
00331 {
00332 USHORT proto = htons(protocol);
00333 DWORD written;
00334
00335 if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
00336 IOCTL_NDISUIO_SET_ETHER_TYPE, &proto,
00337 sizeof(proto), NULL, 0, &written, NULL)) {
00338 wpa_printf(MSG_ERROR, "L2(NDISUIO): "
00339 "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d",
00340 (int) GetLastError());
00341 return -1;
00342 }
00343
00344 return 0;
00345 }
00346
00347
00348 struct l2_packet_data * l2_packet_init(
00349 const char *ifname, const u8 *own_addr, unsigned short protocol,
00350 void (*rx_callback)(void *ctx, const u8 *src_addr,
00351 const u8 *buf, size_t len),
00352 void *rx_callback_ctx, int l2_hdr)
00353 {
00354 struct l2_packet_data *l2;
00355
00356 if (l2_ndisuio_global == NULL) {
00357 l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global));
00358 if (l2_ndisuio_global == NULL)
00359 return NULL;
00360 l2_ndisuio_global->first_proto = protocol;
00361 }
00362 if (l2_ndisuio_global->refcount >= 2) {
00363 wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two "
00364 "simultaneous connections allowed");
00365 return NULL;
00366 }
00367 l2_ndisuio_global->refcount++;
00368
00369 l2 = os_zalloc(sizeof(struct l2_packet_data));
00370 if (l2 == NULL)
00371 return NULL;
00372 l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2;
00373
00374 os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
00375 l2->rx_callback = rx_callback;
00376 l2->rx_callback_ctx = rx_callback_ctx;
00377 l2->l2_hdr = l2_hdr;
00378
00379 if (own_addr)
00380 os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
00381
00382 if (l2_ndisuio_set_ether_type(protocol) < 0) {
00383 os_free(l2);
00384 return NULL;
00385 }
00386
00387 if (l2_ndisuio_global->refcount > 1) {
00388 wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting "
00389 "filtering ethertype to %04x", protocol);
00390 if (l2_ndisuio_global->l2[0])
00391 l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail;
00392 return l2;
00393 }
00394
00395 l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
00396 if (l2->rx_avail == NULL) {
00397 os_free(l2);
00398 return NULL;
00399 }
00400
00401 eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
00402 l2_packet_rx_event, l2, NULL);
00403
00404 #ifdef _WIN32_WCE
00405 l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL);
00406
00407
00408
00409
00410 l2_ndisuio_global->ready_for_read =
00411 CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
00412 l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL);
00413 if (l2_ndisuio_global->stop_request == NULL ||
00414 l2_ndisuio_global->ready_for_read == NULL ||
00415 l2_ndisuio_global->rx_processed == NULL) {
00416 if (l2_ndisuio_global->stop_request) {
00417 CloseHandle(l2_ndisuio_global->stop_request);
00418 l2_ndisuio_global->stop_request = NULL;
00419 }
00420 if (l2_ndisuio_global->ready_for_read) {
00421 CloseHandle(l2_ndisuio_global->ready_for_read);
00422 l2_ndisuio_global->ready_for_read = NULL;
00423 }
00424 if (l2_ndisuio_global->rx_processed) {
00425 CloseHandle(l2_ndisuio_global->rx_processed);
00426 l2_ndisuio_global->rx_processed = NULL;
00427 }
00428 eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
00429 os_free(l2);
00430 return NULL;
00431 }
00432
00433 l2_ndisuio_global->rx_thread = CreateThread(NULL, 0,
00434 l2_packet_rx_thread, l2, 0,
00435 NULL);
00436 if (l2_ndisuio_global->rx_thread == NULL) {
00437 wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX "
00438 "thread: %d", (int) GetLastError());
00439 eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
00440 CloseHandle(l2_ndisuio_global->stop_request);
00441 l2_ndisuio_global->stop_request = NULL;
00442 os_free(l2);
00443 return NULL;
00444 }
00445 #else
00446 l2_ndisuio_start_read(l2, 0);
00447 #endif
00448
00449 return l2;
00450 }
00451
00452
00453 void l2_packet_deinit(struct l2_packet_data *l2)
00454 {
00455 if (l2 == NULL)
00456 return;
00457
00458 if (l2_ndisuio_global) {
00459 l2_ndisuio_global->refcount--;
00460 l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL;
00461 if (l2_ndisuio_global->refcount) {
00462 wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering "
00463 "ethertype to %04x",
00464 l2_ndisuio_global->first_proto);
00465 l2_ndisuio_set_ether_type(
00466 l2_ndisuio_global->first_proto);
00467 return;
00468 }
00469
00470 #ifdef _WIN32_WCE
00471 wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to "
00472 "stop");
00473 SetEvent(l2_ndisuio_global->stop_request);
00474
00475
00476
00477
00478 if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
00479 IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL,
00480 NULL)) {
00481 wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ "
00482 "failed: %d", (int) GetLastError());
00483
00484
00485 }
00486 WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE);
00487 wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited");
00488 CloseHandle(l2_ndisuio_global->rx_thread);
00489 CloseHandle(l2_ndisuio_global->stop_request);
00490 CloseHandle(l2_ndisuio_global->ready_for_read);
00491 CloseHandle(l2_ndisuio_global->rx_processed);
00492 #endif
00493
00494 os_free(l2_ndisuio_global);
00495 l2_ndisuio_global = NULL;
00496 }
00497
00498 #ifndef _WIN32_WCE
00499 CancelIo(driver_ndis_get_ndisuio_handle());
00500 #endif
00501
00502 eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
00503 CloseHandle(l2->rx_avail);
00504 os_free(l2);
00505 }
00506
00507
00508 int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
00509 {
00510 return -1;
00511 }
00512
00513
00514 void l2_packet_notify_auth_start(struct l2_packet_data *l2)
00515 {
00516 }