static int
eth_tx(struct vr_interface *vif, struct vr_packet *pkt)
{
    int ret;
    struct vr_forwarding_md fmd;
    struct vr_interface_stats *stats = vif_get_stats(vif, pkt->vp_cpu);

    /*
     * GRO packets come here twice - once with VP_FLAG_GRO set and
     * once without the flag set. Don't count them twice.
     */
    if (((pkt->vp_flags & VP_FLAG_GRO) == 0) ||
             (vif->vif_type != VIF_TYPE_VIRTUAL)) {
        stats->vis_obytes += pkt_len(pkt);
        stats->vis_opackets++;
    }
    if (vif->vif_flags & VIF_FLAG_MIRROR_TX) {
        vr_init_forwarding_md(&fmd);
        fmd.fmd_dvrf = vif->vif_vrf;
        vr_mirror(vif->vif_router, vif->vif_mirror_id, pkt, &fmd);
    }
        
    ret = hif_ops->hif_tx(vif, pkt);
    if (ret != 0) {
        ret = 0;
        stats->vis_oerrors++;
    }

    return ret;
}
void
vr_dpdk_packet_wakeup(struct vr_interface *vif)
{
    struct vr_interface_stats *stats;
    struct vrouter *router;

    if (unlikely(vif == NULL)) {
        /* get global agent vif */
        router = vrouter_get(0);
        vif = router->vr_agent_if;
    }

    if (likely(vr_dpdk.packet_event_sock != NULL)) {
        if (likely(vif != NULL)) {
            stats = vif_get_stats(vif, rte_lcore_id());
            stats->vis_port_osyscalls++;
        } else {
            /* no agent interface - no counter */
        }
        if (vr_usocket_eventfd_write(vr_dpdk.packet_event_sock) < 0) {
            vr_usocket_close(vr_dpdk.packet_event_sock);
            vr_dpdk.packet_event_sock = NULL;
        }
    }
}
static int
agent_rx(struct vr_interface *vif, struct vr_packet *pkt,
        unsigned short vlan_id __attribute__((unused)))
{
    struct agent_hdr *hdr;
    struct vr_interface *agent_vif;
    struct vr_interface_stats *stats = vif_get_stats(vif, pkt->vp_cpu);

    stats->vis_ibytes += pkt_len(pkt);
    stats->vis_ipackets++;

    hdr = (struct agent_hdr *)pkt_pull(pkt, sizeof(struct vr_eth));
    if (!hdr || !pkt_pull(pkt, sizeof(*hdr))) {
        stats->vis_ierrors++;
        vr_pfree(pkt, VP_DROP_PULL);
        return 0;
    }

    /*
     * Update the original (OS visible) packet to point to the
     * l2 header of the injected packet
     */
    vr_pset_data(pkt, pkt->vp_data);
    if (ntohs(hdr->hdr_cmd) & AGENT_CMD_ROUTE) {
        /*
         * XXX 
         * Packet with command "route" from agent may 
         * result in flow setup, this breaks the 
         * assumption that all packets for a flow will
         * reach same CPU. Need a better way to handle this
         */
        agent_vif = __vrouter_get_interface(vrouter_get(0), 
                                            ntohs(hdr->hdr_ifindex));
        if (!agent_vif) {
            agent_vif = vif;
        }
        pkt->vp_if = agent_vif;
        vr_interface_input(ntohs(hdr->hdr_vrf), agent_vif, pkt, VLAN_ID_INVALID);
    } else {
        vif = __vrouter_get_interface(vrouter_get(0), ntohs(hdr->hdr_ifindex));
        if (!vif) {
            stats->vis_ierrors++;
            vr_pfree(pkt, VP_DROP_INVALID_IF);
            return 0;
        }

        pkt->vp_type = VP_TYPE_AGENT;
        pkt_set_network_header(pkt, pkt->vp_data + sizeof(struct vr_eth));
        pkt_set_inner_network_header(pkt, 
                                     pkt->vp_data + sizeof(struct vr_eth));
        return vif->vif_tx(vif, pkt);
    }

    return 0;
}
void
vif_drop_pkt(struct vr_interface *vif, struct vr_packet *pkt, bool input)
{
    struct vr_interface_stats *stats = vif_get_stats(vif, pkt->vp_cpu);

    if (input)
        stats->vis_ierrors++;
    else
        stats->vis_oerrors++;
    vr_pfree(pkt, VP_DROP_INTERFACE_DROP);
    return;
}
/* vhost driver */
static int
vhost_rx(struct vr_interface *vif, struct vr_packet *pkt,
        unsigned short vlan_id __attribute__((unused)))
{
    struct vr_interface_stats *stats = vif_get_stats(vif, pkt->vp_cpu);

    stats->vis_ibytes += pkt_len(pkt);
    stats->vis_ipackets++;

    /* please see the text on xconnect mode */
    if (vif_mode_xconnect(vif))
        return vif_xconnect(vif, pkt);

    return vr_input(vif->vif_vrf, vif, pkt);
}
/* eth driver */
static int
eth_srx(struct vr_interface *vif, struct vr_packet *pkt,
        unsigned short vlan_id)
{
    unsigned short vrf;
    struct vr_interface_stats *stats = vif_get_stats(vif, pkt->vp_cpu);

    stats->vis_ibytes += pkt_len(pkt);
    stats->vis_ipackets++;

    if (vlan_id >= VIF_VRF_TABLE_ENTRIES)
        vrf = vif->vif_vrf;
    else
        vrf = vif->vif_vrf_table[vlan_id];

    return vr_input(vrf, vif, pkt);
}
static int
agent_tx(struct vr_interface *vif, struct vr_packet *pkt)
{
    int ret;
    struct vr_interface_stats *stats = vif_get_stats(vif, pkt->vp_cpu);

    stats->vis_obytes += pkt_len(pkt);
    stats->vis_opackets++;

    ret = hif_ops->hif_tx(vif, pkt);
    if (ret != 0) {
        ret = 0;
        stats->vis_oerrors++;
    }

    return ret;
}
static int
vhost_tx(struct vr_interface *vif, struct vr_packet *pkt)
{
    int ret;
    struct vr_interface_stats *stats = vif_get_stats(vif, pkt->vp_cpu);

    stats->vis_obytes += pkt_len(pkt);
    stats->vis_opackets++;

    if (vif->vif_type == VIF_TYPE_XEN_LL_HOST)
        memcpy(pkt_data(pkt), vif->vif_mac, sizeof(vif->vif_mac));

    ret = hif_ops->hif_rx(vif, pkt);
    if (ret < 0) {
        ret = 0;
        stats->vis_oerrors++;
    }

    return ret;
}
static void
vr_dpdk_packet_receive(struct vr_usocket *usockp)
{
    const unsigned lcore_id = rte_lcore_id();
    struct vr_dpdk_lcore *lcore = vr_dpdk.lcores[lcore_id];
    struct vr_interface_stats *stats;

    RTE_LOG(DEBUG, USOCK, "%s[%lx]: FD %d\n", __func__, pthread_self(),
                usockp->usock_fd);
    /**
     * Packets is read from the agent's socket here. On success, a counter for
     * packets dequeued from the interface is incremented.
     */
    stats = vif_get_stats(usockp->usock_vif, lcore_id);
    if (usockp->usock_vif) {
        stats->vis_port_ipackets++;
        /* buf_addr and data_off do not change */
        usockp->usock_mbuf->data_len = usockp->usock_read_len;
        usockp->usock_mbuf->pkt_len = usockp->usock_read_len;
        /* convert mbuf to vr_packet */
        vr_dpdk_packet_get(usockp->usock_mbuf, usockp->usock_vif);
        /* send the mbuf to vRouter */
        vr_dpdk_lcore_vroute(lcore, usockp->usock_vif, &usockp->usock_mbuf, 1);
        /* flush packet TX queues immediately */
        vr_dpdk_lcore_flush(lcore);
    } else {
        /**
         * If reading from socket failed, increment counter for interface
         * dequeue drops.
         */
        RTE_LOG(ERR, VROUTER, "Error receiving from packet socket: no vif attached\n");
        vr_dpdk_pfree(usockp->usock_mbuf, VP_DROP_INTERFACE_DROP);
        stats->vis_port_ierrors++;
    }

    usockp->usock_mbuf = NULL;
    usockp->usock_rx_buf = NULL;
    usockp->usock_buf_len = 0;

    return;
}
static int
eth_rx(struct vr_interface *vif, struct vr_packet *pkt, unsigned short vlan_id)
{
    struct vr_interface_stats *stats = vif_get_stats(vif, pkt->vp_cpu);

    stats->vis_ibytes += pkt_len(pkt);
    stats->vis_ipackets++;

    /*
     * please see the text on xconnect mode
     *
     * since we would like the packets to reach the VMs (if they were
     * destined to in the first place), packets have to traverse the
     * stack. so, just mark a flag suggesting that packet is destined
     * for the vrouter and force the vrouter to receive the packet
     */
    if (vif_mode_xconnect(vif))
        pkt->vp_flags |= VP_FLAG_TO_ME;

    return vr_interface_input(vif->vif_vrf, vif, pkt, vlan_id);
}
static void
vr_dpdk_packet_ring_drain(struct vr_usocket *usockp)
{
    int i;
    unsigned nb_pkts;
    struct rte_mbuf *mbuf_arr[VR_DPDK_RX_BURST_SZ];
    const unsigned lcore_id = rte_lcore_id();
    struct vr_interface_stats *stats;

    RTE_LOG(DEBUG, USOCK, "%s[%lx]: draining packet ring...\n", __func__,
            pthread_self());

    if (unlikely(usockp->usock_parent->usock_vif == NULL))
        return;

    rcu_thread_offline();

    stats = vif_get_stats(usockp->usock_parent->usock_vif, lcore_id);
    do {
        nb_pkts = rte_ring_sc_dequeue_burst(vr_dpdk.packet_ring,
            (void **)&mbuf_arr, VR_DPDK_RX_BURST_SZ);
        for (i = 0; i < nb_pkts; i++) {
            if (usock_mbuf_write(usockp->usock_parent, mbuf_arr[i]) >= 0)
                stats->vis_port_opackets++;
            else {
                stats->vis_port_oerrors++;
                RTE_LOG(DEBUG, USOCK,
                        "%s: Error writing mbuf to packet socket: %s (%d)\n",
                        __func__, rte_strerror(errno), errno);
            }

            rte_pktmbuf_free(mbuf_arr[i]);
        }
    } while (nb_pkts > 0);

    rcu_thread_online();
}
static int
agent_send(struct vr_interface *vif, struct vr_packet *pkt,
                void *ifspecific)
{
    int len;
    struct agent_hdr *hdr;
    unsigned char *rewrite;
    struct vr_interface_stats *stats = vif_get_stats(vif, pkt->vp_cpu);
    struct vr_packet *pkt_c;
    struct agent_send_params *params =
        (struct agent_send_params *)ifspecific;

    vr_preset(pkt);

    if (pkt_head_space(pkt) < AGENT_PKT_HEAD_SPACE) {
        len = pkt_len(pkt);

        if (agent_trap_may_truncate(params->trap_reason)) {
            len = MINIMUM(len, VR_AGENT_MIN_PACKET_LEN);
        }

        pkt_c = pkt_copy(pkt, 0, len);
        if (pkt_c) {
            vr_pfree(pkt, VP_DROP_DUPLICATED);
            pkt = pkt_c;
        }
    }

    hdr = (struct agent_hdr *)pkt_push(pkt, sizeof(struct agent_hdr));
    if (!hdr)
        goto drop;

    hdr->hdr_ifindex = htons(pkt->vp_if->vif_idx);
    hdr->hdr_vrf = htons(params->trap_vrf);
    hdr->hdr_cmd = htons(params->trap_reason);

    switch (params->trap_reason) {
    case AGENT_TRAP_FLOW_MISS:
    case AGENT_TRAP_ECMP_RESOLVE:
    case AGENT_TRAP_SOURCE_MISMATCH:
        if (params->trap_param)
            hdr->hdr_cmd_param = htonl(*(unsigned int *)(params->trap_param));
        break;

    case AGENT_TRAP_DIAG:
        if (params->trap_param)
            hdr->hdr_cmd_param = htonl(*(unsigned int *)(params->trap_param));
        break;

    default:
        hdr->hdr_cmd_param = 0;
        break;
    }

    rewrite = pkt_push(pkt, VR_ETHER_HLEN);
    if (!rewrite)
        goto drop;

    memcpy(rewrite, vif->vif_rewrite, VR_ETHER_HLEN);
    return vif->vif_tx(vif, pkt);

drop:
    stats->vis_oerrors++;
    vr_pfree(pkt, VP_DROP_PUSH);
    return 0;
}