Пример #1
0
int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
{
    char nam[NG_NODESIZ];
    struct ifnet *ifp;
    node_p node;
    RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;

    VBOXCURVNET_SET_FROM_UCRED();
    NOREF(pvContext);
    ifp = ifunit(pThis->szName);
    if (ifp == NULL)
        return VERR_INTNET_FLT_IF_NOT_FOUND;

    /* Create a new netgraph node for this instance */
    if (ng_make_node_common(&ng_vboxnetflt_typestruct, &node) != 0)
        return VERR_INTERNAL_ERROR;

    RTSpinlockAcquireNoInts(pThis->hSpinlock, &Tmp);

    ASMAtomicUoWritePtr(&pThis->u.s.ifp, ifp);
    pThis->u.s.node = node;
    bcopy(IF_LLADDR(ifp), &pThis->u.s.MacAddr, ETHER_ADDR_LEN);
    ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false);

    /* Initialize deferred input queue */
    bzero(&pThis->u.s.inq, sizeof(struct ifqueue));
    mtx_init(&pThis->u.s.inq.ifq_mtx, "vboxnetflt inq", NULL, MTX_SPIN);
    TASK_INIT(&pThis->u.s.tskin, 0, vboxNetFltFreeBSDinput, pThis);

    /* Initialize deferred output queue */
    bzero(&pThis->u.s.outq, sizeof(struct ifqueue));
    mtx_init(&pThis->u.s.outq.ifq_mtx, "vboxnetflt outq", NULL, MTX_SPIN);
    TASK_INIT(&pThis->u.s.tskout, 0, vboxNetFltFreeBSDoutput, pThis);

    RTSpinlockReleaseNoInts(pThis->hSpinlock, &Tmp);

    NG_NODE_SET_PRIVATE(node, pThis);

    /* Attempt to name it vboxnetflt_<ifname> */
    snprintf(nam, NG_NODESIZ, "vboxnetflt_%s", pThis->szName);
    ng_name_node(node, nam);

    /* Report MAC address, promiscuous mode and GSO capabilities. */
    /** @todo keep these reports up to date, either by polling for changes or
     *        intercept some control flow if possible. */
    if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
    {
        Assert(pThis->pSwitchPort);
        pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
        pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, vboxNetFltFreeBsdIsPromiscuous(pThis));
        pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
        pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
        vboxNetFltRelease(pThis, true /*fBusy*/);
    }
    VBOXCURVNET_RESTORE();

    return VINF_SUCCESS;
}
Пример #2
0
/**
 * Output processing task, handles outgoing frames
 */
static void vboxNetFltFreeBSDoutput(void *arg, int pending)
{
    PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)arg;
    struct mbuf *m, *m0;
    struct ifnet *ifp = pThis->u.s.ifp;
    unsigned int cSegs = 0;
    bool fDropIt = false, fActive;
    PINTNETSG pSG;

    VBOXCURVNET_SET(ifp->if_vnet);
    vboxNetFltRetain(pThis, true /* fBusy */);
    for (;;)
    {
        mtx_lock_spin(&pThis->u.s.outq.ifq_mtx);
        _IF_DEQUEUE(&pThis->u.s.outq, m);
        mtx_unlock_spin(&pThis->u.s.outq.ifq_mtx);
        if (m == NULL)
            break;

        for (m0 = m; m0 != NULL; m0 = m0->m_next)
            if (m0->m_len > 0)
                cSegs++;

#ifdef PADD_RUNT_FRAMES_FROM_HOST
        if (m_length(m, NULL) < 60)
            cSegs++;
#endif
        /* Create a copy and deliver to the virtual switch */
        pSG = RTMemTmpAlloc(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
        vboxNetFltFreeBSDMBufToSG(pThis, m, pSG, cSegs, 0);
        fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, INTNETTRUNKDIR_HOST);
        RTMemTmpFree(pSG);

        if (fDropIt)
            m_freem(m);
        else
            ether_output_frame(ifp, m);
    }
    vboxNetFltRelease(pThis, true /* fBusy */);
    VBOXCURVNET_RESTORE();
}
Пример #3
0
/**
 * Internal worker for vboxNetFltOsInitInstance and vboxNetFltOsMaybeRediscovered.
 *
 * @returns VBox status code.
 * @param   pThis           The instance.
 * @param   fRediscovery    If set we're doing a rediscovery attempt, so, don't
 *                          flood the release log.
 */
static int vboxNetFltDarwinAttachToInterface(PVBOXNETFLTINS pThis, bool fRediscovery)
{
    LogFlow(("vboxNetFltDarwinAttachToInterface: pThis=%p (%s)\n", pThis, pThis->szName));

    /*
     * Locate the interface first.
     *
     * The pIfNet member is updated before iflt_attach is called and used
     * to deal with the hypothetical case where someone rips out the
     * interface immediately after our iflt_attach call.
     */
    ifnet_t pIfNet = NULL;
    errno_t err = ifnet_find_by_name(pThis->szName, &pIfNet);
    if (err)
    {
        Assert(err == ENXIO);
        if (!fRediscovery)
            LogRel(("VBoxFltDrv: failed to find ifnet '%s' (err=%d)\n", pThis->szName, err));
        else
            Log(("VBoxFltDrv: failed to find ifnet '%s' (err=%d)\n", pThis->szName, err));
        return VERR_INTNET_FLT_IF_NOT_FOUND;
    }

    RTSpinlockAcquire(pThis->hSpinlock);
    ASMAtomicUoWritePtr(&pThis->u.s.pIfNet, pIfNet);
    RTSpinlockReleaseNoInts(pThis->hSpinlock);

    /*
     * Get the mac address while we still have a valid ifnet reference.
     */
    err = ifnet_lladdr_copy_bytes(pIfNet, &pThis->u.s.MacAddr, sizeof(pThis->u.s.MacAddr));
    if (!err)
    {
        /*
         * Try attach the filter.
         */
        struct iff_filter RegRec;
        RegRec.iff_cookie   = pThis;
        RegRec.iff_name     = "VBoxNetFlt";
        RegRec.iff_protocol = 0;
        RegRec.iff_input    = vboxNetFltDarwinIffInput;
        RegRec.iff_output   = vboxNetFltDarwinIffOutput;
        RegRec.iff_event    = vboxNetFltDarwinIffEvent;
        RegRec.iff_ioctl    = vboxNetFltDarwinIffIoCtl;
        RegRec.iff_detached = vboxNetFltDarwinIffDetached;
        interface_filter_t pIfFilter = NULL;
        err = iflt_attach(pIfNet, &RegRec, &pIfFilter);
        Assert(err || pIfFilter);

        RTSpinlockAcquire(pThis->hSpinlock);
        pIfNet = ASMAtomicUoReadPtrT(&pThis->u.s.pIfNet, ifnet_t);
        if (pIfNet && !err)
        {
            ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false);
            ASMAtomicUoWritePtr(&pThis->u.s.pIfFilter, pIfFilter);
            pIfNet = NULL; /* don't dereference it */
        }
        RTSpinlockReleaseNoInts(pThis->hSpinlock);

        /* Report capabilities. */
        if (   !pIfNet
            && vboxNetFltTryRetainBusyNotDisconnected(pThis))
        {
            Assert(pThis->pSwitchPort);
            pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
            pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, vboxNetFltDarwinIsPromiscuous(pThis));
            pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0,  INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
            pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
            vboxNetFltRelease(pThis, true /*fBusy*/);
        }
    }

    /* Release the interface on failure. */
    if (pIfNet)
        ifnet_release(pIfNet);

    int rc = RTErrConvertFromErrno(err);
    if (RT_SUCCESS(rc))
        LogRel(("VBoxFltDrv: attached to '%s' / %.*Rhxs\n", pThis->szName, sizeof(pThis->u.s.MacAddr), &pThis->u.s.MacAddr));
    else
        LogRel(("VBoxFltDrv: failed to attach to ifnet '%s' (err=%d)\n", pThis->szName, err));
    return rc;
}
Пример #4
0
/**
 * Handle data on netgraph hooks.
 * Frames processing is deferred to a taskqueue because this might
 * be called with non-sleepable locks held and code paths inside
 * the virtual switch might sleep.
 */
static int ng_vboxnetflt_rcvdata(hook_p hook, item_p item)
{
    const node_p node = NG_HOOK_NODE(hook);
    PVBOXNETFLTINS pThis = NG_NODE_PRIVATE(node);
    struct ifnet *ifp = pThis->u.s.ifp;
    struct mbuf *m;
    struct m_tag *mtag;
    bool fActive;

    VBOXCURVNET_SET(ifp->if_vnet);
    fActive = vboxNetFltTryRetainBusyActive(pThis);

    NGI_GET_M(item, m);
    NG_FREE_ITEM(item);

    /* Locate tag to see if processing should be skipped for this frame */
    mtag = m_tag_locate(m, MTAG_VBOX, PACKET_TAG_VBOX, NULL);
    if (mtag != NULL)
    {
        m_tag_unlink(m, mtag);
        m_tag_free(mtag);
    }

    /*
     * Handle incoming hook. This is connected to the
     * input path of the interface, thus handling incoming frames.
     */
    if (pThis->u.s.input == hook)
    {
        if (mtag != NULL || !fActive)
        {
            ether_demux(ifp, m);
            if (fActive)
                vboxNetFltRelease(pThis, true /*fBusy*/);
            VBOXCURVNET_RESTORE();
            return (0);
        }
        mtx_lock_spin(&pThis->u.s.inq.ifq_mtx);
        _IF_ENQUEUE(&pThis->u.s.inq, m);
        mtx_unlock_spin(&pThis->u.s.inq.ifq_mtx);
        taskqueue_enqueue_fast(taskqueue_fast, &pThis->u.s.tskin);
    }
    /*
     * Handle mbufs on the outgoing hook, frames going to the interface
     */
    else if (pThis->u.s.output == hook)
    {
        if (mtag != NULL || !fActive)
        {
            int rc = ether_output_frame(ifp, m);
            if (fActive)
                vboxNetFltRelease(pThis, true /*fBusy*/);
            VBOXCURVNET_RESTORE();
            return rc;
        }
        mtx_lock_spin(&pThis->u.s.outq.ifq_mtx);
        _IF_ENQUEUE(&pThis->u.s.outq, m);
        mtx_unlock_spin(&pThis->u.s.outq.ifq_mtx);
        taskqueue_enqueue_fast(taskqueue_fast, &pThis->u.s.tskout);
    }
    else
    {
        m_freem(m);
    }

    if (fActive)
        vboxNetFltRelease(pThis, true /*fBusy*/);
    VBOXCURVNET_RESTORE();
    return (0);
}
Пример #5
0
/**
 * Internal worker for  vboxNetFltDarwinIffInput and vboxNetFltDarwinIffOutput,
 *
 * @returns 0 or EJUSTRETURN.
 * @param   pThis           The instance.
 * @param   pMBuf           The mbuf.
 * @param   pvFrame         The start of the frame, optional.
 * @param   fSrc            Where the packet (allegedly) comes from, one INTNETTRUNKDIR_* value.
 * @param   eProtocol       The protocol.
 */
static errno_t vboxNetFltDarwinIffInputOutputWorker(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame,
                                                    uint32_t fSrc, protocol_family_t eProtocol)
{
    /*
     * Drop it immediately?
     */
    Log2(("vboxNetFltDarwinIffInputOutputWorker: pThis=%p pMBuf=%p pvFrame=%p fSrc=%#x cbPkt=%x\n",
          pThis, pMBuf, pvFrame, fSrc, pMBuf ? mbuf_pkthdr_len(pMBuf) : -1));
    if (!pMBuf)
        return 0;
#if 0 /* debugging lost icmp packets */
    if (mbuf_pkthdr_len(pMBuf) > 0x300)
    {
        uint8_t *pb = (uint8_t *)(pvFrame ? pvFrame : mbuf_data(pMBuf));
        Log3(("D=%.6Rhxs  S=%.6Rhxs  T=%04x IFF\n", pb, pb + 6, RT_BE2H_U16(*(uint16_t *)(pb + 12))));
    }
#endif
    if (vboxNetFltDarwinMBufIsOur(pThis, pMBuf, pvFrame))
        return 0;

    /*
     * Active? Retain the instance and increment the busy counter.
     */
    if (!vboxNetFltTryRetainBusyActive(pThis))
        return 0;

    /*
     * Finalize out-bound packets since the stack puts off finalizing
     * TCP/IP checksums as long as possible.
     * ASSUMES this only applies to outbound IP packets.
     */
    if (    (fSrc & INTNETTRUNKDIR_HOST)
        &&  eProtocol == PF_INET)
    {
        Assert(!pvFrame);
        mbuf_outbound_finalize(pMBuf, eProtocol, sizeof(RTNETETHERHDR));
    }

    /*
     * Create a (scatter/)gather list for the mbuf and feed it to the internal network.
     */
    bool fDropIt = false;
    unsigned cSegs = vboxNetFltDarwinMBufCalcSGSegs(pThis, pMBuf, pvFrame);
    if (cSegs < VBOXNETFLT_DARWIN_MAX_SEGS)
    {
        PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
        vboxNetFltDarwinMBufToSG(pThis, pMBuf, pvFrame, pSG, cSegs, fSrc);

        fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, fSrc);
        if (fDropIt)
        {
            /*
             * Check if this interface is in promiscuous mode. We should not drop
             * any packets before they get to the driver as it passes them to tap
             * callbacks in order for BPF to work properly.
             */
            if (vboxNetFltDarwinIsPromiscuous(pThis))
                fDropIt = false;
            else
                mbuf_freem(pMBuf);
        }
    }

    vboxNetFltRelease(pThis, true /* fBusy */);

    return fDropIt ? EJUSTRETURN : 0;
}