Logo Search packages:      
Sourcecode: virtualbox-ose version File versions

SrvIntNetR0.cpp

Go to the documentation of this file.
/** @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;
 * you can redistribute it and/or modify it under the terms of the GNU
 * 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 <VBox/intnet.h>
#include <VBox/sup.h>
#include <VBox/pdm.h>
#include <VBox/log.h>
#include <iprt/asm.h>
#include <iprt/alloc.h>
#include <iprt/semaphore.h>
#include <iprt/spinlock.h>
#include <iprt/thread.h>
#include <iprt/assert.h>
#include <iprt/string.h>


/*******************************************************************************
*   Structures and Typedefs                                                    *
*******************************************************************************/
/**
 * A network interface.
 */
00043 typedef struct INTNETIF
{
    /** Pointer to the next interface. */
00046     struct INTNETIF        *pNext;
    /** The current MAC address for the interface. */
00048     PDMMAC                  Mac;
    /** Set if the INTNET::Mac member is valid. */
00050     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. */
00053     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.
     */
00058     uint32_t                cYields;
    /** Pointer to the current exchange buffer (ring-0). */
00060     PINTNETBUF              pIntBuf;
    /** Pointer to ring-3 mapping of the current exchange buffer. */
00062     PINTNETBUF              pIntBufR3;
    /** Pointer to the default exchange buffer for the interface. */
00064     PINTNETBUF              pIntBufDefault;
    /** Pointer to ring-3 mapping of the default exchange buffer. */
00066     PINTNETBUF              pIntBufDefaultR3;
    /** Event semaphore which a receiver thread will sleep on while waiting for data to arrive. */
00068     RTSEMEVENT              Event;
    /** Number of threads sleeping on the Event semaphore. */
00070     uint32_t                cSleepers;
    /** The interface handle.
     * When this is INTNET_HANDLE_INVALID a sleeper which is waking up
     * should return with the appropriate error condition. */
00074     INTNETIFHANDLE          hIf;
    /** Pointer to the network this interface is connected to. */
00076     struct INTNETNETWORK   *pNetwork;
    /** The session this interface is associated with. */
00078     PSUPDRVSESSION          pSession;
    /** The SUPR0 object id. */
00080     void                   *pvObj;
} INTNETIF, *PINTNETIF;


/**
 * Internal representation of a network.
 */
00087 typedef struct INTNETNETWORK
{
    /** The Next network in the chain.
     * This is protected by the INTNET::Spinlock. */
00091     struct INTNETNETWORK   *pNext;
    /** The network mutex.
     * It protects everything dealing with this network. */
00094     RTSEMFASTMUTEX          FastMutex;
    /** List of interfaces attached to the network. */
00096     PINTNETIF               pIFs;
    /** Pointer to the instance data. */
00098     struct INTNET          *pIntNet;
    /** The SUPR0 object id. */
00100     void                   *pvObj;
    /**  Access restricted? */
00102     bool                    fRestrictAccess;
    /** The length of the network name. */
00104     uint8_t                 cchName;
    /** The network name. */
00106     char                    szName[INTNET_MAX_NETWORK_NAME];
} INTNETNETWORK, *PINTNETNETWORK;


/**
 * Handle table entry.
 */
00113 typedef union INTNETHTE
{
    /** Pointer to the object we're a handle for. */
00116     PINTNETIF       pIF;
    /** Index to the next free entry. */
00118     uintptr_t       iNext;
} INTNETHTE, *PINTNETHTE;


/**
 * Handle table.
 */
00125 typedef struct INTNETHT
{
    /** Pointer to the handle table. */
00128     PINTNETHTE          paEntries;
    /** The number of allocated handles. */
00130     uint32_t            cAllocated;
    /** The index of the first free handle entry.
     * ~0U means empty list. */
00133     uint32_t volatile   iHead;
    /** The index of the last free handle entry.
     * ~0U means empty list. */
00136     uint32_t volatile   iTail;
} INTNETHT, *PINTNETHT;


/**
 * Internal networking instance.
 */
00143 typedef struct INTNET
{
    /** Mutex protecting the network creation. */
00146     RTSEMFASTMUTEX          FastMutex;
    /** Spinlock protecting the linked list of networks and the interface handle translation table. */
00148     RTSPINLOCK              Spinlock;
    /** List of networks. Protected by INTNET::Spinlock. */
00150     PINTNETNETWORK volatile pNetworks;
    /** Handle table for the interfaces. */
00152     INTNETHT                IfHandles;
} 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.
 */
DECLINLINE(PINTNETIF) INTNETHandle2IFPtr(PINTNET pIntNet, INTNETIFHANDLE hIF)
{
    Assert(pIntNet);
    if ((hIF & INTNET_HANDLE_MAGIC) != INTNET_HANDLE_MAGIC)
        return NULL;

    PINTNETHT       pHT = &pIntNet->IfHandles;
    const uint32_t  i   = hIF & INTNET_HANDLE_INDEX_MASK;
    PINTNETIF       pIF = NULL;
    RTSPINLOCKTMP   Tmp = RTSPINLOCKTMP_INITIALIZER;
    RTSpinlockAcquire(pIntNet->Spinlock, &Tmp);

    if (    i < pHT->cAllocated
        &&  pHT->paEntries[i].iNext >= INTNET_HANDLE_MAX
        &&  pHT->paEntries[i].iNext != ~0U)
        pIF = pHT->paEntries[i].pIF;

    RTSpinlockRelease(pIntNet->Spinlock, &Tmp);

    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.
 */
static INTNETIFHANDLE INTNETHandleAllocate(PINTNET pIntNet, PINTNETIF pIF)
{
    Assert(pIF);
    Assert(pIntNet);
    unsigned    cTries = 10;
    PINTNETHT   pHT = &pIntNet->IfHandles;
    PINTNETHTE  paNew = NULL;

    RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
    RTSpinlockAcquire(pIntNet->Spinlock, &Tmp);
    for (;;)
    {
        /*
         * Check the free list.
         */
        uint32_t i = pHT->iHead;
        if (i != ~0U)
        {
            pHT->iHead = pHT->paEntries[i].iNext;
            if (pHT->iHead == ~0U)
                pHT->iTail = ~0U;

            pHT->paEntries[i].pIF = pIF;
            RTSpinlockRelease(pIntNet->Spinlock, &Tmp);
            if (paNew)
                RTMemFree(paNew);
            return i | INTNET_HANDLE_MAGIC;
        }

        /*
         * Leave the spinlock and allocate a new array.
         */
        const unsigned cNew = pHT->cAllocated + 128;
        RTSpinlockRelease(pIntNet->Spinlock, &Tmp);
        if (--cTries <= 0)
        {
            AssertMsgFailed(("Giving up!\n"));
            break;
        }
        paNew = (PINTNETHTE)RTMemAlloc(sizeof(*paNew) * cNew);
        if (!paNew)
            break;

        /*
         * Acquire the spinlock and check if someone raced us.
         */
        RTSpinlockAcquire(pIntNet->Spinlock, &Tmp);
        if (pHT->cAllocated < cNew)
        {
            /* copy the current table. */
            memcpy(paNew, pHT->paEntries, pHT->cAllocated * sizeof(*paNew));

            /* link the new entries into the free chain. */
            i = pHT->cAllocated;
            uint32_t iTail = pHT->iTail;
            if (iTail == ~0U)
                pHT->iHead = iTail = i++;
            while (i < cNew)
            {
                paNew[iTail].iNext = i;
                iTail = i++;
            }
            paNew[iTail].iNext = ~0U;
            pHT->iTail = iTail;

            /* update the handle table. */
            pHT->cAllocated = cNew;
            paNew = (PINTNETHTE)ASMAtomicXchgPtr((void * volatile *)&pHT->paEntries, paNew);
        }
    }

    if (paNew)
        RTMemFree(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.
 */
static void INTNETHandleFree(PINTNET pIntNet, INTNETIFHANDLE h)
{
    Assert(INTNETHandle2IFPtr(pIntNet, h));
    PINTNETHT       pHT = &pIntNet->IfHandles;
    const uint32_t  i   = h & INTNET_HANDLE_INDEX_MASK;

    RTSPINLOCKTMP   Tmp = RTSPINLOCKTMP_INITIALIZER;
    RTSpinlockAcquire(pIntNet->Spinlock, &Tmp);

    if (i < pHT->cAllocated)
    {
        /*
         * Insert at the end of the free list.
         */
        pHT->paEntries[i].iNext = ~0U;
        const uint32_t iTail = pHT->iTail;
        if (iTail != ~0U)
            pHT->paEntries[iTail].iNext = i;
        else
            pHT->iHead = i;
        pHT->iTail = i;
    }
    else
        AssertMsgFailed(("%d >= %d\n", i, pHT->cAllocated));

    RTSpinlockRelease(pIntNet->Spinlock, &Tmp);
}


#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.
 */
static unsigned INTNETRingReadFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, void *pvFrame)
{
    Assert(pRingBuf->offRead < pBuf->cbBuf);
    Assert(pRingBuf->offRead >= pRingBuf->offStart);
    Assert(pRingBuf->offRead < pRingBuf->offEnd);
    uint32_t    offRead   = pRingBuf->offRead;
    PINTNETHDR  pHdr      = (PINTNETHDR)((uint8_t *)pBuf + offRead);
    const void *pvFrameIn = INTNETHdrGetFramePtr(pHdr, pBuf);
    unsigned    cb        = pHdr->cbFrame;
    memcpy(pvFrame, pvFrameIn, cb);

    /* skip the frame */
    offRead += pHdr->offFrame + cb;
    offRead = RT_ALIGN_32(offRead, sizeof(INTNETHDR));
    Assert(offRead <= pRingBuf->offEnd && offRead >= pRingBuf->offStart);
    if (offRead >= pRingBuf->offEnd)
        offRead = pRingBuf->offStart;
    ASMAtomicXchgU32(&pRingBuf->offRead, offRead);
    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.
     */
    Assert(pBuf);
    Assert(pRingBuf);
    Assert(pvFrame);
    Assert(cbFrame >= sizeof(PDMMAC) * 2);
    uint32_t offWrite = pRingBuf->offWrite;
    Assert(offWrite == RT_ALIGN_32(offWrite, sizeof(INTNETHDR)));
    uint32_t offRead = pRingBuf->offRead;
    Assert(offRead == RT_ALIGN_32(offRead, sizeof(INTNETHDR)));

    const uint32_t cb = RT_ALIGN_32(cbFrame, sizeof(INTNETHDR));
    if (offRead <= offWrite)
    {
        /*
         * Try fit it all before the end of the buffer.
         */
        if (pRingBuf->offEnd - offWrite >= cb + sizeof(INTNETHDR))
        {
            PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
            pHdr->u16Type  = INTNETHDR_TYPE_FRAME;
            pHdr->cbFrame  = cbFrame;
            pHdr->offFrame = sizeof(INTNETHDR);

            memcpy(pHdr + 1, pvFrame, cbFrame);

            offWrite += cb + sizeof(INTNETHDR);
            Assert(offWrite <= pRingBuf->offEnd && offWrite >= pRingBuf->offStart);
            if (offWrite >= pRingBuf->offEnd)
                offWrite = pRingBuf->offStart;
            Log2(("WriteFrame: offWrite: %#x -> %#x (1)\n", pRingBuf->offWrite, offWrite));
            ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
            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));
        if (offRead - pRingBuf->offStart > cb) /* not >= ! */
        {
            PINTNETHDR  pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
            void       *pvFrameOut = (PINTNETHDR)((uint8_t *)pBuf + pRingBuf->offStart);
            pHdr->u16Type  = INTNETHDR_TYPE_FRAME;
            pHdr->cbFrame  = cbFrame;
            pHdr->offFrame = (intptr_t)pvFrameOut - (intptr_t)pHdr;

            memcpy(pvFrameOut, pvFrame, cbFrame);

            offWrite = pRingBuf->offStart + cb;
            ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
            Log2(("WriteFrame: offWrite: %#x -> %#x (2)\n", pRingBuf->offWrite, offWrite));
            return VINF_SUCCESS;
        }
    }
    /*
     * The reader is ahead of the writer, try fit it into that space.
     */
    else if (offRead - offWrite > cb + sizeof(INTNETHDR)) /* not >= ! */
    {
        PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
        pHdr->u16Type  = INTNETHDR_TYPE_FRAME;
        pHdr->cbFrame  = cbFrame;
        pHdr->offFrame = sizeof(INTNETHDR);

        memcpy(pHdr + 1, pvFrame, cbFrame);

        offWrite += cb + sizeof(INTNETHDR);
        ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
        Log2(("WriteFrame: offWrite: %#x -> %#x (3)\n", pRingBuf->offWrite, offWrite));
        return VINF_SUCCESS;
    }

    /* (it didn't fit) */
    /** @todo stats */
    return VERR_BUFFER_OVERFLOW;
}


/**
 * Ethernet header.
 */
#pragma pack(1)
typedef struct INTNETETHERHDR
{
    PDMMAC  MacDst;
    PDMMAC  MacSrc;
} INTNETETHERHDR, *PINTNETETHERHDR;
#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.
 */
00456 static void intnetIfSend(PINTNETIF pIf, const void *pvFrame, unsigned cbFrame)
{
    LogFlow(("intnetIfSend: pIf=%p:{.hIf=%RX32}\n", pIf, pIf->hIf));
    int rc = INTNETRingWriteFrame(pIf->pIntBuf, &pIf->pIntBuf->Recv, pvFrame, cbFrame);
    if (VBOX_SUCCESS(rc))
    {
        pIf->cYields = 0;
        STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatRecvs);
        STAM_REL_COUNTER_ADD(&pIf->pIntBuf->cbStatRecv, cbFrame);
        RTSemEventSignal(pIf->Event);
        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.
     */
    if (pIf->cYields < 100)
    {
        unsigned cYields = 10;
        do
        {
            RTSemEventSignal(pIf->Event);
            RTThreadYield();
            rc = INTNETRingWriteFrame(pIf->pIntBuf, &pIf->pIntBuf->Recv, pvFrame, cbFrame);
            if (VBOX_SUCCESS(rc))
            {
                STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsOk);
                STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatRecvs);
                STAM_REL_COUNTER_ADD(&pIf->pIntBuf->cbStatRecv, cbFrame);
                RTSemEventSignal(pIf->Event);
                return;
            }
            pIf->cYields++;
        } while (--cYields > 0);
        STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsNok);
    }

    /* ok, the frame is lost. */
    STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatLost);
    RTSemEventSignal(pIf->Event);
}


/**
 * 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.
 */
00513 static void intnetNetworkSend(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, const void *pvFrame, unsigned cbFrame)
{
    /*
     * Assert reality.
     */
    Assert(pNetwork);
    Assert(pIfSender);
    Assert(pNetwork == pIfSender->pNetwork);
    Assert(pvFrame);
    if (cbFrame < sizeof(PDMMAC) * 2)
        return;

    /*
     * Send statistics.
     */
    STAM_REL_COUNTER_INC(&pIfSender->pIntBuf->cStatSends);
    STAM_REL_COUNTER_ADD(&pIfSender->pIntBuf->cbStatSend, cbFrame);

    /*
     * Inspect the header updating the mac address of the sender in the process.
     */
    PINTNETETHERHDR pEthHdr = (PINTNETETHERHDR)pvFrame;
    if (memcmp(&pEthHdr->MacSrc, &pIfSender->Mac, sizeof(pIfSender->Mac)))
    {
        /** @todo stats */
        Log2(("IF MAC: %.6Rhxs -> %.6Rhxs\n", &pIfSender->Mac, &pEthHdr->MacSrc));
        pIfSender->Mac = pEthHdr->MacSrc;
        pIfSender->fMacSet = true;
    }

    if (    (pEthHdr->MacDst.au8[0] & 1) /* multicast address */
        ||  (   pEthHdr->MacDst.au16[0] == 0xffff /* broadcast address. s*/
             && pEthHdr->MacDst.au16[1] == 0xffff
             && pEthHdr->MacDst.au16[2] == 0xffff)
        )
    {
        /*
         * 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"));
        for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
            if (pIf != pIfSender)
                intnetIfSend(pIf, pvFrame, cbFrame);
    }
    else
    {
        /*
         * Only send to the interfaces with matching a MAC address.
         */
        Log2(("Dst=%.6Rhxs\n", &pEthHdr->MacDst));
        for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
        {
            Log2(("Dst=%.6Rhxs ?==? %.6Rhxs\n", &pEthHdr->MacDst, &pIf->Mac));
            if (    (   !pIf->fMacSet
                     || !memcmp(&pIf->Mac, &pEthHdr->MacDst, sizeof(pIf->Mac)))
                ||  (   pIf->fPromiscuous
                     && pIf != pIfSender /* promiscuous mode: omit the sender */))
                intnetIfSend(pIf, pvFrame, cbFrame);
        }
    }
}


/**
 * 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.
     */
    AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
    PINTNETIF pIf = INTNETHandle2IFPtr(pIntNet, hIf);
    if (!pIf)
        return VERR_INVALID_HANDLE;
    if (pvFrame && cbFrame)
    {
        AssertReturn(cbFrame < 0x8000, VERR_INVALID_PARAMETER);
        AssertReturn(VALID_PTR(pvFrame), VERR_INVALID_PARAMETER);
        AssertReturn(VALID_PTR((uint8_t *)pvFrame + cbFrame - 1), VERR_INVALID_PARAMETER);

        /* This is the better place to crash, probe the buffer. */
        ASMProbeReadBuffer(pvFrame, cbFrame);
    }

    int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
    if (VBOX_FAILURE(rc))
        return rc;

    /*
     * Process the argument.
     */
    if (pvFrame && cbFrame)
        intnetNetworkSend(pIf->pNetwork, pIf, pvFrame, cbFrame);

    /*
     * Process the send buffer.
     */
    if (pIf->pIntBuf->Send.offRead != pIf->pIntBuf->Send.offWrite)
    {
        for (;;)
        {
            /*
             * Anything we know what is, if so send it.
             */
            PINTNETHDR pHdr = (PINTNETHDR)((uintptr_t)pIf->pIntBuf + pIf->pIntBuf->Send.offRead);
            if (pHdr->u16Type == INTNETHDR_TYPE_FRAME)
            {
                void *pvCurFrame = INTNETHdrGetFramePtr(pHdr, pIf->pIntBuf);
                if (pvCurFrame)
                    intnetNetworkSend(pIf->pNetwork, pIf, pvCurFrame, pHdr->cbFrame);
            }
            /* else: ignore the frame */

            /*
             * Skip to the next frame.
             */
            INTNETRingSkipFrame(pIf->pIntBuf, &pIf->pIntBuf->Send);
        }
    }

    return RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
}


/**
 * 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, PINTNETBUF *ppRing3Buf)
{
    LogFlow(("INTNETR0IfGetRing3Buffer: pIntNet=%p hIf=%RX32 ppRing3Buf=%p\n", pIntNet, hIf, ppRing3Buf));

    /*
     * Validate input.
     */
    AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
    PINTNETIF pIf = INTNETHandle2IFPtr(pIntNet, hIf);
    if (!pIf)
        return VERR_INVALID_HANDLE;
    AssertReturn(VALID_PTR(ppRing3Buf), VERR_INVALID_PARAMETER);

    /*
     * 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.
     */
    int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
    if (VBOX_FAILURE(rc))
        return rc;

    *ppRing3Buf = pIf->pIntBufR3;

    rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
    LogFlow(("INTNETR0IfGetRing3Buffer: returns %Vrc *ppRing3Buf=%p\n", rc, *ppRing3Buf));
    return rc;
}


/**
 * 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.
     */
    AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
    PINTNETIF pIf = INTNETHandle2IFPtr(pIntNet, hIf);
    if (!pIf)
        return VERR_INVALID_HANDLE;
    AssertReturn(VALID_PTR(ppRing0Buf), VERR_INVALID_PARAMETER);

    /*
     * Assuming that we're in Ring-0, this should be rather simple :-)
     */
    int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
    if (VBOX_FAILURE(rc))
        return rc;

    *ppRing0Buf = pIf->pIntBuf;

    rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
    LogFlow(("INTNETR0IfGetRing0Buffer: returns %Vrc *ppRing0Buf=%p\n", rc, *ppRing0Buf));
    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.
     */
    AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
    PINTNETIF pIf = INTNETHandle2IFPtr(pIntNet, hIf);
    if (!pIf)
        return VERR_INVALID_HANDLE;
    AssertReturn(VALID_PTR(paPages), VERR_INVALID_PARAMETER);
    AssertReturn(VALID_PTR((uint8_t *)&paPages[cPages] - 1), VERR_INVALID_PARAMETER);

    /*
     * Assuming that we're in Ring-0, this should be rather simple :-)
     */
    int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
    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);
    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.
     */
    AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
    PINTNETIF pIf = INTNETHandle2IFPtr(pIntNet, hIf);
    if (!pIf)
    {
        LogFlow(("INTNETR0IfSetPromiscuousMode: returns VERR_INVALID_HANDLE\n"));
        return VERR_INVALID_HANDLE;
    }
    if (pIf->fPromiscuous != fPromiscuous)
    {
        Log(("INTNETR0IfSetPromiscuousMode: hIf=%RX32: Changed from %d -> %d\n",
             hIf, !fPromiscuous, !!fPromiscuous));
        ASMAtomicXchgSize(&pIf->fPromiscuous, !!fPromiscuous);
    }
    return VINF_SUCCESS;
}


/**
 * 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.
 */
INTNETR0DECL(int) INTNETR0IfWait(PINTNET pIntNet, INTNETIFHANDLE hIf, unsigned cMillies)
{
    LogFlow(("INTNETR0IfWait: pIntNet=%p hIf=%RX32 cMillies=%u\n", pIntNet, hIf, cMillies));

    /*
     * Get and validate essential handles.
     */
    AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
    PINTNETIF pIf = INTNETHandle2IFPtr(pIntNet, hIf);
    if (!pIf)
    {
        LogFlow(("INTNETR0IfWait: returns VERR_INVALID_HANDLE\n"));
        return VERR_INVALID_HANDLE;
    }
    const INTNETIFHANDLE    hIfSelf = pIf->hIf;
    const RTSEMEVENT        Event = pIf->Event;
    if (    hIfSelf != hIf
        &&  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.
     */
    ASMAtomicIncU32(&pIf->cSleepers);
    int rc = RTSemEventWait(Event, cMillies);
    if (pIf->Event == Event)
    {
        ASMAtomicDecU32(&pIf->cSleepers);
        if (pIf->hIf != hIf)
            rc = VERR_SEM_DESTROYED;
    }
    else
        rc = VERR_SEM_DESTROYED;
    LogFlow(("INTNETR0IfWait: returns %Vrc\n", rc));
    return rc;
}


/**
 * Close an interface.
 *
 * @returns VBox status code.
 * @param   pIntNet     The instance handle.
 * @param   hIf         The interface handle.
 */
INTNETR0DECL(int) INTNETR0IfClose(PINTNET pIntNet, INTNETIFHANDLE hIf)
{
    LogFlow(("INTNETR0IfClose: pIntNet=%p hIf=%RX32\n", pIntNet, hIf));

    /*
     * Get and validate essential handles.
     */
    AssertReturn(VALID_PTR(pIntNet), VERR_INVALID_PARAMETER);
    PINTNETIF pIf = INTNETHandle2IFPtr(pIntNet, hIf);
    if (!pIf)
        return VERR_INVALID_HANDLE;

    int rc = SUPR0ObjRelease(pIf->pvObj, pIf->pSession);
    LogFlow(("INTNETR0IfClose: returns %Vrc\n", rc));
    return rc;
}


/**
 * 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.
 */
static DECLCALLBACK(void) INTNETIfDestruct(void *pvObj, void *pvUser1, void *pvUser2)
{
    LogFlow(("INTNETIfDestruct: pvObj=%p pvUser1=%p pvUser2=%p\n", pvObj, pvUser1, pvUser2));
    PINTNETIF pIf = (PINTNETIF)pvUser1;
    PINTNET   pIntNet = (PINTNET)pvUser2;

    /*
     * Delete the interface handle so the object no longer can be opened.
     */
    if (pIf->hIf != INTNET_HANDLE_INVALID)
    {
        INTNETHandleFree(pIntNet, pIf->hIf);
        ASMAtomicXchgSize(&pIf->hIf, INTNET_HANDLE_INVALID);
    }

    /*
     * If we've got a network unlink ourselves from it.
     * Because of cleanup order we might be an orphan now.
     */
    if (pIf->pNetwork)
        SUPR0ObjRelease(pIf->pNetwork->pvObj, pIf->pSession);
    if (pIf->pNetwork)
    {
        PINTNETNETWORK pNetwork = pIf->pNetwork;
        RTSemFastMutexRequest(pNetwork->FastMutex);
        if (pNetwork->pIFs == pIf)
            pNetwork->pIFs = pIf->pNext;
        else
        {
            PINTNETIF pPrev = pNetwork->pIFs;
            while (pPrev)
                if (pPrev->pNext == pIf)
                {
                    pPrev->pNext = pIf->pNext;
                    break;
                }
            Assert(pPrev);
        }
        RTSemFastMutexRelease(pNetwork->FastMutex);
        pIf->pNext = NULL;
    }

    /*
     * Wakeup anyone waiting on this interface.
     *
     * We *must* make sure they have woken up properly and realized
     * that the interface is no longer valid.
     */
    if (pIf->Event != NIL_RTSEMEVENT)
    {
        RTSEMEVENT Event = pIf->Event;
        ASMAtomicXchgSize(&pIf->Event, NIL_RTSEMEVENT);
        unsigned cMaxWait = 0x1000;
        while (pIf->cSleepers && cMaxWait-- > 0)
        {
            RTSemEventSignal(Event);
            RTThreadYield();
        }
        if (pIf->cSleepers)
        {
            RTThreadSleep(1);

            cMaxWait = pIf->cSleepers;
            while (pIf->cSleepers && cMaxWait-- > 0)
            {
                RTSemEventSignal(Event);
                RTThreadSleep(10);
            }
        }
        RTSemEventDestroy(Event);
    }

    /*
     * Unmap user buffer.
     */
    if (pIf->pIntBuf != pIf->pIntBufDefault)
    {
        /** @todo user buffer */
    }

    /*
     * Unmap and Free the default buffer.
     */
    if (pIf->pIntBufDefault)
    {
        SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
        pIf->pIntBufDefault = NULL;
        pIf->pIntBufDefaultR3 = NULL;
        pIf->pIntBufR3 = NULL;
        pIf->pIntBuf = NULL;
    }

    /*
     * The interface.
     */
    pIf->pvObj = NULL;
    RTMemFree(pIf);
}



/**
 * 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",
             pNetwork, pSession, cbSend, cbRecv, phIf));

    /*
     * Assert input.
     */
    Assert(VALID_PTR(pNetwork));
    Assert(VALID_PTR(phIf));

    /*
     * Allocate and initialize the interface structure.
     */
    PINTNETIF pIf = (PINTNETIF)RTMemAllocZ(sizeof(*pIf));
    if (!pIf)
        return VERR_NO_MEMORY;

    memset(&pIf->Mac, 0xff, sizeof(pIf->Mac)); /* broadcast */
    //pIf->fMacSet = 0;
    int rc = RTSemEventCreate(&pIf->Event);
    if (VBOX_SUCCESS(rc))
    {
        pIf->pSession = pSession;
        pIf->pNetwork = pNetwork;

        /*
         * Create the default buffer.
         */
        cbRecv = RT_ALIGN(RT_MAX(cbRecv, sizeof(INTNETHDR) * 4), sizeof(INTNETHDR));
        cbSend = RT_ALIGN(RT_MAX(cbSend, sizeof(INTNETHDR) * 4), sizeof(INTNETHDR));
        const unsigned cbBuf = RT_ALIGN(sizeof(*pIf->pIntBuf), sizeof(INTNETHDR)) + cbRecv + cbSend;
        rc = SUPR0MemAlloc(pIf->pSession, cbBuf, (PRTR0PTR)&pIf->pIntBufDefault, (PRTR3PTR)&pIf->pIntBufDefaultR3);
        if (VBOX_SUCCESS(rc))
        {
            pIf->pIntBuf = pIf->pIntBufDefault;
            pIf->pIntBufR3 = pIf->pIntBufDefaultR3;
            pIf->pIntBuf->cbBuf = cbBuf;
            pIf->pIntBuf->cbRecv = cbRecv;
            pIf->pIntBuf->cbSend = cbSend;
            /* receive ring buffer. */
            pIf->pIntBuf->Recv.offStart = RT_ALIGN_32(sizeof(*pIf->pIntBuf), sizeof(INTNETHDR));
            pIf->pIntBuf->Recv.offRead  = pIf->pIntBuf->Recv.offStart;
            pIf->pIntBuf->Recv.offWrite = pIf->pIntBuf->Recv.offStart;
            pIf->pIntBuf->Recv.offEnd   = pIf->pIntBuf->Recv.offStart + cbRecv;
            /* send ring buffer. */
            pIf->pIntBuf->Send.offStart = pIf->pIntBuf->Recv.offEnd;
            pIf->pIntBuf->Send.offRead  = pIf->pIntBuf->Send.offStart;
            pIf->pIntBuf->Send.offWrite = pIf->pIntBuf->Send.offStart;
            pIf->pIntBuf->Send.offEnd   = pIf->pIntBuf->Send.offStart + cbSend;

            /*
             * Link the interface to the network.
             */
            rc = RTSemFastMutexRequest(pNetwork->FastMutex);
            if (VBOX_SUCCESS(rc))
            {
                pIf->pNext = pNetwork->pIFs;
                pNetwork->pIFs = pIf;
                RTSemFastMutexRelease(pNetwork->FastMutex);

                /*
                 * Register the interface with the session.
                 */
                pIf->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK_INTERFACE, INTNETIfDestruct, pIf, pNetwork->pIntNet);
                if (pIf->pvObj)
                {
                    pIf->hIf = INTNETHandleAllocate(pNetwork->pIntNet, pIf);
                    if (pIf->hIf != INTNET_HANDLE_INVALID)
                    {
                        *phIf = pIf->hIf;
                        LogFlow(("INTNETNetworkCreateIf: returns VINF_SUCCESS *phIf=%p\n", *phIf));
                        return VINF_SUCCESS;
                    }
                    rc = VERR_NO_MEMORY;

                    SUPR0ObjRelease(pIf->pvObj, pSession);
                    LogFlow(("INTNETNetworkCreateIf: returns %Vrc\n", rc));
                    return rc;
                }
                rc = VERR_NO_MEMORY;
                RTSemFastMutexDestroy(pNetwork->FastMutex);
                pNetwork->FastMutex = NIL_RTSEMFASTMUTEX;
            }
            SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
            pIf->pIntBufDefault = NULL;
            pIf->pIntBuf = NULL;
        }

        RTSemEventDestroy(pIf->Event);
        pIf->Event = NIL_RTSEMEVENT;
    }
    RTMemFree(pIf);
    LogFlow(("INTNETNetworkCreateIf: returns %Vrc\n", rc));
    return rc;
}


/**
 * Close a network which was opened/created using INTNETOpenNetwork()/INTNETCreateNetwork().
 *
 * @param   pNetwork    The network to close.
 * @param   pSession    The session handle.
 */
static int INTNETNetworkClose(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession)
{
    LogFlow(("INTNETNetworkClose: pNetwork=%p pSession=%p\n", pNetwork, pSession));
    AssertReturn(VALID_PTR(pSession), VERR_INVALID_PARAMETER);
    AssertReturn(VALID_PTR(pNetwork), VERR_INVALID_PARAMETER);
    PINTNET         pIntNet = pNetwork->pIntNet;
    RTSPINLOCKTMP   Tmp = RTSPINLOCKTMP_INITIALIZER;

    RTSpinlockAcquire(pIntNet->Spinlock, &Tmp);
    int rc = SUPR0ObjRelease(pNetwork->pvObj, pSession);
    RTSpinlockRelease(pIntNet->Spinlock, &Tmp);
    LogFlow(("INTNETNetworkClose: return %Vrc\n", rc));
    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.
 */
static DECLCALLBACK(void) INTNETNetworkDestruct(void *pvObj, void *pvUser1, void *pvUser2)
{
    LogFlow(("INTNETNetworkDestruct: pvObj=%p pvUser1=%p pvUser2=%p\n", pvObj, pvUser1, pvUser2));
    RTSPINLOCKTMP   Tmp = RTSPINLOCKTMP_INITIALIZER;
    PINTNETNETWORK  pNetwork = (PINTNETNETWORK)pvUser1;
    PINTNET         pIntNet = (PINTNET)pvUser2;
    Assert(pNetwork->pIntNet == pIntNet);

    /*
     * Unlink the network.s
     */
    RTSpinlockAcquire(pIntNet->Spinlock, &Tmp);
    PINTNETNETWORK pPrev = pIntNet->pNetworks;
    if (pPrev == pNetwork)
        pIntNet->pNetworks = pNetwork->pNext;
    else
    {
        for (; pPrev; pPrev = pPrev->pNext)
            if (pPrev->pNext == pNetwork)
            {
                pPrev->pNext = pNetwork->pNext;
                break;
            }
        Assert(pPrev);
    }
    pNetwork->pNext = NULL;
    pNetwork->pvObj = NULL;
    RTSpinlockRelease(pIntNet->Spinlock, &Tmp);

    /*
     * 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.
     */
    RTSemFastMutexRequest(pNetwork->FastMutex);
    PINTNETIF pCur = pNetwork->pIFs;
    while (pCur)
    {
        PINTNETIF pNext = pCur->pNext;
        pCur->pNext    = NULL;
        pCur->pNetwork = NULL;
        pCur = pNext;
    }
    RTSemFastMutexRelease(pNetwork->FastMutex);

    /*
     * Free resources.
     */
    RTSemFastMutexDestroy(pNetwork->FastMutex);
    pNetwork->FastMutex = NIL_RTSEMFASTMUTEX;
    RTMemFree(pNetwork);
}


/**
 * 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",
             pIntNet, pSession, pszNetwork, pszNetwork, ppNetwork));

    Assert(VALID_PTR(pIntNet));
    Assert(VALID_PTR(pSession));
    Assert(VALID_PTR(pszNetwork));
    Assert(VALID_PTR(ppNetwork));
    *ppNetwork = NULL;

    /*
     * Search networks by name.
     */
    PINTNETNETWORK pCur;
    uint8_t cchName = strlen(pszNetwork);
    Assert(cchName && cchName < sizeof(pCur->szName)); /* caller ensures this */

    RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
    RTSpinlockAcquire(pIntNet->Spinlock, &Tmp);
    pCur = pIntNet->pNetworks;
    while (pCur)
    {
        if (    pCur->cchName == cchName
            &&  !memcmp(pCur->szName, pszNetwork, cchName))
        {
            /*
             * Increment the reference and check that the
             * session can access this network.
             */
            int rc = SUPR0ObjAddRef(pCur->pvObj, pSession);
            RTSpinlockRelease(pIntNet->Spinlock, &Tmp);

            if (VBOX_SUCCESS(rc))
            {
                if (pCur->fRestrictAccess)
                    rc = SUPR0ObjVerifyAccess(pCur->pvObj, pSession, pCur->szName);
                if (VBOX_SUCCESS(rc))
                    *ppNetwork = pCur;
                else
                {
                    RTSpinlockAcquire(pIntNet->Spinlock, &Tmp);
                    SUPR0ObjRelease(pCur->pvObj, pSession);
                    RTSpinlockRelease(pIntNet->Spinlock, &Tmp);
                }
            }
            LogFlow(("INTNETOpenNetwork: returns %Vrc *ppNetwork=%p\n", rc, *ppNetwork));
            return rc;
        }
        pCur = pCur->pNext;
    }
    RTSpinlockRelease(pIntNet->Spinlock, &Tmp);

    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",
             pIntNet, pSession, pszNetwork, pszNetwork, ppNetwork));

    Assert(VALID_PTR(pIntNet));
    Assert(VALID_PTR(pSession));
    Assert(VALID_PTR(pszNetwork));
    Assert(VALID_PTR(ppNetwork));
    *ppNetwork = NULL;

    /*
     * Verify that the network doesn't exist.
     */
    const uint8_t cchName = strlen(pszNetwork);
    RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
    RTSpinlockAcquire(pIntNet->Spinlock, &Tmp);
    for (PINTNETNETWORK pCur = pIntNet->pNetworks; pCur; pCur = pCur->pNext)
        if (    pCur->cchName == cchName
            &&  !memcmp(pCur->szName, pszNetwork, cchName))
        {
            RTSpinlockRelease(pIntNet->Spinlock, &Tmp);
            LogFlow(("INTNETCreateNetwork: returns VERR_ALREADY_EXISTS\n"));
            return VERR_ALREADY_EXISTS;
        }
    RTSpinlockRelease(pIntNet->Spinlock, &Tmp);

    /*
     * Allocate and initialize.
     */
    PINTNETNETWORK pNew = (PINTNETNETWORK)RTMemAllocZ(sizeof(*pNew));
    if (!pNew)
        return VERR_NO_MEMORY;
    int rc = RTSemFastMutexCreate(&pNew->FastMutex);
    if (VBOX_SUCCESS(rc))
    {
        //pNew->pIFs = NULL;
        pNew->pIntNet = pIntNet;
        pNew->cchName = cchName;
        pNew->fRestrictAccess = fRestrictAccess;
        Assert(cchName && cchName < sizeof(pNew->szName));  /* caller's responsibility. */
        memcpy(pNew->szName, pszNetwork, cchName);          /* '\0' by alloc. */

        /*
         * Register the object in the current session.
         */
        pNew->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK, INTNETNetworkDestruct, pNew, pIntNet);
        if (pNew->pvObj)
        {
            /*
             * Insert the network into the list.
             * This must be done before we attempt any SUPR0ObjRelease call.
             */
            RTSpinlockAcquire(pIntNet->Spinlock, &Tmp);
            pNew->pNext = pIntNet->pNetworks;
            pIntNet->pNetworks = pNew;
            RTSpinlockRelease(pIntNet->Spinlock, &Tmp);

            /*
             * 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.
             */
            rc = SUPR0ObjVerifyAccess(pNew->pvObj, pSession, pNew->szName);
            if (VBOX_SUCCESS(rc))
            {
                *ppNetwork = pNew;
                LogFlow(("INTNETCreateNetwork: returns VINF_SUCCESS *ppNetwork=%p\n", pNew));
                return VINF_SUCCESS;
            }

            /* The release will destroy the object. */
            SUPR0ObjRelease(pNew->pvObj, pSession);
            LogFlow(("INTNETCreateNetwork: returns %Vrc\n", rc));
            return rc;
        }
        rc = VERR_NO_MEMORY;

        RTSemFastMutexDestroy(pNew->FastMutex);
        pNew->FastMutex = NIL_RTSEMFASTMUTEX;
    }
    RTMemFree(pNew);
    LogFlow(("INTNETCreateNetwork: returns %Vrc\n", rc));
    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",
             pIntNet, pSession, pszNetwork, pszNetwork, cbSend, cbRecv, phIf));

    /*
     * Validate input.
     */
    AssertReturn(VALID_PTR(pIntNet), VERR_INVALID_PARAMETER);
    AssertReturn(VALID_PTR(pszNetwork), VERR_INVALID_PARAMETER);
    const char *pszNetworkEnd = (const char *)memchr(pszNetwork, '\0', INTNET_MAX_NETWORK_NAME);
    AssertReturn(pszNetworkEnd, VERR_INVALID_PARAMETER);
    size_t cchNetwork = pszNetworkEnd - pszNetwork;
    AssertReturn(cchNetwork, VERR_INVALID_PARAMETER);
    AssertReturn(VALID_PTR(phIf), VERR_INVALID_PARAMETER);

    /*
     * Acquire the mutex to serialize open/create.
     */
    int rc = RTSemFastMutexRequest(pIntNet->FastMutex);
    if (VBOX_FAILURE(rc))
        return rc;

    /*
     * Try open/create the network.
     */
    PINTNETNETWORK pNetwork;
    rc = INTNETOpenNetwork(pIntNet, pSession, pszNetwork, &pNetwork);
    if (rc == VERR_FILE_NOT_FOUND)
        rc = INTNETCreateNetwork(pIntNet, pSession, pszNetwork, fRestrictAccess, &pNetwork);
    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).
         */
        rc = INTNETNetworkCreateIf(pNetwork, pSession, cbSend, cbRecv, phIf);
        if (VBOX_FAILURE(rc))
            INTNETNetworkClose(pNetwork, pSession);
    }

    RTSemFastMutexRelease(pIntNet->FastMutex);

    LogFlow(("INTNETR0Open: return %Vrc *phIf=%RX32\n", rc, *phIf));
    return rc;
}


/**
 * Destroys an instance of the Ring-0 internal networking service.
 *
 * @param   pIntNet     Pointer to the instance data.
 */
INTNETR0DECL(void) INTNETR0Destroy(PINTNET pIntNet)
{
    LogFlow(("INTNETR0Destroy: pIntNet=%p\n", pIntNet));

    /*
     * Allow NULL pointers.
     */
    if (!pIntNet)
        return;

    /*
     * There is not supposed to be any networks hanging around at this time.
     */
    Assert(pIntNet->pNetworks == NULL);
    if (pIntNet->FastMutex != NIL_RTSEMFASTMUTEX)
    {
        RTSemFastMutexDestroy(pIntNet->FastMutex);
        pIntNet->FastMutex = NIL_RTSEMFASTMUTEX;
    }
    if (pIntNet->Spinlock != NIL_RTSPINLOCK)
    {
        RTSpinlockDestroy(pIntNet->Spinlock);
        pIntNet->Spinlock = NIL_RTSPINLOCK;
    }

    RTMemFree(pIntNet);
}


/**
 * Create an instance of the Ring-0 internal networking service.
 *
 * @returns VBox status code.
 * @param   ppIntNet    Where to store the instance pointer.
 */
INTNETR0DECL(int) INTNETR0Create(PINTNET *ppIntNet)
{
    LogFlow(("INTNETR0Create: ppIntNet=%p\n", ppIntNet));
    int rc = VERR_NO_MEMORY;
    PINTNET pIntNet = (PINTNET)RTMemAllocZ(sizeof(*pIntNet));
    if (pIntNet)
    {
        //pIntNet->pNetworks              = NULL;
        //pIntNet->IfHandles.paEntries    = NULL;
        //pIntNet->IfHandles.cAllocated   = 0;
        pIntNet->IfHandles.iHead        = ~0U;
        pIntNet->IfHandles.iTail        = ~0U;

        rc = RTSemFastMutexCreate(&pIntNet->FastMutex);
        if (VBOX_SUCCESS(rc))
        {
            rc = RTSpinlockCreate(&pIntNet->Spinlock);
            if (VBOX_SUCCESS(rc))
            {
                *ppIntNet = pIntNet;
                LogFlow(("INTNETR0Create: returns VINF_SUCCESS *ppIntNet=%p\n", pIntNet));
                return VINF_SUCCESS;
            }
            RTSemFastMutexDestroy(pIntNet->FastMutex);
        }
        RTMemFree(pIntNet);
    }
    *ppIntNet = NULL;
    LogFlow(("INTNETR0Create: returns %Vrc\n", rc));
    return rc;
}


Generated by  Doxygen 1.6.0   Back to index