/* * 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; }
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; }
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; }
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; }
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; }
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; }