SrvIntNetR0.cpp revision 1d8cb4b9afff2549ac88e5ebc861303809282446
/** @file
*
* VBox network devices:
* Internal networking, ring 0
*/
/*
* Copyright (C) 2006-2007 innotek GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License as published by the Free Software Foundation,
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
* distribution. VirtualBox OSE is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_SRV_INTNET
#include <iprt/semaphore.h>
#include <iprt/spinlock.h>
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* A network interface.
*/
typedef struct INTNETIF
{
/** Pointer to the next interface. */
/** The current MAC address for the interface. */
/** Set if the INTNET::Mac member is valid. */
bool fMacSet;
/** Set if the interface is in promiscuous mode.
* In promiscuous mode the interface will receive all packages except the one it's sending. */
bool fPromiscuous;
/** Number of yields done to try make the interface read pending data.
* We will stop yeilding when this reaches a threshold assuming that the VM is paused or
* that it simply isn't worth all the delay. It is cleared when a successful send has been done.
*/
/** Pointer to the current exchange buffer (ring-0). */
/** Pointer to ring-3 mapping of the current exchange buffer. */
/** Pointer to the default exchange buffer for the interface. */
/** Pointer to ring-3 mapping of the default exchange buffer. */
/** Event semaphore which a receiver thread will sleep on while waiting for data to arrive. */
/** Number of threads sleeping on the Event semaphore. */
/** The interface handle.
* When this is INTNET_HANDLE_INVALID a sleeper which is waking up
* should return with the appropriate error condition. */
/** Pointer to the network this interface is connected to. */
struct INTNETNETWORK *pNetwork;
/** The session this interface is associated with. */
/** The SUPR0 object id. */
void *pvObj;
/**
* Internal representation of a network.
*/
typedef struct INTNETNETWORK
{
/** The Next network in the chain.
* This is protected by the INTNET::Spinlock. */
struct INTNETNETWORK *pNext;
/** The network mutex.
* It protects everything dealing with this network. */
/** List of interfaces attached to the network. */
/** Pointer to the instance data. */
/** The SUPR0 object id. */
void *pvObj;
/** Access restricted? */
bool fRestrictAccess;
/** The length of the network name. */
/** The network name. */
char szName[INTNET_MAX_NETWORK_NAME];
/**
* Handle table entry.
*/
typedef union INTNETHTE
{
/** Pointer to the object we're a handle for. */
/** Index to the next free entry. */
} INTNETHTE, *PINTNETHTE;
/**
* Handle table.
*/
typedef struct INTNETHT
{
/** Pointer to the handle table. */
/** The number of allocated handles. */
/** The index of the first free handle entry.
* ~0U means empty list. */
/** The index of the last free handle entry.
* ~0U means empty list. */
/**
* Internal networking instance.
*/
typedef struct INTNET
{
/** Mutex protecting the network creation. */
/** Spinlock protecting the linked list of networks and the interface handle translation table. */
/** List of networks. Protected by INTNET::Spinlock. */
PINTNETNETWORK volatile pNetworks;
/** Handle table for the interfaces. */
} INTNET;
/**
* Validates and translates an interface handle to a interface pointer.
*
* @returns Pointer to interface.
* @returns NULL if the handle is invalid.
* @param pIntNet Pointer to the instance data.
* @param hIF The interface handle to validate and translate.
*/
{
return NULL;
if ( i < pHT->cAllocated
return pIF;
}
/**
* Allocates a handle for an interface.
*
* @returns Handle on success.
* @returns Invalid handle on failure.
* @param pIntNet Pointer to the instance data.
* @param pIF The interface which we're allocating a handle for.
*/
{
unsigned cTries = 10;
for (;;)
{
/*
* Check the free list.
*/
if (i != ~0U)
{
if (paNew)
return i | INTNET_HANDLE_MAGIC;
}
/*
* Leave the spinlock and allocate a new array.
*/
if (--cTries <= 0)
{
AssertMsgFailed(("Giving up!\n"));
break;
}
if (!paNew)
break;
/*
* Acquire the spinlock and check if someone raced us.
*/
{
/* copy the current table. */
/* link the new entries into the free chain. */
i = pHT->cAllocated;
if (iTail == ~0U)
while (i < cNew)
{
iTail = i++;
}
/* update the handle table. */
}
}
if (paNew)
return INTNET_HANDLE_INVALID;
}
/**
* Frees a handle.
*
* @returns Handle on success.
* @returns Invalid handle on failure.
* @param pIntNet Pointer to the instance data.
* @param h The handle we're freeing.
*/
{
const uint32_t i = h & INTNET_HANDLE_INDEX_MASK;
if (i < pHT->cAllocated)
{
/*
* Insert at the end of the free list.
*/
if (iTail != ~0U)
else
}
else
}
#ifdef IN_INTNET_TESTCASE
/**
* Reads the next frame in the buffer.
* The caller is responsible for ensuring that there is a valid frame in the buffer.
*
* @returns Size of the frame in bytes.
* @param pBuf The buffer.
* @param pRingBuff The ring buffer to read from.
* @param pvFrame Where to put the frame. The caller is responsible for
* ensuring that there is sufficient space for the frame.
*/
{
/* skip the frame */
return cb;
}
#endif
/**
* Writes a frame packet to the buffer.
*
* @returns VBox status code.
* @param pBuf The buffer.
* @param pRingBuf The ring buffer to read from.
* @param pvFrame The frame to write.
* @param cbFrame The size of the frame.
*/
static int INTNETRingWriteFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, const void *pvFrame, uint32_t cbFrame)
{
/*
* Validate input.
*/
{
/*
* Try fit it all before the end of the buffer.
*/
{
return VINF_SUCCESS;
}
/*
* Try fit the frame at the start of the buffer.
* (The header fits before the end of the buffer because of alignment.)
*/
AssertMsg(pRingBuf->offEnd - offWrite >= sizeof(INTNETHDR), ("offEnd=%x offWrite=%x\n", pRingBuf->offEnd, offWrite));
{
return VINF_SUCCESS;
}
}
/*
* The reader is ahead of the writer, try fit it into that space.
*/
{
return VINF_SUCCESS;
}
/* (it didn't fit) */
/** @todo stats */
return VERR_BUFFER_OVERFLOW;
}
/**
* Ethernet header.
*/
#pragma pack(1)
typedef struct INTNETETHERHDR
{
#pragma pack()
/**
* Sends a frame to a specific interface.
*
* @param pIf The interface.
* @param pvFrame The frame data.
* @param cbFrame The size of the frame.
*/
{
if (VBOX_SUCCESS(rc))
{
return;
}
/*
* Retry a few times, yielding the CPU in between.
* But don't let a unresponsive VM harm performance, so give up after a short while.
*/
{
unsigned cYields = 10;
do
{
if (VBOX_SUCCESS(rc))
{
return;
}
} while (--cYields > 0);
}
/* ok, the frame is lost. */
}
/**
* Sends a frame.
*
* This function will distribute the frame to the interfaces it is addressed to.
* It will also update the MAC address of the sender.
*
* The caller must own the network mutex.
*
* @param pNetwork The network the frame is being sent to.
* @param pIfSender The interface sending the frame.
* @param pvFrame The frame data.
* @param cbFrame The size of the frame.
*/
static void intnetNetworkSend(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, const void *pvFrame, unsigned cbFrame)
{
/*
* Assert reality.
*/
return;
/*
* Send statistics.
*/
/*
* Inspect the header updating the mac address of the sender in the process.
*/
{
/** @todo stats */
}
)
{
/*
* This is a broadcast or multicast address. For the present we treat those
* two as the same - investigating multicast is left for later.
*
* Write the packet to all the interfaces and signal them.
*/
Log2(("Broadcast\n"));
}
else
{
/*
* Only send to the interfaces with matching a MAC address.
*/
{
|| ( pIf->fPromiscuous
}
}
}
/**
* Sends one or more frames.
*
* The function will first the frame which is passed as the optional
* arguments pvFrame and cbFrame. These are optional since it also
* possible to chain together one or more frames in the send buffer
* which the function will process after considering it's arguments.
*
* @returns VBox status code.
* @param pIntNet The instance data.
* @param hIf The interface handle.
* @param pvFrame Pointer to the frame.
* @param cbFrame Size of the frame.
*/
INTNETR0DECL(int) INTNETR0IfSend(PINTNET pIntNet, INTNETIFHANDLE hIf, const void *pvFrame, unsigned cbFrame)
{
LogFlow(("INTNETR0IfSend: pIntNet=%p hIf=%RX32 pvFrame=%p cbFrame=%u\n", pIntNet, hIf, pvFrame, cbFrame));
/*
* Validate input.
*/
if (!pIf)
return VERR_INVALID_HANDLE;
{
/* This is the better place to crash, probe the buffer. */
}
if (VBOX_FAILURE(rc))
return rc;
/*
* Process the argument.
*/
/*
* Process the send buffer.
*/
{
/* Send the frame if the type is sane. */
{
if (pvCurFrame)
}
/* else: ignore the frame */
/* Skip to the next frame. */
}
}
/**
* VMMR0 request wrapper for INTNETR0IfSend.
*
* @returns see INTNETR0IfSend.
* @param pIntNet The internal networking instance.
* @param pReq The request packet.
*/
{
return VERR_INVALID_PARAMETER;
}
/**
* Maps the default buffer into ring 3.
*
* @returns VBox status code.
* @param pIntNet The instance data.
* @param hIf The interface handle.
* @param ppRing3Buf Where to store the address of the ring-3 mapping.
*/
INTNETR0DECL(int) INTNETR0IfGetRing3Buffer(PINTNET pIntNet, INTNETIFHANDLE hIf, R3PTRTYPE(PINTNETBUF) *ppRing3Buf)
{
LogFlow(("INTNETR0IfGetRing3Buffer: pIntNet=%p hIf=%RX32 ppRing3Buf=%p\n", pIntNet, hIf, ppRing3Buf));
/*
* Validate input.
*/
if (!pIf)
return VERR_INVALID_HANDLE;
/*
* ASSUMES that only the process that created an interface can use it.
* ASSUMES that we created the ring-3 mapping when selecting or
* allocating the buffer.
*/
if (VBOX_FAILURE(rc))
return rc;
return rc;
}
/**
* VMMR0 request wrapper for INTNETR0IfGetRing3Buffer.
*
* @returns see INTNETR0IfGetRing3Buffer.
* @param pIntNet The internal networking instance.
* @param pReq The request packet.
*/
{
return VERR_INVALID_PARAMETER;
}
/**
* Gets the ring-0 address of the current buffer.
*
* @returns VBox status code.
* @param pIntNet The instance data.
* @param hIf The interface handle.
* @param ppRing0Buf Where to store the address of the ring-3 mapping.
*/
INTNETR0DECL(int) INTNETR0IfGetRing0Buffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PINTNETBUF *ppRing0Buf)
{
LogFlow(("INTNETR0IfGetRing0Buffer: pIntNet=%p hIf=%RX32 ppRing0Buf=%p\n", pIntNet, hIf, ppRing0Buf));
/*
* Validate input.
*/
if (!pIf)
return VERR_INVALID_HANDLE;
/*
* Assuming that we're in Ring-0, this should be rather simple :-)
*/
if (VBOX_FAILURE(rc))
return rc;
return rc;
}
#if 0
/**
* Gets the physical addresses of the default interface buffer.
*
* @returns VBox status code.
* @param pIntNet The instance data.
* @param hIF The interface handle.
* @param paPages Where to store the addresses. (The reserved fields will be set to zero.)
* @param cPages
*/
INTNETR0DECL(int) INTNETR0IfGetPhysBuffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPPAGE paPages, unsigned cPages)
{
/*
* Validate input.
*/
if (!pIf)
return VERR_INVALID_HANDLE;
/*
* Assuming that we're in Ring-0, this should be rather simple :-)
*/
if (VBOX_FAILURE(rc))
return rc;
/** @todo make a SUPR0 api for obtaining the array. SUPR0 is keeping track of everything, there
* is no need for any extra bookkeeping here.. */
//*ppRing0Buf = pIf->pIntBuf;
//return RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
return VERR_NOT_IMPLEMENTED;
}
#endif
/**
* Sets the promiscuous mode property of an interface.
*
* @returns VBox status code.
* @param pIntNet The instance handle.
* @param hIf The interface handle.
* @param fPromiscuous Set if the interface should be in promiscuous mode, clear if not.
*/
INTNETR0DECL(int) INTNETR0IfSetPromiscuousMode(PINTNET pIntNet, INTNETIFHANDLE hIf, bool fPromiscuous)
{
LogFlow(("INTNETR0IfSetPromiscuousMode: pIntNet=%p hIf=%RX32 fPromiscuous=%d\n", pIntNet, hIf, fPromiscuous));
/*
* Get and validate essential handles.
*/
if (!pIf)
{
LogFlow(("INTNETR0IfSetPromiscuousMode: returns VERR_INVALID_HANDLE\n"));
return VERR_INVALID_HANDLE;
}
{
Log(("INTNETR0IfSetPromiscuousMode: hIf=%RX32: Changed from %d -> %d\n",
}
return VINF_SUCCESS;
}
/**
* VMMR0 request wrapper for INTNETR0IfSetPromiscuousMode.
*
* @returns see INTNETR0IfSetPromiscuousMode.
* @param pIntNet The internal networking instance.
* @param pReq The request packet.
*/
INTNETR0DECL(int) INTNETR0IfSetPromiscuousModeReq(PINTNET pIntNet, PINTNETIFSETPROMISCUOUSMODEREQ pReq)
{
return VERR_INVALID_PARAMETER;
}
/**
* Wait for the interface to get signaled.
* The interface will be signaled when is put into the receive buffer.
*
* @returns VBox status code.
* @param pIntNet The instance handle.
* @param hIf The interface handle.
* @param cMillies Number of milliseconds to wait. RT_INDEFINITE_WAIT should be
* used if indefinite wait is desired.
*/
{
/*
* Get and validate essential handles.
*/
if (!pIf)
{
LogFlow(("INTNETR0IfWait: returns VERR_INVALID_HANDLE\n"));
return VERR_INVALID_HANDLE;
}
&& Event != NIL_RTSEMEVENT)
{
LogFlow(("INTNETR0IfWait: returns VERR_SEM_DESTROYED\n"));
return VERR_SEM_DESTROYED;
}
/*
* It is tempting to check if there is data to be read here,
* but the problem with such an approach is that it will cause
* one unnecessary supervisor->user->supervisor trip. There is
* already a risk for such, so we don't need to increase this.
*/
/*
* Increment the number of waiters before starting the wait.
* Upon wakeup we must assert reality checking that we're not
* already destroyed or in the process of being destroyed.
*/
{
}
else
return rc;
}
/**
* VMMR0 request wrapper for INTNETR0IfWait.
*
* @returns see INTNETR0IfWait.
* @param pIntNet The internal networking instance.
* @param pReq The request packet.
*/
{
return VERR_INVALID_PARAMETER;
}
/**
* Close an interface.
*
* @returns VBox status code.
* @param pIntNet The instance handle.
* @param hIf The interface handle.
*/
{
/*
* Get and validate essential handles.
*/
if (!pIf)
return VERR_INVALID_HANDLE;
return rc;
}
/**
* VMMR0 request wrapper for INTNETR0IfCloseReq.
*
* @returns see INTNETR0IfClose.
* @param pIntNet The internal networking instance.
* @param pReq The request packet.
*/
{
return VERR_INVALID_PARAMETER;
}
/**
* Interface destructor callback.
* This is called for reference counted objectes when the count reaches 0.
*
* @param pvObj The object pointer.
* @param pvUser1 Pointer to the interface.
* @param pvUser2 Pointer to the INTNET instance data.
*/
{
/*
* Delete the interface handle so the object no longer can be opened.
*/
{
}
/*
* If we've got a network unlink ourselves from it.
* Because of cleanup order we might be an orphan now.
*/
{
else
{
while (pPrev)
{
break;
}
}
}
/*
* Wakeup anyone waiting on this interface.
*
* We *must* make sure they have woken up properly and realized
* that the interface is no longer valid.
*/
{
unsigned cMaxWait = 0x1000;
{
}
{
RTThreadSleep(1);
{
RTThreadSleep(10);
}
}
}
/*
* Unmap user buffer.
*/
{
/** @todo user buffer */
}
/*
* Unmap and Free the default buffer.
*/
if (pIf->pIntBufDefault)
{
pIf->pIntBufDefaultR3 = 0;
}
/*
* The interface.
*/
}
/**
* Creates a new network interface.
*
* The call must have opened the network for the new interface
* and is responsible for closing it on failure. On success
* it must leave the network opened so the interface destructor
* can close it.
*
* @returns VBox status code.
* @param pNetwork The network.
* @param pSession The session handle.
* @param cbSend The size of the send buffer.
* @param cbRecv The size of the receive buffer.
* @param phIf Where to store the interface handle.
*/
static int INTNETNetworkCreateIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession, unsigned cbSend, unsigned cbRecv, PINTNETIFHANDLE phIf)
{
LogFlow(("INTNETNetworkCreateIf: pNetwork=%p pSession=%p cbSend=%u cbRecv=%u phIf=%p\n",
/*
* Assert input.
*/
/*
* Allocate and initialize the interface structure.
*/
if (!pIf)
return VERR_NO_MEMORY;
//pIf->fMacSet = 0;
if (VBOX_SUCCESS(rc))
{
/*
* Create the default buffer.
*/
rc = SUPR0MemAlloc(pIf->pSession, cbBuf, (PRTR0PTR)&pIf->pIntBufDefault, (PRTR3PTR)&pIf->pIntBufDefaultR3);
if (VBOX_SUCCESS(rc))
{
/* receive ring buffer. */
/* send ring buffer. */
/*
* Link the interface to the network.
*/
if (VBOX_SUCCESS(rc))
{
/*
* Register the interface with the session.
*/
pIf->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK_INTERFACE, INTNETIfDestruct, pIf, pNetwork->pIntNet);
{
{
return VINF_SUCCESS;
}
rc = VERR_NO_MEMORY;
return rc;
}
rc = VERR_NO_MEMORY;
}
}
}
return rc;
}
/**
*
* @param pNetwork The network to close.
* @param pSession The session handle.
*/
{
return rc;
}
/**
* Object destructor callback.
* This is called for reference counted objectes when the count reaches 0.
*
* @param pvObj The object pointer.
* @param pvUser1 Pointer to the network.
* @param pvUser2 Pointer to the INTNET instance data.
*/
{
/*
* Unlink the network.s
*/
else
{
{
break;
}
}
/*
* Because of the undefined order of the per session object dereferencing when closing a session,
* we have to handle the case where the network is destroyed before the interfaces. We'll
* deal with this by simply orphaning the interfaces.
*/
while (pCur)
{
}
/*
* Free resources.
*/
}
/**
* Opens an existing network.
*
* @returns VBox status code.
* @param pIntNet The instance data.
* @param pSession The current session.
* @param pszNetwork The network name. This has a valid length.
* @param ppNetwork Where to store the pointer to the network on success.
*/
static int INTNETOpenNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, PINTNETNETWORK *ppNetwork)
{
LogFlow(("INTNETOpenNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} ppNetwork=%p\n",
/*
* Search networks by name.
*/
while (pCur)
{
{
/*
* Increment the reference and check that the
* session can access this network.
*/
if (VBOX_SUCCESS(rc))
{
if (pCur->fRestrictAccess)
if (VBOX_SUCCESS(rc))
else
{
}
}
return rc;
}
}
LogFlow(("INTNETOpenNetwork: returns VERR_FILE_NOT_FOUND\n"));
return VERR_FILE_NOT_FOUND;
}
/**
* Creates a new network.
*
* The call must own the INTNET::FastMutex and has already
* attempted opening the network.
*
* @returns VBox status code.
* @param pIntNet The instance data.
* @param pszNetwork The name of the network. This must be at least one character long and no longer
* than the INTNETNETWORK::szName.
* @param fRestrictAccess Whether new participants should be subjected to access check or not.
* @param pSession The session handle.
* @param ppNetwork Where to store the network.
*/
static int INTNETCreateNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, bool fRestrictAccess, PINTNETNETWORK *ppNetwork)
{
LogFlow(("INTNETCreateNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} ppNetwork=%p\n",
/*
* Verify that the network doesn't exist.
*/
{
LogFlow(("INTNETCreateNetwork: returns VERR_ALREADY_EXISTS\n"));
return VERR_ALREADY_EXISTS;
}
/*
* Allocate and initialize.
*/
if (!pNew)
return VERR_NO_MEMORY;
if (VBOX_SUCCESS(rc))
{
//pNew->pIFs = NULL;
/*
* Register the object in the current session.
*/
pNew->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK, INTNETNetworkDestruct, pNew, pIntNet);
{
/*
* Insert the network into the list.
* This must be done before we attempt any SUPR0ObjRelease call.
*/
/*
* Check if the current session is actually allowed to create and open
* the network. It is possible to implement network name based policies
* and these must be checked now. SUPR0ObjRegister does no such checks.
*/
if (VBOX_SUCCESS(rc))
{
return VINF_SUCCESS;
}
/* The release will destroy the object. */
return rc;
}
rc = VERR_NO_MEMORY;
}
return rc;
}
/**
* Opens a network interface and attaches it to the specified network.
*
* @returns VBox status code.
* @param pIntNet The internal network instance.
* @param pSession The session handle.
* @param pszNetwork The network name.
* @param cbSend The send buffer size.
* @param cbRecv The receive buffer size.
* @param fRestrictAccess Whether new participants should be subjected to access check or not.
* @param phIf Where to store the handle to the network interface.
*/
INTNETR0DECL(int) INTNETR0Open(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, unsigned cbSend, unsigned cbRecv, bool fRestrictAccess, PINTNETIFHANDLE phIf)
{
LogFlow(("INTNETR0Open: pIntNet=%p pSession=%p pszNetwork=%p:{%s} cbSend=%u cbRecv=%u phIf=%p\n",
/*
* Validate input.
*/
/*
*/
if (VBOX_FAILURE(rc))
return rc;
/*
*/
if (rc == VERR_FILE_NOT_FOUND)
if (VBOX_SUCCESS(rc))
{
/*
* Create a new interface to this network.
* On failure we close the network. On success it remains open untill the
* interface is destroyed or the last session is doing cleanup (order problems).
*/
if (VBOX_FAILURE(rc))
}
return rc;
}
/**
* VMMR0 request wrapper for GMMR0MapUnmapChunk.
*
* @returns see GMMR0MapUnmapChunk.
* @param pIntNet The internal networking instance.
* @param pSession The session handle.
* @param pReq The request packet.
*/
{
return VERR_INVALID_PARAMETER;
return INTNETR0Open(pIntNet, pSession, &pReq->szNetwork[0], pReq->cbSend, pReq->cbRecv, pReq->fRestrictAccess, &pReq->hIf);
}
/**
* Destroys an instance of the Ring-0 internal networking service.
*
* @param pIntNet Pointer to the instance data.
*/
{
/*
* Allow NULL pointers.
*/
if (!pIntNet)
return;
/*
* There is not supposed to be any networks hanging around at this time.
*/
{
}
{
}
}
/**
* Create an instance of the Ring-0 internal networking service.
*
* @returns VBox status code.
* @param ppIntNet Where to store the instance pointer.
*/
{
int rc = VERR_NO_MEMORY;
if (pIntNet)
{
//pIntNet->pNetworks = NULL;
//pIntNet->IfHandles.paEntries = NULL;
//pIntNet->IfHandles.cAllocated = 0;
if (VBOX_SUCCESS(rc))
{
if (VBOX_SUCCESS(rc))
{
return VINF_SUCCESS;
}
}
}
return rc;
}