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; }
/* * This funciton parses the ethernet packet and assigns the * pkt->vp_type, network protocol of the packet. The ethernet header can * start from an offset from vp_data */ int vr_pkt_type(struct vr_packet *pkt, unsigned short offset, struct vr_forwarding_md *fmd) { unsigned char *eth = pkt_data(pkt) + offset; unsigned short eth_proto; int pull_len, pkt_len = pkt_head_len(pkt) - offset; struct vr_vlan_hdr *vlan; pull_len = VR_ETHER_HLEN; if (pkt_len < pull_len) return -1; pkt->vp_flags &= ~(VP_FLAG_MULTICAST); /* L2 broadcast/multicast packets are multicast packets */ if (IS_MAC_BMCAST(eth)) pkt->vp_flags |= VP_FLAG_MULTICAST; eth_proto = ntohs(*(unsigned short *)(eth + VR_ETHER_PROTO_OFF)); while (eth_proto == VR_ETH_PROTO_VLAN) { if (pkt_len < (pull_len + sizeof(*vlan))) return -1; vlan = (struct vr_vlan_hdr *)(eth + pull_len); if (fmd && (fmd->fmd_vlan == VLAN_ID_INVALID)) fmd->fmd_vlan = vlan->vlan_tag & 0xFFF; eth_proto = ntohs(vlan->vlan_proto); pull_len += sizeof(*vlan); } pkt_set_network_header(pkt, pkt->vp_data + offset + pull_len); pkt_set_inner_network_header(pkt, pkt->vp_data + offset + pull_len); pkt->vp_type = vr_eth_proto_to_pkt_type(eth_proto); return 0; }
int vr_mpls_input(struct vrouter *router, struct vr_packet *pkt, struct vr_forwarding_md *fmd) { unsigned int label; unsigned short vrf; struct vr_nexthop *nh; unsigned char *data; struct vr_ip *ip; unsigned short drop_reason = 0; label = ntohl(*(unsigned int *)pkt_data(pkt)); label >>= VR_MPLS_LABEL_SHIFT; if (label >= router->vr_max_labels) { drop_reason = VP_DROP_INVALID_LABEL; goto dropit; } /* Set network header to inner ip header only if unicast */ if (vr_mpls_is_label_mcast(label) == true) { vr_mcast_mpls_input(router, pkt, fmd); return 0; } /* drop the TOStack label */ data = pkt_pull(pkt, VR_MPLS_HDR_LEN); if (!data) { drop_reason = VP_DROP_PULL; goto dropit; } /* this is the new network header and inner network header too*/ pkt_set_network_header(pkt, pkt->vp_data); pkt_set_inner_network_header(pkt, pkt->vp_data); pkt->vp_type = VP_TYPE_IP; nh = router->vr_ilm[label]; if (!nh) { drop_reason = VP_DROP_INVALID_NH; goto dropit; } /* * We are typically looking at interface nexthops, and hence we will * hit the vrf of the destination device. But, labels can also point * to composite nexthops (ECMP being case in point), in which case we * will take the vrf from the nexthop. When everything else fails, we * will forward the packet in the vrf in which it came i.e fabric */ if (nh->nh_vrf >= 0) vrf = nh->nh_vrf; else if (nh->nh_dev) vrf = nh->nh_dev->vif_vrf; else vrf = pkt->vp_if->vif_vrf; ip = (struct vr_ip *)pkt_data(pkt); if (ip->ip_csum == VR_DIAG_IP_CSUM) { pkt->vp_flags |= VP_FLAG_DIAG; } else if (vr_perfr) { pkt->vp_flags |= VP_FLAG_GRO; } nh_output(vrf, pkt, nh, fmd); return 0; dropit: vr_pfree(pkt, drop_reason); return 0; }
int vr_mpls_input(struct vrouter *router, struct vr_packet *pkt, struct vr_forwarding_md *fmd) { int ttl, l2_offset = 0; unsigned int label; unsigned short drop_reason; struct vr_nexthop *nh; struct vr_ip *ip; struct vr_forwarding_md c_fmd; if (!fmd) { vr_init_forwarding_md(&c_fmd); fmd = &c_fmd; } label = ntohl(*(unsigned int *)pkt_data(pkt)); ttl = label & 0xFF; label >>= VR_MPLS_LABEL_SHIFT; if (label >= router->vr_max_labels) { drop_reason = VP_DROP_INVALID_LABEL; goto dropit; } if (--ttl <= 0) { drop_reason = VP_DROP_TTL_EXCEEDED; goto dropit; } ip = (struct vr_ip *)pkt_network_header(pkt); fmd->fmd_outer_src_ip = ip->ip_saddr; vr_forwarding_md_set_label(fmd, label, VR_LABEL_TYPE_MPLS); /* Store the TTL in packet. Will be used for multicast replication */ pkt->vp_ttl = ttl; /* drop the TOStack label */ if (!pkt_pull(pkt, VR_MPLS_HDR_LEN)) { drop_reason = VP_DROP_PULL; goto dropit; } nh = __vrouter_get_label(router, label); if (!nh) { drop_reason = VP_DROP_INVALID_LABEL; goto dropit; } /* * Mark it for GRO. Diag, L2 and multicast nexthops unmark if * required */ if (vr_perfr) pkt->vp_flags |= VP_FLAG_GRO; /* Reset the flags which get defined below */ pkt->vp_flags &= ~VP_FLAG_MULTICAST; fmd->fmd_vlan = VLAN_ID_INVALID; if (nh->nh_family == AF_INET) { ip = (struct vr_ip *)pkt_data(pkt); if (vr_ip_is_ip4(ip)) { pkt->vp_type = VP_TYPE_IP; } else if (vr_ip_is_ip6(ip)) { pkt->vp_type = VP_TYPE_IP6; } else { drop_reason = VP_DROP_INVALID_PROTOCOL; goto dropit; } pkt_set_network_header(pkt, pkt->vp_data); pkt_set_inner_network_header(pkt, pkt->vp_data); } else if (nh->nh_family == AF_BRIDGE) { if (nh->nh_type == NH_COMPOSITE) { if (label >= VR_MAX_UCAST_LABELS) l2_offset = VR_L2_MCAST_CTRL_DATA_LEN + VR_VXLAN_HDR_LEN; } if (vr_pkt_type(pkt, l2_offset, fmd) < 0) { drop_reason = VP_DROP_INVALID_PACKET; goto dropit; } } else { drop_reason = VP_DROP_INVALID_NH; goto dropit; } /* * We are typically looking at interface nexthops, and hence we will * hit the vrf of the destination device. But, labels can also point * to composite nexthops (ECMP being case in point), in which case we * will take the vrf from the nexthop. When everything else fails, we * will forward the packet in the vrf in which it came i.e fabric */ if (nh->nh_vrf >= 0) fmd->fmd_dvrf = nh->nh_vrf; else if (nh->nh_dev) fmd->fmd_dvrf = nh->nh_dev->vif_vrf; else fmd->fmd_dvrf = pkt->vp_if->vif_vrf; nh_output(pkt, nh, fmd); return 0; dropit: vr_pfree(pkt, drop_reason); return 0; }
/* * vr_input is called from linux(host) ingress path. we are not allowed to * sleep here. return value should indicate whether the router consumed the * packet or not. if the router did not consume, host will continue with * its packet processing with the same packet. if the router did consume, * host will not touch the packet again. a return of 0 will tell the handler * that router consumed it, while all other return values are passed as is. * maybe we need a return value to host return mapping, but at a later time ? */ unsigned int vr_input(unsigned short vrf, struct vr_interface *vif, struct vr_packet *pkt) { unsigned char *data = pkt_data(pkt); unsigned char *eth = data; unsigned char *dmac = ð[VR_ETHER_DMAC_OFF]; unsigned short eth_proto; struct vr_vlan_hdr *vlan; struct vrouter *router = vif->vif_router; struct vr_forwarding_md fmd; int reason; if (vif->vif_flags & VIF_FLAG_MIRROR_RX) { vr_init_forwarding_md(&fmd); fmd.fmd_dvrf = vif->vif_vrf; vr_mirror(vif->vif_router, vif->vif_mirror_id, pkt, &fmd); } /* * we will optimise for the most likely case i.e that of IPv4. need * to see what needs to happen for v6 when it comes */ data = pkt_pull(pkt, VR_ETHER_HLEN); if (!data) { vif_drop_pkt(vif, pkt, 1); return 0; } eth_proto = ntohs(*(unsigned short *)(eth + VR_ETHER_PROTO_OFF)); while (eth_proto == VR_ETH_PROTO_VLAN) { vlan = (struct vr_vlan_hdr *)data; eth_proto = ntohs(vlan->vlan_proto); data = pkt_pull(pkt, sizeof(*vlan)); if (!data) { vif_drop_pkt(vif, pkt, 1); return 0; } } vr_init_forwarding_md(&fmd); pkt_set_network_header(pkt, pkt->vp_data); pkt_set_inner_network_header(pkt, pkt->vp_data); if (eth_proto == VR_ETH_PROTO_IP) { if (vr_from_vm_mss_adj && vr_pkt_from_vm_tcp_mss_adj && (vif->vif_type == VIF_TYPE_VIRTUAL)) { if ((reason = vr_pkt_from_vm_tcp_mss_adj(pkt))) { vr_pfree(pkt, reason); return 0; } } return vr_flow_inet_input(router, vrf, pkt, eth_proto, &fmd); } else if (eth_proto == VR_ETH_PROTO_ARP) return vr_arp_input(router, vrf, pkt); /* rest of the stuff is for slow path and we should be ok doing this */ if (well_known_mac(dmac)) return vr_trap(pkt, vrf, AGENT_TRAP_L2_PROTOCOLS, NULL); return vr_default_input(pkt); }