bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
{
    struct ifnet *ifp, *ifp0;

    ifp = ASMAtomicUoReadPtrT(&pThis->u.s.ifp, struct ifnet *);
    VBOXCURVNET_SET(ifp->if_vnet);
    /*
     * Attempt to check if the interface is still there and re-initialize if
     * something has changed.
     */
    ifp0 = ifunit(pThis->szName);
    if (ifp != ifp0)
    {
        ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, true);
        ng_rmnode_self(pThis->u.s.node);
        pThis->u.s.node = NULL;
    }

    if (ifp0 != NULL)
    {
        vboxNetFltOsDeleteInstance(pThis);
        vboxNetFltOsInitInstance(pThis, NULL);
    }
    VBOXCURVNET_RESTORE();

    return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
}
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;
}
/**
 * Called to deliver a frame to either the host, the wire or both.
 */
int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
{
    NOREF(pvIfData);

    void (*input_f)(struct ifnet *, struct mbuf *);
    struct ifnet *ifp;
    struct mbuf *m;
    struct m_tag *mtag;
    bool fActive;
    int error;

    ifp = ASMAtomicUoReadPtrT(&pThis->u.s.ifp, struct ifnet *);
    VBOXCURVNET_SET(ifp->if_vnet);

    if (fDst & INTNETTRUNKDIR_WIRE)
    {
        m = vboxNetFltFreeBSDSGMBufFromSG(pThis, pSG);
        if (m == NULL)
            return VERR_NO_MEMORY;
        m = m_pullup(m, ETHER_HDR_LEN);
        if (m == NULL)
            return VERR_NO_MEMORY;

        m->m_flags |= M_PKTHDR;
        ether_output_frame(ifp, m);
    }

    if (fDst & INTNETTRUNKDIR_HOST)
    {
        m = vboxNetFltFreeBSDSGMBufFromSG(pThis, pSG);
        if (m == NULL)
            return VERR_NO_MEMORY;
        m = m_pullup(m, ETHER_HDR_LEN);
        if (m == NULL)
            return VERR_NO_MEMORY;
        /*
         * Delivering packets to the host will be captured by the
         * input hook. Tag the packet with a mbuf tag so that we
         * can skip re-delivery of the packet to the guest during
         * input hook processing.
         */
        mtag = m_tag_alloc(MTAG_VBOX, PACKET_TAG_VBOX, 0, M_NOWAIT);
        if (mtag == NULL)
        {
            m_freem(m);
            return VERR_NO_MEMORY;
        }

        m_tag_init(m);
        m_tag_prepend(m, mtag);
        m->m_flags |= M_PKTHDR;
        m->m_pkthdr.rcvif = ifp;
        ifp->if_input(ifp, m);
    }
    VBOXCURVNET_RESTORE();
    return VINF_SUCCESS;
}
void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
{

    taskqueue_drain(taskqueue_fast, &pThis->u.s.tskin);
    taskqueue_drain(taskqueue_fast, &pThis->u.s.tskout);

    mtx_destroy(&pThis->u.s.inq.ifq_mtx);
    mtx_destroy(&pThis->u.s.outq.ifq_mtx);

    VBOXCURVNET_SET_FROM_UCRED();
    if (pThis->u.s.node != NULL)
        ng_rmnode_self(pThis->u.s.node);
    VBOXCURVNET_RESTORE();
    pThis->u.s.node = NULL;
}
/**
 * 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();
}
void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
{
    struct ifnet *ifp;
    struct ifreq ifreq;
    int error;
    node_p node;
    struct ng_mesg *msg;
    struct ngm_connect *con;
    struct ngm_rmhook *rm;
    char path[NG_PATHSIZ];

    Log(("%s: fActive:%d\n", __func__, fActive));

    ifp = ASMAtomicUoReadPtrT(&pThis->u.s.ifp, struct ifnet *);
    VBOXCURVNET_SET(ifp->if_vnet);
    node = ASMAtomicUoReadPtrT(&pThis->u.s.node, node_p);

    memset(&ifreq, 0, sizeof(struct ifreq));
    /* Activate interface */
    if (fActive)
    {
        pThis->u.s.flags = ifp->if_flags;
        ifpromisc(ifp, 1);

        /* ng_ether nodes are named after the interface name */
        snprintf(path, sizeof(path), "%s:", ifp->if_xname);

        /*
         * Send a netgraph connect message to the ng_ether node
         * assigned to the bridged interface. Connecting
         * the hooks 'lower' (ng_ether) to out 'input'.
         */
        NG_MKMESSAGE(msg, NGM_GENERIC_COOKIE, NGM_CONNECT,
                     sizeof(struct ngm_connect), M_NOWAIT);
        if (msg == NULL)
            return;
        con = (struct ngm_connect *)msg->data;
        snprintf(con->path, NG_PATHSIZ, "vboxnetflt_%s:", ifp->if_xname);
        strlcpy(con->ourhook, "lower", NG_HOOKSIZ);
        strlcpy(con->peerhook, "input", NG_HOOKSIZ);
        NG_SEND_MSG_PATH(error, node, msg, path, 0);

        /*
         * Do the same for the hooks 'upper' (ng_ether) and our
         * 'output' hook.
         */
        NG_MKMESSAGE(msg, NGM_GENERIC_COOKIE, NGM_CONNECT,
                     sizeof(struct ngm_connect), M_NOWAIT);
        if (msg == NULL)
            return;
        con = (struct ngm_connect *)msg->data;
        snprintf(con->path, NG_PATHSIZ, "vboxnetflt_%s:",
                 ifp->if_xname);
        strlcpy(con->ourhook, "upper", sizeof(con->ourhook));
        strlcpy(con->peerhook, "output", sizeof(con->peerhook));
        NG_SEND_MSG_PATH(error, node, msg, path, 0);
    }
    else
    {
        /* De-activate interface */
        pThis->u.s.flags = 0;
        ifpromisc(ifp, 0);

        /* Disconnect msgs are addressed to ourself */
        snprintf(path, sizeof(path), "vboxnetflt_%s:", ifp->if_xname);

        /*
         * Send a netgraph message to disconnect our 'input' hook
         */
        NG_MKMESSAGE(msg, NGM_GENERIC_COOKIE, NGM_RMHOOK,
                     sizeof(struct ngm_rmhook), M_NOWAIT);
        if (msg == NULL)
            return;
        rm = (struct ngm_rmhook *)msg->data;
        strlcpy(rm->ourhook, "input", NG_HOOKSIZ);
        NG_SEND_MSG_PATH(error, node, msg, path, 0);

        /*
         * Send a netgraph message to disconnect our 'output' hook
         */
        NG_MKMESSAGE(msg, NGM_GENERIC_COOKIE, NGM_RMHOOK,
                     sizeof(struct ngm_rmhook), M_NOWAIT);
        if (msg == NULL)
            return;
        rm = (struct ngm_rmhook *)msg->data;
        strlcpy(rm->ourhook, "output", NG_HOOKSIZ);
        NG_SEND_MSG_PATH(error, node, msg, path, 0);
    }
    VBOXCURVNET_RESTORE();
}
/**
 * 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);
}