VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DrvTAPWin32.cpp@ 8760

Last change on this file since 8760 was 8610, checked in by vboxsync, 17 years ago

PCNet: completed fix for broken hostif with certain guests. Make sure the PCNet poll timer is actually running if fMaybeOutOfSpace is set. Never exit the TAP recv thread if pfnWaitReceiveAvail() as this means we were woken up during a VM state transition.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.4 KB
Line 
1/** @file
2 *
3 * VBox network devices:
4 * Linux/Win32 TUN network transport driver
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DRV_TUN
28#include <VBox/pdmdrv.h>
29#include <VBox/stam.h>
30
31#include <VBox/log.h>
32#include <iprt/assert.h>
33#include <iprt/file.h>
34#include <iprt/string.h>
35#include <iprt/thread.h>
36#include <iprt/asm.h>
37#include <iprt/semaphore.h>
38
39#include <windows.h>
40#include <VBox/tapwin32.h>
41
42#include "Builtins.h"
43
44/*******************************************************************************
45* Structures and Typedefs *
46*******************************************************************************/
47/**
48 * Block driver instance data.
49 */
50typedef struct
51{
52 /** The network interface. */
53 PDMINETWORKCONNECTOR INetworkConnector;
54 /** The network interface. */
55 PPDMINETWORKPORT pPort;
56 /** Pointer to the driver instance. */
57 PPDMDRVINS pDrvIns;
58 /** TAP device file handle. */
59 HANDLE hFile;
60
61 HANDLE hEventWrite;
62 HANDLE hEventRead;
63
64 OVERLAPPED overlappedRead;
65 DWORD dwNumberOfBytesRead;
66 uint8_t readBuffer[4096];
67
68 TAP_VERSION tapVersion;
69
70 /** The thread handle. NIL_RTTHREAD if no thread. */
71 PPDMTHREAD pThread;
72 /** The event semaphore the thread is waiting on. */
73 HANDLE hHaltAsyncEventSem;
74
75#ifdef DEBUG
76 DWORD dwLastReadTime;
77 DWORD dwLastWriteTime;
78#endif
79
80#ifdef VBOX_WITH_STATISTICS
81 /** Number of sent packets. */
82 STAMCOUNTER StatPktSent;
83 /** Number of sent bytes. */
84 STAMCOUNTER StatPktSentBytes;
85 /** Number of received packets. */
86 STAMCOUNTER StatPktRecv;
87 /** Number of received bytes. */
88 STAMCOUNTER StatPktRecvBytes;
89 /** Profiling packet transmit runs. */
90 STAMPROFILEADV StatTransmit;
91 /** Profiling packet receive runs. */
92 STAMPROFILEADV StatReceive;
93 STAMPROFILE StatRecvOverflows;
94#endif /* VBOX_WITH_STATISTICS */
95} DRVTAP, *PDRVTAP;
96
97/** Converts a pointer to TUN::INetworkConnector to a PRDVTUN. */
98#define PDMINETWORKCONNECTOR_2_DRVTAP(pInterface) ( (PDRVTAP)((uintptr_t)pInterface - RT_OFFSETOF(DRVTAP, INetworkConnector)) )
99
100/**
101 * Send data to the network.
102 *
103 * @returns VBox status code.
104 * @param pInterface Pointer to the interface structure containing the called function pointer.
105 * @param pvBuf Data to send.
106 * @param cb Number of bytes to send.
107 * @thread EMT
108 */
109static DECLCALLBACK(int) drvTAPW32Send(PPDMINETWORKCONNECTOR pInterface, const void *pvBuf, size_t cb)
110{
111 OVERLAPPED overlapped;
112 DWORD cbBytesWritten;
113 int rc;
114 PDRVTAP pData = PDMINETWORKCONNECTOR_2_DRVTAP(pInterface);
115
116 Log2(("drvTAPW32Send%d: pvBuf=%p cb=%#x\n"
117 "%.*Vhxd\n", pData->pDrvIns->iInstance, pvBuf, cb, cb, pvBuf));
118
119#ifdef DEBUG
120 pData->dwLastReadTime = timeGetTime();
121 Log(("drvTAPW32Send %d bytes at %08x - delta %x\n", cb, pData->dwLastReadTime, pData->dwLastReadTime - pData->dwLastWriteTime));
122#endif
123
124 STAM_COUNTER_INC(&pData->StatPktSent);
125 STAM_COUNTER_ADD(&pData->StatPktSentBytes, cb);
126 STAM_PROFILE_ADV_START(&pData->StatTransmit, a);
127
128 memset(&overlapped, 0, sizeof(overlapped));
129 overlapped.hEvent = pData->hEventWrite;
130
131 rc = VINF_SUCCESS;
132 if (WriteFile(pData->hFile, pvBuf, cb, &cbBytesWritten, &overlapped) == FALSE)
133 {
134 if (GetLastError() == ERROR_IO_PENDING)
135 {
136 Log(("drvTAPW32Send: IO pending!!\n"));
137 rc = WaitForSingleObject(overlapped.hEvent, INFINITE);
138 AssertMsg(rc == WAIT_OBJECT_0, ("WaitForSingleObject failed with %x\n", rc));
139 rc = VINF_SUCCESS;
140 }
141 else
142 {
143 AssertMsgFailed(("WriteFile failed with %d\n", GetLastError()));
144 rc = RTErrConvertFromWin32(GetLastError());
145 }
146 }
147 STAM_PROFILE_ADV_STOP(&pData->StatTransmit, a);
148 AssertRC(rc);
149 return rc;
150}
151
152
153/**
154 * Set promiscuous mode.
155 *
156 * This is called when the promiscuous mode is set. This means that there doesn't have
157 * to be a mode change when it's called.
158 *
159 * @param pInterface Pointer to the interface structure containing the called function pointer.
160 * @param fPromiscuous Set if the adaptor is now in promiscuous mode. Clear if it is not.
161 * @thread EMT
162 */
163static DECLCALLBACK(void) drvTAPW32SetPromiscuousMode(PPDMINETWORKCONNECTOR pInterface, bool fPromiscuous)
164{
165 LogFlow(("drvTAPW32SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
166 /* nothing to do */
167}
168
169
170/**
171 * Notification on link status changes.
172 *
173 * @param pInterface Pointer to the interface structure containing the called function pointer.
174 * @param enmLinkState The new link state.
175 * @thread EMT
176 */
177static DECLCALLBACK(void) drvTAPW32NotifyLinkChanged(PPDMINETWORKCONNECTOR pInterface, PDMNETWORKLINKSTATE enmLinkState)
178{
179 LogFlow(("drvNATW32NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
180 /** @todo take action on link down and up. Stop the polling and such like. */
181}
182
183
184/**
185 * Async I/O thread for an interface.
186 */
187static DECLCALLBACK(int) drvTAPW32AsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
188{
189 PDRVTAP pData = PDMINS2DATA(pDrvIns, PDRVTAP);
190 HANDLE haWait[2];
191 DWORD rc = ERROR_SUCCESS, dwNumberOfBytesTransferred;
192
193 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
194 return VINF_SUCCESS;
195
196 Assert(pData);
197 haWait[0] = pData->hEventRead;
198 haWait[1] = pData->hHaltAsyncEventSem;
199
200 while(1)
201 {
202 BOOL bRet;
203
204 memset(&pData->overlappedRead, 0, sizeof(pData->overlappedRead));
205 pData->overlappedRead.hEvent = pData->hEventRead;
206 bRet = ReadFile(pData->hFile, pData->readBuffer, sizeof(pData->readBuffer),
207 &dwNumberOfBytesTransferred, &pData->overlappedRead);
208 if (bRet == FALSE)
209 {
210 rc = GetLastError();
211 AssertMsg(rc == ERROR_IO_PENDING || rc == ERROR_MORE_DATA, ("ReadFile failed with rc=%d\n", rc));
212 if (rc != ERROR_IO_PENDING && rc != ERROR_MORE_DATA)
213 break;
214
215 rc = WaitForMultipleObjects(2, &haWait[0], FALSE, INFINITE);
216 AssertMsg(rc == WAIT_OBJECT_0 || rc == WAIT_OBJECT_0+1, ("WaitForSingleObject failed with %x\n", rc));
217
218 if (rc != WAIT_OBJECT_0)
219 break; /* asked to quit or fatal error. */
220
221 rc = GetOverlappedResult(pData->hFile, &pData->overlappedRead, &dwNumberOfBytesTransferred, FALSE);
222 Assert(rc == TRUE);
223
224 /* If GetOverlappedResult() returned with TRUE, the operation was finished successfully */
225 }
226
227 /*
228 * Wait for the device to have some room. A return code != VINF_SUCCESS
229 * means that we were woken up during a VM state transition. Drop the
230 * current packet and wait for the next one.
231 */
232 rc = pData->pPort->pfnWaitReceiveAvail(pData->pPort, RT_INDEFINITE_WAIT);
233 if (RT_FAILURE(rc))
234 continue;
235
236 STAM_COUNTER_INC(&pData->StatPktRecv);
237 STAM_COUNTER_ADD(&pData->StatPktRecvBytes, dwNumberOfBytesTransferred);
238#ifdef DEBUG
239 pData->dwLastWriteTime = timeGetTime();
240 Log(("drvTAPW32AsyncIo %d bytes at %08x - delta %x\n", dwNumberOfBytesTransferred,
241 pData->dwLastWriteTime, pData->dwLastWriteTime - pData->dwLastReadTime));
242#endif
243 rc = pData->pPort->pfnReceive(pData->pPort, pData->readBuffer, dwNumberOfBytesTransferred);
244 AssertRC(rc);
245 }
246
247 SetEvent(pData->hHaltAsyncEventSem);
248 Log(("drvTAPW32AsyncIo: exit thread!!\n"));
249 return VINF_SUCCESS;
250}
251
252
253/**
254 * Unblock the send thread so it can respond to a state change.
255 *
256 * @returns VBox status code.
257 * @param pDevIns The pcnet device instance.
258 * @param pThread The send thread.
259 */
260static DECLCALLBACK(int) drvTAPW32AsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
261{
262 PDRVTAP pData = PDMINS2DATA(pDrvIns, PDRVTAP);
263
264 /** @todo this isn't a safe method to notify the async thread; it might be using the instance
265 * data after we've been destroyed; could wait for it to terminate, but that's not
266 * without risks either.
267 */
268 SetEvent(pData->hHaltAsyncEventSem);
269
270 /* Yield or else our async thread will never acquire the event semaphore */
271 RTThreadSleep(16);
272 /* Wait for the async thread to quit; up to half a second */
273 WaitForSingleObject(pData->hHaltAsyncEventSem, 500);
274
275 return VINF_SUCCESS;
276}
277
278/**
279 * Queries an interface to the driver.
280 *
281 * @returns Pointer to interface.
282 * @returns NULL if the interface was not supported by the driver.
283 * @param pInterface Pointer to this interface structure.
284 * @param enmInterface The requested interface identification.
285 * @thread Any thread.
286 */
287static DECLCALLBACK(void *) drvTAPW32QueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
288{
289 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
290 PDRVTAP pData = PDMINS2DATA(pDrvIns, PDRVTAP);
291 switch (enmInterface)
292 {
293 case PDMINTERFACE_BASE:
294 return &pDrvIns->IBase;
295 case PDMINTERFACE_NETWORK_CONNECTOR:
296 return &pData->INetworkConnector;
297 default:
298 return NULL;
299 }
300}
301
302
303/**
304 * Destruct a driver instance.
305 *
306 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
307 * resources can be freed correctly.
308 *
309 * @param pDrvIns The driver instance data.
310 */
311static DECLCALLBACK(void) drvTAPW32Destruct(PPDMDRVINS pDrvIns)
312{
313 PDRVTAP pData = PDMINS2DATA(pDrvIns, PDRVTAP);
314 TAP_MEDIASTATUS mediastatus;
315 DWORD dwLength;
316
317 LogFlow(("drvTAPW32Destruct\n"));
318
319 mediastatus.fConnect = FALSE;
320 BOOL ret = DeviceIoControl(pData->hFile, TAP_IOCTL_SET_MEDIA_STATUS,
321 &mediastatus, sizeof(mediastatus), NULL, 0, &dwLength, NULL);
322 Assert(ret);
323
324 CloseHandle(pData->hEventWrite);
325 CancelIo(pData->hFile);
326 CloseHandle(pData->hFile);
327}
328
329
330/**
331 * Construct a TUN network transport driver instance.
332 *
333 * @returns VBox status.
334 * @param pDrvIns The driver instance data.
335 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
336 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
337 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
338 * iInstance it's expected to be used a bit in this function.
339 */
340static DECLCALLBACK(int) drvTAPW32Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
341{
342 PDRVTAP pData = PDMINS2DATA(pDrvIns, PDRVTAP);
343
344 /*
345 * Init the static parts.
346 */
347 pData->pDrvIns = pDrvIns;
348 pData->hFile = INVALID_HANDLE_VALUE;
349 /* IBase */
350 pDrvIns->IBase.pfnQueryInterface = drvTAPW32QueryInterface;
351 /* INetwork */
352 pData->INetworkConnector.pfnSend = drvTAPW32Send;
353 pData->INetworkConnector.pfnSetPromiscuousMode = drvTAPW32SetPromiscuousMode;
354 pData->INetworkConnector.pfnNotifyLinkChanged = drvTAPW32NotifyLinkChanged;
355
356 /*
357 * Validate the config.
358 */
359 if (!CFGMR3AreValuesValid(pCfgHandle, "Device\0HostInterfaceName\0GUID\0"))
360 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
361
362 /*
363 * Check that no-one is attached to us.
364 */
365 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, NULL);
366 if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
367 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_NO_ATTACH,
368 N_("Configuration error: Cannot attach drivers to the TUN driver"));
369
370 /*
371 * Query the network port interface.
372 */
373 pData->pPort = (PPDMINETWORKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_NETWORK_PORT);
374 if (!pData->pPort)
375 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
376 N_("Configuration error: the above device/driver didn't export the network port interface"));
377
378 /*
379 * Read the configuration.
380 */
381 char *pszHostDriver = NULL;
382 rc = CFGMR3QueryStringAlloc(pCfgHandle, "HostInterfaceName", &pszHostDriver);
383 if (VBOX_FAILURE(rc))
384 return PDMDRV_SET_ERROR(pDrvIns, rc,
385 N_("Configuration error: query for \"HostInterfaceName\" failed"));
386
387 TAP_MEDIASTATUS mediastatus;
388 DWORD length;
389 char szFullDriverName[256];
390 char szDriverGUID[256] = {0};
391
392 rc = CFGMR3QueryBytes(pCfgHandle, "GUID", szDriverGUID, sizeof(szDriverGUID));
393 if (VBOX_FAILURE(rc))
394 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
395 N_("Configuration error: could not query GUID"));
396
397 RTStrPrintfEx(NULL, NULL, szFullDriverName, sizeof(szFullDriverName), "\\\\.\\Global\\%s.tap", szDriverGUID);
398
399 pData->hFile = CreateFile(szFullDriverName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
400 FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
401
402 if (pData->hFile == INVALID_HANDLE_VALUE)
403 {
404 rc = GetLastError();
405
406 AssertMsgFailed(("Configuration error: TAP device name %s is not valid! (rc=%d)\n", szFullDriverName, rc));
407 if (rc == ERROR_SHARING_VIOLATION)
408 return VERR_PDM_HIF_SHARING_VIOLATION;
409
410 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_HIF_OPEN_FAILED,
411 N_("Failed to open Host Interface Networking device driver"));
412 }
413
414 BOOL ret = DeviceIoControl(pData->hFile, TAP_IOCTL_GET_VERSION, &pData->tapVersion, sizeof (pData->tapVersion),
415 &pData->tapVersion, sizeof(pData->tapVersion), &length, NULL);
416 if (ret == FALSE)
417 {
418 CloseHandle(pData->hFile);
419 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_HIF_INVALID_VERSION,
420 N_("Failed to get the Host Interface Networking device driver version"));;
421 }
422 LogRel(("TAP version %d.%d\n", pData->tapVersion.major, pData->tapVersion.minor));
423
424 /* Must be at least version 8.1 */
425 if ( pData->tapVersion.major != 8
426 || pData->tapVersion.minor < 1)
427 {
428 CloseHandle(pData->hFile);
429 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_HIF_INVALID_VERSION,
430 N_("Invalid Host Interface Networking device driver version"));;
431 }
432
433 mediastatus.fConnect = TRUE;
434 ret = DeviceIoControl(pData->hFile, TAP_IOCTL_SET_MEDIA_STATUS, &mediastatus, sizeof(mediastatus), NULL, 0, &length, NULL);
435 if (ret == FALSE)
436 {
437 CloseHandle(pData->hFile);
438 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
439 }
440
441 if (pszHostDriver)
442 MMR3HeapFree(pszHostDriver);
443
444 pData->hEventWrite = CreateEvent(NULL, FALSE, FALSE, NULL);
445 pData->hEventRead = CreateEvent(NULL, FALSE, FALSE, NULL);
446 memset(&pData->overlappedRead, 0, sizeof(pData->overlappedRead));
447
448 pData->hHaltAsyncEventSem = CreateEvent(NULL, FALSE, FALSE, NULL);
449 Assert(pData->hHaltAsyncEventSem != NULL);
450
451 /* Create asynchronous thread */
452 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pData->pThread, pData, drvTAPW32AsyncIoThread, drvTAPW32AsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "TAP");
453 AssertRCReturn(rc, rc);
454
455#ifdef VBOX_WITH_STATISTICS
456 /*
457 * Statistics.
458 */
459 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktSent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of sent packets.", "/Drivers/TAP%d/Packets/Sent", pDrvIns->iInstance);
460 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktSentBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of sent bytes.", "/Drivers/TAP%d/Bytes/Sent", pDrvIns->iInstance);
461 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktRecv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of received packets.", "/Drivers/TAP%d/Packets/Received", pDrvIns->iInstance);
462 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktRecvBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of received bytes.", "/Drivers/TAP%d/Bytes/Received", pDrvIns->iInstance);
463 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.", "/Drivers/TAP%d/Transmit", pDrvIns->iInstance);
464 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.", "/Drivers/TAP%d/Receive", pDrvIns->iInstance);
465 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatRecvOverflows,STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling packet receive overflows.", "/Drivers/TAP%d/RecvOverflows", pDrvIns->iInstance);
466#endif
467
468 return rc;
469}
470
471
472/**
473 * Host Interface network transport driver registration record.
474 */
475const PDMDRVREG g_DrvHostInterface =
476{
477 /* u32Version */
478 PDM_DRVREG_VERSION,
479 /* szDriverName */
480 "HostInterface",
481 /* pszDescription */
482 "Host Interface Network Transport Driver",
483 /* fFlags */
484 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
485 /* fClass. */
486 PDM_DRVREG_CLASS_NETWORK,
487 /* cMaxInstances */
488 ~0,
489 /* cbInstance */
490 sizeof(DRVTAP),
491 /* pfnConstruct */
492 drvTAPW32Construct,
493 /* pfnDestruct */
494 drvTAPW32Destruct,
495 /* pfnIOCtl */
496 NULL,
497 /* pfnPowerOn */
498 NULL,
499 /* pfnReset */
500 NULL,
501 /* pfnSuspend */
502 NULL,
503 /* pfnResume */
504 NULL,
505 /* pfnDetach */
506 NULL,
507 /* pfnPowerOff */
508 NULL
509};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette