DrvTAP.cpp revision e8337e527775f93549a93be513bd5b4b9d658569
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Universial TAP network transport driver.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Copyright (C) 2006-2007 Sun Microsystems, Inc.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * available from http://www.virtualbox.org. This file is free software;
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * you can redistribute it and/or modify it under the terms of the GNU
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * General Public License (GPL) as published by the Free Software
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * additional information or have any questions.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync/*******************************************************************************
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync* Header Files *
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync*******************************************************************************/
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync/*******************************************************************************
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync* Structures and Typedefs *
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync*******************************************************************************/
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Block driver instance data.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsynctypedef struct DRVTAP
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** The network interface. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** The network interface. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** Pointer to the driver instance. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** TAP device file handle. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** The configured TAP device name. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** Crossbow: MAC address of the device. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** Crossbow: Handle of the NIC. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** IP device file handle (/dev/udp). */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** Whether device name is obtained from setup application. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** TAP setup application. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** TAP terminate application. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** The write end of the control pipe. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** The read end of the control pipe. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** Reader thread. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** Number of sent packets. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** Number of sent bytes. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** Number of received packets. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** Number of received bytes. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** Profiling packet transmit runs. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** Profiling packet receive runs. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync#endif /* VBOX_WITH_STATISTICS */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** The nano ts of the last transfer. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** The nano ts of the last receive. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync/** Converts a pointer to TAP::INetworkConnector to a PRDVTAP. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync#define PDMINETWORKCONNECTOR_2_DRVTAP(pInterface) ( (PDRVTAP)((uintptr_t)pInterface - RT_OFFSETOF(DRVTAP, INetworkConnector)) )
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync/*******************************************************************************
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync* Internal Functions *
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync*******************************************************************************/
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Send data to the network.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * @returns VBox status code.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * @param pInterface Pointer to the interface structure containing the called function pointer.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * @param pvBuf Data to send.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * @param cb Number of bytes to send.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * @thread EMT
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsyncstatic DECLCALLBACK(int) drvTAPSend(PPDMINETWORKCONNECTOR pInterface, const void *pvBuf, size_t cb)
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync PDRVTAP pData = PDMINETWORKCONNECTOR_2_DRVTAP(pInterface);
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync LogFlow(("drvTAPSend: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync cb, u64Now, u64Now - pData->u64LastReceiveTS, u64Now - pData->u64LastTransferTS));
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync "%.*Vhxd\n",
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync int rc = RTFileWrite(pData->FileDevice, pvBuf, cb, NULL);
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Set promiscuous mode.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * This is called when the promiscuous mode is set. This means that there doesn't have
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * to be a mode change when it's called.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * @param pInterface Pointer to the interface structure containing the called function pointer.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * @param fPromiscuous Set if the adaptor is now in promiscuous mode. Clear if it is not.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * @thread EMT
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsyncstatic DECLCALLBACK(void) drvTAPSetPromiscuousMode(PPDMINETWORKCONNECTOR pInterface, bool fPromiscuous)
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync LogFlow(("drvTAPSetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /* nothing to do */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Notification on link status changes.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * @param pInterface Pointer to the interface structure containing the called function pointer.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * @param enmLinkState The new link state.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * @thread EMT
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsyncstatic DECLCALLBACK(void) drvTAPNotifyLinkChanged(PPDMINETWORKCONNECTOR pInterface, PDMNETWORKLINKSTATE enmLinkState)
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync LogFlow(("drvNATNotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** @todo take action on link down and up. Stop the polling and such like. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Asynchronous I/O thread for handling receive.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * @returns VINF_SUCCESS (ignored).
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * @param Thread Thread handle.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * @param pvUser Pointer to a DRVTAP structure.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsyncstatic DECLCALLBACK(int) drvTAPAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync LogFlow(("drvTAPAsyncIoThread: pData=%p\n", pData));
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Polling loop.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync while (pThread->enmState == PDMTHREADSTATE_RUNNING)
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Wait for something to become available.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync aFDs[1].events = POLLIN | POLLPRI | POLLERR | POLLHUP;
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync int rc = poll(&aFDs[0], RT_ELEMENTS(aFDs), -1 /* infinite */);
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /* this might have changed in the meantime */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Read the frame.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync rc = g_pfnLibDlpiRecv(pData->pDeviceHandle, NULL, NULL, achBuf, &cbRead, -1, NULL);
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync rc = RT_LIKELY(rc == DLPI_SUCCESS) ? VINF_SUCCESS : SolarisDLPIErr2VBoxErr(rc);
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync /** @note At least on Linux we will never receive more than one network packet
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * after poll() returned successfully. I don't know why but a second
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * RTFileRead() operation will return with VERR_TRY_AGAIN in any case. */
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync rc = RTFileRead(pData->FileDevice, achBuf, sizeof(achBuf), &cbRead);
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Wait for the device to have space for this frame.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Most guests use frame-sized receive buffers, hence non-zero cbMax
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * automatically means there is enough room for entire frame. Some
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * guests (eg. Solaris) use large chains of small receive buffers
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * (each 128 or so bytes large). We will still start receiving as soon
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * as cbMax is non-zero because:
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * - it would be quite expensive for pfnCanReceive to accurately
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * determine free receive buffer space
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * - if we were waiting for enough free buffers, there is a risk
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * of deadlocking because the guest could be waiting for a receive
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * overflow error to allocate more receive buffers
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync int rc = pData->pPort->pfnWaitReceiveAvail(pData->pPort, RT_INDEFINITE_WAIT);
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * A return code != VINF_SUCCESS means that we were woken up during a VM
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * state transistion. Drop the packet and wait for the next one.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync * Pass the data up.
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync LogFlow(("drvTAPAsyncIoThread: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
3183efc91c7b8252f1dc50dca3efd2d8ae627813vboxsync cbRead, u64Now, u64Now - pData->u64LastReceiveTS, u64Now - pData->u64LastTransferTS));
else if ( rc > 0
LogFlow(("drvTAPAsyncIoThread: Control message: enmState=%d revents=%#x\n", pThread->enmState, aFDs[1].revents));
char ch;
Log(("rc=%d revents=%#x,%#x errno=%p %s\n", rc, aFDs[0].revents, aFDs[1].revents, errno, strerror(errno)));
AssertMsgFailed(("rc=%d revents=%#x,%#x errno=%p %s\n", rc, aFDs[0].revents, aFDs[1].revents, errno, strerror(errno)));
return VINF_SUCCESS;
return VINF_SUCCESS;
#if defined(RT_OS_SOLARIS)
* Calls OS-specific TAP setup application/script.
#ifdef VBOX_WITH_CROSSBOW
for (unsigned int i = 0; i < sizeof(RTMAC); i++)
*pMacAddr8++;
if (pfSetupHandle == 0)
return VERR_HOSTIF_INIT_FAILED;
return VERR_HOSTIF_INIT_FAILED;
return VERR_HOSTIF_INIT_FAILED;
return VERR_HOSTIF_INIT_FAILED;
return VINF_SUCCESS;
* Calls OS-specific TAP terminate application/script.
Log2(("Starting TAP terminate application: %s %s\n", pData->pszTerminateApplication, pData->pszDeviceName));
return VINF_SUCCESS;
LogRel(("TAP#%d: Error running TAP terminate application: %s\n", pData->pDrvIns->iInstance, pData->pszTerminateApplication));
LogRel(("TAP#%d: RTProcWait failed for: %s\n", pData->pDrvIns->iInstance, pData->pszTerminateApplication));
LogRel(("TAP#%d: Failed to fork() process for running TAP terminate application: %s\n", pData->pDrvIns->iInstance,
return VERR_HOSTIF_TERM_FAILED;
#ifdef RT_OS_SOLARIS
# ifdef VBOX_WITH_CROSSBOW
rc = g_pfnLibDlpiSetPhysAddr(pData->pDeviceHandle, DL_CURR_PHYS_ADDR, &pData->MacAddress, ETHERADDRL);
return VINF_SUCCESS;
return rc;
switch (rc)
case DLPI_EVERNOTSUP:
case DLPI_EMODENOTSUP:
case DLPI_ERAWNOTSUP:
case DLPI_ENOLINK:
case DLPI_EBADLINK:
case DLPI_EBADMSG:
return VERR_UNRESOLVED_ERROR;
if (IPFileDes < 0)
if (TapFileDes < 0)
if (iPPA < 0)
if (!InterfaceFD)
LogRel(("TAP#%d: Failed to get interface flags after setting PPA. errno=%d\n", pData->pDrvIns->iInstance, errno));
#ifdef VBOX_SOLARIS_TAP_ARP
LogRel(("TAP#%d: Failed to push ARP to Interface FD. errno=%d\n", pData->pDrvIns->iInstance, errno));
if (ARPFileDes < 0)
LogRel(("TAP#%d: Failed to open for /dev/tap for ARP. errno=%d", pData->pDrvIns->iInstance, errno));
#ifdef VBOX_SOLARIS_TAP_ARP
#ifdef VBOX_SOLARIS_TAP_ARP
#ifdef VBOX_SOLARIS_TAP_ARP
#ifdef VBOX_SOLARIS_TAP_ARP
return VINF_SUCCESS;
switch (enmInterface)
case PDMINTERFACE_BASE:
return NULL;
#ifdef RT_OS_SOLARIS
/** @todo r=bird: This *does* need checking against ConsoleImpl2.cpp if used on non-solaris systems. */
# ifndef VBOX_WITH_CROSSBOW
#ifdef RT_OS_SOLARIS
#ifdef RT_OS_SOLARIS
# ifdef VBOX_WITH_CROSSBOW
if (!CFGMR3AreValuesValid(pCfgHandle, "Device\0InitProg\0TermProg\0FileHandle\0TAPSetupApplication\0TAPTerminateApplication\0MAC"))
pData->pPort = (PPDMINETWORKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_NETWORK_PORT);
#if defined(RT_OS_SOLARIS) /** @todo Other platforms' TAP code should be moved here from ConsoleImpl & VBoxBFE. */
return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Configuration error: failed to query \"TAPTerminateApplication\""));
rc = CFGMR3QueryStringAlloc(pCfgHandle, "TAPTerminateApplication", &pData->pszTerminateApplication);
return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Configuration error: failed to query \"TAPTerminateApplication\""));
# ifdef VBOX_WITH_CROSSBOW
# ifdef VBOX_WITH_CROSSBOW
if (!VBoxLibDlpiFound())
return rc;
#ifdef RT_OS_L4
return rc;
rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pData->pThread, pData, drvTAPAsyncIoThread, drvTapAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "TAP");
#ifdef VBOX_WITH_STATISTICS
PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktSent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of sent packets.", "/Drivers/TAP%d/Packets/Sent", pDrvIns->iInstance);
PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktSentBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of sent bytes.", "/Drivers/TAP%d/Bytes/Sent", pDrvIns->iInstance);
PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktRecv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of received packets.", "/Drivers/TAP%d/Packets/Received", pDrvIns->iInstance);
PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktRecvBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of received bytes.", "/Drivers/TAP%d/Bytes/Received", pDrvIns->iInstance);
PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.", "/Drivers/TAP%d/Transmit", pDrvIns->iInstance);
PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.", "/Drivers/TAP%d/Receive", pDrvIns->iInstance);
return rc;
sizeof(DRVTAP),
NULL,
NULL,
NULL,
NULL,
NULL,