VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DrvTAPWin.cpp@ 9594

Last change on this file since 9594 was 8773, checked in by vboxsync, 16 years ago

Network\DrvTAPWin32.cpp -> Network\DrvTAPWin.cpp

  • 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