/* * vr_interface_input() is invoked if a packet ingresses an interface. * This function demultiplexes the packet to right input * function depending on the protocols enabled on the VIF */ unsigned int vr_virtual_input(unsigned short vrf, struct vr_interface *vif, struct vr_packet *pkt, unsigned short vlan_id) { struct vr_forwarding_md fmd; vr_init_forwarding_md(&fmd); fmd.fmd_vlan = vlan_id; fmd.fmd_dvrf = vrf; if (vif->vif_flags & VIF_FLAG_MIRROR_RX) { fmd.fmd_dvrf = vif->vif_vrf; vr_mirror(vif->vif_router, vif->vif_mirror_id, pkt, &fmd); } if (vr_pkt_type(pkt, 0, &fmd) < 0) { vif_drop_pkt(vif, pkt, 1); return 0; } /* * we really do not allow any broadcast packets from interfaces * that are part of transparent service chain, since transparent * service chain bridges packets across vrf (and hence loops can * happen) */ if ((pkt->vp_flags & VP_FLAG_MULTICAST) && (vif_is_service(vif))) { vif_drop_pkt(vif, pkt, 1); return 0; } if (!vr_flow_forward(pkt->vp_if->vif_router, pkt, &fmd)) return 0; vr_bridge_input(vif->vif_router, pkt, &fmd); return 0; }
static int vr_flow_lookup(struct vrouter *router, unsigned short vrf, struct vr_flow_key *key, struct vr_packet *pkt, unsigned short proto, struct vr_forwarding_md *fmd) { unsigned int fe_index; struct vr_flow_entry *flow_e; pkt->vp_flags |= VP_FLAG_FLOW_SET; flow_e = vr_find_flow(router, key, &fe_index); if (!flow_e) { if (pkt->vp_nh && (pkt->vp_nh->nh_flags & NH_FLAG_RELAXED_POLICY)) return vr_flow_forward(vrf, pkt, proto, fmd); if (vr_flow_table_hold_count(router) > VR_MAX_FLOW_TABLE_HOLD_COUNT) { vr_pfree(pkt, VP_DROP_FLOW_UNUSABLE); return 0; } flow_e = vr_find_free_entry(router, key, &fe_index); if (!flow_e) { vr_pfree(pkt, VP_DROP_FLOW_TABLE_FULL); return 0; } flow_e->fe_vrf = vrf; /* mark as hold */ vr_flow_entry_set_hold(router, flow_e); vr_do_flow_action(router, flow_e, fe_index, pkt, proto, fmd); return 0; } return vr_do_flow_action(router, flow_e, fe_index, pkt, proto, fmd); }
unsigned int vr_flow_inet_input(struct vrouter *router, unsigned short vrf, struct vr_packet *pkt, unsigned short proto, struct vr_forwarding_md *fmd) { struct vr_flow_key key, *key_p = &key; struct vr_ip *ip, *icmp_pl_ip = NULL; struct vr_fragment *frag; unsigned int flow_parse_res; unsigned int trap_res = 0; unsigned int sip, dip; unsigned short *t_hdr, sport, dport; unsigned char ip_proto; struct vr_icmp *icmph; /* * interface is in a mode where it wants all packets to be received * without doing lookups to figure out whether packets were destined * to me or not */ if (pkt->vp_flags & VP_FLAG_TO_ME) return vr_ip_rcv(router, pkt, fmd); ip = (struct vr_ip *)pkt_network_header(pkt); ip_proto = ip->ip_proto; /* if the packet is not a fragment, we easily know the sport, and dport */ if (vr_ip_transport_header_valid(ip)) { t_hdr = (unsigned short *)((char *)ip + (ip->ip_hl * 4)); if (ip_proto == VR_IP_PROTO_ICMP) { icmph = (struct vr_icmp *)t_hdr; if (vr_icmp_error(icmph)) { icmp_pl_ip = (struct vr_ip *)(icmph + 1); ip_proto = icmp_pl_ip->ip_proto; t_hdr = (unsigned short *)((char *)icmp_pl_ip + (icmp_pl_ip->ip_hl * 4)); if (ip_proto == VR_IP_PROTO_ICMP) icmph = (struct vr_icmp *)t_hdr; } } if (ip_proto == VR_IP_PROTO_ICMP) { if (icmph->icmp_type == VR_ICMP_TYPE_ECHO || icmph->icmp_type == VR_ICMP_TYPE_ECHO_REPLY) { sport = icmph->icmp_eid; dport = VR_ICMP_TYPE_ECHO_REPLY; } else { sport = 0; dport = icmph->icmp_type; } } else { if (icmp_pl_ip) { sport = *(t_hdr + 1); dport = *t_hdr; } else { sport = *t_hdr; dport = *(t_hdr + 1); } } } else { /* ...else, we need to get it from somewhere */ flow_parse_res = vr_flow_parse(router, NULL, pkt, &trap_res); /* ...and it really matters only if we need to do a flow lookup */ if (flow_parse_res == VR_FLOW_LOOKUP) { frag = vr_fragment_get(router, vrf, ip); if (!frag) { vr_pfree(pkt, VP_DROP_FRAGMENTS); return 0; } sport = frag->f_sport; dport = frag->f_dport; if (vr_ip_fragment_tail(ip)) vr_fragment_del(frag); } else { /* * since there is no other way of deriving a key, set the * key_p to NULL, indicating to code below that there is * indeed no need for flow lookup */ key_p = NULL; } } if (key_p) { /* we have everything to make a key */ if (icmp_pl_ip) { sip = icmp_pl_ip->ip_daddr; dip = icmp_pl_ip->ip_saddr; } else { sip = ip->ip_saddr; dip = ip->ip_daddr; } vr_get_flow_key(key_p, fmd->fmd_vlan, pkt, sip, dip, ip_proto, sport, dport); flow_parse_res = vr_flow_parse(router, key_p, pkt, &trap_res); if (flow_parse_res == VR_FLOW_LOOKUP && vr_ip_fragment_head(ip)) vr_fragment_add(router, vrf, ip, key_p->key_src_port, key_p->key_dst_port); if (flow_parse_res == VR_FLOW_BYPASS) { return vr_flow_forward(vrf, pkt, proto, fmd); } else if (flow_parse_res == VR_FLOW_TRAP) { return vr_trap(pkt, vrf, trap_res, NULL); } return vr_flow_lookup(router, vrf, key_p, pkt, proto, fmd); } /* * ...come here, when there is not enough information to do a * flow lookup */ return vr_flow_forward(vrf, pkt, proto, fmd); }
static int vr_flow_action(struct vrouter *router, struct vr_flow_entry *fe, unsigned int index, struct vr_packet *pkt, unsigned short proto, struct vr_forwarding_md *fmd) { int ret = 0, valid_src; unsigned short vrf; struct vr_forwarding_md mirror_fmd; struct vr_nexthop *src_nh; struct vr_packet *pkt_clone; vrf = 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 */ vr_flow_set_forwarding_md(router, fe, index, fmd); src_nh = __vrouter_get_nexthop(router, fe->fe_src_nh_index); if (!src_nh) { vr_pfree(pkt, VP_DROP_INVALID_NH); return 0; } if (src_nh->nh_validate_src) { valid_src = src_nh->nh_validate_src(vrf, pkt, src_nh, fmd); if (valid_src == NH_SOURCE_INVALID) { vr_pfree(pkt, VP_DROP_INVALID_SOURCE); return 0; } 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, vrf, AGENT_TRAP_ECMP_RESOLVE, &fmd->fmd_flow_index); } } } } if (fe->fe_flags & VR_FLOW_FLAG_VRFT) vrf = fe->fe_dvrf; 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); break; case VR_FLOW_ACTION_FORWARD: ret = vr_flow_forward(vrf, pkt, proto, fmd); break; case VR_FLOW_ACTION_NAT: ret = vr_flow_nat(vrf, fe, pkt, proto, fmd); break; default: vr_pfree(pkt, VP_DROP_FLOW_ACTION_INVALID); break; } return ret; }
static int vr_flow_nat(unsigned short vrf, struct vr_flow_entry *fe, struct vr_packet *pkt, unsigned short proto, struct vr_forwarding_md *fmd) { unsigned int ip_inc, inc = 0; unsigned short *t_sport, *t_dport; struct vrouter *router = pkt->vp_if->vif_router; struct vr_flow_entry *rfe; struct vr_ip *ip, *icmp_pl_ip; struct vr_icmp *icmph; bool hdr_update = false; if (fe->fe_rflow < 0) goto drop; rfe = vr_get_flow_entry(router, fe->fe_rflow); if (!rfe) goto drop; ip = (struct vr_ip *)pkt_data(pkt); if (ip->ip_proto == VR_IP_PROTO_ICMP) { icmph = (struct vr_icmp *)((unsigned char *)ip + (ip->ip_hl * 4)); if (vr_icmp_error(icmph)) { icmp_pl_ip = (struct vr_ip *)(icmph + 1); if (fe->fe_flags & VR_FLOW_FLAG_SNAT) { icmp_pl_ip->ip_daddr = rfe->fe_key.key_dest_ip; hdr_update = true; } if (fe->fe_flags & VR_FLOW_FLAG_DNAT) { icmp_pl_ip->ip_saddr = rfe->fe_key.key_src_ip; hdr_update = true; } if (hdr_update) icmp_pl_ip->ip_csum = vr_ip_csum(icmp_pl_ip); t_sport = (unsigned short *)((unsigned char *)icmp_pl_ip + (icmp_pl_ip->ip_hl * 4)); t_dport = t_sport + 1; if (fe->fe_flags & VR_FLOW_FLAG_SPAT) *t_dport = rfe->fe_key.key_dst_port; if (fe->fe_flags & VR_FLOW_FLAG_DPAT) *t_sport = rfe->fe_key.key_src_port; } } if ((fe->fe_flags & VR_FLOW_FLAG_SNAT) && (ip->ip_saddr == fe->fe_key.key_src_ip)) { vr_incremental_diff(ip->ip_saddr, rfe->fe_key.key_dest_ip, &inc); ip->ip_saddr = rfe->fe_key.key_dest_ip; } if (fe->fe_flags & VR_FLOW_FLAG_DNAT) { vr_incremental_diff(ip->ip_daddr, rfe->fe_key.key_src_ip, &inc); ip->ip_daddr = rfe->fe_key.key_src_ip; } ip_inc = inc; if (vr_ip_transport_header_valid(ip)) { t_sport = (unsigned short *)((unsigned char *)ip + (ip->ip_hl * 4)); t_dport = t_sport + 1; if (fe->fe_flags & VR_FLOW_FLAG_SPAT) { vr_incremental_diff(*t_sport, rfe->fe_key.key_dst_port, &inc); *t_sport = rfe->fe_key.key_dst_port; } if (fe->fe_flags & VR_FLOW_FLAG_DPAT) { vr_incremental_diff(*t_dport, rfe->fe_key.key_src_port, &inc); *t_dport = rfe->fe_key.key_src_port; } } #ifdef VROUTER_CONFIG_DIAG if (ip->ip_csum != VR_DIAG_IP_CSUM) vr_ip_update_csum(pkt, ip_inc, inc); #else vr_ip_update_csum(pkt, ip_inc, inc); #endif /* * If VRF is translated lets chose a new nexthop */ if ((fe->fe_flags & VR_FLOW_FLAG_VRFT) && pkt->vp_nh && pkt->vp_nh->nh_vrf != vrf) pkt->vp_nh = NULL; return vr_flow_forward(vrf, pkt, proto, fmd); drop: vr_pfree(pkt, VP_DROP_FLOW_NAT_NO_RFLOW); return 0; }