VBoxNetFlt.c revision 75479ee94ecd639290ae67b38c9497d9492f89e1
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync * VBoxNetFlt - Network Filter Driver (Host), Common Code.
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * Copyright (C) 2008-2009 Oracle Corporation
e64031e20c39650a7bc902a3e1aba613b9415deevboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * available from http://www.virtualbox.org. This file is free software;
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * you can redistribute it and/or modify it under the terms of the GNU
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * General Public License (GPL) as published by the Free Software
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync/** @page pg_netflt VBoxNetFlt - Network Interface Filter
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * This is a kernel module that attaches to a real interface on the host and
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * filters and injects packets.
db553e573b3425818839b753fa24e41e0282b4bcvboxsync * In the big picture we're one of the three trunk interface on the internal
db553e573b3425818839b753fa24e41e0282b4bcvboxsync * network, the one named "NIC Filter Driver": @image html Networking_Overview.gif
db553e573b3425818839b753fa24e41e0282b4bcvboxsync * @section sec_netflt_locking Locking and Potential Races
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * The main challenge here is to make sure the netfilter and internal network
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * instances won't be destroyed while someone is calling into them.
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * The main calls into or out of of the filter driver are:
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * - Async send completion (not implemented yet)
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * - Release by the internal network.
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * - Receive.
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * - Disappearance of the host networking interface.
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * - Reappearance of the host networking interface.
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * The latter two calls are can be caused by driver unloading/loading or the
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * device being physical unplugged (e.g. a USB network device). Actually, the
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * unload scenario must fervently be prevent as it will cause panics because the
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * internal network will assume the trunk is around until it releases it.
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * @todo Need to figure which host allow unloading and block/fix it.
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * Currently the netfilter instance lives until the internal network releases
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * it. So, it is the internal networks responsibility to make sure there are no
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * active calls when it releases the trunk and destroys the network. The
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * netfilter assists in this by providing INTNETTRUNKIFPORT::pfnSetState and
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * INTNETTRUNKIFPORT::pfnWaitForIdle. The trunk state is used to enable/disable
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * promiscuous mode on the hardware NIC (or similar activation) as well
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * indicating that disconnect is imminent and no further calls shall be made
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * into the internal network. After changing the state to disconnecting and
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * prior to invoking INTNETTRUNKIFPORT::pfnDisconnectAndRelease, the internal
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * network will use INTNETTRUNKIFPORT::pfnWaitForIdle to wait for any still
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * active calls to complete.
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * The netfilter employs a busy counter and an internal state in addition to the
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * public trunk state. All these variables are protected using a spinlock.
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * @section sec_netflt_msc Locking / Sequence Diagrams - OBSOLETE
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * !OBSOLETE! - THIS WAS THE OLD APPROACH!
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * This secion contains a few sequence diagrams describing the problematic
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * transitions of a host interface filter instance.
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * The thing that makes it all a bit problematic is that multiple events may
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * happen at the same time, and that we have to be very careful to avoid
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * deadlocks caused by mixing our locks with the ones in the host kernel. The
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * main events are receive, send, async send completion, disappearance of the
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * host networking interface and its reappearance. The latter two events are
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * can be caused by driver unloading/loading or the device being physical
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * unplugged (e.g. a USB network device).
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * The strategy for dealing with these issues are:
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * - Use a simple state machine.
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * - Require the user (IntNet) to serialize all its calls to us,
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * while at the same time not owning any lock used by any of the
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * the callbacks we might call on receive and async send completion.
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * - Make sure we're 100% idle before disconnecting, and have a
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * disconnected status on both sides to fend off async calls.
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * - Protect the host specific interface handle and the state variables
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * using a spinlock.
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * @subsection subsec_netflt_msc_dis_rel Disconnect from the network and release - OBSOLETE
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * VM, IntNet, NetFlt, Kernel, Wire;
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * VM->IntNet [label="pkt0", linecolor="green", textcolor="green"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet=>IntNet [label="Lock Network", linecolor="green", textcolor="green" ];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet=>IntNet [label="Route packet -> wire", linecolor="green", textcolor="green" ];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet=>IntNet [label="Unlock Network", linecolor="green", textcolor="green" ];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet=>NetFlt [label="pkt0 to wire", linecolor="green", textcolor="green" ];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * NetFlt=>Kernel [label="pkt0 to wire", linecolor="green", textcolor="green"];
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * Kernel->Wire [label="pkt0 to wire", linecolor="green", textcolor="green"];
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * --- [label="Suspending the trunk interface"];
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * IntNet=>IntNet [label="Lock Network"];
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * Wire->Kernel [label="pkt1 - racing us", linecolor="red", textcolor="red"];
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * Kernel=>>NetFlt [label="pkt1 - racing us", linecolor="red", textcolor="red"];
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * NetFlt=>>IntNet [label="pkt1 recv - blocks", linecolor="red", textcolor="red"];
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * IntNet=>IntNet [label="Mark Trunk Suspended"];
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * IntNet=>IntNet [label="Unlock Network"];
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * IntNet=>NetFlt [label="pfnSetActive(false)"];
b53ae39cd277866a2b502bcd33548af30d430b64vboxsync * NetFlt=>NetFlt [label="Mark inactive (atomic)"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet<<NetFlt;
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet=>NetFlt [label="pfnWaitForIdle(forever)"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet=>>NetFlt [label="pkt1 to host", linecolor="red", textcolor="red"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * NetFlt=>>Kernel [label="pkt1 to host", linecolor="red", textcolor="red"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * Kernel<-Wire [label="pkt0 on wire", linecolor="green", textcolor="green"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * NetFlt<<Kernel [label="pkt0 on wire", linecolor="green", textcolor="green"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet<<=NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet<<=IntNet [label="Lock Net, free SG, Unlock Net", linecolor="green", textcolor="green"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet>>NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * NetFlt<-NetFlt [label="idle", linecolor="green", textcolor="green"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet<<NetFlt [label="idle (pfnWaitForIdle)"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * Wire->Kernel [label="pkt2", linecolor="red", textcolor="red"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * Kernel=>>NetFlt [label="pkt2", linecolor="red", textcolor="red"];
d72aa6b0dab3e9b60aa78bfca99c767c48a406b0vboxsync * NetFlt=>>Kernel [label="pkt2 to host", linecolor="red", textcolor="red"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * VM->IntNet [label="pkt3", linecolor="green", textcolor="green"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet=>IntNet [label="Lock Network", linecolor="green", textcolor="green" ];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet=>IntNet [label="Route packet -> drop", linecolor="green", textcolor="green" ];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet=>IntNet [label="Unlock Network", linecolor="green", textcolor="green" ];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * --- [label="The trunk interface is idle now, disconnect it"];
bfd2448384a97d1c16a54af5d1523ae1b861ce26vboxsync * IntNet=>IntNet [label="Lock Network"];
* NetFlt=>NetFlt [label="!pIf || fRediscoveryPending (w/ spinlock)", linecolor="red", textcolor="red"];
#include "VBoxNetFltInternal.h"
static PVBOXNETFLTINS vboxNetFltFindInstanceLocked(PVBOXNETFLTGLOBALS pGlobals, const char *pszName)
return pCur;
return NULL;
return pRet;
bool fRediscovered;
bool fDoIt;
if (fDoIt)
if (fDoIt)
if (fRediscovered)
return fRediscovered;
static DECLCALLBACK(int) vboxNetFltPortXmit(PINTNETTRUNKIFPORT pIfPort, PINTNETSG pSG, uint32_t fDst)
return rc;
int rc;
return rc;
static DECLCALLBACK(INTNETTRUNKIFSTATE) vboxNetFltPortSetState(PINTNETTRUNKIFPORT pIfPort, INTNETTRUNKIFSTATE enmState)
AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, INTNETTRUNKIFSTATE_INVALID);
return enmOldTrunkState;
#ifdef VBOXNETFLT_STATIC_CONFIG
int rc;
#ifdef VBOXNETFLT_STATIC_CONFIG
if (fBusy)
if (!cRefs)
if (!cRefs)
if (fBusy)
bool fRc;
if (fRc)
return fRc;
bool fRc;
if (fRc)
return fRc;
static int vboxNetFltConnectIt(PVBOXNETFLTINS pThis, PINTNETTRUNKSWPORT pSwitchPort, PINTNETTRUNKIFPORT *ppIfPort)
int rc;
#ifdef VBOXNETFLT_STATIC_CONFIG
return rc;
static int vboxNetFltNewInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PINTNETTRUNKSWPORT pSwitchPort,
int rc;
if (!pNew)
return VERR_INTNET_FLT_IF_FAILED;
#ifdef VBOXNETFLT_STATIC_CONFIG
return rc;
return rc;
return rc;
#ifdef VBOXNETFLT_STATIC_CONFIG
DECLHIDDEN(int) vboxNetFltSearchCreateInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PVBOXNETFLTINS *ppInstance, void *pvContext)
int rc;
while (pCur)
switch (enmState)
return VINF_ALREADY_INITIALIZED;
LogRel(("VBoxNetFlt: Huh? An instance of '%s' already exists! [pCur=%p cRefs=%d fDfH=%RTbool enmState=%d]\n",
return VERR_INTNET_FLT_IF_BUSY;
# ifdef RT_STRICT
return rc;
static DECLCALLBACK(int) vboxNetFltFactoryCreateAndConnect(PINTNETTRUNKFACTORY pIfFactory, const char *pszName,
PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pIfFactory - RT_OFFSETOF(VBOXNETFLTGLOBALS, TrunkFactory));
int rc;
LogFlow(("vboxNetFltFactoryCreateAndConnect: pszName=%p:{%s} fFlags=%#x\n", pszName, pszName, fFlags));
if (pCur)
#ifdef VBOXNETFLT_STATIC_CONFIG
if (pCur)
return rc;
#ifdef VBOXNETFLT_STATIC_CONFIG
NULL,
ppIfPort);
return rc;
PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pIfFactory - RT_OFFSETOF(VBOXNETFLTGLOBALS, TrunkFactory));
static DECLCALLBACK(void *) vboxNetFltQueryFactoryInterface(PCSUPDRVFACTORY pSupDrvFactory, PSUPDRVSESSION pSession, const char *pszInterfaceUuid)
PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pSupDrvFactory - RT_OFFSETOF(VBOXNETFLTGLOBALS, SupDrvFactory));
#ifdef LOG_ENABLED
return NULL;
return fRc;
int rc;
return VERR_WRONG_ORDER;
return VERR_WRONG_ORDER;
return rc;
int rc;
rc = SUPR0IdcOpen(&pGlobals->SupDrvIDC, 0 /* iReqVersion = default */, 0 /* iMinVersion = default */, NULL, NULL, NULL);
return rc;
return rc;
return rc;
return rc;
return rc;
return rc;
return rc;