l2_packet_ndis.c
Go to the documentation of this file.
00001 /*
00002  * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO
00003  * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License version 2 as
00007  * published by the Free Software Foundation.
00008  *
00009  * Alternatively, this software may be distributed under the terms of BSD
00010  * license.
00011  *
00012  * See README and COPYING for more details.
00013  *
00014  * This implementation requires Windows specific event loop implementation,
00015  * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with
00016  * driver_ndis.c, so only that driver interface can be used and
00017  * CONFIG_USE_NDISUIO must be defined.
00018  *
00019  * WinXP version of the code uses overlapped I/O and a single threaded design
00020  * with callback functions from I/O code. WinCE version uses a separate RX
00021  * thread that blocks on ReadFile() whenever the media status is connected.
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 /* _WIN32_WCE */
00032 
00033 #include "common.h"
00034 #include "eloop.h"
00035 #include "l2_packet.h"
00036 
00037 #ifndef _WIN32_WCE
00038 /* from nuiouser.h */
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 /* _WIN32_WCE */
00046 
00047 /* From driver_ndis.c to shared the handle to NDISUIO */
00048 HANDLE driver_ndis_get_ndisuio_handle(void);
00049 
00050 /*
00051  * NDISUIO supports filtering of only one ethertype at the time, so we must
00052  * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth
00053  * whenever wpa_supplicant is trying to pre-authenticate and then switching
00054  * back to EAPOL when pre-authentication has been completed.
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 /* _WIN32_WCE */
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; /* whether to include layer 2 (Ethernet) header in calls to
00080                      * rx_callback and l2_packet_send() */
00081         HANDLE rx_avail;
00082 #ifndef _WIN32_WCE
00083         OVERLAPPED rx_overlapped;
00084 #endif /* _WIN32_WCE */
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 /* _WIN32_WCE */
00106         OVERLAPPED *o;
00107 
00108         if (l2 == NULL)
00109                 return -1;
00110 
00111 #ifdef _WIN32_WCE
00112         o = NULL;
00113 #else /* _WIN32_WCE */
00114         os_memset(&overlapped, 0, sizeof(overlapped));
00115         o = &overlapped;
00116 #endif /* _WIN32_WCE */
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                         /* For now, just assume that the packet will be sent in
00141                          * time before the next write happens. This could be
00142                          * cleaned up at some point to actually wait for
00143                          * completion before starting new writes.
00144                          */
00145                         return 0;
00146                 }
00147 #endif /* _WIN32_WCE */
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                  * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED
00172                  * error whenever the connection is not up. Yield the thread to
00173                  * avoid triggering a busy loop. Connection event should stop
00174                  * us from looping for long, but we need to allow enough CPU
00175                  * for the main thread to process the media disconnection.
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          * Notify the main thread about the availability of a frame and wait
00186          * for the frame to be processed.
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          * Unfortunately, NDISUIO on WinCE does not seem to support waiting
00209          * on the handle. There do not seem to be anything else that we could
00210          * wait for either. If one were to modify NDISUIO to set a named event
00211          * whenever packets are available, this event could be used here to
00212          * avoid having to poll for new packets or we could even move to use a
00213          * single threaded design.
00214          *
00215          * In addition, NDISUIO on WinCE is returning
00216          * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while
00217          * the adapter is not in connected state. For now, we are just using a
00218          * local event to allow ReadFile calls only after having received NDIS
00219          * media connect event. This event could be easily converted to handle
00220          * another event if the protocol driver is replaced with somewhat more
00221          * useful design.
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 /* _WIN32_WCE */
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                  * Once read is completed, l2_packet_rx_event() will be
00265                  * called.
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 /* _WIN32_WCE */
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 /* _WIN32_WCE */
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 /* _WIN32_WCE */
00321 
00322         l2_packet_callback(l2);
00323 
00324 #ifdef _WIN32_WCE
00325         SetEvent(l2_ndisuio_global->rx_processed);
00326 #endif /* _WIN32_WCE */
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          * This event is being set based on media connect/disconnect
00408          * notifications in driver_ndis.c.
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 /* _WIN32_WCE */
00446         l2_ndisuio_start_read(l2, 0);
00447 #endif /* _WIN32_WCE */
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                  * Cancel pending ReadFile() in the RX thread (if we were still
00476                  * connected at this point).
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                         /* RX thread will exit blocking ReadFile once NDISUIO
00484                          * notices that the adapter is disconnected. */
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 /* _WIN32_WCE */
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 /* _WIN32_WCE */
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 }


wpa_supplicant
Author(s): Package maintained by Blaise Gassend
autogenerated on Thu Apr 24 2014 15:34:35