/* * arp responses from vhostX need to be cross connected. nothing * needs to be done for arp responses from VMs, while responses * from fabric needs to be Xconnected and sent to agent */ static int vr_handle_arp_reply(struct vr_arp *sarp, struct vr_packet *pkt, struct vr_forwarding_md *fmd) { struct vr_interface *vif = pkt->vp_if; struct vr_packet *cloned_pkt; if (vif_mode_xconnect(vif) || vif->vif_type == VIF_TYPE_HOST) return vif_xconnect(vif, pkt, fmd); if (vif->vif_type != VIF_TYPE_PHYSICAL) { if (vif_is_virtual(vif)) { vr_preset(pkt); return vr_trap(pkt, fmd->fmd_dvrf, AGENT_TRAP_ARP, NULL); } vr_pfree(pkt, VP_DROP_INVALID_IF); return 0; } cloned_pkt = vr_pclone(pkt); if (cloned_pkt) { vr_preset(cloned_pkt); vif_xconnect(vif, cloned_pkt, fmd); } return vr_trap(pkt, fmd->fmd_dvrf, AGENT_TRAP_ARP, NULL); }
static void vr_flow_init_close(struct vrouter *router, struct vr_flow_entry *flow_e, struct vr_packet *pkt, struct vr_forwarding_md *fmd) { unsigned int flow_index; unsigned int head_room = sizeof(struct agent_hdr) + sizeof(struct vr_eth); struct vr_packet *pkt_c; pkt_c = vr_pclone(pkt); if (!pkt_c) return; vr_preset(pkt_c); if (vr_pcow(pkt_c, head_room)) { vr_pfree(pkt_c, VP_DROP_PCOW_FAIL); return; } flow_index = fmd->fmd_flow_index; vr_trap(pkt_c, fmd->fmd_dvrf, AGENT_TRAP_SESSION_CLOSE, (void *)&flow_index); return; }
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; }
unsigned int vr_trap_flow(struct vrouter *router, struct vr_flow_entry *fe, struct vr_packet *pkt, unsigned int index) { unsigned int trap_reason; struct vr_packet *npkt; struct vr_flow_trap_arg ta; npkt = vr_pclone(pkt); if (!npkt) return -ENOMEM; vr_preset(npkt); switch (fe->fe_flags & VR_FLOW_FLAG_TRAP_MASK) { default: trap_reason = AGENT_TRAP_FLOW_MISS; ta.vfta_index = index; if ((fe->fe_type == VP_TYPE_IP) || (fe->fe_type == VP_TYPE_IP6)) ta.vfta_nh_index = fe->fe_key.flow_nh_id; break; } return vr_trap(npkt, fe->fe_vrf, trap_reason, &ta); }
static int vr_default_input(struct vr_packet *pkt) { struct vr_interface *vif = pkt->vp_if; struct vrouter *router = vif->vif_router; if (router->vr_host_if && (vif != router->vr_host_if)) { vr_preset(pkt); return router->vr_host_if->vif_tx(router->vr_host_if, pkt); } vr_pfree(pkt, VP_DROP_NOWHERE_TO_GO); return 0; }
int vif_xconnect(struct vr_interface *vif, struct vr_packet *pkt) { struct vr_interface *bridge; if (!vif) goto free_pkt; bridge = vif->vif_bridge; if (bridge) { vr_preset(pkt); return bridge->vif_tx(bridge, pkt); } free_pkt: if (vif) vif_drop_pkt(vif, pkt, 1); return 0; }
/* * arp responses from vhostX need to be cross connected. nothing * needs to be done for arp responses from VMs, while responses * from fabric needs to be Xconnected and sent to agent */ static int vr_handle_arp_reply(struct vrouter *router, unsigned short vrf, struct vr_arp *sarp, struct vr_packet *pkt) { struct vr_interface *vif = pkt->vp_if; struct vr_packet *cloned_pkt; if (vif_mode_xconnect(vif) || vif->vif_type == VIF_TYPE_HOST) return vif_xconnect(vif, pkt); if (vif->vif_type != VIF_TYPE_PHYSICAL) { vr_pfree(pkt, VP_DROP_INVALID_IF); return 0; } cloned_pkt = vr_pclone(pkt); if (cloned_pkt) { vr_preset(cloned_pkt); vif_xconnect(vif, cloned_pkt); } return vr_trap(pkt, vrf, AGENT_TRAP_ARP, NULL); }
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; }
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; }
static flow_result_t vr_flow_action(struct vrouter *router, struct vr_flow_entry *fe, unsigned int index, struct vr_packet *pkt, struct vr_forwarding_md *fmd) { int valid_src; flow_result_t result; struct vr_forwarding_md mirror_fmd; struct vr_nexthop *src_nh; struct vr_packet *pkt_clone; fmd->fmd_dvrf = fe->fe_vrf; /* * for now, we will not use dvrf if VRFT is set, because the RPF * check needs to happen in the source vrf */ src_nh = __vrouter_get_nexthop(router, fe->fe_src_nh_index); if (!src_nh) { vr_pfree(pkt, VP_DROP_INVALID_NH); return FLOW_CONSUMED; } if (src_nh->nh_validate_src) { valid_src = src_nh->nh_validate_src(pkt, src_nh, fmd, NULL); if (valid_src == NH_SOURCE_INVALID) { vr_pfree(pkt, VP_DROP_INVALID_SOURCE); return FLOW_CONSUMED; } if (valid_src == NH_SOURCE_MISMATCH) { pkt_clone = vr_pclone(pkt); if (pkt_clone) { vr_preset(pkt_clone); if (vr_pcow(pkt_clone, sizeof(struct vr_eth) + sizeof(struct agent_hdr))) { vr_pfree(pkt_clone, VP_DROP_PCOW_FAIL); } else { vr_trap(pkt_clone, fmd->fmd_dvrf, AGENT_TRAP_ECMP_RESOLVE, &fmd->fmd_flow_index); } } } } if (fe->fe_flags & VR_FLOW_FLAG_VRFT) { if (fmd->fmd_dvrf != fe->fe_dvrf) { fmd->fmd_dvrf = fe->fe_dvrf; fmd->fmd_to_me = 1; } } if (fe->fe_flags & VR_FLOW_FLAG_MIRROR) { if (fe->fe_mirror_id < VR_MAX_MIRROR_INDICES) { mirror_fmd = *fmd; mirror_fmd.fmd_ecmp_nh_index = -1; vr_mirror(router, fe->fe_mirror_id, pkt, &mirror_fmd); } if (fe->fe_sec_mirror_id < VR_MAX_MIRROR_INDICES) { mirror_fmd = *fmd; mirror_fmd.fmd_ecmp_nh_index = -1; vr_mirror(router, fe->fe_sec_mirror_id, pkt, &mirror_fmd); } } switch (fe->fe_action) { case VR_FLOW_ACTION_DROP: vr_pfree(pkt, VP_DROP_FLOW_ACTION_DROP); result = FLOW_CONSUMED; break; case VR_FLOW_ACTION_FORWARD: result = FLOW_FORWARD; break; case VR_FLOW_ACTION_NAT: result = vr_flow_nat(fe, pkt, fmd); break; default: vr_pfree(pkt, VP_DROP_FLOW_ACTION_INVALID); result = FLOW_CONSUMED; break; } return result; }
static int vr_handle_arp_request(struct vrouter *router, unsigned short vrf, struct vr_arp *sarp, struct vr_packet *pkt) { struct vr_packet *cloned_pkt; struct vr_interface *vif = pkt->vp_if; unsigned short proto = htons(VR_ETH_PROTO_ARP); struct vr_eth *eth; struct vr_arp *arp; unsigned int dpa; bool should_proxy = false; /* * still @ l2 level, and hence we can use the mode of the interface * to figure out whether we need to xconnect or not. in the xconnect * mode, just pass it to the peer so that he can handle the arp requests */ if (vif_mode_xconnect(vif)) return vif_xconnect(vif, pkt); should_proxy = vr_should_proxy(vif, sarp->arp_dpa, sarp->arp_spa); /* * if vr should not proxy, all the other arp requests should go out on * the physical interface */ if (vif->vif_type == VIF_TYPE_HOST && !should_proxy) return vif_xconnect(vif, pkt); /* * grat arp from * * VMs - need to be dropped * Fabric - need to be xconnected and also sent to agent * Vhost - xconnected above */ if (vr_grat_arp(sarp)) { if (vif->vif_type == VIF_TYPE_VIRTUAL) { vr_pfree(pkt, VP_DROP_GARP_FROM_VM); return 0; } cloned_pkt = vr_pclone(pkt); if (cloned_pkt) { vr_preset(cloned_pkt); vif_xconnect(vif, cloned_pkt); } return vr_trap(pkt, vrf, AGENT_TRAP_ARP, NULL); } if (should_proxy) { pkt_reset(pkt); eth = (struct vr_eth *)pkt_data(pkt); memcpy(eth->eth_dmac, sarp->arp_sha, VR_ETHER_ALEN); memcpy(eth->eth_smac, vif->vif_mac, VR_ETHER_ALEN); memcpy(ð->eth_proto, &proto, sizeof(proto)); arp = (struct vr_arp *)pkt_pull_tail(pkt, VR_ETHER_HLEN); sarp->arp_op = htons(VR_ARP_OP_REPLY); memcpy(sarp->arp_sha, vif->vif_mac, VR_ETHER_ALEN); memcpy(sarp->arp_dha, eth->eth_dmac, VR_ETHER_ALEN); dpa = sarp->arp_dpa; memcpy(&sarp->arp_dpa, &sarp->arp_spa, sizeof(sarp->arp_dpa)); memcpy(&sarp->arp_spa, &dpa, sizeof(sarp->arp_spa)); memcpy(arp, sarp, sizeof(*sarp)); pkt_pull_tail(pkt, sizeof(*arp)); vif->vif_tx(vif, pkt); } else { /* requests for which vr doesn't have to do anything */ vr_pfree(pkt, VP_DROP_INVALID_ARP); } return 0; }