l2_packet_ndis.c

Go to the documentation of this file.
00001 
00025 #include "includes.h"
00026 #include <winsock2.h>
00027 #include <ntddndis.h>
00028 
00029 #ifdef _WIN32_WCE
00030 #include <winioctl.h>
00031 #include <nuiouser.h>
00032 #endif /* _WIN32_WCE */
00033 
00034 #include "common.h"
00035 #include "eloop.h"
00036 #include "l2_packet.h"
00037 
00038 #ifndef _WIN32_WCE
00039 /* from nuiouser.h */
00040 #define FSCTL_NDISUIO_BASE      FILE_DEVICE_NETWORK
00041 #define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
00042         CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
00043 #define IOCTL_NDISUIO_SET_ETHER_TYPE \
00044         _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
00045                           FILE_READ_ACCESS | FILE_WRITE_ACCESS)
00046 #endif /* _WIN32_WCE */
00047 
00048 /* From driver_ndis.c to shared the handle to NDISUIO */
00049 HANDLE driver_ndis_get_ndisuio_handle(void);
00050 
00051 /*
00052  * NDISUIO supports filtering of only one ethertype at the time, so we must
00053  * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth
00054  * whenever wpa_supplicant is trying to pre-authenticate and then switching
00055  * back to EAPOL when pre-authentication has been completed.
00056  */
00057 
00058 struct l2_packet_data;
00059 
00060 struct l2_packet_ndisuio_global {
00061         int refcount;
00062         unsigned short first_proto;
00063         struct l2_packet_data *l2[2];
00064 #ifdef _WIN32_WCE
00065         HANDLE rx_thread;
00066         HANDLE stop_request;
00067         HANDLE ready_for_read;
00068         HANDLE rx_processed;
00069 #endif /* _WIN32_WCE */
00070 };
00071 
00072 static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL;
00073 
00074 struct l2_packet_data {
00075         char ifname[100];
00076         u8 own_addr[ETH_ALEN];
00077         void (*rx_callback)(void *ctx, const u8 *src_addr,
00078                             const u8 *buf, size_t len);
00079         void *rx_callback_ctx;
00080         int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to
00081                      * rx_callback and l2_packet_send() */
00082         HANDLE rx_avail;
00083 #ifndef _WIN32_WCE
00084         OVERLAPPED rx_overlapped;
00085 #endif /* _WIN32_WCE */
00086         u8 rx_buf[1514];
00087         DWORD rx_written;
00088 };
00089 
00090 
00091 int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
00092 {
00093         os_memcpy(addr, l2->own_addr, ETH_ALEN);
00094         return 0;
00095 }
00096 
00097 
00098 int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
00099                    const u8 *buf, size_t len)
00100 {
00101         BOOL res;
00102         DWORD written;
00103         struct l2_ethhdr *eth;
00104 #ifndef _WIN32_WCE
00105         OVERLAPPED overlapped;
00106 #endif /* _WIN32_WCE */
00107         OVERLAPPED *o;
00108 
00109         if (l2 == NULL)
00110                 return -1;
00111 
00112 #ifdef _WIN32_WCE
00113         o = NULL;
00114 #else /* _WIN32_WCE */
00115         os_memset(&overlapped, 0, sizeof(overlapped));
00116         o = &overlapped;
00117 #endif /* _WIN32_WCE */
00118 
00119         if (l2->l2_hdr) {
00120                 res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len,
00121                                 &written, o);
00122         } else {
00123                 size_t mlen = sizeof(*eth) + len;
00124                 eth = os_malloc(mlen);
00125                 if (eth == NULL)
00126                         return -1;
00127 
00128                 os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
00129                 os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
00130                 eth->h_proto = htons(proto);
00131                 os_memcpy(eth + 1, buf, len);
00132                 res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen,
00133                                 &written, o);
00134                 os_free(eth);
00135         }
00136 
00137         if (!res) {
00138                 DWORD err = GetLastError();
00139 #ifndef _WIN32_WCE
00140                 if (err == ERROR_IO_PENDING) {
00141                         /* For now, just assume that the packet will be sent in
00142                          * time before the next write happens. This could be
00143                          * cleaned up at some point to actually wait for
00144                          * completion before starting new writes.
00145                          */
00146                         return 0;
00147                 }
00148 #endif /* _WIN32_WCE */
00149                 wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d",
00150                            (int) GetLastError());
00151                 return -1;
00152         }
00153 
00154         return 0;
00155 }
00156 
00157 
00158 static void l2_packet_callback(struct l2_packet_data *l2);
00159 
00160 #ifdef _WIN32_WCE
00161 static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2)
00162 {
00163         HANDLE handles[2];
00164 
00165         wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile");
00166         if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
00167                       sizeof(l2->rx_buf), &l2->rx_written, NULL)) {
00168                 DWORD err = GetLastError();
00169                 wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: "
00170                            "%d", (int) err);
00171                 /*
00172                  * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED
00173                  * error whenever the connection is not up. Yield the thread to
00174                  * avoid triggering a busy loop. Connection event should stop
00175                  * us from looping for long, but we need to allow enough CPU
00176                  * for the main thread to process the media disconnection.
00177                  */
00178                 Sleep(100);
00179                 return;
00180         }
00181 
00182         wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet",
00183                    (int) l2->rx_written);
00184 
00185         /*
00186          * Notify the main thread about the availability of a frame and wait
00187          * for the frame to be processed.
00188          */
00189         SetEvent(l2->rx_avail);
00190         handles[0] = l2_ndisuio_global->stop_request;
00191         handles[1] = l2_ndisuio_global->rx_processed;
00192         WaitForMultipleObjects(2, handles, FALSE, INFINITE);
00193         ResetEvent(l2_ndisuio_global->rx_processed);
00194 }
00195 
00196 
00197 static DWORD WINAPI l2_packet_rx_thread(LPVOID arg)
00198 {
00199         struct l2_packet_data *l2 = arg;
00200         DWORD res;
00201         HANDLE handles[2];
00202         int run = 1;
00203 
00204         wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started");
00205         handles[0] = l2_ndisuio_global->stop_request;
00206         handles[1] = l2_ndisuio_global->ready_for_read;
00207 
00208         /*
00209          * Unfortunately, NDISUIO on WinCE does not seem to support waiting
00210          * on the handle. There do not seem to be anything else that we could
00211          * wait for either. If one were to modify NDISUIO to set a named event
00212          * whenever packets are available, this event could be used here to
00213          * avoid having to poll for new packets or we could even move to use a
00214          * single threaded design.
00215          *
00216          * In addition, NDISUIO on WinCE is returning
00217          * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while
00218          * the adapter is not in connected state. For now, we are just using a
00219          * local event to allow ReadFile calls only after having received NDIS
00220          * media connect event. This event could be easily converted to handle
00221          * another event if the protocol driver is replaced with somewhat more
00222          * useful design.
00223          */
00224 
00225         while (l2_ndisuio_global && run) {
00226                 res = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
00227                 switch (res) {
00228                 case WAIT_OBJECT_0:
00229                         wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received "
00230                                    "request to stop RX thread");
00231                         run = 0;
00232                         break;
00233                 case WAIT_OBJECT_0 + 1:
00234                         l2_packet_rx_thread_try_read(l2);
00235                         break;
00236                 case WAIT_FAILED:
00237                 default:
00238                         wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: "
00239                                    "WaitForMultipleObjects failed: %d",
00240                                    (int) GetLastError());
00241                         run = 0;
00242                         break;
00243                 }
00244         }
00245 
00246         wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped");
00247 
00248         return 0;
00249 }
00250 #else /* _WIN32_WCE */
00251 static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive)
00252 {
00253         os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped));
00254         l2->rx_overlapped.hEvent = l2->rx_avail;
00255         if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
00256                       sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped))
00257         {
00258                 DWORD err = GetLastError();
00259                 if (err != ERROR_IO_PENDING) {
00260                         wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: "
00261                                    "%d", (int) err);
00262                         return -1;
00263                 }
00264                 /*
00265                  * Once read is completed, l2_packet_rx_event() will be
00266                  * called.
00267                  */
00268         } else {
00269                 wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data "
00270                            "without wait for completion");
00271                 if (!recursive)
00272                         l2_packet_callback(l2);
00273         }
00274 
00275         return 0;
00276 }
00277 #endif /* _WIN32_WCE */
00278 
00279 
00280 static void l2_packet_callback(struct l2_packet_data *l2)
00281 {
00282         const u8 *rx_buf, *rx_src;
00283         size_t rx_len;
00284         struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf;
00285 
00286         wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes",
00287                    (int) l2->rx_written);
00288 
00289         if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) {
00290                 rx_buf = (u8 *) ethhdr;
00291                 rx_len = l2->rx_written;
00292         } else {
00293                 rx_buf = (u8 *) (ethhdr + 1);
00294                 rx_len = l2->rx_written - sizeof(*ethhdr);
00295         }
00296         rx_src = ethhdr->h_source;
00297 
00298         l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len);
00299 #ifndef _WIN32_WCE
00300         l2_ndisuio_start_read(l2, 1);
00301 #endif /* _WIN32_WCE */
00302 }
00303 
00304 
00305 static void l2_packet_rx_event(void *eloop_data, void *user_data)
00306 {
00307         struct l2_packet_data *l2 = eloop_data;
00308 
00309         if (l2_ndisuio_global)
00310                 l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1];
00311 
00312         ResetEvent(l2->rx_avail);
00313 
00314 #ifndef _WIN32_WCE
00315         if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(),
00316                                  &l2->rx_overlapped, &l2->rx_written, FALSE)) {
00317                 wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult "
00318                            "failed: %d", (int) GetLastError());
00319                 return;
00320         }
00321 #endif /* _WIN32_WCE */
00322 
00323         l2_packet_callback(l2);
00324 
00325 #ifdef _WIN32_WCE
00326         SetEvent(l2_ndisuio_global->rx_processed);
00327 #endif /* _WIN32_WCE */
00328 }
00329 
00330 
00331 static int l2_ndisuio_set_ether_type(unsigned short protocol)
00332 {
00333         USHORT proto = htons(protocol);
00334         DWORD written;
00335 
00336         if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
00337                              IOCTL_NDISUIO_SET_ETHER_TYPE, &proto,
00338                              sizeof(proto), NULL, 0, &written, NULL)) {
00339                 wpa_printf(MSG_ERROR, "L2(NDISUIO): "
00340                            "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d",
00341                            (int) GetLastError());
00342                 return -1;
00343         }
00344 
00345         return 0;
00346 }
00347 
00348 
00349 struct l2_packet_data * l2_packet_init(
00350         const char *ifname, const u8 *own_addr, unsigned short protocol,
00351         void (*rx_callback)(void *ctx, const u8 *src_addr,
00352                             const u8 *buf, size_t len),
00353         void *rx_callback_ctx, int l2_hdr)
00354 {
00355         struct l2_packet_data *l2;
00356 
00357         if (l2_ndisuio_global == NULL) {
00358                 l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global));
00359                 if (l2_ndisuio_global == NULL)
00360                         return NULL;
00361                 l2_ndisuio_global->first_proto = protocol;
00362         }
00363         if (l2_ndisuio_global->refcount >= 2) {
00364                 wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two "
00365                            "simultaneous connections allowed");
00366                 return NULL;
00367         }
00368         l2_ndisuio_global->refcount++;
00369 
00370         l2 = os_zalloc(sizeof(struct l2_packet_data));
00371         if (l2 == NULL)
00372                 return NULL;
00373         l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2;
00374 
00375         os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
00376         l2->rx_callback = rx_callback;
00377         l2->rx_callback_ctx = rx_callback_ctx;
00378         l2->l2_hdr = l2_hdr;
00379 
00380         if (own_addr)
00381                 os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
00382 
00383         if (l2_ndisuio_set_ether_type(protocol) < 0) {
00384                 os_free(l2);
00385                 return NULL;
00386         }
00387 
00388         if (l2_ndisuio_global->refcount > 1) {
00389                 wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting "
00390                            "filtering ethertype to %04x", protocol);
00391                 if (l2_ndisuio_global->l2[0])
00392                         l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail;
00393                 return l2;
00394         }
00395 
00396         l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
00397         if (l2->rx_avail == NULL) {
00398                 os_free(l2);
00399                 return NULL;
00400         }
00401 
00402         eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
00403                              l2_packet_rx_event, l2, NULL);
00404 
00405 #ifdef _WIN32_WCE
00406         l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL);
00407         /*
00408          * This event is being set based on media connect/disconnect
00409          * notifications in driver_ndis.c.
00410          */
00411         l2_ndisuio_global->ready_for_read =
00412                 CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
00413         l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL);
00414         if (l2_ndisuio_global->stop_request == NULL ||
00415             l2_ndisuio_global->ready_for_read == NULL ||
00416             l2_ndisuio_global->rx_processed == NULL) {
00417                 if (l2_ndisuio_global->stop_request) {
00418                         CloseHandle(l2_ndisuio_global->stop_request);
00419                         l2_ndisuio_global->stop_request = NULL;
00420                 }
00421                 if (l2_ndisuio_global->ready_for_read) {
00422                         CloseHandle(l2_ndisuio_global->ready_for_read);
00423                         l2_ndisuio_global->ready_for_read = NULL;
00424                 }
00425                 if (l2_ndisuio_global->rx_processed) {
00426                         CloseHandle(l2_ndisuio_global->rx_processed);
00427                         l2_ndisuio_global->rx_processed = NULL;
00428                 }
00429                 eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
00430                 os_free(l2);
00431                 return NULL;
00432         }
00433 
00434         l2_ndisuio_global->rx_thread = CreateThread(NULL, 0,
00435                                                     l2_packet_rx_thread, l2, 0,
00436                                                     NULL);
00437         if (l2_ndisuio_global->rx_thread == NULL) {
00438                 wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX "
00439                            "thread: %d", (int) GetLastError());
00440                 eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
00441                 CloseHandle(l2_ndisuio_global->stop_request);
00442                 l2_ndisuio_global->stop_request = NULL;
00443                 os_free(l2);
00444                 return NULL;
00445         }
00446 #else /* _WIN32_WCE */
00447         l2_ndisuio_start_read(l2, 0);
00448 #endif /* _WIN32_WCE */
00449 
00450         return l2;
00451 }
00452 
00453 
00454 void l2_packet_deinit(struct l2_packet_data *l2)
00455 {
00456         if (l2 == NULL)
00457                 return;
00458 
00459         if (l2_ndisuio_global) {
00460                 l2_ndisuio_global->refcount--;
00461                 l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL;
00462                 if (l2_ndisuio_global->refcount) {
00463                         wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering "
00464                                    "ethertype to %04x",
00465                                    l2_ndisuio_global->first_proto);
00466                         l2_ndisuio_set_ether_type(
00467                                 l2_ndisuio_global->first_proto);
00468                         return;
00469                 }
00470 
00471 #ifdef _WIN32_WCE
00472                 wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to "
00473                            "stop");
00474                 SetEvent(l2_ndisuio_global->stop_request);
00475                 /*
00476                  * Cancel pending ReadFile() in the RX thread (if we were still
00477                  * connected at this point).
00478                  */
00479                 if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
00480                                      IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL,
00481                                      NULL)) {
00482                         wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ "
00483                                    "failed: %d", (int) GetLastError());
00484                         /* RX thread will exit blocking ReadFile once NDISUIO
00485                          * notices that the adapter is disconnected. */
00486                 }
00487                 WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE);
00488                 wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited");
00489                 CloseHandle(l2_ndisuio_global->rx_thread);
00490                 CloseHandle(l2_ndisuio_global->stop_request);
00491                 CloseHandle(l2_ndisuio_global->ready_for_read);
00492                 CloseHandle(l2_ndisuio_global->rx_processed);
00493 #endif /* _WIN32_WCE */
00494 
00495                 os_free(l2_ndisuio_global);
00496                 l2_ndisuio_global = NULL;
00497         }
00498 
00499 #ifndef _WIN32_WCE
00500         CancelIo(driver_ndis_get_ndisuio_handle());
00501 #endif /* _WIN32_WCE */
00502 
00503         eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
00504         CloseHandle(l2->rx_avail);
00505         os_free(l2);
00506 }
00507 
00508 
00509 int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
00510 {
00511         return -1;
00512 }
00513 
00514 
00515 void l2_packet_notify_auth_start(struct l2_packet_data *l2)
00516 {
00517 }
00518 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines

Generated on Sat Nov 21 23:16:52 2009 for hostapd by  doxygen 1.6.1