/*
 * handle unicast arp requests and neighbor refreshes. In many cases,
 * we wouldn't like the unicast arp requests from gateway (such as MX)
 * to reach the VMs and change the gateway mac to ip(6) binding, since
 * for vms the gateway is always agent. We would like such requests
 * to go only if the mode is l2
 */
int
vif_plug_mac_request(struct vr_interface *vif, struct vr_packet *pkt,
        struct vr_forwarding_md *fmd)
{
    int handled = 1;
    int nheader;

    struct vr_arp *sarp;

    if (pkt->vp_flags & VP_FLAG_MULTICAST)
        goto unhandled;

    nheader = pkt_network_header(pkt) - pkt_data(pkt);
    if (nheader < 0 || (pkt->vp_data + nheader > pkt->vp_end))
        goto unhandled;

    if (pkt->vp_type == VP_TYPE_ARP) {
        if (pkt->vp_len < (nheader + sizeof(*sarp)))
            goto unhandled;

        sarp = (struct vr_arp *)(pkt_data(pkt) + nheader);
        if (ntohs(sarp->arp_op) != VR_ARP_OP_REQUEST)
            goto unhandled;

        pkt_pull(pkt, nheader);

        handled = vr_arp_input(pkt, fmd);
        if (!handled) {
            pkt_push(pkt, nheader);
        }
        return handled;
    } else if (pkt->vp_type == VP_TYPE_IP6) {
        if (pkt->vp_len < (nheader + sizeof(struct vr_ip6) +
                    sizeof(struct vr_icmp) + VR_IP6_ADDRESS_LEN +
                    sizeof(struct vr_neighbor_option) + VR_ETHER_ALEN))
            goto unhandled;

        pkt_pull(pkt, nheader);

        handled = vr_neighbor_input(pkt, fmd);
        if (!handled) {
            pkt_push(pkt, nheader);
        }
        return handled;
    }

unhandled:
    return !handled;
}
unsigned int
vr_fabric_input(struct vr_interface *vif, struct vr_packet *pkt,
                unsigned short vlan_id)
{
    int handled = 0;
    unsigned short pull_len;
    struct vr_forwarding_md fmd;

    vr_init_forwarding_md(&fmd);
    fmd.fmd_vlan = vlan_id;
    fmd.fmd_dvrf = vif->vif_vrf;

    if (vr_pkt_type(pkt, 0, &fmd) < 0) {
        vif_drop_pkt(vif, pkt, 1);
        return 0;
    }

    if (pkt->vp_type == VP_TYPE_IP6)
        return vif_xconnect(vif, pkt, &fmd);

    pull_len = pkt_get_network_header_off(pkt) - pkt_head_space(pkt);
    pkt_pull(pkt, pull_len);

    if (pkt->vp_type == VP_TYPE_IP || pkt->vp_type == VP_TYPE_IP6)
        handled = vr_l3_input(pkt, &fmd);
    else if (pkt->vp_type == VP_TYPE_ARP)
        handled = vr_arp_input(pkt, &fmd);

    if (!handled) {
        pkt_push(pkt, pull_len);
        return vif_xconnect(vif, pkt, &fmd);
    }

    return 0;
}
static unsigned char *
agent_set_rewrite(struct vr_interface *vif, struct vr_packet *pkt,
        unsigned char *rewrite, unsigned short len)
{
    unsigned char *head;
    unsigned int hdr_len;
    struct agent_hdr *hdr;

    vr_preset(pkt);

    hdr_len = sizeof(struct agent_hdr) + len;
    if (pkt_head_space(pkt) < hdr_len) {
        pkt = vr_pexpand_head(pkt, hdr_len - pkt_head_space(pkt));
        if (!pkt)
            return NULL;
    }

    head = pkt_push(pkt, hdr_len);
    if (!head)
        return NULL;

    /* copy the rewrite first */
    memcpy(head, rewrite, len);

    hdr = (struct agent_hdr *)(head + len);
    hdr->hdr_ifindex = htons(pkt->vp_if->vif_idx);
    hdr->hdr_vrf = htons(pkt->vp_if->vif_vrf);
    /* this needs some thought */
    hdr->hdr_cmd = htons(AGENT_TRAP_NEXTHOP);
    hdr->hdr_cmd_param = 0;

    return head;
}
static void
vr_arp_proxy(struct vr_arp *sarp, struct vr_packet *pkt,
        struct vr_forwarding_md *fmd, unsigned char *dmac)
{
    struct vr_eth *eth;
    struct vr_arp *arp;
    struct vr_forwarding_md fmd_new;
    struct vr_interface *vif = pkt->vp_if;

    eth = (struct vr_eth *)pkt_push(pkt, sizeof(*eth));
    if (!eth) {
        vr_pfree(pkt, VP_DROP_PUSH);
        return;
    }

    memcpy(eth->eth_dmac, sarp->arp_sha, VR_ETHER_ALEN);
    memcpy(eth->eth_smac, dmac, VR_ETHER_ALEN);
    eth->eth_proto = htons(VR_ETH_PROTO_ARP);

    arp = (struct vr_arp *)(pkt_data(pkt) + sizeof(*eth));
    arp->arp_hw = htons(VR_ARP_HW_TYPE_ETHER);
    arp->arp_proto = htons(VR_ETH_PROTO_IP);
    arp->arp_hwlen = VR_ETHER_ALEN;
    arp->arp_protolen = VR_IP_ADDRESS_LEN;
    arp->arp_op = htons(VR_ARP_OP_REPLY);
    memcpy(arp->arp_sha, dmac, VR_ETHER_ALEN);
    memcpy(arp->arp_dha, sarp->arp_sha, VR_ETHER_ALEN);
    memcpy(&arp->arp_dpa, &sarp->arp_spa, sizeof(sarp->arp_spa));
    memcpy(&arp->arp_spa, &sarp->arp_dpa, sizeof(sarp->arp_dpa));

    vr_init_forwarding_md(&fmd_new);
    fmd_new.fmd_dvrf = fmd->fmd_dvrf;
    vr_pkt_type(pkt, 0, &fmd_new);

    /*
     * XXX: for vcp ports, there won't be bridge table entries. to avoid
     * doing vr_bridge_input, we check for the flag NO_ARP_PROXY and
     * and if set, directly send out on that interface
     */
    if (vif_is_vhost(vif) ||
            (vif->vif_flags & VIF_FLAG_NO_ARP_PROXY)) {
        vif->vif_tx(vif, pkt, fmd);
    } else {
        vr_bridge_input(vif->vif_router, pkt, &fmd_new);
    }

    return;
}
Exemple #5
0
static int ccgfs_write(const char *path, const char *buffer, size_t size,
    off_t offset, struct fuse_file_info *filp)
{
	struct lo_packet *rq;

	if (size > 8192)
		return -EIO;

	rq = mpkt_init(CCGFS_WRITE_REQUEST, PV_STRING + 2 * PV_32 + PV_64);
	pkt_push_32(rq, filp->fh);
	pkt_push_64(rq, size);
	pkt_push_64(rq, offset);
	pkt_push(rq, buffer, size, PT_DATA);
	mpkt_send(out_fd, rq);

	return mpkt_recv(CCGFS_ERRNO_RESPONSE, NULL);
}
int
vr_gro_input(struct vr_packet *pkt, struct vr_nexthop *nh)
{
    unsigned short *nh_id;
    int handled = 1;

    if (!vr_gro_process)
        return !handled;

    nh_id = (unsigned short *)pkt_push(pkt, sizeof(*nh_id));
    if (!nh_id) {
        vr_pfree(pkt, VP_DROP_PUSH);
        return handled;
    }

    *nh_id = nh->nh_id;
    handled = vr_gro_process(pkt, nh->nh_dev, (nh->nh_family == AF_BRIDGE));
    return handled;
}
/*
 * in the rewrite case, we will assume the positive case of caller
 * passing us valid rewrite ptr and len and will not check for those
 */
static unsigned char *
vif_cmn_rewrite(struct vr_interface *vif, struct vr_packet *pkt,
        unsigned char *rewrite, unsigned short len)
{
    unsigned char *head;

    if (pkt_head_space(pkt) < len) {
        pkt = vr_pexpand_head(pkt, len - pkt_head_space(pkt));
        if (!pkt)
            return NULL;
    }

    head = pkt_push(pkt, len);
    if (!head)
        return NULL;

    memcpy(head, rewrite, len);
    return head;
}
/*
 * Function to add vlan tag to ethernet header. As it modifies vr_packet
 * structure and not skb, one is expected to invoke vr_pset_data() to
 * modify the data pointer of skb
 */
int
vr_tag_pkt(struct vr_packet *pkt, unsigned short vlan_id)
{
    struct vr_eth *new_eth, *eth;
    unsigned short *vlan_tag;

    eth = (struct vr_eth *)pkt_data(pkt);
    if (eth->eth_proto == htons(VR_ETH_PROTO_VLAN))
        return 0;

    new_eth = (struct vr_eth *)pkt_push(pkt, VR_VLAN_HLEN);
    if (!new_eth)
        return -1;

    memmove(new_eth, eth, (2 * VR_ETHER_ALEN));
    new_eth->eth_proto = htons(VR_ETH_PROTO_VLAN);
    vlan_tag = (unsigned short *)(new_eth + 1);
    *vlan_tag = htons(vlan_id);
    return 0;
}
Exemple #9
0
bool
vr_l2_mcast_control_data_add(struct vr_packet *pkt)
{

    unsigned int *data;

    if (pkt_head_space(pkt) < VR_L2_MCAST_CTRL_DATA_LEN) {
        pkt = vr_pexpand_head(pkt, VR_L2_MCAST_CTRL_DATA_LEN - 
                                                pkt_head_space(pkt));
        if (!pkt)
            return false;
    }

    data = (unsigned int *)pkt_push(pkt, VR_L2_MCAST_CTRL_DATA_LEN);
    if (!data)
        return false;

    *data = VR_L2_MCAST_CTRL_DATA;
    pkt->vp_type = VP_TYPE_L2;
    pkt->vp_flags |= VP_FLAG_MULTICAST;
    return true;
}
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;
}
Exemple #11
0
int
vr_mirror(struct vrouter *router, uint8_t mirror_id, struct vr_packet *pkt,
            struct vr_forwarding_md *fmd, mirror_type_t mtype)
{
    bool reset = true;
    void *mirror_md;
    unsigned char *buf, default_mme[2] = {0xff, 0x0};
    unsigned int captured_len, clone_len = 0;
    unsigned int mirror_md_len = 0, drop_reason;
    struct vr_nexthop *nh, *pkt_nh;
    struct vr_mirror_entry *mirror;
    struct vr_mirror_meta_entry *mme;
    struct vr_forwarding_md new_fmd;

    /* If the packet is already mirrored, dont mirror again */
    if (pkt->vp_flags & VP_FLAG_FROM_DP)
        return 0;

    if (mtype <= MIRROR_TYPE_UNKNOWN || mtype >= MIRROR_TYPE_MAX)
        return 0;

    mirror = router->vr_mirrors[mirror_id];
    if (!mirror)
        return 0;

    if (mirror->mir_flags & VR_MIRROR_FLAG_HW_ASSISTED) {
        vr_fmd_put_mirror_vlan(fmd, mirror->mir_vlan_id);
        return 0;
    }

    memcpy(&new_fmd, fmd, sizeof(*fmd));
    new_fmd.fmd_ecmp_nh_index = -1;
    fmd = &new_fmd;

    vr_fmd_put_mirror_type(fmd, mtype);

    nh = mirror->mir_nh;
    if (!nh || !(nh->nh_flags & NH_FLAG_VALID))
        return 0;

    pkt = vr_pclone(pkt);
    if (!pkt)
        return 0;

    /* Mark as mirrored */
    pkt->vp_flags |= VP_FLAG_FROM_DP;

    /* Set the GSO and partial checksum flag */
    pkt->vp_flags |= (VP_FLAG_FLOW_SET | VP_FLAG_GSO);
    vr_pkt_unset_gro(pkt);

    if (mirror->mir_flags & VR_MIRROR_FLAG_DYNAMIC) {

        if (mtype == MIRROR_TYPE_ACL) {
            if (fmd->fmd_flow_index >= 0) {
                mme = (struct vr_mirror_meta_entry *)
                    vr_itable_get(router->vr_mirror_md, fmd->fmd_flow_index);
                if (mme) {
                    mirror_md_len = mme->mirror_md_len;
                    mirror_md = mme->mirror_md;
                }
            }
        } else if (mtype == MIRROR_TYPE_PORT_RX) {
            if (!pkt->vp_if) {
                drop_reason = VP_DROP_INVALID_IF;
                goto fail;
            }

            mirror_md_len = pkt->vp_if->vif_in_mirror_md_len;
            mirror_md = pkt->vp_if->vif_in_mirror_md;
        } else {
            if (!pkt->vp_nh || !pkt->vp_nh->nh_dev) {
                drop_reason = VP_DROP_INVALID_NH;
                goto fail;
            }

            mirror_md_len = pkt->vp_nh->nh_dev->vif_out_mirror_md_len;
            mirror_md = pkt->vp_nh->nh_dev->vif_out_mirror_md;
        }

        if (!mirror_md_len) {
            mirror_md = default_mme;
            mirror_md_len = sizeof(default_mme);
        }

        clone_len += mirror_md_len;
        clone_len += VR_MIRROR_PKT_HEAD_SPACE;
    } else {
        clone_len += VR_VXLAN_HDR_LEN;
        fmd->fmd_label = mirror->mir_vni;
    }

    if (pkt->vp_if && (pkt->vp_if->vif_type == VIF_TYPE_PHYSICAL)) {
        /* No need to mirror the Tunnel headers. So packet cant be reset */
        reset = false;

        /* Identify whether the packet currently has L2 header. If not a
         * port mirroring, we need to add the extra L2 header
         */
        if (mtype == MIRROR_TYPE_ACL) {

            pkt_nh = pkt->vp_nh;
            if (pkt_nh && (pkt_nh->nh_flags & NH_FLAG_VALID) &&
                        (pkt_nh->nh_type == NH_ENCAP) &&
                        (pkt_nh->nh_family == AF_INET)) {

                clone_len += pkt_nh->nh_encap_len;

                if (vr_pcow(&pkt, clone_len)) {
                    drop_reason = VP_DROP_PCOW_FAIL;
                    goto fail;
                }

                clone_len = 0;

                if (pkt_nh->nh_dev->vif_set_rewrite(pkt_nh->nh_dev, pkt, fmd,
                                    pkt_nh->nh_data, pkt_nh->nh_encap_len) < 0) {
                    drop_reason = VP_DROP_REWRITE_FAIL;
                    goto fail;
                }
            }
        }
    }

    if (reset)
        vr_preset(pkt);

    if (clone_len) {
        if (vr_pcow(&pkt, clone_len)) {
            drop_reason = VP_DROP_PCOW_FAIL;
            goto fail;
        }
    }

    captured_len = htonl(pkt_len(pkt));
    if (mirror_md_len) {
        buf = pkt_push(pkt, mirror_md_len);
        if (!buf) {
            drop_reason = VP_DROP_PUSH;
            goto fail;
        }
        memcpy(buf, mirror_md, mirror_md_len);
    }

    if (nh->nh_vrf >= 0)
        fmd->fmd_dvrf = nh->nh_vrf;

    /*
     * we are now in the mirroring context and there isn't a flow for this
     * mirror packet. hence, set the flow index to -1.
     */
    fmd->fmd_flow_index = -1;

    fmd->fmd_outer_src_ip = 0;

    nh_output(pkt, nh, fmd);
    return 0;

fail:
    vr_pfree(pkt, drop_reason);
    return 0;
}
Exemple #12
0
unsigned int
vr_bridge_input(struct vrouter *router, struct vr_packet *pkt,
                struct vr_forwarding_md *fmd)
{
    struct vr_route_req rt;
    struct vr_forwarding_md cmd;
    struct vr_nexthop *nh;
    unsigned short pull_len, overlay_len = VROUTER_L2_OVERLAY_LEN;
    int reason;

    rt.rtr_req.rtr_label_flags = 0;
    rt.rtr_req.rtr_index = VR_BE_INVALID_INDEX;
    rt.rtr_req.rtr_mac_size = VR_ETHER_ALEN;
    rt.rtr_req.rtr_mac =(int8_t *) pkt_data(pkt);
    /* If multicast L2 packet, use broadcast composite nexthop */
    if (IS_MAC_BMCAST(rt.rtr_req.rtr_mac))
        rt.rtr_req.rtr_mac = (int8_t *)vr_bcast_mac;
    rt.rtr_req.rtr_vrf_id = fmd->fmd_dvrf;

    nh = vr_bridge_lookup(fmd->fmd_dvrf, &rt);
    if (!nh) {
        vr_pfree(pkt, VP_DROP_L2_NO_ROUTE);
        return 0;
    }

    if (nh->nh_type == NH_L2_RCV)
        overlay_len = VROUTER_OVERLAY_LEN;

    if (pkt->vp_type == VP_TYPE_IP || pkt->vp_type == VP_TYPE_IP6) {

        if (vif_is_virtual(pkt->vp_if) &&
                vr_from_vm_mss_adj && vr_pkt_from_vm_tcp_mss_adj) {

            pull_len = pkt_get_network_header_off(pkt) - pkt_head_space(pkt);
            if (!pkt_pull(pkt, pull_len)) {
                vr_pfree(pkt, VP_DROP_PULL);
                return 0;
            }

            if ((reason = vr_pkt_from_vm_tcp_mss_adj(pkt, overlay_len))) {
                vr_pfree(pkt, reason);
                return 0;
            }
            if (!pkt_push(pkt, pull_len)) {
                vr_pfree(pkt, VP_DROP_PUSH);
                return 0;
            }
        }
    }


    /*
     * If there is a label attached to this bridge entry add the
     * label
     */
    if (rt.rtr_req.rtr_label_flags & VR_RT_LABEL_VALID_FLAG) {
        if (!fmd) {
            vr_init_forwarding_md(&cmd);
            fmd = &cmd;
        }
        fmd->fmd_label = rt.rtr_req.rtr_label;
    }

    nh_output(pkt, nh, fmd);
    return 0;
}
Exemple #13
0
unsigned int
vr_bridge_input(struct vrouter *router, struct vr_packet *pkt,
                struct vr_forwarding_md *fmd)
{
    int reason, handled;
    l4_pkt_type_t l4_type = L4_TYPE_UNKNOWN;
    unsigned short pull_len, overlay_len = VROUTER_OVERLAY_LEN;

    int8_t *dmac;
    struct vr_bridge_entry *be;
    struct vr_nexthop *nh = NULL;
    struct vr_vrf_stats *stats;

    dmac = (int8_t *) pkt_data(pkt);

    if (pkt->vp_if->vif_flags & VIF_FLAG_MAC_LEARN) {
        if (vr_bridge_learn(router, pkt, fmd)) {
            return 0;
        }
    }

    pull_len = 0;
    if ((pkt->vp_type == VP_TYPE_IP) || (pkt->vp_type == VP_TYPE_IP6) ||
            (pkt->vp_type == VP_TYPE_ARP)) {
        pull_len = pkt_get_network_header_off(pkt) - pkt_head_space(pkt);
        if (pull_len && !pkt_pull(pkt, pull_len)) {
            vr_pfree(pkt, VP_DROP_PULL);
            return 0;
        }
    }

    if ((pkt->vp_type == VP_TYPE_IP) || (pkt->vp_type == VP_TYPE_IP6)) {
        if (fmd->fmd_dscp < 0) {
            if (pkt->vp_type == VP_TYPE_IP) {
                fmd->fmd_dscp =
                    vr_inet_get_tos((struct vr_ip *)pkt_network_header(pkt));
            } else if (pkt->vp_type == VP_TYPE_IP6) {
                fmd->fmd_dscp =
                    vr_inet6_get_tos((struct vr_ip6 *)pkt_network_header(pkt));
            }
        }
    } else {
        if (fmd->fmd_dotonep < 0) {
            fmd->fmd_dotonep = vr_vlan_get_tos(pkt_data(pkt));
        }
    }

    /* Do the bridge lookup for the packets not meant for "me" */
    if (!fmd->fmd_to_me) {
        /*
         * If DHCP packet coming from VM, Trap it to Agent before doing the bridge
         * lookup itself
         */
        if (vif_is_virtual(pkt->vp_if)) {
            if (pkt->vp_type == VP_TYPE_IP)
                l4_type = vr_ip_well_known_packet(pkt);
            else if (pkt->vp_type == VP_TYPE_IP6)
                l4_type = vr_ip6_well_known_packet(pkt);

            if (l4_type == L4_TYPE_DHCP_REQUEST) {
                if (pkt->vp_if->vif_flags & VIF_FLAG_DHCP_ENABLED) {
                    vr_trap(pkt, fmd->fmd_dvrf,  AGENT_TRAP_L3_PROTOCOLS, NULL);
                    return 0;
                }
            }

            /*
             * Handle the unicast ARP, coming from VM, not
             * destined to us. Broadcast ARP requests would be handled
             * in L2 multicast nexthop. Multicast ARP on fabric
             * interface also would be handled in L2 multicast nexthop.
             * Unicast ARP packets on fabric interface would be handled
             * in plug routines of interface.
             */
            if (!IS_MAC_BMCAST(dmac)) {
                handled = 0;
                if (pkt->vp_type == VP_TYPE_ARP) {
                    handled = vr_arp_input(pkt, fmd, dmac);
                } else if (l4_type == L4_TYPE_NEIGHBOUR_SOLICITATION) {
                    handled = vr_neighbor_input(pkt, fmd, dmac);
                }

                if (handled)
                    return 0;
            }
        }

        be = bridge_lookup(dmac, fmd);
        if (be)
            nh = be->be_nh;

        if (!nh || nh->nh_type == NH_DISCARD) {

            /* If Flooding of unknown unicast not allowed, drop the packet */
            if (!vr_unknown_uc_flood(pkt->vp_if, pkt->vp_nh) ||
                                 IS_MAC_BMCAST(dmac)) {
                vr_pfree(pkt, VP_DROP_L2_NO_ROUTE);
                return 0;
            }

            be = bridge_lookup(vr_bcast_mac, fmd);
            nh = be->be_nh;
            if (!nh) {
                vr_pfree(pkt, VP_DROP_L2_NO_ROUTE);
                return 0;
            }
            stats = vr_inet_vrf_stats(fmd->fmd_dvrf, pkt->vp_cpu);
            if (stats)
                stats->vrf_uuc_floods++;

            /* Treat this unknown unicast packet as multicast */
            pkt->vp_flags |= VP_FLAG_MULTICAST;
        }

        if (be)
            __sync_fetch_and_add(&be->be_packets, 1);

        if (nh->nh_type != NH_L2_RCV)
            overlay_len = VROUTER_L2_OVERLAY_LEN;
    }


    /* Adjust MSS for V4 and V6 packets */
    if ((pkt->vp_type == VP_TYPE_IP) || (pkt->vp_type == VP_TYPE_IP6)) {

        if (vif_is_virtual(pkt->vp_if) &&
                vr_from_vm_mss_adj && vr_pkt_from_vm_tcp_mss_adj) {

            if ((reason = vr_pkt_from_vm_tcp_mss_adj(pkt, overlay_len))) {
                vr_pfree(pkt, reason);
                return 0;
            }
        }

        if (fmd->fmd_to_me) {
            handled = vr_l3_input(pkt, fmd);
            if (!handled) {
                vr_pfree(pkt, VP_DROP_NOWHERE_TO_GO);
            }
            return 0;
        }
    }

    if (pull_len && !pkt_push(pkt, pull_len)) {
        vr_pfree(pkt, VP_DROP_PUSH);
        return 0;
    }

    nh_output(pkt, nh, fmd);
    return 0;
}