VirtualBox

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

Last change on this file since 8483 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.3 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 /* Not very nice, but what else can we do? */
228 rc = pData->pPort->pfnWaitReceiveAvail(pData->pPort, RT_INDEFINITE_WAIT);
229 if (RT_FAILURE(rc))
230 break;
231
232 STAM_COUNTER_INC(&pData->StatPktRecv);
233 STAM_COUNTER_ADD(&pData->StatPktRecvBytes, dwNumberOfBytesTransferred);
234#ifdef DEBUG
235 pData->dwLastWriteTime = timeGetTime();
236 Log(("drvTAPW32AsyncIo %d bytes at %08x - delta %x\n", dwNumberOfBytesTransferred,
237 pData->dwLastWriteTime, pData->dwLastWriteTime - pData->dwLastReadTime));
238#endif
239 rc = pData->pPort->pfnReceive(pData->pPort, pData->readBuffer, dwNumberOfBytesTransferred);
240 AssertRC(rc);
241 }
242
243 SetEvent(pData->hHaltAsyncEventSem);
244 Log(("drvTAPW32AsyncIo: exit thread!!\n"));
245 return VINF_SUCCESS;
246}
247
248
249/**
250 * Unblock the send thread so it can respond to a state change.
251 *
252 * @returns VBox status code.
253 * @param pDevIns The pcnet device instance.
254 * @param pThread The send thread.
255 */
256static DECLCALLBACK(int) drvTAPW32AsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
257{
258 PDRVTAP pData = PDMINS2DATA(pDrvIns, PDRVTAP);
259
260 /** @todo this isn't a safe method to notify the async thread; it might be using the instance
261 * data after we've been destroyed; could wait for it to terminate, but that's not
262 * without risks either.
263 */
264 SetEvent(pData->hHaltAsyncEventSem);
265
266 /* Yield or else our async thread will never acquire the event semaphore */
267 RTThreadSleep(16);
268 /* Wait for the async thread to quit; up to half a second */
269 WaitForSingleObject(pData->hHaltAsyncEventSem, 500);
270
271 return VINF_SUCCESS;
272}
273
274/**
275 * Queries an interface to the driver.
276 *
277 * @returns Pointer to interface.
278 * @returns NULL if the interface was not supported by the driver.
279 * @param pInterface Pointer to this interface structure.
280 * @param enmInterface The requested interface identification.
281 * @thread Any thread.
282 */
283static DECLCALLBACK(void *) drvTAPW32QueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
284{
285 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
286 PDRVTAP pData = PDMINS2DATA(pDrvIns, PDRVTAP);
287 switch (enmInterface)
288 {
289 case PDMINTERFACE_BASE:
290 return &pDrvIns->IBase;
291 case PDMINTERFACE_NETWORK_CONNECTOR:
292 return &pData->INetworkConnector;
293 default:
294 return NULL;
295 }
296}
297
298
299/**
300 * Destruct a driver instance.
301 *
302 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
303 * resources can be freed correctly.
304 *
305 * @param pDrvIns The driver instance data.
306 */
307static DECLCALLBACK(void) drvTAPW32Destruct(PPDMDRVINS pDrvIns)
308{
309 PDRVTAP pData = PDMINS2DATA(pDrvIns, PDRVTAP);
310 TAP_MEDIASTATUS mediastatus;
311 DWORD dwLength;
312
313 LogFlow(("drvTAPW32Destruct\n"));
314
315 mediastatus.fConnect = FALSE;
316 BOOL ret = DeviceIoControl(pData->hFile, TAP_IOCTL_SET_MEDIA_STATUS,
317 &mediastatus, sizeof(mediastatus), NULL, 0, &dwLength, NULL);
318 Assert(ret);
319
320 CloseHandle(pData->hEventWrite);
321 CancelIo(pData->hFile);
322 CloseHandle(pData->hFile);
323}
324
325
326/**
327 * Construct a TUN network transport driver instance.
328 *
329 * @returns VBox status.
330 * @param pDrvIns The driver instance data.
331 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
332 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
333 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
334 * iInstance it's expected to be used a bit in this function.
335 */
336static DECLCALLBACK(int) drvTAPW32Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
337{
338 PDRVTAP pData = PDMINS2DATA(pDrvIns, PDRVTAP);
339
340 /*
341 * Init the static parts.
342 */
343 pData->pDrvIns = pDrvIns;
344 pData->hFile = INVALID_HANDLE_VALUE;
345 /* IBase */
346 pDrvIns->IBase.pfnQueryInterface = drvTAPW32QueryInterface;
347 /* INetwork */
348 pData->INetworkConnector.pfnSend = drvTAPW32Send;
349 pData->INetworkConnector.pfnSetPromiscuousMode = drvTAPW32SetPromiscuousMode;
350 pData->INetworkConnector.pfnNotifyLinkChanged = drvTAPW32NotifyLinkChanged;
351
352 /*
353 * Validate the config.
354 */
355 if (!CFGMR3AreValuesValid(pCfgHandle, "Device\0HostInterfaceName\0GUID\0"))
356 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
357
358 /*
359 * Check that no-one is attached to us.
360 */
361 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, NULL);
362 if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
363 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_NO_ATTACH,
364 N_("Configuration error: Cannot attach drivers to the TUN driver"));
365
366 /*
367 * Query the network port interface.
368 */
369 pData->pPort = (PPDMINETWORKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_NETWORK_PORT);
370 if (!pData->pPort)
371 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
372 N_("Configuration error: the above device/driver didn't export the network port interface"));
373
374 /*
375 * Read the configuration.
376 */
377 char *pszHostDriver = NULL;
378 rc = CFGMR3QueryStringAlloc(pCfgHandle, "HostInterfaceName", &pszHostDriver);
379 if (VBOX_FAILURE(rc))
380 return PDMDRV_SET_ERROR(pDrvIns, rc,
381 N_("Configuration error: query for \"HostInterfaceName\" failed"));
382
383 TAP_MEDIASTATUS mediastatus;
384 DWORD length;
385 char szFullDriverName[256];
386 char szDriverGUID[256] = {0};
387
388 rc = CFGMR3QueryBytes(pCfgHandle, "GUID", szDriverGUID, sizeof(szDriverGUID));
389 if (VBOX_FAILURE(rc))
390 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
391 N_("Configuration error: could not query GUID"));
392
393 RTStrPrintfEx(NULL, NULL, szFullDriverName, sizeof(szFullDriverName), "\\\\.\\Global\\%s.tap", szDriverGUID);
394
395 pData->hFile = CreateFile(szFullDriverName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
396 FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
397
398 if (pData->hFile == INVALID_HANDLE_VALUE)
399 {
400 rc = GetLastError();
401
402 AssertMsgFailed(("Configuration error: TAP device name %s is not valid! (rc=%d)\n", szFullDriverName, rc));
403 if (rc == ERROR_SHARING_VIOLATION)
404 return VERR_PDM_HIF_SHARING_VIOLATION;
405
406 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_HIF_OPEN_FAILED,
407 N_("Failed to open Host Interface Networking device driver"));
408 }
409
410 BOOL ret = DeviceIoControl(pData->hFile, TAP_IOCTL_GET_VERSION, &pData->tapVersion, sizeof (pData->tapVersion),
411 &pData->tapVersion, sizeof(pData->tapVersion), &length, NULL);
412 if (ret == FALSE)
413 {
414 CloseHandle(pData->hFile);
415 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_HIF_INVALID_VERSION,
416 N_("Failed to get the Host Interface Networking device driver version"));;
417 }
418 LogRel(("TAP version %d.%d\n", pData->tapVersion.major, pData->tapVersion.minor));
419
420 /* Must be at least version 8.1 */
421 if ( pData->tapVersion.major != 8
422 || pData->tapVersion.minor < 1)
423 {
424 CloseHandle(pData->hFile);
425 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_HIF_INVALID_VERSION,
426 N_("Invalid Host Interface Networking device driver version"));;
427 }
428
429 mediastatus.fConnect = TRUE;
430 ret = DeviceIoControl(pData->hFile, TAP_IOCTL_SET_MEDIA_STATUS, &mediastatus, sizeof(mediastatus), NULL, 0, &length, NULL);
431 if (ret == FALSE)
432 {
433 CloseHandle(pData->hFile);
434 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
435 }
436
437 if (pszHostDriver)
438 MMR3HeapFree(pszHostDriver);
439
440 pData->hEventWrite = CreateEvent(NULL, FALSE, FALSE, NULL);
441 pData->hEventRead = CreateEvent(NULL, FALSE, FALSE, NULL);
442 memset(&pData->overlappedRead, 0, sizeof(pData->overlappedRead));
443
444 pData->hHaltAsyncEventSem = CreateEvent(NULL, FALSE, FALSE, NULL);
445 Assert(pData->hHaltAsyncEventSem != NULL);
446
447 /* Create asynchronous thread */
448 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pData->pThread, pData, drvTAPW32AsyncIoThread, drvTAPW32AsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "TAP");
449 AssertRCReturn(rc, rc);
450
451#ifdef VBOX_WITH_STATISTICS
452 /*
453 * Statistics.
454 */
455 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktSent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of sent packets.", "/Drivers/TAP%d/Packets/Sent", pDrvIns->iInstance);
456 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktSentBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of sent bytes.", "/Drivers/TAP%d/Bytes/Sent", pDrvIns->iInstance);
457 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktRecv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of received packets.", "/Drivers/TAP%d/Packets/Received", pDrvIns->iInstance);
458 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktRecvBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of received bytes.", "/Drivers/TAP%d/Bytes/Received", pDrvIns->iInstance);
459 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.", "/Drivers/TAP%d/Transmit", pDrvIns->iInstance);
460 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.", "/Drivers/TAP%d/Receive", pDrvIns->iInstance);
461 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatRecvOverflows,STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling packet receive overflows.", "/Drivers/TAP%d/RecvOverflows", pDrvIns->iInstance);
462#endif
463
464 return rc;
465}
466
467
468/**
469 * Host Interface network transport driver registration record.
470 */
471const PDMDRVREG g_DrvHostInterface =
472{
473 /* u32Version */
474 PDM_DRVREG_VERSION,
475 /* szDriverName */
476 "HostInterface",
477 /* pszDescription */
478 "Host Interface Network Transport Driver",
479 /* fFlags */
480 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
481 /* fClass. */
482 PDM_DRVREG_CLASS_NETWORK,
483 /* cMaxInstances */
484 ~0,
485 /* cbInstance */
486 sizeof(DRVTAP),
487 /* pfnConstruct */
488 drvTAPW32Construct,
489 /* pfnDestruct */
490 drvTAPW32Destruct,
491 /* pfnIOCtl */
492 NULL,
493 /* pfnPowerOn */
494 NULL,
495 /* pfnReset */
496 NULL,
497 /* pfnSuspend */
498 NULL,
499 /* pfnResume */
500 NULL,
501 /* pfnDetach */
502 NULL,
503 /* pfnPowerOff */
504 NULL
505};
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