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